diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /gfx/harfbuzz | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/harfbuzz')
440 files changed, 163133 insertions, 0 deletions
diff --git a/gfx/harfbuzz/AUTHORS b/gfx/harfbuzz/AUTHORS new file mode 100644 index 0000000000..83c0c66f99 --- /dev/null +++ b/gfx/harfbuzz/AUTHORS @@ -0,0 +1,14 @@ +Behdad Esfahbod +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/gfx/harfbuzz/COPYING b/gfx/harfbuzz/COPYING new file mode 100644 index 0000000000..1dd917e9f2 --- /dev/null +++ b/gfx/harfbuzz/COPYING @@ -0,0 +1,42 @@ +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-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 + +For full copyright notices consult the individual files in the package. + + +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. diff --git a/gfx/harfbuzz/Makefile.am b/gfx/harfbuzz/Makefile.am new file mode 100644 index 0000000000..ec53d67bd0 --- /dev/null +++ b/gfx/harfbuzz/Makefile.am @@ -0,0 +1,83 @@ +# Process this file with automake to produce Makefile.in + +NULL = + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src util test perf docs + +EXTRA_DIST = \ + autogen.sh \ + harfbuzz.doap \ + README.md \ + README.python.md \ + BUILD.md \ + CONFIG.md \ + RELEASING.md \ + TESTING.md \ + CMakeLists.txt \ + replace-enum-strings.cmake \ + meson.build \ + meson_options.txt \ + subprojects/cairo.wrap \ + subprojects/freetype2.wrap \ + subprojects/glib.wrap \ + subprojects/google-benchmark.wrap \ + subprojects/ragel.wrap \ + subprojects/packagefiles/ragel/meson.build \ + mingw-configure.sh \ + xkcd.png \ + $(NULL) + +MAINTAINERCLEANFILES = \ + $(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \ + $(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL) \ + $(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \ + $(srcdir)/INSTALL \ + $(srcdir)/ChangeLog \ + $(srcdir)/gtk-doc.make \ + $(srcdir)/m4/gtk-doc.m4 \ + $(NULL) + + +# +# ChangeLog generation +# +CHANGELOG_RANGE = +ChangeLog: $(srcdir)/ChangeLog +$(srcdir)/ChangeLog: + $(AM_V_GEN) if test -d "$(top_srcdir)/.git"; then \ + (GIT_DIR=$(top_srcdir)/.git \ + $(GIT) log $(CHANGELOG_RANGE) --stat) > $@.tmp \ + && mv -f $@.tmp "$(srcdir)/ChangeLog" \ + || ($(RM) $@.tmp; \ + echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \ + (test -f $@ || echo git-log is required to generate this file >> "$(srcdir)/$@")); \ + else \ + test -f $@ || \ + (echo A git checkout and git-log is required to generate ChangeLog >&2 && \ + echo A git checkout and git-log is required to generate this file >> "$(srcdir)/$@"); \ + fi +.PHONY: ChangeLog $(srcdir)/ChangeLog + + +# +# Release engineering +# + +DISTCHECK_CONFIGURE_FLAGS = \ + --enable-gtk-doc \ + --disable-doc-cross-references \ + --with-gobject \ + --enable-introspection \ + $(NULL) + +# TAR_OPTIONS is not set as env var for 'make dist'. How to fix that? +TAR_OPTIONS = --owner=0 --group=0 + +dist-hook: dist-clear-sticky-bits +# Clean up any sticky bits we may inherit from parent dir +dist-clear-sticky-bits: + chmod -R a-s $(distdir) + +-include $(top_srcdir)/git.mk diff --git a/gfx/harfbuzz/NEWS b/gfx/harfbuzz/NEWS new file mode 100644 index 0000000000..386a106736 --- /dev/null +++ b/gfx/harfbuzz/NEWS @@ -0,0 +1,3399 @@ +Overview of changes leading to 8.3.0 +Saturday, November 11, 2023 +==================================== +- Improve memory barrier to fix potential segfaults. +- Various build fixes. +- Various subsetting and instancing fixes. +- Rename “hb-subset” option “--instance” to “--variations” to match the other + tools. Old option is kept as an alias. + +- New API: +HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION + +- Deprecated API: +HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION + +Overview of changes leading to 8.2.2 +Wednesday, October 18, 2023 +“From the river to the sea, Palestine will be free” +==================================== +- Fix regression from 8.1.0 in shaping fonts with duplicate feature tags. +- Fix regression from 8.2.0 in parsing CSS-style feature strings. +- Variable fonts instanciation now handles more tables. +- Various CMake build improvements. +- various fixes to build without errors with gcc 4.9.2. + + +Overview of changes leading to 8.2.1 +Monday, September 18, 2023 +==================================== +- Unicode 15.1 support. + + +Overview of changes leading to 8.2.0 +Friday, September 8, 2023 +==================================== +- Various build and fuzzing fixes +- Improvements to COLRv1 painting. + +- New API: ++hb_paint_color_glyph_func_t ++hb_paint_funcs_set_color_glyph_func ++hb_paint_color_glyph + + +Overview of changes leading to 8.1.1 +Wednesday, August 2, 2023 +==================================== +- Fix shaping of contextual rules at the end of string, introduced in 8.1.0 +- Fix stack-overflow in repacker with malicious fonts. +- 30% speed up loading Noto Duployan font. + + +Overview of changes leading to 8.1.0 +Tuesday, August 1, 2023 +==================================== +- Fix long-standing build issue with the AIX compiler and older Apple clang. + +- Revert optimization that could cause timeout during subsetting with malicious fonts. + +- More optimization work: + - 45% speed up in shaping Noto Duployan font. + - 10% speed up in subsetting Noto Duployan font. + - Another 8% speed up in shaping Gulzar. + - 5% speed up in loading Roboto. + +- New API: ++hb_ot_layout_collect_features_map() + + +Overview of changes leading to 8.0.1 +Wednesday, July 12, 2023 +==================================== +- Build fix on 32-bit ARM. + +- More speed optimizations: + - 60% speed up in retain-gid (used for IFT) subsetting of SourceHanSans-VF. + - 16% speed up in retain-gid (used for IFT) subsetting of NotoSansCJKkr. + - 38% speed up in subsetting (beyond-64k) mega-merged Noto. + + +Overview of changes leading to 8.0.0 +Sunday, July 9, 2023 +==================================== +- New, experimental, WebAssembly (WASM) shaper, that provides greater + flexibility over OpenType/AAT/Graphite shaping, using WebAssembly embedded + inside the font file. Currently WASM shaper is disabled by default and needs + to be enabled at build time. For details, see: + + https://github.com/harfbuzz/harfbuzz/blob/main/docs/wasm-shaper.md + + For example fonts making use of the WASM shaper, see: + + https://github.com/harfbuzz/harfbuzz-wasm-examples + +- Improvements to Experimental features introduced in earlier releases: + - Support for subsetting beyond-64k and VarComposites fonts. + - Support for instancing variable fonts with cubic “glyf” table. + +- Many big speed optimizations: + - Up to 89% speedup loading variable fonts for shaping. + - Up to 88% speedup in small subsets of large (eg. CJK) fonts (both TTF and + OTF), essential for Incremental Font Transfer (IFT). + - Over 50% speedup in loading Roboto font for shaping. + - Up to 40% speed up in loading (sanitizing) complex fonts. + - 30% speed up in shaping Gulzar font. + - Over 25% speedup in glyph loading Roboto font. + - 10% speed up loading glyph shapes in VarComposite Hangul font. + - hb-hashmap optimizations & hashing improvements. + +- New macro HB_ALWAYS_INLINE. HarfBuzz now inlines functions more aggressively, + which results in some speedup at the expense of bigger code size. To disable + this feature define the macro to just inline. + +- New API: ++HB_CODEPOINT_INVALID ++hb_ot_layout_get_baseline2() ++hb_ot_layout_get_baseline_with_fallback2() ++hb_ot_layout_get_font_extents() ++hb_ot_layout_get_font_extents2() ++hb_subset_input_set_axis_range() + + +Overview of changes leading to 7.3.0 +Tuesday, May 9, 2023 +==================================== +- Speedup applying glyph variation in VarComposites fonts (over 40% speedup). + (Behdad Esfahbod) +- Speedup instancing some fonts (over 20% speedup in instancing RobotoFlex). + (Behdad Esfahbod) +- Speedup shaping some fonts (over 30% speedup in shaping Roboto). + (Behdad Esfahbod) +- Support subsetting VarComposites and beyond-64k fonts. (Behdad Esfahbod) +- New configuration macro HB_MINIMIZE_MEMORY_USAGE to favor optimizing memory + usage over speed. (Behdad Esfahbod) +- Supporting setting the mapping between old and new glyph indices during + subsetting. (Garret Rieger) +- Various fixes and improvements. + (Behdad Esfahbod, Denis Rochette, Garret Rieger, Han Seung Min, Qunxin Liu) + +- New API: ++hb_subset_input_old_to_new_glyph_mapping() + + +Overview of changes leading to 7.2.0 +Thursday, April 27, 2023 +==================================== +- Add Tifinagh to the list of scripts that can natively be either right-to-left + or left-to-right, to improve handling of its glyph positioning. + (Simon Cozens) +- Return also single substitution from hb_ot_layout_lookup_get_glyph_alternates() + (Behdad Esfahbod) +- Fix 4.2.0 regression in applying across syllables in syllabic scripts. + (Behdad Esfahbod) +- Add flag to avoid glyph substitution closure during subsetting, and the + corresponding “--no-layout-closure” option to “hb-subset” command line tool. + (Garret Rieger) +- Support instancing COLRv1 table. (Qunxin Liu) +- Don’t drop used user-defined name table entries during subsetting. + (Qunxin Liu) +- Optimize handling of “gvar” table. (Behdad Esfahbod) +- Various subsetter bug fixes and improvements. (Garret Rieger, Qunxin Liu) +- Various documentation improvements. (Behdad Esfahbod, Josef Friedrich) + +- New API: ++HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE ++HB_UNICODE_COMBINING_CLASS_CCC132 + +- Deprecated API: ++HB_UNICODE_COMBINING_CLASS_CCC133 + + +Overview of changes leading to 7.1.0 +Friday, March 3, 2023 +==================================== +- New experimental hb_shape_justify() API that uses font variations to expand + or shrink the text to a given advance. (Behdad Esfahbod) +- Various build and bug fixes. (Behdad Esfahbod, Garret Rieger, Qunxin Liu) + +- New API: ++hb_font_set_variation() + + +Overview of changes leading to 7.0.1 +Monday, February 20, 2023 +==================================== +- Various build and bug fixes. + + +Overview of changes leading to 7.0.0 +Saturday, February 11, 2023 +==================================== +- New hb-paint API that is designed mainly to paint “COLRv1” glyphs, but can be + also used as a unified API to paint any of the glyph representations + supported by HarfBuzz (B/W outlines, color layers, or color bitmaps). + (Behdad Esfahbod, Matthias Clasen) +- New hb-cairo API for integrating with cairo graphics library. This is provided + as a separate harfbuzz-cairo library. (Behdad Esfahbod, Matthias Clasen) +- Support for instancing “CFF2” table. (Behdad Esfahbod) +- Support font emboldening. (Behdad Esfahbod) +- Support feature ranges with AAT shaping. (Behdad Esfahbod) +- Experimental support to cubic curves in “glyf” table, see + https://github.com/harfbuzz/boring-expansion-spec/blob/main/glyf1-cubicOutlines.md + for spec. (Behdad Esfahbod) +- Various subsetter improvements. (Garret Rieger, Qunxin Liu, Behdad Esfahbod) +- Various documentation improvements. + (Behdad Esfahbod, Matthias Clasen, Khaled Hosny) +- Significantly reduced memory use during shaping. (Behdad Esfahbod) +- Greatly reduced memory use during subsetting “CFF” table. (Behdad Esfahbod) +- New command line utility, hb-info, for querying various font information. + (Behdad Esfahbod, Matthias Clasen) +- New hb-shape/hb-view options: --glyphs, --color-palette, --font-bold, + --font-grade, and --named-instance. (Behdad Esfahbod) +- Miscellaneous fixes and improvements. + (Amir Masoud Abdol, Andres Salomon, Behdad Esfahbod, Chun-wei Fan, + Garret Rieger, Jens Kutilek, Khaled Hosny, Konstantin Käfer, Matthias Clasen, + Nirbheek Chauhan, Pedro J. Estébanez, Qunxin Liu, Sergei Trofimovich) + +- New API: ++HB_FONT_NO_VAR_NAMED_INSTANCE ++HB_PAINT_IMAGE_FORMAT_BGRA ++HB_PAINT_IMAGE_FORMAT_PNG ++HB_PAINT_IMAGE_FORMAT_SVG ++hb_cairo_font_face_create_for_face ++hb_cairo_font_face_create_for_font ++hb_cairo_font_face_get_face ++hb_cairo_font_face_get_font ++hb_cairo_font_face_get_scale_factor ++hb_cairo_font_face_set_font_init_func ++hb_cairo_font_face_set_scale_factor ++hb_cairo_font_init_func_t ++hb_cairo_glyphs_from_buffer ++hb_cairo_scaled_font_get_font ++hb_color_line_get_color_stops ++hb_color_line_get_color_stops_func_t ++hb_color_line_get_extend ++hb_color_line_get_extend_func_t ++hb_color_line_t ++hb_color_stop_t ++hb_draw_funcs_get_empty ++hb_draw_funcs_get_user_data ++hb_draw_funcs_set_user_data ++hb_face_collect_nominal_glyph_mapping ++hb_font_draw_glyph ++hb_font_draw_glyph_func_t ++hb_font_funcs_set_draw_glyph_func ++hb_font_funcs_set_paint_glyph_func ++hb_font_get_synthetic_bold ++hb_font_get_var_named_instance ++hb_font_paint_glyph ++hb_font_paint_glyph_func_t ++hb_font_set_synthetic_bold ++hb_map_keys ++hb_map_next ++hb_map_update ++hb_map_values ++hb_ot_color_glyph_has_paint ++hb_ot_color_has_paint ++hb_ot_layout_script_select_language2 ++hb_ot_name_id_predefined_t ++hb_paint_color ++hb_paint_color_func_t ++hb_paint_composite_mode_t ++hb_paint_custom_palette_color ++hb_paint_custom_palette_color_func_t ++hb_paint_extend_t ++hb_paint_funcs_create ++hb_paint_funcs_destroy ++hb_paint_funcs_get_empty ++hb_paint_funcs_get_user_data ++hb_paint_funcs_is_immutable ++hb_paint_funcs_make_immutable ++hb_paint_funcs_reference ++hb_paint_funcs_set_color_func ++hb_paint_funcs_set_custom_palette_color_func ++hb_paint_funcs_set_image_func ++hb_paint_funcs_set_linear_gradient_func ++hb_paint_funcs_set_pop_clip_func ++hb_paint_funcs_set_pop_group_func ++hb_paint_funcs_set_pop_transform_func ++hb_paint_funcs_set_push_clip_glyph_func ++hb_paint_funcs_set_push_clip_rectangle_func ++hb_paint_funcs_set_push_group_func ++hb_paint_funcs_set_push_transform_func ++hb_paint_funcs_set_radial_gradient_func ++hb_paint_funcs_set_sweep_gradient_func ++hb_paint_funcs_set_user_data ++hb_paint_funcs_t ++hb_paint_image ++hb_paint_image_func_t ++hb_paint_linear_gradient ++hb_paint_linear_gradient_func_t ++hb_paint_pop_clip ++hb_paint_pop_clip_func_t ++hb_paint_pop_group ++hb_paint_pop_group_func_t ++hb_paint_pop_transform ++hb_paint_pop_transform_func_t ++hb_paint_push_clip_glyph ++hb_paint_push_clip_glyph_func_t ++hb_paint_push_clip_rectangle ++hb_paint_push_clip_rectangle_func_t ++hb_paint_push_group ++hb_paint_push_group_func_t ++hb_paint_push_transform ++hb_paint_push_transform_func_t ++hb_paint_radial_gradient ++hb_paint_radial_gradient_func_t ++hb_paint_sweep_gradient ++hb_paint_sweep_gradient_func_t ++hb_set_is_inverted ++hb_subset_input_keep_everything + +- Deprecated API: ++hb_font_funcs_set_glyph_shape_func ++hb_font_get_glyph_shape_func_t ++hb_font_get_glyph_shape + + +Overview of changes leading to 6.0.0 +Friday, December 16, 2022 +==================================== +- A new API have been added to pre-process the face and speed up future + subsetting operations on that face. Provides up to a 95% reduction in + subsetting times when the same face is subset more than once. + + For more details and benchmarks, see: + https://github.com/harfbuzz/harfbuzz/blob/main/docs/subset-preprocessing.md + + (Garret Rieger, Behdad Esfahbod) + +- Shaping have been speedup by skipping entire lookups when the buffer contents + don't intersect with the lookup. Shows up to a 10% speedup in shaping some + fonts. (Behdad Esfahbod) + +- A new experimental feature, “Variable Composites” (enabled by passing + -Dexperimental_api=true to meson), is also featured in this release. + This technology enables drastic compression of fonts in the Chinese, + Japanese, Korean, and other writing systems, by reusing the OpenType Font + Variations technology for encoding “smart components” into the font. + + The specification for these extensions to the font format can be found in: + https://github.com/harfbuzz/boring-expansion-spec/blob/glyf1/glyf1.md + + A test variable-font with ~7160 Hangul syllables derived from the + NotoSerifKR-VF font has been built, with existing OpenType technology, as + well as with the new Variable Composites (VarComposites) technology. The + VarComposites font is over 90% smaller than the OpenType version of the font! + Both fonts can be obtained from the “smarties” repository: + https://github.com/behdad/smarties/tree/3.0/fonts/hangul/serif + + When building HarfBuzz with experimental features enabled, you can test + the “smarties” font with a sample character like this: + + $ hb-view butchered-hangul-serif-smarties-variable.ttf -u AE01 --variations=wght=700 + + (Behdad Esfahbod) + +- The HarfBuzz subsetter can now drop axes by pinning them to specific values + (also referred to as instancing). There are a couple of restrictions + currently: + + - Only works with TrueType (“glyf”) based fonts. “CFF2” fonts are not yet + supported. + - Only supports the case where all axes in a font are pinned. + + (Garret Rieger, Qunxin Liu) + +- Miscellaneous fixes and improvements. + + (Behdad Esfahbod, Christoph Reiter, David Corbett, Eli Schwartz, Garret + Rieger, Joel Auterson, Jordan Petridis, Khaled Hosny, Lorenz Wildberg, + Marco Rebhan, Martin Storsjö, Matthias Clasen, Qunxin Liu, Satadru Pramanik) + + +- New API ++hb_subset_input_pin_axis_location() ++hb_subset_input_pin_axis_to_default() ++hb_subset_preprocess() + + +Overview of changes leading to 5.3.1 +Wednesday, October 19, 2022 +==================================== +- Subsetter repacker fixes. (Garret Rieger) +- Adjust Grapheme clusters for Katakana voiced sound marks. (Behdad Esfahbod) +- New “hb-subset” option “--preprocess-face”. (Garret Rieger) + + +Overview of changes leading to 5.3.0 +Saturday, October 8, 2022 +"Women, Life, Freedom" #MahsaAmini +==================================== +- Don’t add glyphs from dropped MATH or COLR tables to the subset glyphs. + (Khaled Hosny) +- Map “rlig” to appropriate AAT feature selectors. (Jonathan Kew) +- Update USE data files to latest version. (David Corbett) +- Check “CBDT” extents first before outline tables, to help with fonts that + also include an empty “glyf” table. (Khaled Hosny) +- More work towards variable font instancing in the subsetter. (Qunxin Liu) +- Subsetter repacker improvements. (Garret Rieger) +- New API: ++hb_ot_layout_lookup_get_optical_bound() ++hb_face_builder_sort_tables() + + +Overview of changes leading to 5.2.0 +Saturday, September 17, 2022 +==================================== +- Fix regressions in hb-ft font functions for FT_Face’s with transformation + matrix. (Behdad Esfahbod) +- The experimental hb-repacker API now supports splitting several GPOS subtable + types when needed. (Garret Rieger) +- The HarfBuzz extensions to OpenType font format are now opt-in behind + build-time flags. (Behdad Esfahbod) +- The experimental hb-subset variable fonts instantiation API can now + instantiate more font tables and arbitrary axis locations. (Qunxin Liu) +- Unicode 15 support. (David Corbett) +- Various documentation improvements. (Behdad Esfahbod, Matthias Clasen) +- The hb-view command line tool now detects WezTerm inline images support. + (Wez Furlong) +- Fix FreeType and ICU dependency lookup with meson. (Xavier Claessens) + +- New API: ++HB_SCRIPT_KAWI ++HB_SCRIPT_NAG_MUNDARI + + +Overview of changes leading to 5.1.0 +Sunday, July 31, 2022 +==================================== +- More extensive buffer tracing messages. (Behdad Esfahbod) +- Fix hb-ft regression in bitmap fonts rendering. (Behdad Esfahbod) +- Support extension promotion of lookups in hb-subset-repacker. (Garret Rieger) +- A new HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL for scripts that use elongation + (e.g. Arabic) to signify where it is safe to insert tatweel glyph without + interrupting shaping. (Behdad Esfahbod) +- Add “--safe-to-insert-tatweel” to “hb-shape” tool. (Behdad Esfahbod) + +- New API ++HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL ++HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL + + +Overview of changes leading to 5.0.1 +Saturday, July 23, 2022 +==================================== +- Fix version 2 “avar” table with hb-ft. (Behdad Esfahbod) + + +Overview of changes leading to 5.0.0 +Saturday, July 23, 2022 +==================================== +- Support fonts with more than 65535 glyphs in “GDEF”, “GSUB”, and “GPOS” + tables. This is part of https://github.com/be-fonts/boring-expansion-spec to + extend OpenType in a backward-compatible way. + (Behdad Esfahbod, Garret Rieger) +- Complete support for more than 65535 glyphs in “glyf” table that started in + 4.0.0 release. Part of boring-expansion-spec. (Behdad Esfahbod) +- Support version 2 of “avar” table. Part of boring-expansion-spec. + (Behdad Esfahbod) +- Fix mark attachment on multiple substitutions in some cases. + (Behdad Esfahbod) +- Fix application of “calt”, “rclt”, and “ccmp” features to better match + Uniscribe behaviour with some Arabic fonts. (Behdad Esfahbod) +- Improvement to interaction between multiple cursive attachments. + (Behdad Esfahbod) +- Improve multiple mark interactions in Hebrew. (Behdad Esfahbod) +- Implement language-specific forms in AAT shaping. (Behdad Esfahbod) +- Fix variation of “VORG” table. (Behdad Esfahbod) +- Support for specific script tags to be retained in the subsetter, and add + “--layout-scripts” option to “hb-subset” tool. (Garret Rieger) +- Accept space as delimiter for --features/--variations in command line tools. +- Improve subsetting of “COLR” table. (Qunxin Liu) +- Improved fuzzing coverage for ot-math API. (Frédéric Wang) +- Fix “kern” table version 2 (AAT) sanitization on 32-bit systems. + (Behdad Esfahbod) +- Allow negative glyph advances from “graphite2” shaper. (Stephan Bergmann) +- Implement loading (color) bitmap fonts with hb-ft. (Behdad Esfahbod) +- Fix regression in hb-ft when changing font size. (Behdad Esfahbod) +- Fix build on GCC < 7. (Kleis Auke Wolthuizen) +- Dynamically load dwrite.dll on windows if “directwrite” shaper is enabled. + (Luca Bacci) +- Provide a single-file harfbuzz-subset.cc file for easier alternate building + of hb-subset library, similar to harfbuzz.cc. (Khaled Hosny) + +- New API ++HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG ++hb_language_matches() + + +Overview of changes leading to 4.4.1 +Wednesday, June 29, 2022 +==================================== +- Fix test failure with some compilers. +- Fix Telugu and Kannada kerning regression. + + +Overview of changes leading to 4.4.0 +Monday, June 27, 2022 +==================================== +- Caching of variable fonts shaping, in particular when using HarfBuzz’s own + font loading functions (ot). Bringing performance of variable shaping in par + with non-variable fonts shaping. (Behdad Esfahbod) +- Caching of format 2 “Contextual Substitution” and “Chained Contexts + Substitution” lookups. Resulting in up to 20% speedup of lookup-heavy fonts + like Gulzar or Noto Nastaliq Urdu. (Behdad Esfahbod) +- Improved ANSI output from hb-view. (Behdad Esfahbod) +- Support for shaping legacy, pre-OpenType Windows 3.1-era, Arabic fonts that + relied on a fixed PUA encoding. (Khaled Hosny, Behdad Esfahbod) +- Sinhala script is now shaped by the USE shaper instead of “indic” one. + (Behdad Esfahbod, David Corbett) +- Thai shaper improvements. (David Corbett) +- hb-ot-name API supports approximate BCP-47 language matching, for example + asking for “en_US” in a font that has only “en” names will return them. + (Behdad Esfahbod) +- Optimized TrueType glyph shape loading. (Behdad Esfahbod) +- Fix subsetting of HarfBuzz faces created via hb_face_create_for_tables(). + (Garret Rieger) +- Add 32 bit var store support to the subsetter. (Garret Rieger) + +- New API ++HB_BUFFER_FLAG_DEFINED ++HB_BUFFER_SERIALIZE_FLAG_DEFINED ++hb_font_changed() ++hb_font_get_serial() ++hb_ft_hb_font_changed() ++hb_set_hash() ++hb_map_copy() ++hb_map_hash() + + +Overview of changes leading to 4.3.0 +Friday, May 20, 2022 +==================================== +- Major speed up in loading and subsetting fonts, especially in + handling CFF table. Subsetting some fonts is now 3 times faster. + (Behdad Esfahbod, Garret Rieger) +- Speed up blending CFF2 table. (Behdad Esfahbod) +- Speed up hb_ot_tags_from_language(). (Behdad Esfahbod, David Corbett) +- Fix USE classification of U+10A38 to fix multiple marks on single Kharoshthi + base. (David Corbett) +- Fix parsing of empty CFF Index. (Behdad Esfahbod) +- Fix subsetting CPAL table with partial palette overlaps. (Garret Rieger) + +- New API ++hb_map_is_equal() (Behdad Esfahbod) + + +Overview of changes leading to 4.2.1 +Sunday, April 24, 2022 +==================================== +- Make sure hb_blob_create_from_file_or_fail() always returns nullptr in case + of failure and not empty blob sometimes. (Khaled Hosny) +- Add --passthrough-tables option to hb-subset. (Cosimo Lupo) +- Reinstate a pause after basic features in Khmer shaper, fixing a regression + introduced in previous release. (Behdad Esfahbod) +- Better handling of Regional_Indicator when shaped with RTL-native scripts, + reverting earlier fix that caused regressions in AAT shaping. (Behdad Esfahbod) + + +Overview of changes leading to 4.2.0 +Wednesday, March 30, 2022 +==================================== +- Source code reorganization, splitting large hb-ot-layout files into smaller, + per-subtable ones under OT/Layout/*. Code for more tables will follow suit in + later releases. (Garret Rieger, Behdad Esfahbod) +- Revert Indic shaper change in previous release that broke some fonts and + instead make per-syllable restriction of “GSUB” application limited to + script-specific Indic features, while applying them and discretionary + features in one go. (Behdad Esfahbod) +- Fix decoding of private in gvar table. (Behdad Esfahbod) +- Fix handling of contextual lookups that delete too many glyphs. (Behdad Esfahbod) +- Make “morx” deleted glyphs don’t block “GPOS” application. (Behdad Esfahbod) +- Various build fixes. (Chun-wei Fan, Khaled Hosny) + +- New API ++hb_set_next_many() (Andrew John) + + +Overview of changes leading to 4.1.0 +Wednesday, March 23, 2022 +==================================== +- Various OSS-Fuzz fixes. (Behdad Esfahbod) +- Make fallback vertical-origin match FreeType’s. (Behdad Esfahbod) +- Treat visible viramas like dependent vowels in USE shaper. (David Corbett) +- Apply presentation forms features and discretionary features in one go in + Indic shaper, which seems to match Uniscribe and CoreText behaviour. + (Behdad Esfahbod, David Corbett) +- Various bug fixes. + +- New API ++hb_set_add_sorted_array() (Andrew John) + + +Overview of changes leading to 4.0.1 +Friday, March 11, 2022 +==================================== +- Update OpenType to AAT mappings for “hist” and “vrtr” features. + (Florian Pircher) +- Update IANA Language Subtag Registry to 2022-03-02. (David Corbett) +- Update USE shaper to allow any non-numeric tail in a symbol cluster, and + remove obsolete data overrides. (David Corbett) +- Fix handling of baseline variations to return correctly scaled values. + (Matthias Clasen) +- A new experimental hb_subset_repack_or_fail() to repack an array of objects, + eliminating offset overflows. The API is not available unless HarfBuzz is + built with experimental APIs enabled. (Qunxin Liu) + +- New experimental API ++hb_link_t ++hb_object_t ++hb_subset_repack_or_fail() + + +Overview of changes leading to 4.0.0 +Tuesday, March 1, 2022 +==================================== +- New public API to create subset plan and gather information on things like + glyph mappings in the final subset. The plan can then be passed on to perform + the subsetting operation. (Garret Rieger) +- Draw API for extracting glyph shapes have been extended and finalized and is + no longer an experimental API. The draw API supports glyf, CFF and CFF2 + glyph outlines tables, and applies variation settings set on the font as well + as synthetic slant. The new public API is not backward compatible with the + previous, non-public, experimental API. (Behdad Esfahbod) +- The hb-view tool will use HarfBuzz draw API to render the glyphs instead of + cairo-ft when compiled with Cairo 1.17.5 or newer, setting HB_DRAW + environment variable to 1 or 0 will force using or not use the draw API, + respectively. (Behdad Esfahbod) +- The hb-shape and hb-view tools now default to using HarfBuzz’s own font + loading functions (ot) instead of FreeType ones (ft). They also have a new + option, --font-slant, to apply synthetic slant to the font. (Behdad Esfahbod) +- HarfBuzz now supports more than 65535 (the OpenType limit) glyph shapes and + metrics. See https://github.com/be-fonts/boring-expansion-spec/issues/6 and + https://github.com/be-fonts/boring-expansion-spec/issues/7 for details. + (Behdad Esfahbod) +- New API to get the dominant horizontal baseline tag for a given script. + (Behdad Esfahbod) +- New API to get the baseline positions from the font, and synthesize missing + ones. As well as new API to get font metrics and synthesize missing ones. + (Matthias Clasen) +- Improvements to finding dependencies on Windows when building with Visual + Studio. (Chun-wei Fan) +- New buffer flag, HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT, that must be set + during shaping for HB_GLYPH_FLAG_UNSAFE_TO_CONCAT flag to be reliably + produced. This is to limit the performance hit of producing this flag to when + it is actually needed. (Behdad Esfahbod) +- Documentation improvements. (Matthias Clasen) + +- New API + - General: + +HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT + +hb_var_num_t + + - Draw: + +hb_draw_funcs_t + +hb_draw_funcs_create() + +hb_draw_funcs_reference() + +hb_draw_funcs_destroy() + +hb_draw_funcs_is_immutable() + +hb_draw_funcs_make_immutable() + +hb_draw_move_to_func_t + +hb_draw_funcs_set_move_to_func() + +hb_draw_line_to_func_t + +hb_draw_funcs_set_line_to_func() + +hb_draw_quadratic_to_func_t + +hb_draw_funcs_set_quadratic_to_func() + +hb_draw_cubic_to_func_t + +hb_draw_funcs_set_cubic_to_func() + +hb_draw_close_path_func_t + +hb_draw_funcs_set_close_path_func() + +hb_draw_state_t + +HB_DRAW_STATE_DEFAULT + +hb_draw_move_to() + +hb_draw_line_to() + +hb_draw_quadratic_to() + +hb_draw_cubic_to() + +hb_draw_close_path() + +hb_font_get_glyph_shape_func_t + +hb_font_funcs_set_glyph_shape_func() + +hb_font_get_glyph_shape() + + - OpenType layout + +HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL + +HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL + +hb_ot_layout_get_horizontal_baseline_tag_for_script() + +hb_ot_layout_get_baseline_with_fallback() + + - Metrics: + +hb_ot_metrics_get_position_with_fallback() + + - Subset: + +hb_subset_plan_t + +hb_subset_plan_create_or_fail() + +hb_subset_plan_reference() + +hb_subset_plan_destroy() + +hb_subset_plan_set_user_data() + +hb_subset_plan_get_user_data() + +hb_subset_plan_execute_or_fail() + +hb_subset_plan_unicode_to_old_glyph_mapping() + +hb_subset_plan_new_to_old_glyph_mapping() + +hb_subset_plan_old_to_new_glyph_mapping() + + +Overview of changes leading to 3.4.0 +Sunday, February 13, 2022 +==================================== +- Perform sanity checks on shaping results is now part of “harfbuzz” library + and can be enabled by setting the buffer flag HB_BUFFER_FLAG_VERIFY. + (Behdad Esfahbod) +- Arabic Mark Transient Reordering Algorithm have been updated to revision 6. + (Khaled Hosny) +- ISO 15924 code for mathematical notation, ‘Zmth’, now maps to the OpenType + ‘math’ tag. (Alexis King) +- It is now possible to get at once all math kerning values for a given glyph + at a given corner. (Alexis King) +- Fix locale_t portability issues on systems the typedef’s it to a void + pointer. (Behdad Esfahbod) + +- New API: ++HB_BUFFER_FLAG_VERIFY ++HB_OT_TAG_MATH_SCRIPT ++HB_SCRIPT_MATH ++hb_ot_math_kern_entry_t ++hb_ot_math_get_glyph_kernings() + +- Deprecated API ++HB_OT_MATH_SCRIPT + + +Overview of changes leading to 3.3.2 +Sunday, February 6, 2022 +==================================== +- Revert splitting of pair positioning values introduced in 3.3.0 as it proved + problematic. (Behdad Esfahbod) + + +Overview of changes leading to 3.3.1 +Monday, January 31, 2022 +==================================== +- Fix heap-use-after-free in harfbuzz-subset introduced in previous release. + (Garret Rieger) + + +Overview of changes leading to 3.3.0 +Monday, January 31, 2022 +==================================== +- Improved documentation. (Matthias Clasen) +- Internal code cleanup, using C++ standard library more. (Behdad Esfahbod) +- The low 16-bits of face index will be used by hb_face_create() to select a + face inside a font collection file format, while the high 16-bits will be + used by hb_font_create() to load the named instance. (Behdad Esfahbod) +- Glyph positions and other font metrics now apply synthetic slant set by + hb_font_set_synthetic_slant(), for improved positioning for synthetically + slanted fonts. (Behdad Esfahbod) +- Fixed unintentional locale dependency in hb_variation_to_string() for decimal + point representation. (Matthias Clasen) +- When applying pair positioning (kerning) the positioning value is split + between the two sides of the pair for improved cursor positioning between + such pairs. (Behdad Esfahbod) +- Introduced new HB_GLYPH_FLAG_UNSAFE_TO_CONCAT, to be used in conjunction + with HB_GLYPH_FLAG_UNSAFE_TO_BREAK for optimizing re-shaping during line + breaking. Check the documentation for further details. (Behdad Esfahbod) +- Improved handling of macrolanguages when mapping BCP 47 codes to OpenType + tags. (David Corbett) + +- New API: ++HB_GLYPH_FLAG_UNSAFE_TO_CONCAT ++hb_segment_properties_overlay() ++hb_buffer_create_similar() ++hb_font_set_synthetic_slant() ++hb_font_get_synthetic_slant() ++hb_font_get_var_coords_design() + + +Overview of changes leading to 3.2.0 +Friday, November 26, 2021 +==================================== +“harfbuzz” library improvements: +- Fixed shaping of Apple Color Emoji flags in right-to-left context. (Behdad Esfahbod) +- Fixed positioning of CFF fonts in HB_TINY profile. (Behdad Esfahbod) +- OpenType 1.9 language tags update. (David Corbett) +- Add HB_NO_VERTICAL config option. +- Add HB_CONFIG_OVERRIDE_H for easier configuration. (Behdad Esfahbod) + +“harfbuzz-subset” library improvements: +- Improved packing of cmap, loca, and Ligature tables. (Garret Rieger) +- Significantly improved overflow-resolution strategy in the repacker. (Garret Rieger) + + +Overview of changes leading to 3.1.2 +Friday, November 26, 2021 +==================================== +- hb-shape / hb-view: revert treating text on the commandline as single + paragraph (was introduced in 3.0.0); add new --single-par to do that. + (Behdad Esfahbod) +- Subsetter bug fixes. (Garret Rieger, Qunxin Liu, Behdad Esfahbod) + + +Overview of changes leading to 3.1.1 +Wednesday, November 8, 2021 +==================================== +- Work around GCC cast-align error/warning on some platforms. (Behdad Esfahbod) +- Documentation improvements. (Matthias Clasen) + + +Overview of changes leading to 3.1.0 +Wednesday, November 3, 2021 +==================================== +- Better offset-overflow handling in the subsetter library. (Garret Rieger) +- Improved Unicode 14 properties in the USE shaper, and various other USE + shaper fixes. (David Corbett) +- MATH and COLR v1 tables subsetting support, and various other subsetter fixes. + (Qunxin Liu) +- Support for Pwo Karen / Ason Chin medial la. (Simon Cozens) +- Apply GPOS positioning when substituting with morx table, if kerx is missing. + (Behdad Esfahbod) +- Apply calt and clig features across syllable boundaries in Indic shaper. + (Behdad Esfahbod) +- meson option for enabling Graphite 2 has been renamed to graphite2. +- Build and documentation fixes. + +- New API: ++hb_buffer_set_not_found_glyph() ++hb_buffer_get_not_found_glyph() + + +Overview of changes leading to 3.0.0 +Friday, September 17, 2021 +==================================== +- Unicode 14.0 support (David Corbett). +- The hb-subset API and the harfbuzz-subset library's ABI are now declared + stable. The harfbuzz-subset library would not have been possible without the + work of Garret Rieger and Qunxin Liu from Google Fonts, and the earlier work + of Michiharu Ariza from Adobe. +- The hb-style API is now stable and no longer experimental. + +- New API: ++hb_style_tag_t ++hb_style_get_value() ++hb_subset_input_t ++hb_subset_flags_t ++hb_subset_sets_t ++hb_subset_input_create_or_fail() ++hb_subset_input_reference() ++hb_subset_input_destroy() ++hb_subset_input_set_user_data() ++hb_subset_input_get_user_data() ++hb_subset_input_unicode_set() ++hb_subset_input_glyph_set() ++hb_subset_input_set() ++hb_subset_input_get_flags() ++hb_subset_input_set_flags() ++hb_subset_or_fail() + +- Removed old unstable harfbuzz-subset API: +-hb_subset_input_nameid_set() +-hb_subset_input_namelangid_set() +-hb_subset_input_layout_features_set() +-hb_subset_input_no_subset_tables_set() +-hb_subset_input_drop_tables_set() +-hb_subset_input_set_drop_hints() +-hb_subset_input_get_drop_hints() +-hb_subset_input_set_desubroutinize() +-hb_subset_input_get_desubroutinize() +-hb_subset_input_set_retain_gids() +-hb_subset_input_get_retain_gids() +-hb_subset_input_set_name_legacy() +-hb_subset_input_get_name_legacy() +-hb_subset_input_set_overlaps_flag() +-hb_subset_input_get_overlaps_flag() +-hb_subset_input_set_notdef_outline() +-hb_subset_input_get_notdef_outline() +-hb_subset_input_set_no_prune_unicode_ranges() +-hb_subset_input_get_no_prune_unicode_ranges() +-hb_subset() + + +Overview of changes leading to 2.9.1 +Tuesday, September 7, 2021 +==================================== +- Final subset API is in place and if no issues are discovered, it will be the + stable subset API of HarfBuzz 3.0.0. Old API is kept to ease transition, but + will be removed in 3.0.0. +- Various fuzzer-found bug fixes. +- hb_buffer_append() now handles the pre- and post-context which previously + were left unchanged in the destination buffer. +- hb-view / hb-shape now accept following new arguments: + o --unicodes-before/after: takes a list of hex numbers that represent Unicode + codepoints. +- Undeprecated API: + hb_set_invert() + + +Overview of changes leading to 2.9.0 +Wednesday, August 18, 2021 +History Repeats Itself (Afghanistan) +==================================== +- Subsetter API is being stabilized, with the first stable API to happen in + 3.0.0 release (https://github.com/harfbuzz/harfbuzz/issues/3078). +- Support multiple variation axes with same tag, aka HOI. +- The “coretext” testing shaper now passes font variations to CoreText. +- hb-shape/hb-view does not break line at new lines unless text is read from + file. +- hb-view and hb-subset has a --batch now, similar to hb-shape. +- The --batch mode now uses ; as argument separator instead of : used previously. +- The --batch in hb-shape does not expect 0th argument anymore. That is, the + lines read are interpreted as argv[1:], instead of argv[0:]. +- The --batch option has been undocumented. We are ready to document it; send + feedback if you find it useful. +- hb-subset got arguments revamps. Added much-requested --gids-file, --glyphs, + --glyphs-file, --unicodes-file, supporting ranges in --unicodes. +- Various bug fixes. + + +Overview of changes leading to 2.8.2 +Tuesday, July 8, 2021 +==================================== +- Shaping LTR digits for RTL scripts now makes the native direction of the + digits LTR, applying shaping and positioning rules on the same glyph order as + Uniscribe. (Jonathan Kew, Khaled Hosny). +- Subsetting COLR v1 and CPAL tables is now supported. (Garret Rieger, Qunxin Liu) +- Various fixes and improvements to the subsetter. (Garret Rieger, Qunxin Liu, Behdad) +- When applying morx table, mark glyph widths should not be zeroed. (Jonathan Kew) +- GPOS is preferred over kerx, if GSUB was applied. (Behdad) +- Regional_Indicator pairs are grouped together when clustering. (Behdad) +- New API: ++hb_blob_create_or_fail() ++hb_blob_create_from_file_or_fail() ++hb_set_copy() + + +Overview of changes leading to 2.8.1 +Tuesday, May 4, 2021 +==================================== +- Subsetter now fully supports GSUB/GPOS/GDEF tables (including variations); as + such, layout tables are retained by subsetter by default. (Garret Rieger, Qunxin Liu) +- Build scripts no longer check for FontConfig as HarfBuzz does not use it. +- hb-view supports iTerm2 and kitty inline image protocols (Khaled Hosny), + it can also use Chafa for terminal graphics if available (Hans Petter Jansson). + +Overview of changes leading to 2.8.0 +Tuesday, March 16, 2021 +==================================== +- Shape joining scripts other than Arabic/Syriac using the Universal Shaping Engine. + Previously these were shaped using the generalized Arabic shaper. (David Corbett) +- Fix regression in shaping of U+0B55 ORIYA SIGN OVERLINE. (David Corbett) +- Update language tags. (David Corbett) +- Variations: reduce error: do not round each interpolated delta. (Just van Rossum) +- Documentation improvements. (Khaled Hosny, Nathan Willis) +- Subsetter improvements: subsets most, if not all, lookup types now. (Garret Rieger, Qunxin Liu) +- Fuzzer-found fixes and other improvements when memory failures happen. (Behdad) +- Removed most atomic implementations now that we have C++11 atomic impl. (Behdad) +- General codebase upkeep; using more C++11 features: constexpr constructors, etc. (Behdad) + + +Overview of changes leading to 2.7.4 +Sunday, December 27, 2020 +==================================== +- Fix missing --enable-introspection configure option from previous release + tarball. +- Documentation updates. + + +Overview of changes leading to 2.7.3 +Wednesday, December 23, 2020 +==================================== +- Update USE shaper to 2020-08-13 specification, and other improvements. +- Don’t disable liga feature in myanmar shaper, to match Uniscribe. +- Improvements to language and script tags handling. +- Update language system tag registry to OpenType 1.8.4 +- Support for serializing and deserializing Unicode buffers. Serialized buffers + are now delimited with `<>` or `[]` based on whether it is a Unicode or + glyphs buffer. +- Increase buffer work limits to handle fonts with many complex lookups. +- Handle more shaping operations in trace output. +- Memory access fixes. +- More OOM fixes. +- Improved documentation. +- Build system improvements. +- New API: ++hb_buffer_has_positions() ++hb_buffer_serialize() ++hb_buffer_serialize_unicode() ++hb_buffer_deserialize_unicode() + + +Overview of changes leading to 2.7.2 +Saturday, August 29, 2020 +==================================== +- Fix a regression in the previous release that caused a crash with Kaithi. +- More OOM fixes. + + +Overview of changes leading to 2.7.1 +Thursday, August 13, 2020 +==================================== +- ot-funcs now handles variable empty glyphs better when hvar/vvar isn't present. +- Reverted a GDEF processing regression. +- A couple of fixes to handle OOM better. + + +Overview of changes leading to 2.7.0 +Saturday, July 25, 2020 +==================================== +- Use an implementation for round that always rounds up, some minor fluctuations + are expected on var font specially when hb-ot callback is used. +- Fix an AAT's `kerx` issue on broken rendering of Devanagari Sangam MN. +- Remove AAT's `lcar` table support from _get_ligature_carets API, not even much + use on macOS installed fonts (only two files). GDEF support is the recommended + one and expected to work properly after issues fixed two releases ago. +- Minor memory fixes to handle OOM better specially in hb-ft. +- Minor .so files versioning scheme change and remove stable/unstable scheme + differences, was never used in practice (always default to stable scheme). +- We are now suggesting careful packaging of the library using meson, + https://github.com/harfbuzz/harfbuzz/wiki/Notes-on-migration-to-meson + for more information. +- Distribution package URL is changed, either use GitHub generated tarballs, + `https://github.com/harfbuzz/harfbuzz/archive/$pkgver.tar.gz` + or, even more preferably use commit hash of the release and git checkouts like, + `git+https://github.com/harfbuzz/harfbuzz#commit=$commit` + + +Overview of changes leading to 2.6.8 +Monday, June 22, 2020 +==================================== +- New API to fetch glyph alternates from GSUB table. +- hb-coretext build fix for macOS < 10.10. +- Meson build fixes, cmake port removal is postponed but please prepare for + it and give us feedback. + Autotools is still our main build system however please consider + experimenting with meson also for packaging the library. +- New API: ++hb_ot_layout_lookup_get_glyph_alternates() + + +Overview of changes leading to 2.6.7 +Wednesday, June 3, 2020 +==================================== +- Update to Unicode 13.0.0. +- Fix hb_ot_layout_get_ligature_carets for fonts without lcar table, it was + completely broken for all the other fonts since 2.1.2. +- As a part of our migration to meson, this release will be the last one + to provide cmake port files but autotools still is our main build system. + There is a possibility that the next version or the after be released + using meson. + + +Overview of changes leading to 2.6.6 +Tuesday, May 12, 2020 +==================================== +- A fix in AAT kerning for Geeza Pro. +- Better support for resource fork fonts on macOS. + + +Overview of changes leading to 2.6.5 +Friday, April 17, 2020 +==================================== +- Add experimental meson build system. Autotools is still the primary + and supported build system. +- AAT is now always preferred for horizontal scripts when both AAT and OT + layout tables exist at the same time. +- Subsetter improvements. +- New API: ++hb_ft_font_lock_face() ++hb_ft_font_unlock_face() + + +Overview of changes leading to 2.6.4 +Monday, October 29, 2019 +==================================== +- Small bug fix. +- Build fixes. + + +Overview of changes leading to 2.6.3 +Monday, October 28, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. +- New API: ++hb_font_get_nominal_glyphs() + + +Overview of changes leading to 2.6.2 +Monday, September 30, 2019 +==================================== +- Misc small fixes, mostly to build-related issues. + + +Overview of changes leading to 2.6.1 +Thursday, August 22, 2019 +==================================== +- Fix regression with hb_font_create_sub_font scaling introduced in 2.6.0. +- Change interpretation of font PTEM size / CoreText font size handling. + See https://github.com/harfbuzz/harfbuzz/pull/1484 +- hb-ot-font: Prefer symbol cmap subtable if present. +- Apply 'dist'/'abvm'/'blwm' features to all scripts. +- Drop experimental DirectWrite API. + + +Overview of changes leading to 2.6.0 +Tuesday, August 13, 2019 +==================================== +- New OpenType metrics, baseline, and metadata table access APIs. +- New API to set font variations to a named-instance. +- New hb-gdi.h header and API for creating hb_face_t from HFONT. +- Amalgam: Provide a single-file harfbuzz.cc file for easier alternate building. +- More size-reduction configurable options, enabled by HB_TINY. +- New API: ++hb_font_set_var_named_instance() ++hb_gdi_face_create() ++hb_ot_layout_baseline_tag_t ++hb_ot_layout_get_baseline() ++hb_ot_meta_tag_t ++hb_ot_meta_get_entry_tags() ++hb_ot_meta_reference_entry() ++hb_ot_metrics_tag_t ++hb_ot_metrics_get_position() ++hb_ot_metrics_get_variation() ++hb_ot_metrics_get_x_variation() ++hb_ot_metrics_get_y_variation() + + +Overview of changes leading to 2.5.3 +Wednesday, June 26, 2019 +==================================== +- Fix UCD script data for Unicode 10+ scripts. This was broken since 2.5.0. +- More optimizations for HB_TINY. + + +Overview of changes leading to 2.5.2 +Thursday, June 20, 2019 +==================================== +- More hb-config.hh facilities to shrink library size, namely when built as + HB_TINY. +- New documentation of custom configurations in CONFIG.md. +- Fix build on gcc 4.8. That's supported again. +- Universal Shaping Engine improvements thanks to David Corbett. +- API Changes: Undeprecate some horizontal-kerning API and re-enable in hb-ft, + such that Type1 fonts will continue kerning. + + +Overview of changes leading to 2.5.1 +Friday, May 31, 2019 +==================================== +- Fix build with various versions of Visual Studio. +- Improved documentation, thanks to Nathan Willis. +- Bugfix in subsetting glyf table. +- Improved scripts for cross-compiling for Windows using mingw. +- Rename HB_MATH_GLYPH_PART_FLAG_EXTENDER to HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER. + A deprecated macro is added for backwards-compatibility. + + +Overview of changes leading to 2.5.0 +Friday, May 24, 2019 +==================================== +- This release does not include much functional changes, but includes major internal + code-base changes. We now require C++11. Support for gcc 4.8 and earlier has been + dropped. +- New hb-config.hh facility for compiling smaller library for embedded and web usecases. +- New Unicode Character Database implementation that is half the size of previously-used + UCDN. +- Subsetter improvements. +- Improved documentation, thanks to Nathan Willis. +- Misc shaping fixes. + + +Overview of changes leading to 2.4.0 +Monday, March 25, 2019 +==================================== +- Unicode 12. +- Misc fixes. +- Subsetter improvements. +- New API: +HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE +hb_directwrite_face_create() + + +Overview of changes leading to 2.3.1 +Wednesday, January 30, 2019 +==================================== +- AAT bug fixes. +- Misc internal housekeeping cleanup. + + +Overview of changes leading to 2.3.0 +Thursday, December 20, 2018 +==================================== +- Fix regression on big-endian architectures. Ouch! +- Misc bug and build fixes. +- Fix subsetting of simple GSUB/GDEF. +- Merge CFF / CFF2 support contributed by Adobe. This mostly involves + the subsetter, but also get_glyph_extents on CFF fonts. + +New API in hb-aat.h: ++hb_aat_layout_has_substitution() ++hb_aat_layout_has_positioning() ++hb_aat_layout_has_tracking() + + +Overview of changes leading to 2.2.0 +Thursday, November 29, 2018 +==================================== +- Misc shaping bug fixes. +- Add font variations named-instance API. +- Deprecate font variations axis enumeration API and add replacement. +- AAT shaping improvements: + o Fixed 'kern' table Format 2 implementation. + o Implement 'feat' table API for feature detection. + o Blacklist 'GSUB' table of fonts from 'MUTF' foundry that also have 'morx'. + +New API: ++hb_aat_layout_feature_type_t ++hb_aat_layout_feature_selector_t ++hb_aat_layout_get_feature_types() ++hb_aat_layout_feature_type_get_name_id ++hb_aat_layout_feature_selector_info_t ++HB_AAT_LAYOUT_NO_SELECTOR_INDEX ++hb_aat_layout_feature_type_get_selector_infos() ++hb_ot_var_axis_flags_t ++hb_ot_var_axis_info_t ++hb_ot_var_get_axis_infos() ++hb_ot_var_find_axis_info() ++hb_ot_var_get_named_instance_count() ++hb_ot_var_named_instance_get_subfamily_name_id() ++hb_ot_var_named_instance_get_postscript_name_id() ++hb_ot_var_named_instance_get_design_coords() + +Deprecated API: ++HB_OT_VAR_NO_AXIS_INDEX ++hb_ot_var_axis_t ++hb_ot_var_get_axes() ++hb_ot_var_find_axis() + + +Overview of changes leading to 2.1.3 +Friday, November 16, 2018 +==================================== +- Fix AAT 'mort' shaping, which was broken in 2.1.2 + + +Overview of changes leading to 2.1.2 +Friday, November 16, 2018 +==================================== +- Various internal changes. +- AAT shaping improvements: + o Implement kern table Format 1 state-machine-based kerning. + o Implement cross-stream kerning (cursive positioning, etc). + o Ignore emptyish GSUB tables (zero scripts) if morx present. + o Don't apply GPOS if morx is being applied. Matches Apple. + + +-Overview of changes leading to 2.1.1 +Monday, November 5, 2018 +==================================== +- AAT improvements: + o Implement 'mort' table. + o Implement 'kern' subtables Format 1 and Format 3. + + +Overview of changes leading to 2.1.0 +Tuesday, October 30, 2018 +==================================== +- AAT shaping improvements: + o Allow user controlling AAT features, for whole buffer only currently. + o Several 'morx' fixes. + o Implement tuple-kerns in 'kerx'; Fixes kerning with Apple default + San Francisco fonts. +- Support for color fonts: + o COLR/CPAL API to fetch color layers. + o SVG table to fetch SVG documents. + o CBDT/sbix API to fetch PNG images. +- New 'name' table API. +- hb-ot-font now uses 'VORG' table to correctly position CFF glyphs + in vertical layout. +- Various fuzzer-found bug fixes. + +Changed API: + +A type and a macro added in 2.0.0 were renamed: + +hb_name_id_t -> hb_ot_name_id_t +HB_NAME_ID_INVALID -> HB_OT_NAME_ID_INVALID + +New API: + ++hb_color_t ++HB_COLOR ++hb_color_get_alpha() ++hb_color_get_red() ++hb_color_get_green() ++hb_color_get_blue() ++hb_ot_color_has_palettes() ++hb_ot_color_palette_get_count() ++hb_ot_color_palette_get_name_id() ++hb_ot_color_palette_color_get_name_id() ++hb_ot_color_palette_flags_t ++hb_ot_color_palette_get_flags() ++hb_ot_color_palette_get_colors() ++hb_ot_color_has_layers() ++hb_ot_color_layer_t ++hb_ot_color_glyph_get_layers() ++hb_ot_color_has_svg() ++hb_ot_color_glyph_reference_svg() ++hb_ot_color_has_png() ++hb_ot_color_glyph_reference_png() + ++hb_ot_name_id_t ++HB_OT_NAME_ID_INVALID ++HB_OT_NAME_ID_COPYRIGHT ++HB_OT_NAME_ID_FONT_FAMILY ++HB_OT_NAME_ID_FONT_SUBFAMILY ++HB_OT_NAME_ID_UNIQUE_ID ++HB_OT_NAME_ID_FULL_NAME ++HB_OT_NAME_ID_VERSION_STRING ++HB_OT_NAME_ID_POSTSCRIPT_NAME ++HB_OT_NAME_ID_TRADEMARK ++HB_OT_NAME_ID_MANUFACTURER ++HB_OT_NAME_ID_DESIGNER ++HB_OT_NAME_ID_DESCRIPTION ++HB_OT_NAME_ID_VENDOR_URL ++HB_OT_NAME_ID_DESIGNER_URL ++HB_OT_NAME_ID_LICENSE ++HB_OT_NAME_ID_LICENSE_URL ++HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY ++HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY ++HB_OT_NAME_ID_MAC_FULL_NAME ++HB_OT_NAME_ID_SAMPLE_TEXT ++HB_OT_NAME_ID_CID_FINDFONT_NAME ++HB_OT_NAME_ID_WWS_FAMILY ++HB_OT_NAME_ID_WWS_SUBFAMILY ++HB_OT_NAME_ID_LIGHT_BACKGROUND ++HB_OT_NAME_ID_DARK_BACKGROUND ++HB_OT_NAME_ID_VARIATIONS_PS_PREFIX ++hb_ot_name_entry_t ++hb_ot_name_list_names() ++hb_ot_name_get_utf8() ++hb_ot_name_get_utf16() ++hb_ot_name_get_utf32() + + +Overview of changes leading to 2.0.2 +Saturday, October 20, 2018 +==================================== +- Fix two minor memory access issues in AAT tables. + + +Overview of changes leading to 2.0.1 +Friday, October 19, 2018 +==================================== +- Fix hb-version.h reported release version that went wrong (1.8.0) + with previous release. +- Fix extrapolation in 'trak' table. +- Fix hb-font infinite-recursion issue with some font funcs and + subclassed fonts. +- Implement variation-kerning format in kerx table, although without + variation. +- Fix return value of hb_map_is_empty(). + + +Overview of changes leading to 2.0.0 +Thursday, October 18, 2018 +==================================== +- Added AAT shaping support (morx/kerx/trak). + Automatically used if GSUB/GPOS are not available respectively. + Set HB_OPTIONS=aat env var to have morx/kerx preferred over + GSUB/GPOS. +- Apply TrueType kern table internally, instead of relying on + hb_font_t callbacks. +- Khmer shaper significantly rewritten to better match Uniscribe. +- Indic3 tags ('dev3', etc) are passed to USE shaper. +- .dfont Mac font containers implemented. +- Script- and language-mapping revamped to better use BCP 47. +- Misc USE and Indic fixes. +- Misc everything fixes. +- Too many things to list. Biggest release since 0.9.1, with + over 500 commits in just over 5 weeks! Didn't intend it to + be a big release. Just happened to become. +- hb-ft now locks underlying FT_Face during use. + +API changes: + +- Newly-created hb_font_t's now have our internal "hb-ot-font" + callbacks set on them, so they should work out of the box + without any callbacks set. If callbacks are set, everything + is back to what it was before, the fallback callbacks are + null. If you to get the internal implementation modified, + sub_font it. + +- New hb_font_funcs_set_nominal_glyphs_func() allows speeding + up character to glyph mapping. + +New API: ++HB_FEATURE_GLOBAL_START ++HB_FEATURE_GLOBAL_END ++hb_buffer_set_invisible_glyph() ++hb_buffer_get_invisible_glyph() ++hb_font_funcs_set_nominal_glyphs_func() ++hb_ot_layout_table_select_script() ++hb_ot_layout_script_select_language() ++hb_ot_layout_feature_get_name_ids() ++hb_ot_layout_feature_get_characters() ++hb_name_id_t ++HB_NAME_ID_INVALID ++HB_OT_MAX_TAGS_PER_SCRIPT ++hb_ot_tags_from_script_and_language() ++hb_ot_tags_to_script_and_language() + +Deprecated API: +-hb_font_funcs_set_glyph_func() +-hb_unicode_eastasian_width_func_t +-hb_unicode_funcs_set_eastasian_width_func() +-hb_unicode_eastasian_width() +-hb_unicode_decompose_compatibility_func_t +-HB_UNICODE_MAX_DECOMPOSITION_LEN +-hb_unicode_funcs_set_decompose_compatibility_func() +-hb_unicode_decompose_compatibility() +-hb_font_funcs_set_glyph_h_kerning_func() +-hb_font_funcs_set_glyph_v_kerning_func() +-hb_font_get_glyph_h_kerning() +-hb_font_get_glyph_v_kerning() +-hb_font_get_glyph_kerning_for_direction() +-hb_ot_layout_table_choose_script() +-hb_ot_layout_script_find_language() +-hb_ot_tags_from_script() +-hb_ot_tag_from_language() + + +Overview of changes leading to 1.9.0 +Monday, September 10, 2018 +==================================== +- Added 'cmap' API to hb_face_t. +- Face-builder API. +- hb-ot-font re-creation should be much leaner now, as the + font tables it uses are cached on hb_face_t now. +- Internal source header file name changes: + hb-*-private.hh is renamed to hb-*.hh. + +New API: ++HB_UNICODE_MAX ++hb_face_collect_unicodes() ++hb_face_collect_variation_selectors() ++hb_face_collect_variation_unicodes() ++hb_face_builder_create() ++hb_face_builder_add_table() + + +Overview of changes leading to 1.8.8 +Tuesday, August 14, 2018 +==================================== +- Fix hb-icu crash on architectures where compare_exchange_weak() can + fail falsely. This bug was introduced in 1.8.4. + https://bugs.chromium.org/p/chromium/issues/detail?id=873568 +- More internal refactoring of atomic operations and singletons. +- API changes: + The following functions do NOT reference their return value before + returning: + * hb_unicode_funcs_get_default() + * hb_glib_get_unicode_funcs() + * hb_icu_get_unicode_funcs() + This is consistent with their naming ("get", instead of "reference") + as well as how they are used in the wild (ie. no one calls destroy() + on their return value.) + + +Overview of changes leading to 1.8.7 +Wednesday, August 8, 2018 +==================================== +- Fix assertion failure with GDEF-blacklisted fonts. + + +Overview of changes leading to 1.8.6 +Tuesday, August 7, 2018 +==================================== +- Internal code shuffling. +- New API to speed up getting advance widths for implementations + that have heavy overhead in get_h_advance callback: ++hb_font_funcs_set_glyph_h_advances_func ++hb_font_funcs_set_glyph_v_advances_func ++hb_font_get_glyph_advances_for_direction ++hb_font_get_glyph_h_advances ++hb_font_get_glyph_h_advances_func_t ++hb_font_get_glyph_v_advances ++hb_font_get_glyph_v_advances_func_t + + +Overview of changes leading to 1.8.5 +Wednesday, August 1, 2018 +==================================== +- Major Khmer shaper improvements to better match Microsoft. +- Indic bug fixes. +- Internal improvements to atomic operations. + + +Overview of changes leading to 1.8.4 +Tuesday, July 17, 2018 +==================================== +- Fix build on non-C++11. +- Use C++-style GCC atomics and C++11 atomics. + + +Overview of changes leading to 1.8.3 +Wednesday, July 11, 2018 +==================================== +- A couple of Indic / USE bug fixes. +- Disable vectorization, as it was causing unaligned access bus error on + certain 32bit architectures. + + +Overview of changes leading to 1.8.2 +Tuesday, July 3, 2018 +==================================== +- Fix infinite loop in Khmer shaper. +- Improve hb_blob_create_from_file() for streams. + + +Overview of changes leading to 1.8.1 +Tuesday, June 12, 2018 +==================================== +- Fix hb-version.h file generation; last two releases went out with wrong ones. +- Add correctness bug in hb_set_t operations, introduced in 1.7.7. +- Remove HB_SUBSET_BUILTIN build option. Not necessary. + + +Overview of changes leading to 1.8.0 +Tuesday, June 5, 2018 +==================================== +- Update to Unicode 11.0.0. + + +Overview of changes leading to 1.7.7 +Tuesday, June 5, 2018 +==================================== +- Lots of internal changes, but not yet exposed externally. +- All HarfBuzz objects are significantly smaller in size now. +- Sinhala: Position repha on top of post-consonant, not base. + This better matches Windows 10 behavior, which was changed + from previous Windows versions. +- New build options: + o New cpp macro HB_NO_ATEXIT + o New cpp macro HB_SUBSET_BUILTIN +- Significant libharfbuzz-subset changes. API subject to change. +- New API in libharfbuzz: + ++hb_blob_create_from_file() ++hb_face_count() + +A hashmap implementation: ++hb-map.h ++HB_MAP_VALUE_INVALID ++hb_map_t ++hb_map_create() ++hb_map_get_empty() ++hb_map_reference() ++hb_map_destroy() ++hb_map_set_user_data() ++hb_map_get_user_data() ++hb_map_allocation_successful() ++hb_map_clear() ++hb_map_is_empty() ++hb_map_get_population() ++hb_map_set() ++hb_map_get() ++hb_map_del() ++hb_map_has() + + +Overview of changes leading to 1.7.6 +Wednesday, March 7, 2018 +==================================== + +- Fix to hb_set_t binary operations. Ouch. +- New experimental harfbuzz-subset library. All of hb-subset.h + is experimental right now and API WILL change. + +- New API: +hb_blob_copy_writable_or_fail() +HB_OT_TAG_BASE +hb_set_previous() +hb_set_previous_range() + + +Overview of changes leading to 1.7.5 +Tuesday, January 30, 2018 +==================================== + +- Separate Khmer shaper from Indic. +- First stab at AAT morx. Not hooked up. +- Misc bug fixes. + + +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 stabilize 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 parameters, + 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/gfx/harfbuzz/README.md b/gfx/harfbuzz/README.md new file mode 100644 index 0000000000..33165091a8 --- /dev/null +++ b/gfx/harfbuzz/README.md @@ -0,0 +1,104 @@ +[![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, Adobe Photoshop, Illustrator, InDesign, +Godot Engine, and other places. + +[![xkcd-derived image](xkcd.png)](https://xkcd.com/2347/) + +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]. + +<!--See past and upcoming [HarfBuzz Hackfests](https://freedesktop.org/wiki/Software/HarfBuzz/Hackfests/)!--> + +## 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. + +<details> + <summary>Packaging status of HarfBuzz</summary> + +[![Packaging status](https://repology.org/badge/vertical-allrepos/harfbuzz.svg?header=harfbuzz)](https://repology.org/project/harfbuzz/versions) + +</details> + +[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/gfx/harfbuzz/THANKS b/gfx/harfbuzz/THANKS new file mode 100644 index 0000000000..88cb7e9ea1 --- /dev/null +++ b/gfx/harfbuzz/THANKS @@ -0,0 +1,7 @@ +Bradley Grainger +Kenichi Ishibashi +Ivan Kuckir <https://photopea.com/> +Ryan Lortie +Jeff Muizelaar +suzuki toshiya +Philip Withnall diff --git a/gfx/harfbuzz/autogen.sh b/gfx/harfbuzz/autogen.sh new file mode 100755 index 0000000000..085b4d8632 --- /dev/null +++ b/gfx/harfbuzz/autogen.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +test -n "$srcdir" || srcdir=`dirname "$0"` +test -n "$srcdir" || srcdir=. + +olddir=`pwd` +cd $srcdir + +#printf "checking for ragel... " +#which ragel || { +# echo "You need to install ragel... See http://www.complang.org/ragel/" +# exit 1 +#} + +printf "checking for pkg-config... " +which pkg-config || { + echo "*** No pkg-config found, please install it ***" + exit 1 +} + +printf "checking for libtoolize... " +which glibtoolize || which libtoolize || { + echo "*** No libtoolize (libtool) found, please install it ***" + exit 1 +} +printf "checking for gtkdocize... " +if which gtkdocize ; then + gtkdocize --copy || exit 1 +else + echo "*** No gtkdocize (gtk-doc) found, skipping documentation ***" + echo "EXTRA_DIST = " > gtk-doc.make +fi + +printf "checking for autoreconf... " +which autoreconf || { + echo "*** No autoreconf (autoconf) found, please install it ***" + exit 1 +} + +echo "running autoreconf --force --install --verbose" +autoreconf --force --install --verbose || exit $? + +cd $olddir +test -n "$NOCONFIGURE" || { + echo "running configure $@" + "$srcdir/configure" "$@" +} diff --git a/gfx/harfbuzz/configure.ac b/gfx/harfbuzz/configure.ac new file mode 100644 index 0000000000..0d5b3d9675 --- /dev/null +++ b/gfx/harfbuzz/configure.ac @@ -0,0 +1,509 @@ +AC_PREREQ([2.64]) +AC_INIT([HarfBuzz], + [8.3.0], + [https://github.com/harfbuzz/harfbuzz/issues/new], + [harfbuzz], + [http://harfbuzz.org/]) + +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_SRCDIR([src/harfbuzz.pc.in]) +AC_CONFIG_HEADERS([config.h]) + +AM_INIT_AUTOMAKE([1.13.0 gnits tar-ustar dist-xz no-dist-gzip -Wall no-define color-tests -Wno-portability]) +AM_SILENT_RULES([yes]) +AX_CODE_COVERAGE + +# Initialize libtool +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) +LT_PREREQ([2.2]) +LT_INIT([disable-static]) + +# Check for programs +AC_PROG_CC +AC_PROG_CC_C99 +AM_PROG_CC_C_O +AC_PROG_CXX +AX_CXX_COMPILE_STDCXX(11) +AC_SYS_LARGEFILE +PKG_PROG_PKG_CONFIG([0.28]) +AM_MISSING_PROG([RAGEL], [ragel]) +AM_MISSING_PROG([GIT], [git]) + +# Version +m4_define(hb_version_triplet,m4_split(AC_PACKAGE_VERSION,[[.]])) +m4_define(hb_version_major,m4_argn(1,hb_version_triplet)) +m4_define(hb_version_minor,m4_argn(2,hb_version_triplet)) +m4_define(hb_version_micro,m4_argn(3,hb_version_triplet)) +HB_VERSION_MAJOR=hb_version_major +HB_VERSION_MINOR=hb_version_minor +HB_VERSION_MICRO=hb_version_micro +HB_VERSION=AC_PACKAGE_VERSION +AC_SUBST(HB_VERSION_MAJOR) +AC_SUBST(HB_VERSION_MINOR) +AC_SUBST(HB_VERSION_MICRO) +AC_SUBST(HB_VERSION) + +# Libtool version +m4_define([hb_version_int], + m4_eval(60000 + hb_version_major*100 + hb_version_minor*10 + hb_version_micro)) +HB_LIBTOOL_VERSION_INFO=hb_version_int:0:hb_version_int +AC_SUBST(HB_LIBTOOL_VERSION_INFO) + +AC_ARG_WITH([libstdc++], + [AS_HELP_STRING([--with-libstdc++=@<:@yes/no@:>@], + [Allow linking with libstdc++ @<:@default=no@:>@])], + [with_libstdcxx=$withval], + [with_libstdcxx=no]) +AM_CONDITIONAL(WITH_LIBSTDCXX, [test "x$with_libstdcxx" = "xyes"]) + +# Documentation +have_gtk_doc=false +m4_ifdef([GTK_DOC_CHECK], [ +GTK_DOC_CHECK([1.15],[--flavour no-tmpl]) + if test "x$enable_gtk_doc" = xyes; then + have_gtk_doc=true + fi +], [ + AM_CONDITIONAL([ENABLE_GTK_DOC], false) +]) + +# Functions and headers +AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty newlocale uselocale sincosf) +AC_CHECK_HEADERS(unistd.h sys/mman.h stdbool.h xlocale.h) + +# Compiler flags +AC_CANONICAL_HOST +AC_CHECK_ALIGNOF([struct{char;}]) +if test "x$GCC" = "xyes"; then + + # Make symbols link locally + AX_CHECK_LINK_FLAG([[-Bsymbolic-functions]], [LDFLAGS="$LDFLAGS -Bsymbolic-functions"]) + + # Make it possible to not link to libstdc++ + # No threadsafe statics in C++ as we do it ourselves. + # We don't use these features, so it's safe to disable them + # even in the cases where we DO link to libstdc++. + # Put -fno-rtti before $CXXFLAGS such that users can re-enable it + # by overriding CXXFLAGS. + CXXFLAGS="-fno-rtti $CXXFLAGS -fno-exceptions -fno-threadsafe-statics" + + case "$host" in + *-*-mingw*) + ;; + *) + # Hide inline methods + CXXFLAGS="$CXXFLAGS -fvisibility-inlines-hidden" + ;; + esac + + case "$host" in + arm-*-*) + if test "x$ac_cv_alignof_struct_char__" != x1; then + # Request byte alignment + CXXFLAGS="$CXXFLAGS -mstructure-size-boundary=8" + fi + ;; + esac +fi + +AM_CONDITIONAL(HAVE_GCC, test "x$GCC" = "xyes") + +hb_os_win32=no +AC_MSG_CHECKING([for native Win32]) +case "$host" in + *-*-mingw*) + hb_os_win32=yes + ;; +esac +AC_MSG_RESULT([$hb_os_win32]) +AM_CONDITIONAL(OS_WIN32, test "$hb_os_win32" = "yes") + +have_pthread=false +AX_PTHREAD([have_pthread=true]) +if $have_pthread; then + AC_DEFINE(HAVE_PTHREAD, 1, [Have POSIX threads]) +fi +AM_CONDITIONAL(HAVE_PTHREAD, $have_pthread) + +dnl ========================================================================== + +AC_ARG_WITH(glib, + [AS_HELP_STRING([--with-glib=@<:@yes/no/auto@:>@], + [Use glib @<:@default=auto@:>@])],, + [with_glib=auto]) +have_glib=false +GLIB_DEPS="glib-2.0 >= 2.19.1" +AC_SUBST(GLIB_DEPS) +if test "x$with_glib" = "xyes" -o "x$with_glib" = "xauto"; then + PKG_CHECK_MODULES(GLIB, $GLIB_DEPS, have_glib=true, :) +fi +if test "x$with_glib" = "xyes" -a "x$have_glib" != "xtrue"; then + AC_MSG_ERROR([glib support requested but glib-2.0 not found]) +fi +if $have_glib; then + AC_DEFINE(HAVE_GLIB, 1, [Have glib2 library]) +fi +AM_CONDITIONAL(HAVE_GLIB, $have_glib) + +dnl =========================================================================== + +AC_ARG_WITH(gobject, + [AS_HELP_STRING([--with-gobject=@<:@yes/no/auto@:>@], + [Use gobject @<:@default=no@:>@])],, + [with_gobject=no]) +have_gobject=false +if test "x$with_gobject" = "xyes" -o "x$with_gobject" = "xauto"; then + PKG_CHECK_MODULES(GOBJECT, gobject-2.0 glib-2.0, have_gobject=true, :) +fi +if test "x$with_gobject" = "xyes" -a "x$have_gobject" != "xtrue"; then + AC_MSG_ERROR([gobject support requested but gobject-2.0 / glib-2.0 not found]) +fi +if $have_gobject; then + AC_DEFINE(HAVE_GOBJECT, 1, [Have gobject2 library]) + GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` + AC_SUBST(GLIB_MKENUMS) +fi +AM_CONDITIONAL(HAVE_GOBJECT, $have_gobject) +AC_SUBST(have_gobject) + +dnl =========================================================================== + + +dnl =========================================================================== +# Gobject-Introspection +have_introspection=false +m4_ifdef([GOBJECT_INTROSPECTION_CHECK], [ + if $have_gobject; then + GOBJECT_INTROSPECTION_CHECK([1.34.0]) + if test "x$found_introspection" = xyes; then + have_introspection=true + fi + else + AM_CONDITIONAL([HAVE_INTROSPECTION], false) + fi +], [ + AM_CONDITIONAL([HAVE_INTROSPECTION], false) +]) + +dnl ========================================================================== + +AC_ARG_WITH(cairo, + [AS_HELP_STRING([--with-cairo=@<:@yes/no/auto@:>@], + [Use cairo @<:@default=auto@:>@])],, + [with_cairo=auto]) +have_cairo=false +if test "x$with_cairo" = "xyes" -o "x$with_cairo" = "xauto"; then + PKG_CHECK_MODULES(CAIRO, cairo >= 1.8.0, have_cairo=true, :) + save_libs=$LIBS + LIBS="$LIBS $CAIRO_LIBS" + AC_CHECK_FUNCS(cairo_user_font_face_set_render_color_glyph_func) + LIBS=$save_libs +fi +if test "x$with_cairo" = "xyes" -a "x$have_cairo" != "xtrue"; then + AC_MSG_ERROR([cairo support requested but not found]) +fi +if $have_cairo; then + AC_DEFINE(HAVE_CAIRO, 1, [Have cairo graphics library]) +fi +AM_CONDITIONAL(HAVE_CAIRO, $have_cairo) + +have_cairo_ft=false +if $have_cairo; then + PKG_CHECK_MODULES(CAIRO_FT, cairo-ft, have_cairo_ft=true, :) +fi +if $have_cairo_ft; then + AC_DEFINE(HAVE_CAIRO_FT, 1, [Have cairo-ft support in cairo graphics library]) +fi +AM_CONDITIONAL(HAVE_CAIRO_FT, $have_cairo_ft) + +dnl ========================================================================== + +AC_ARG_WITH(chafa, + [AS_HELP_STRING([--with-chafa=@<:@yes/no/auto@:>@], + [Use chafa @<:@default=auto@:>@])],, + [with_chafa=auto]) +have_chafa=false +if test "x$with_chafa" = "xyes" -o "x$with_chafa" = "xauto"; then + PKG_CHECK_MODULES(CHAFA, chafa >= 1.6.0, have_chafa=true, :) +fi +if test "x$with_chafa" = "xyes" -a "x$have_chafa" != "xtrue"; then + AC_MSG_ERROR([chafa support requested but not found]) +fi +if $have_chafa; then + AC_DEFINE(HAVE_CHAFA, 1, [Have chafa terminal graphics library]) +fi +AM_CONDITIONAL(HAVE_CHAFA, $have_chafa) + +dnl ========================================================================== + +AC_ARG_WITH(icu, + [AS_HELP_STRING([--with-icu=@<:@yes/no/builtin/auto@:>@], + [Use ICU @<:@default=auto@:>@])],, + [with_icu=auto]) +have_icu=false +if test "x$with_icu" = "xyes" -o "x$with_icu" = "xbuiltin" -o "x$with_icu" = "xauto"; then + PKG_CHECK_MODULES(ICU, icu-uc, have_icu=true, :) +fi +if test \( "x$with_icu" = "xyes" -o "x$with_icu" = "xbuiltin" \) -a "x$have_icu" != "xtrue"; then + AC_MSG_ERROR([icu support requested but icu-uc not found]) +fi + +if $have_icu; then + CXXFLAGS="$CXXFLAGS `$PKG_CONFIG --variable=CXXFLAGS icu-uc`" + AC_DEFINE(HAVE_ICU, 1, [Have ICU library]) + if test "x$with_icu" = "xbuiltin"; then + AC_DEFINE(HAVE_ICU_BUILTIN, 1, [Use hb-icu Unicode callbacks]) + fi +fi +AM_CONDITIONAL(HAVE_ICU, $have_icu) +AM_CONDITIONAL(HAVE_ICU_BUILTIN, $have_icu && test "x$with_icu" = "xbuiltin") + +dnl =========================================================================== + +AC_ARG_WITH(graphite2, + [AS_HELP_STRING([--with-graphite2=@<:@yes/no/auto@:>@], + [Use the graphite2 library @<:@default=no@:>@])],, + [with_graphite2=no]) +have_graphite2=false +GRAPHITE2_DEPS="graphite2 >= 1.2.0" +AC_SUBST(GRAPHITE2_DEPS) +if test "x$with_graphite2" = "xyes" -o "x$with_graphite2" = "xauto"; then + PKG_CHECK_MODULES(GRAPHITE2, $GRAPHITE2_DEPS, have_graphite2=true, :) + if test "x$have_graphite2" != "xtrue"; then + # If pkg-config is not available, graphite2 can still be there + ac_save_CFLAGS="$CFLAGS" + ac_save_CPPFLAGS="$CPPFLAGS" + CFLAGS="$CFLAGS $GRAPHITE2_CFLAGS" + CPPFLAGS="$CPPFLAGS $GRAPHITE2_CFLAGS" + AC_CHECK_HEADER(graphite2/Segment.h, have_graphite2=true, :) + CPPFLAGS="$ac_save_CPPFLAGS" + CFLAGS="$ac_save_CFLAGS" + fi +fi +if test "x$with_graphite2" = "xyes" -a "x$have_graphite2" != "xtrue"; then + AC_MSG_ERROR([graphite2 support requested but libgraphite2 not found]) +fi +if $have_graphite2; then + AC_DEFINE(HAVE_GRAPHITE2, 1, [Have Graphite2 library]) +fi +AM_CONDITIONAL(HAVE_GRAPHITE2, $have_graphite2) + +dnl ========================================================================== + +AC_ARG_WITH(freetype, + [AS_HELP_STRING([--with-freetype=@<:@yes/no/auto@:>@], + [Use the FreeType library @<:@default=auto@:>@])],, + [with_freetype=auto]) +have_freetype=false +FREETYPE_DEPS="freetype2 >= 12.0.6" +AC_SUBST(FREETYPE_DEPS) +if test "x$with_freetype" = "xyes" -o "x$with_freetype" = "xauto"; then + # See freetype/docs/VERSION.DLL; 12.0.6 means freetype-2.4.2 + PKG_CHECK_MODULES(FREETYPE, $FREETYPE_DEPS, have_freetype=true, :) +fi +if test "x$with_freetype" = "xyes" -a "x$have_freetype" != "xtrue"; then + AC_MSG_ERROR([FreeType support requested but libfreetype2 not found]) +fi +if $have_freetype; then + AC_DEFINE(HAVE_FREETYPE, 1, [Have FreeType 2 library]) + save_libs=$LIBS + LIBS="$LIBS $FREETYPE_LIBS" + AC_CHECK_FUNCS(FT_Get_Var_Blend_Coordinates FT_Set_Var_Blend_Coordinates FT_Done_MM_Var FT_Get_Transform) + LIBS=$save_libs +fi +AM_CONDITIONAL(HAVE_FREETYPE, $have_freetype) + +dnl =========================================================================== + +AC_ARG_WITH(uniscribe, + [AS_HELP_STRING([--with-uniscribe=@<:@yes/no/auto@:>@], + [Use the Uniscribe library @<:@default=no@:>@])],, + [with_uniscribe=no]) +have_uniscribe=false +if test "x$with_uniscribe" = "xyes" -o "x$with_uniscribe" = "xauto"; then + AC_CHECK_HEADERS(usp10.h windows.h, have_uniscribe=true) +fi +if test "x$with_uniscribe" = "xyes" -a "x$have_uniscribe" != "xtrue"; then + AC_MSG_ERROR([uniscribe support requested but not found]) +fi +if $have_uniscribe; then + UNISCRIBE_CFLAGS= + UNISCRIBE_LIBS="-lusp10 -lgdi32 -lrpcrt4" + AC_SUBST(UNISCRIBE_CFLAGS) + AC_SUBST(UNISCRIBE_LIBS) + AC_DEFINE(HAVE_UNISCRIBE, 1, [Have Uniscribe library]) +fi +AM_CONDITIONAL(HAVE_UNISCRIBE, $have_uniscribe) + +dnl =========================================================================== + +AC_ARG_WITH(gdi, + [AS_HELP_STRING([--with-gdi=@<:@yes/no/auto@:>@], + [Provide GDI integration helpers @<:@default=no@:>@])],, + [with_gdi=no]) +have_gdi=false +if test "x$with_gdi" = "xyes" -o "x$with_gdi" = "xauto"; then + AC_CHECK_HEADERS(windows.h, have_gdi=true) +fi +if test "x$with_gdi" = "xyes" -a "x$have_gdi" != "xtrue"; then + AC_MSG_ERROR([gdi support requested but not found]) +fi +if $have_gdi; then + GDI_CFLAGS= + GDI_LIBS="-lgdi32" + AC_SUBST(GDI_CFLAGS) + AC_SUBST(GDI_LIBS) + AC_DEFINE(HAVE_GDI, 1, [Have GDI library]) +fi +AM_CONDITIONAL(HAVE_GDI, $have_gdi) + +dnl =========================================================================== + +AC_ARG_WITH(directwrite, + [AS_HELP_STRING([--with-directwrite=@<:@yes/no/auto@:>@], + [Use the DirectWrite library (experimental) @<:@default=no@:>@])],, + [with_directwrite=no]) +have_directwrite=false +AC_LANG_PUSH([C++]) +if test "x$with_directwrite" = "xyes" -o "x$with_directwrite" = "xauto"; then + AC_CHECK_HEADERS(dwrite_1.h, have_directwrite=true) +fi +AC_LANG_POP([C++]) +if test "x$with_directwrite" = "xyes" -a "x$have_directwrite" != "xtrue"; then + AC_MSG_ERROR([directwrite support requested but not found]) +fi +if $have_directwrite; then + AC_DEFINE(HAVE_DIRECTWRITE, 1, [Have DirectWrite library]) +fi +AM_CONDITIONAL(HAVE_DIRECTWRITE, $have_directwrite) + +dnl =========================================================================== + +AC_ARG_WITH(coretext, + [AS_HELP_STRING([--with-coretext=@<:@yes/no/auto@:>@], + [Use CoreText @<:@default=no@:>@])],, + [with_coretext=no]) +have_coretext=false +if test "x$with_coretext" = "xyes" -o "x$with_coretext" = "xauto"; then + AC_CHECK_TYPE(CTFontRef, have_coretext=true,, [#include <ApplicationServices/ApplicationServices.h>]) + + if $have_coretext; then + CORETEXT_CFLAGS= + CORETEXT_LIBS="-framework ApplicationServices" + AC_SUBST(CORETEXT_CFLAGS) + AC_SUBST(CORETEXT_LIBS) + else + # On iOS CoreText and CoreGraphics are stand-alone frameworks + if test "x$have_coretext" != "xtrue"; then + # Check for a different symbol to avoid getting cached result. + AC_CHECK_TYPE(CTRunRef, have_coretext=true,, [#include <CoreText/CoreText.h>]) + fi + + if $have_coretext; then + CORETEXT_CFLAGS= + CORETEXT_LIBS="-framework CoreText -framework CoreGraphics -framework CoreFoundation" + AC_SUBST(CORETEXT_CFLAGS) + AC_SUBST(CORETEXT_LIBS) + fi + fi +fi +if test "x$with_coretext" = "xyes" -a "x$have_coretext" != "xtrue"; then + AC_MSG_ERROR([CoreText support requested but libcoretext not found]) +fi +if $have_coretext; then + AC_DEFINE(HAVE_CORETEXT, 1, [Have Core Text backend]) +fi +AM_CONDITIONAL(HAVE_CORETEXT, $have_coretext) + +dnl =========================================================================== + +AC_ARG_WITH(wasm, + [AS_HELP_STRING([--with-wasm=@<:@yes/no/auto@:>@], + [Use the wasm-micro-runtime library @<:@default=no@:>@])],, + [with_wasm=no]) +have_wasm=false +if test "x$with_wasm" = "xyes" -o "x$with_wasm" = "xauto"; then + AC_CHECK_HEADERS(wasm_export.h, have_wasm=true) +fi +if test "x$with_wasm" = "xyes" -a "x$have_wasm" != "xtrue"; then + AC_MSG_ERROR([wasm support requested but not found]) +fi +if $have_wasm; then + WASM_CFLAGS= + WASM_LIBS="-liwasm" + AC_SUBST(WASM_CFLAGS) + AC_SUBST(WASM_LIBS) + AC_DEFINE(HAVE_WASM, 1, [Have wasm-micro-runtime library]) +fi +AM_CONDITIONAL(HAVE_WASM, $have_wasm) + +dnl =========================================================================== + +AC_CONFIG_FILES([ +Makefile +src/Makefile +src/harfbuzz-config.cmake +util/Makefile +test/Makefile +test/api/Makefile +test/fuzzing/Makefile +test/shape/Makefile +test/shape/data/Makefile +test/shape/data/aots/Makefile +test/shape/data/in-house/Makefile +test/shape/data/text-rendering-tests/Makefile +test/subset/Makefile +test/subset/data/Makefile +test/subset/data/repack_tests/Makefile +test/threads/Makefile +perf/Makefile +docs/Makefile +docs/version.xml +]) + +AC_OUTPUT + +echo +echo "C++ compiler version:" +$CXX --version +echo + +AC_MSG_NOTICE([ + +Autotools is no longer our supported build system for building the library +for *nix distributions, please migrate to meson. + +]) + + +AC_MSG_NOTICE([ + +Build configuration: + +Unicode callbacks (you want at least one): + Builtin true + Glib: ${have_glib} + ICU: ${have_icu} + +Font callbacks (the more the merrier): + FreeType: ${have_freetype} + +Tools used for command-line utilities: + Cairo: ${have_cairo} + Chafa: ${have_chafa} + +Additional shapers: + Graphite2: ${have_graphite2} + +Platform shapers (not normally needed): + CoreText: ${have_coretext} + DirectWrite: ${have_directwrite} + GDI: ${have_gdi} + Uniscribe: ${have_uniscribe} + WebAssembly: ${have_wasm} + +Other features: + Documentation: ${enable_gtk_doc} + GObject bindings: ${have_gobject} + Introspection: ${have_introspection} +]) diff --git a/gfx/harfbuzz/git.mk b/gfx/harfbuzz/git.mk new file mode 100644 index 0000000000..cd52db1eb6 --- /dev/null +++ b/gfx/harfbuzz/git.mk @@ -0,0 +1,401 @@ +# git.mk, a small Makefile to autogenerate .gitignore files +# for autotools-based projects. +# +# Copyright 2009, Red Hat, Inc. +# Copyright 2010,2011,2012,2013 Behdad Esfahbod +# Written by Behdad Esfahbod +# +# Copying and distribution of this file, with or without modification, +# is permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. +# +# The latest version of this file can be downloaded from: +GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk +# +# Bugs, etc, should be reported upstream at: +# https://github.com/behdad/git.mk +# +# To use in your project, import this file in your git repo's toplevel, +# then do "make -f git.mk". This modifies all Makefile.am files in +# your project to -include git.mk. Remember to add that line to new +# Makefile.am files you create in your project, or just rerun the +# "make -f git.mk". +# +# This enables automatic .gitignore generation. If you need to ignore +# more files, add them to the GITIGNOREFILES variable in your Makefile.am. +# But think twice before doing that. If a file has to be in .gitignore, +# chances are very high that it's a generated file and should be in one +# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. +# +# The only case that you need to manually add a file to GITIGNOREFILES is +# when remove files in one of mostlyclean-local, clean-local, distclean-local, +# or maintainer-clean-local make targets. +# +# Note that for files like editor backup, etc, there are better places to +# ignore them. See "man gitignore". +# +# If "make maintainer-clean" removes the files but they are not recognized +# by this script (that is, if "git status" shows untracked files still), send +# me the output of "git status" as well as your Makefile.am and Makefile for +# the directories involved and I'll diagnose. +# +# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see +# Makefile.am.sample in the git.mk git repo. +# +# Don't EXTRA_DIST this file. It is supposed to only live in git clones, +# not tarballs. It serves no useful purpose in tarballs and clutters the +# build dir. +# +# This file knows how to handle autoconf, automake, libtool, gtk-doc, +# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata, +# appstream, hotdoc. +# +# This makefile provides the following targets: +# +# - all: "make all" will build all gitignore files. +# - gitignore: makes all gitignore files in the current dir and subdirs. +# - .gitignore: make gitignore file for the current dir. +# - gitignore-recurse: makes all gitignore files in the subdirs. +# +# KNOWN ISSUES: +# +# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the +# submodule doesn't find us. If you have configure.{in,ac} files in +# subdirs, add a proxy git.mk file in those dirs that simply does: +# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. +# And add those files to git. See vte/gnome-pty-helper/git.mk for +# example. +# + + + +############################################################################### +# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES: +############################################################################### + +# +# Most autotools-using modules should be fine including this variable in their +# toplevel MAINTAINERCLEANFILES: +GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ + $(srcdir)/aclocal.m4 \ + $(srcdir)/autoscan.log \ + $(srcdir)/configure.scan \ + `AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \ + test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \ + for x in \ + ar-lib \ + compile \ + config.guess \ + config.rpath \ + config.sub \ + depcomp \ + install-sh \ + ltmain.sh \ + missing \ + mkinstalldirs \ + test-driver \ + ylwrap \ + ; do echo "$$AUX_DIR/$$x"; done` \ + `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \ + head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done` +# +# All modules should also be fine including the following variable, which +# removes automake-generated Makefile.in files: +GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \ + `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \ + while read f; do \ + case $$f in Makefile|*/Makefile) \ + test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \ + done` +# +# Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this, +# though it's harmless to include regardless. +GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ + `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ + if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ + for x in \ + libtool.m4 \ + ltoptions.m4 \ + ltsugar.m4 \ + ltversion.m4 \ + lt~obsolete.m4 \ + ; do echo "$$MACRO_DIR/$$x"; done; \ + fi` +# +# Modules that use gettext and use AC_CONFIG_MACRO_DIR() may also include this, +# though it's harmless to include regardless. +GITIGNORE_MAINTAINERCLEANFILES_M4_GETTEXT = \ + `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ + if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ + for x in \ + codeset.m4 \ + extern-inline.m4 \ + fcntl-o.m4 \ + gettext.m4 \ + glibc2.m4 \ + glibc21.m4 \ + iconv.m4 \ + intdiv0.m4 \ + intl.m4 \ + intldir.m4 \ + intlmacosx.m4 \ + intmax.m4 \ + inttypes-pri.m4 \ + inttypes_h.m4 \ + lcmessage.m4 \ + lib-ld.m4 \ + lib-link.m4 \ + lib-prefix.m4 \ + lock.m4 \ + longlong.m4 \ + nls.m4 \ + po.m4 \ + printf-posix.m4 \ + progtest.m4 \ + size_max.m4 \ + stdint_h.m4 \ + threadlib.m4 \ + uintmax_t.m4 \ + visibility.m4 \ + wchar_t.m4 \ + wint_t.m4 \ + xsize.m4 \ + ; do echo "$$MACRO_DIR/$$x"; done; \ + fi` + + + +############################################################################### +# Default rule is to install ourselves in all Makefile.am files: +############################################################################### + +git-all: git-mk-install + +git-mk-install: + @echo "Installing git makefile" + @any_failed=; \ + find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ + if grep 'include .*/git.mk' $$x >/dev/null; then \ + echo "$$x already includes git.mk"; \ + else \ + failed=; \ + echo "Updating $$x"; \ + { cat $$x; \ + echo ''; \ + echo '-include $$(top_srcdir)/git.mk'; \ + } > $$x.tmp || failed=1; \ + if test x$$failed = x; then \ + mv $$x.tmp $$x || failed=1; \ + fi; \ + if test x$$failed = x; then : else \ + echo "Failed updating $$x"; >&2 \ + any_failed=1; \ + fi; \ + fi; done; test -z "$$any_failed" + +git-mk-update: + wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk + +.PHONY: git-all git-mk-install git-mk-update + + + +############################################################################### +# Actual .gitignore generation: +############################################################################### + +$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk $(top_srcdir)/configure.ac + @echo "git.mk: Generating $@" + @{ \ + if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ + for x in \ + $(DOC_MODULE)-decl-list.txt \ + $(DOC_MODULE)-decl.txt \ + tmpl/$(DOC_MODULE)-unused.sgml \ + "tmpl/*.bak" \ + $(REPORT_FILES) \ + $(DOC_MODULE).pdf \ + xml html \ + ; do echo "/$$x"; done; \ + FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \ + case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \ + if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \ + echo "/$(DOC_MODULE).types"; \ + fi; \ + if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \ + echo "/$(DOC_MODULE)-sections.txt"; \ + fi; \ + if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ + for x in \ + $(SETUP_FILES) \ + $(DOC_MODULE).types \ + ; do echo "/$$x"; done; \ + fi; \ + fi; \ + if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ + for lc in $(DOC_LINGUAS); do \ + for x in \ + $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ + $(DOC_PAGES) \ + $(DOC_INCLUDES) \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + for x in \ + $(_DOC_OMF_ALL) \ + $(_DOC_DSK_ALL) \ + $(_DOC_HTML_ALL) \ + $(_DOC_MOFILES) \ + $(DOC_H_FILE) \ + "*/.xml2po.mo" \ + "*/*.omf.out" \ + ; do echo /$$x; done; \ + fi; \ + if test "x$(HOTDOC)" = x; then :; else \ + $(foreach project, $(HOTDOC_PROJECTS),echo "/$(call HOTDOC_TARGET,$(project))"; \ + echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-conf-path output)" ; \ + echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-private-folder)" ; \ + ) \ + for x in \ + .hotdoc.d \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ + for lc in $(HELP_LINGUAS); do \ + for x in \ + $(HELP_FILES) \ + "$$lc.stamp" \ + "$$lc.mo" \ + ; do echo "/$$lc/$$x"; done; \ + done; \ + fi; \ + if test "x$(gsettings_SCHEMAS)" = x; then :; else \ + for x in \ + $(gsettings_SCHEMAS:.xml=.valid) \ + $(gsettings__enum_file) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appdata_XML)" = x; then :; else \ + for x in \ + $(appdata_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(appstream_XML)" = x; then :; else \ + for x in \ + $(appstream_XML:.xml=.valid) \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/po/Makefile.in.in; then \ + for x in \ + ABOUT-NLS \ + po/Makefile.in.in \ + po/Makefile.in.in~ \ + po/Makefile.in \ + po/Makefile \ + po/Makevars.template \ + po/POTFILES \ + po/Rules-quot \ + po/stamp-it \ + po/stamp-po \ + po/.intltool-merge-cache \ + "po/*.gmo" \ + "po/*.header" \ + "po/*.mo" \ + "po/*.sed" \ + "po/*.sin" \ + po/$(GETTEXT_PACKAGE).pot \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ + ; do echo "/$$x"; done; \ + fi; \ + if test -f $(srcdir)/configure; then \ + for x in \ + autom4te.cache \ + configure \ + config.h \ + stamp-h1 \ + libtool \ + config.lt \ + ; do echo "/$$x"; done; \ + fi; \ + if test "x$(DEJATOOL)" = x; then :; else \ + for x in \ + $(DEJATOOL) \ + ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ + echo /site.exp; \ + fi; \ + if test "x$(am__dirstamp)" = x; then :; else \ + echo "$(am__dirstamp)"; \ + fi; \ + if test "x$(findstring libtool,$(LTCOMPILE))" = x -a "x$(findstring libtool,$(LTCXXCOMPILE))" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ + for x in \ + "*.lo" \ + ".libs" "_libs" \ + ; do echo "$$x"; done; \ + fi; \ + for x in \ + .gitignore \ + $(GITIGNOREFILES) \ + $(CLEANFILES) \ + $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ + $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ + $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ + so_locations \ + $(MOSTLYCLEANFILES) \ + $(TEST_LOGS) \ + $(TEST_LOGS:.log=.trs) \ + $(TEST_SUITE_LOG) \ + $(TESTS:=.test) \ + "*.gcda" \ + "*.gcno" \ + $(DISTCLEANFILES) \ + $(am__CONFIG_DISTCLEAN_FILES) \ + $(CONFIG_CLEAN_FILES) \ + TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ + "*.tab.c" \ + $(MAINTAINERCLEANFILES) \ + $(BUILT_SOURCES) \ + $(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \ + $(filter %_vala.stamp,$(DIST_COMMON)) \ + $(filter %.vapi,$(DIST_COMMON)) \ + $(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \ + Makefile \ + Makefile.in \ + "*.orig" \ + "*.rej" \ + "*.bak" \ + "*~" \ + ".*.sw[nop]" \ + ".dirstamp" \ + ; do echo "/$$x"; done; \ + for x in \ + "*.$(OBJEXT)" \ + $(DEPDIR) \ + ; do echo "$$x"; done; \ + } | \ + sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ + sed 's@/[.]/@/@g' | \ + LC_ALL=C sort | uniq > .gitignore.tmp && \ + (mv .gitignore.tmp $@ || (echo "WARNING: Cannot create $@ file; skipping"; \ + $(RM) .gitignore.tmp)); + +all: $(srcdir)/.gitignore gitignore-recurse-maybe +gitignore: $(srcdir)/.gitignore gitignore-recurse + +gitignore-recurse-maybe: + @for subdir in $(DIST_SUBDIRS); do \ + case " $(SUBDIRS) " in \ + *" $$subdir "*) :;; \ + *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \ + esac; \ + done +gitignore-recurse: + @for subdir in $(DIST_SUBDIRS); do \ + test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \ + done + +maintainer-clean: gitignore-clean +gitignore-clean: + -rm -f $(srcdir)/.gitignore + +.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe diff --git a/gfx/harfbuzz/harfbuzz.doap b/gfx/harfbuzz/harfbuzz.doap new file mode 100644 index 0000000000..2a5c0e62e6 --- /dev/null +++ b/gfx/harfbuzz/harfbuzz.doap @@ -0,0 +1,24 @@ +<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" + xmlns:foaf="http://xmlns.com/foaf/0.1/" + xmlns="http://usefulinc.com/ns/doap#"> + + <name xml:lang="en">harfbuzz</name> + <shortdesc xml:lang="en">Text shaping library</shortdesc> + + <homepage + rdf:resource="https://github.com/harfbuzz/harfbuzz" /> + <mailing-list + rdf:resource="https://github.com/harfbuzz/harfbuzz/discussions" /> + <download-page + rdf:resource="https://github.com/harfbuzz/harfbuzz/releases" /> + <bug-database + rdf:resource="https://github.com/harfbuzz/harfbuzz/issues" /> + + <maintainer> + <foaf:Person> + <foaf:name>Behdad Esfahbod</foaf:name> + <foaf:mbox rdf:resource="mailto:harfbuzz@behdad.org" /> + </foaf:Person> + </maintainer> +</Project> diff --git a/gfx/harfbuzz/moz.yaml b/gfx/harfbuzz/moz.yaml new file mode 100644 index 0000000000..c94f303221 --- /dev/null +++ b/gfx/harfbuzz/moz.yaml @@ -0,0 +1,69 @@ +# Version of this schema +schema: 1 + +bugzilla: + # Bugzilla product and component for this directory and subdirectories + product: Core + component: "Graphics: Text" + +# Document the source of externally hosted code +origin: + + # Short name of the package/library + name: harfbuzz + + description: HarfBuzz is a text shaping engine. + + # Full URL for the package's homepage/etc + # Usually different from repository url + url: https://github.com/harfbuzz/harfbuzz + + # Human-readable identifier for this version/release + # Generally "version NNN", "tag SSS", "bookmark SSS" + release: 8.3.0 (2023-11-11T16:07:57+02:00). + + # Revision to pull in + # Must be a long or short commit SHA (long preferred) + revision: 8.3.0 + + # The package's license, where possible using the mnemonic from + # https://spdx.org/licenses/ + # Multiple licenses can be specified (as a YAML list) + # A "LICENSE" file must exist containing the full license text + license: MIT + license-file: COPYING + + notes: > + To test the upstream's main branch tip, one can + run `./mach vendor --revision tip gfx/harfbuzz/moz.yaml` + +vendoring: + url: https://github.com/harfbuzz/harfbuzz + source-hosting: github + tracking: tag + + include: + - AUTHORS + - autogen.sh + - configure.ac + - COPYING + - git.mk + - harfbuzz.doap + - Makefile.am + - NEWS + - README.md + - src + - THANKS + + exclude: + - "**" + - "src/test*" + - src/hb-ucdn + +updatebot: + maintainer-phab: jfkthame + maintainer-bz: jfkthame@gmail.com + fuzzy-query: "'crashtest | 'reftest !'print !'jsref" + tasks: + - type: vendoring + enabled: true diff --git a/gfx/harfbuzz/src/ArabicPUASimplified.txt b/gfx/harfbuzz/src/ArabicPUASimplified.txt new file mode 100644 index 0000000000..a74ef266fd --- /dev/null +++ b/gfx/harfbuzz/src/ArabicPUASimplified.txt @@ -0,0 +1,250 @@ +# +# Name: Legacy Simplified Arabic encoding +# +# Format: Three tab-separated columns +# Column #1 is the PUA code (in hex as 0xXXXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in PUA order +# +0xF100 0x063B # ARABIC LETTER KEHEH WITH TWO DOTS ABOVE +0xF100 0x063C # ARABIC LETTER KEHEH WITH THREE DOTS BELOW +0xF100 0x063D # ARABIC LETTER FARSI YEH WITH INVERTED V +0xF100 0x063E # ARABIC LETTER FARSI YEH WITH TWO DOTS ABOVE +0xF100 0x063F # ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE +0xF100 0x0653 # ARABIC MADDAH ABOVE +0xF100 0x0654 # ARABIC HAMZA ABOVE +0xF100 0x0655 # ARABIC HAMZA BELOW +0xF100 0x0656 # ARABIC SUBSCRIPT ALEF +0xF100 0x0657 # ARABIC INVERTED DAMMA +0xF100 0x0658 # ARABIC MARK NOON GHUNNA +0xF100 0x0659 # ARABIC ZWARAKAY +0xF100 0x065A # ARABIC VOWEL SIGN SMALL V ABOVE +0xF100 0x065B # ARABIC VOWEL SIGN INVERTED SMALL V ABOVE +0xF100 0x065C # ARABIC VOWEL SIGN DOT BELOW +0xF100 0x065D # ARABIC REVERSED DAMMA +0xF100 0x065E # ARABIC FATHA WITH TWO DOTS +0xF10C 0x200C # ZERO WIDTH NON-JOINER +0xF10D 0x200D # ZERO WIDTH JOINER +0xF10E 0x200E # LEFT-TO-RIGHT MARK +0xF10F 0x200F # RIGHT-TO-LEFT MARK +0xF120 0x0020 # SPACE +0xF121 0x0021 # EXCLAMATION MARK +0xF122 0x0022 # QUOTATION MARK +0xF123 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF124 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF125 0x0025 # PERCENT SIGN +0xF126 0x00D7 # MULTIPLICATION SIGN +0xF127 0x00F7 # DIVISION SIGN +0xF128 0x0028 # LEFT PARENTHESIS +0xF129 0x0029 # RIGHT PARENTHESIS +0xF12A 0x002A # ASTERISK +0xF12B 0x002B # PLUS SIGN +0xF12C 0x060C # ARABIC COMMA +0xF12D 0x002D # HYPHEN-MINUS +0xF12E 0x002E # FULL STOP +0xF12F 0x002F # SOLIDUS +0xF130 0x0660 # ARABIC-INDIC DIGIT ZERO +0xF131 0x0661 # ARABIC-INDIC DIGIT ONE +0xF132 0x0662 # ARABIC-INDIC DIGIT TWO +0xF133 0x0663 # ARABIC-INDIC DIGIT THREE +0xF134 0x0664 # ARABIC-INDIC DIGIT FOUR +0xF135 0x0665 # ARABIC-INDIC DIGIT FIVE +0xF136 0x0666 # ARABIC-INDIC DIGIT SIX +0xF137 0x0667 # ARABIC-INDIC DIGIT SEVEN +0xF138 0x0668 # ARABIC-INDIC DIGIT EIGHT +0xF139 0x0669 # ARABIC-INDIC DIGIT NINE +0xF13A 0x003A # COLON +0xF13B 0x003B # SEMICOLON +0xF13B 0x061B # ARABIC SEMICOLON +0xF13C 0x2018 # LEFT SINGLE QUOTATION MARK +0xF13D 0x003D # EQUALS SIGN +0xF13E 0x2019 # RIGHT SINGLE QUOTATION MARK +0xF13F 0x003F # QUESTION MARK +0xF13F 0x061F # ARABIC QUESTION MARK +0xF141 0x0627 # ARABIC LETTER ALEF +0xF141 0xFE8D # ARABIC LETTER ALEF ISOLATED FORM +0xF142 0xFE8E # ARABIC LETTER ALEF FINAL FORM +0xF143 0x0623 # ARABIC LETTER ALEF WITH HAMZA ABOVE +0xF143 0xFE83 # ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF144 0xFE84 # ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM +0xF145 0x0622 # ARABIC LETTER ALEF WITH MADDA ABOVE +0xF145 0xFE81 # ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM +0xF146 0xFE82 # ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM +0xF147 0x0625 # ARABIC LETTER ALEF WITH HAMZA BELOW +0xF147 0xFE87 # ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM +0xF148 0xFE88 # ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM +0xF149 0xFE91 # ARABIC LETTER BEH INITIAL FORM +0xF149 0xFE92 # ARABIC LETTER BEH MEDIAL FORM +0xF14A 0x0628 # ARABIC LETTER BEH +0xF14A 0xFE8F # ARABIC LETTER BEH ISOLATED FORM +0xF14A 0xFE90 # ARABIC LETTER BEH FINAL FORM +0xF14B 0xFE97 # ARABIC LETTER TEH INITIAL FORM +0xF14B 0xFE98 # ARABIC LETTER TEH MEDIAL FORM +0xF14C 0x062A # ARABIC LETTER TEH +0xF14C 0xFE95 # ARABIC LETTER TEH ISOLATED FORM +0xF14C 0xFE96 # ARABIC LETTER TEH FINAL FORM +0xF14D 0xFE9B # ARABIC LETTER THEH INITIAL FORM +0xF14D 0xFE9C # ARABIC LETTER THEH MEDIAL FORM +0xF14E 0x062B # ARABIC LETTER THEH +0xF14E 0xFE99 # ARABIC LETTER THEH ISOLATED FORM +0xF14E 0xFE9A # ARABIC LETTER THEH FINAL FORM +0xF14F 0xFE9F # ARABIC LETTER JEEM INITIAL FORM +0xF14F 0xFEA0 # ARABIC LETTER JEEM MEDIAL FORM +0xF150 0xFE9E # ARABIC LETTER JEEM FINAL FORM +0xF151 0x062C # ARABIC LETTER JEEM +0xF151 0xFE9D # ARABIC LETTER JEEM ISOLATED FORM +0xF152 0xFEA3 # ARABIC LETTER HAH INITIAL FORM +0xF152 0xFEA4 # ARABIC LETTER HAH MEDIAL FORM +0xF153 0xFEA2 # ARABIC LETTER HAH FINAL FORM +0xF154 0x062D # ARABIC LETTER HAH +0xF154 0xFEA1 # ARABIC LETTER HAH ISOLATED FORM +0xF155 0xFEA7 # ARABIC LETTER KHAH INITIAL FORM +0xF155 0xFEA8 # ARABIC LETTER KHAH MEDIAL FORM +0xF156 0xFEA6 # ARABIC LETTER KHAH FINAL FORM +0xF157 0x062E # ARABIC LETTER KHAH +0xF157 0xFEA5 # ARABIC LETTER KHAH ISOLATED FORM +0xF158 0x062F # ARABIC LETTER DAL +0xF158 0xFEA9 # ARABIC LETTER DAL ISOLATED FORM +0xF158 0xFEAA # ARABIC LETTER DAL FINAL FORM +0xF159 0x0630 # ARABIC LETTER THAL +0xF159 0xFEAB # ARABIC LETTER THAL ISOLATED FORM +0xF159 0xFEAC # ARABIC LETTER THAL FINAL FORM +0xF15A 0x0631 # ARABIC LETTER REH +0xF15A 0xFEAD # ARABIC LETTER REH ISOLATED FORM +0xF15A 0xFEAE # ARABIC LETTER REH FINAL FORM +0xF15B 0x005B # LEFT SQUARE BRACKET +0xF15C 0x005C # REVERSE SOLIDUS +0xF15D 0x005D # RIGHT SQUARE BRACKET +0xF15E 0x002C # COMMA +0xF15E 0x066B # ARABIC DECIMAL SEPARATOR +0xF15E 0x066C # ARABIC THOUSANDS SEPARATOR +0xF15F 0x0640 # ARABIC TATWEEL +0xF160 0x0632 # ARABIC LETTER ZAIN +0xF160 0xFEAF # ARABIC LETTER ZAIN ISOLATED FORM +0xF160 0xFEB0 # ARABIC LETTER ZAIN FINAL FORM +0xF161 0xFEB3 # ARABIC LETTER SEEN INITIAL FORM +0xF161 0xFEB4 # ARABIC LETTER SEEN MEDIAL FORM +0xF162 0x0633 # ARABIC LETTER SEEN +0xF162 0xFEB1 # ARABIC LETTER SEEN ISOLATED FORM +0xF162 0xFEB2 # ARABIC LETTER SEEN FINAL FORM +0xF163 0xFEB7 # ARABIC LETTER SHEEN INITIAL FORM +0xF163 0xFEB8 # ARABIC LETTER SHEEN MEDIAL FORM +0xF164 0x0634 # ARABIC LETTER SHEEN +0xF164 0xFEB5 # ARABIC LETTER SHEEN ISOLATED FORM +0xF164 0xFEB6 # ARABIC LETTER SHEEN FINAL FORM +0xF165 0xFEBB # ARABIC LETTER SAD INITIAL FORM +0xF165 0xFEBC # ARABIC LETTER SAD MEDIAL FORM +0xF166 0x0635 # ARABIC LETTER SAD +0xF166 0xFEB9 # ARABIC LETTER SAD ISOLATED FORM +0xF166 0xFEBA # ARABIC LETTER SAD FINAL FORM +0xF167 0xFEBF # ARABIC LETTER DAD INITIAL FORM +0xF167 0xFEC0 # ARABIC LETTER DAD MEDIAL FORM +0xF168 0x0636 # ARABIC LETTER DAD +0xF168 0xFEBD # ARABIC LETTER DAD ISOLATED FORM +0xF168 0xFEBE # ARABIC LETTER DAD FINAL FORM +0xF169 0x0637 # ARABIC LETTER TAH +0xF169 0xFEC1 # ARABIC LETTER TAH ISOLATED FORM +0xF169 0xFEC2 # ARABIC LETTER TAH FINAL FORM +0xF169 0xFEC3 # ARABIC LETTER TAH INITIAL FORM +0xF169 0xFEC4 # ARABIC LETTER TAH MEDIAL FORM +0xF16A 0x0638 # ARABIC LETTER ZAH +0xF16A 0xFEC5 # ARABIC LETTER ZAH ISOLATED FORM +0xF16A 0xFEC6 # ARABIC LETTER ZAH FINAL FORM +0xF16A 0xFEC7 # ARABIC LETTER ZAH INITIAL FORM +0xF16A 0xFEC8 # ARABIC LETTER ZAH MEDIAL FORM +0xF16B 0xFECB # ARABIC LETTER AIN INITIAL FORM +0xF16C 0xFECC # ARABIC LETTER AIN MEDIAL FORM +0xF16D 0xFECA # ARABIC LETTER AIN FINAL FORM +0xF16E 0x0639 # ARABIC LETTER AIN +0xF16E 0xFEC9 # ARABIC LETTER AIN ISOLATED FORM +0xF16F 0xFECF # ARABIC LETTER GHAIN INITIAL FORM +0xF170 0xFED0 # ARABIC LETTER GHAIN MEDIAL FORM +0xF171 0xFECE # ARABIC LETTER GHAIN FINAL FORM +0xF172 0x063A # ARABIC LETTER GHAIN +0xF172 0xFECD # ARABIC LETTER GHAIN ISOLATED FORM +0xF173 0xFED3 # ARABIC LETTER FEH INITIAL FORM +0xF174 0xFED4 # ARABIC LETTER FEH MEDIAL FORM +0xF175 0x0641 # ARABIC LETTER FEH +0xF175 0xFED1 # ARABIC LETTER FEH ISOLATED FORM +0xF175 0xFED2 # ARABIC LETTER FEH FINAL FORM +0xF176 0xFED7 # ARABIC LETTER QAF INITIAL FORM +0xF177 0xFED8 # ARABIC LETTER QAF MEDIAL FORM +0xF178 0x0642 # ARABIC LETTER QAF +0xF178 0xFED5 # ARABIC LETTER QAF ISOLATED FORM +0xF178 0xFED6 # ARABIC LETTER QAF FINAL FORM +0xF179 0xFEDB # ARABIC LETTER KAF INITIAL FORM +0xF179 0xFEDC # ARABIC LETTER KAF MEDIAL FORM +0xF17A 0x0643 # ARABIC LETTER KAF +0xF17A 0xFED9 # ARABIC LETTER KAF ISOLATED FORM +0xF17A 0xFEDA # ARABIC LETTER KAF FINAL FORM +0xF17B 0xFEDF # ARABIC LETTER LAM INITIAL FORM +0xF17B 0xFEE0 # ARABIC LETTER LAM MEDIAL FORM +0xF17C 0x0644 # ARABIC LETTER LAM +0xF17C 0xFEDD # ARABIC LETTER LAM ISOLATED FORM +0xF17C 0xFEDE # ARABIC LETTER LAM FINAL FORM +0xF17D 0xFEE3 # ARABIC LETTER MEEM INITIAL FORM +0xF17D 0xFEE4 # ARABIC LETTER MEEM MEDIAL FORM +0xF17E 0x0645 # ARABIC LETTER MEEM +0xF17E 0xFEE1 # ARABIC LETTER MEEM ISOLATED FORM +0xF17E 0xFEE2 # ARABIC LETTER MEEM FINAL FORM +0xF17F 0xFEE7 # ARABIC LETTER NOON INITIAL FORM +0xF17F 0xFEE8 # ARABIC LETTER NOON MEDIAL FORM +0xF1A1 0xFEEB # ARABIC LETTER HEH INITIAL FORM +0xF1A2 0xFEEC # ARABIC LETTER HEH MEDIAL FORM +0xF1A3 0xFEEA # ARABIC LETTER HEH FINAL FORM +0xF1A4 0x0647 # ARABIC LETTER HEH +0xF1A4 0xFEE9 # ARABIC LETTER HEH ISOLATED FORM +0xF1A5 0x0648 # ARABIC LETTER WAW +0xF1A5 0xFEED # ARABIC LETTER WAW ISOLATED FORM +0xF1A5 0xFEEE # ARABIC LETTER WAW FINAL FORM +0xF1A6 0xFEF3 # ARABIC LETTER YEH INITIAL FORM +0xF1A6 0xFEF4 # ARABIC LETTER YEH MEDIAL FORM +0xF1A7 0xFEF2 # ARABIC LETTER YEH FINAL FORM +0xF1A8 0x064A # ARABIC LETTER YEH +0xF1A8 0xFEF1 # ARABIC LETTER YEH ISOLATED FORM +0xF1A9 0x0629 # ARABIC LETTER TEH MARBUTA +0xF1A9 0xFE93 # ARABIC LETTER TEH MARBUTA ISOLATED FORM +0xF1AA 0xFE94 # ARABIC LETTER TEH MARBUTA FINAL FORM +0xF1AB 0xFEF0 # ARABIC LETTER ALEF MAKSURA FINAL FORM +0xF1AC 0x0649 # ARABIC LETTER ALEF MAKSURA +0xF1AC 0xFEEF # ARABIC LETTER ALEF MAKSURA ISOLATED FORM +0xF1AD 0x0621 # ARABIC LETTER HAMZA +0xF1AE 0xFE8B # ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM +0xF1AE 0xFE8C # ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM +0xF1AF 0xFE8A # ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM +0xF1B0 0x0030 # DIGIT ZERO +0xF1B1 0x0031 # DIGIT ONE +0xF1B2 0x0032 # DIGIT TWO +0xF1B3 0x0033 # DIGIT THREE +0xF1B4 0x0034 # DIGIT FOUR +0xF1B5 0x0035 # DIGIT FIVE +0xF1B6 0x0036 # DIGIT SIX +0xF1B7 0x0037 # DIGIT SEVEN +0xF1B8 0x0038 # DIGIT EIGHT +0xF1B9 0x0039 # DIGIT NINE +0xF1BA 0x0626 # ARABIC LETTER YEH WITH HAMZA ABOVE +0xF1BA 0xFE89 # ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM +0xF1BB 0x0624 # ARABIC LETTER WAW WITH HAMZA ABOVE +0xF1BB 0xFE85 # ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM +0xF1BB 0xFE86 # ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM +0xF1BC 0xFEFC # ARABIC LIGATURE LAM WITH ALEF FINAL FORM +0xF1BD 0xFEFB # ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM +0xF1BE 0xFEF7 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF1BF 0xFEF8 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM +0xF1C0 0xFEF5 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM +0xF1C1 0xFEF6 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM +0xF1C2 0xFEF9 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM +0xF1C3 0xFEFA # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM +0xF1C4 0x064E # ARABIC FATHA +0xF1C5 0x064F # ARABIC DAMMA +0xF1C6 0x0652 # ARABIC SUKUN +0xF1C7 0x064B # ARABIC FATHATAN +0xF1C8 0x064C # ARABIC DAMMATAN +0xF1C9 0x0651 # ARABIC SHADDA +0xF1CA 0x0650 # ARABIC KASRA +0xF1CB 0x064D # ARABIC KASRATAN +0xF1E1 0x0646 # ARABIC LETTER NOON +0xF1E1 0xFEE5 # ARABIC LETTER NOON ISOLATED FORM +0xF1E1 0xFEE6 # ARABIC LETTER NOON FINAL FORM diff --git a/gfx/harfbuzz/src/ArabicPUATraditional.txt b/gfx/harfbuzz/src/ArabicPUATraditional.txt new file mode 100644 index 0000000000..e14b383c98 --- /dev/null +++ b/gfx/harfbuzz/src/ArabicPUATraditional.txt @@ -0,0 +1,295 @@ +# +# Name: Legacy Traditional Arabic encoding +# +# Format: Three tab-separated columns +# Column #1 is the PUA code (in hex as 0xXXXX) +# Column #2 is the Unicode (in hex as 0xXXXX) +# Column #3 is the Unicode name (follows a comment sign, '#') +# +# The entries are in PUA order +# +0xF200 0x063B # ARABIC LETTER KEHEH WITH TWO DOTS ABOVE +0xF200 0x063C # ARABIC LETTER KEHEH WITH THREE DOTS BELOW +0xF200 0x063D # ARABIC LETTER FARSI YEH WITH INVERTED V +0xF200 0x063E # ARABIC LETTER FARSI YEH WITH TWO DOTS ABOVE +0xF200 0x063F # ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE +0xF200 0x0653 # ARABIC MADDAH ABOVE +0xF200 0x0654 # ARABIC HAMZA ABOVE +0xF200 0x0655 # ARABIC HAMZA BELOW +0xF200 0x0656 # ARABIC SUBSCRIPT ALEF +0xF200 0x0657 # ARABIC INVERTED DAMMA +0xF200 0x0658 # ARABIC MARK NOON GHUNNA +0xF200 0x0659 # ARABIC ZWARAKAY +0xF200 0x065A # ARABIC VOWEL SIGN SMALL V ABOVE +0xF200 0x065B # ARABIC VOWEL SIGN INVERTED SMALL V ABOVE +0xF200 0x065C # ARABIC VOWEL SIGN DOT BELOW +0xF200 0x065D # ARABIC REVERSED DAMMA +0xF200 0x065E # ARABIC FATHA WITH TWO DOTS +0xF202 0xFC08 # ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM +0xF203 0xFC0E # ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM +0xF204 0xFC12 # ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM +0xF205 0xFC42 # ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM +0xF206 0xFC4E # ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM +0xF20C 0x200C # ZERO WIDTH NON-JOINER +0xF20D 0x200D # ZERO WIDTH JOINER +0xF20E 0x200E # LEFT-TO-RIGHT MARK +0xF20F 0x200F # RIGHT-TO-LEFT MARK +0xF210 0xFD88 # ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM +0xF212 0xFC3F # ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM +0xF213 0xFC40 # ARABIC LIGATURE LAM WITH HAH ISOLATED FORM +0xF214 0xFC41 # ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM +0xF215 0xFC6A # ARABIC LIGATURE BEH WITH REH FINAL FORM +0xF216 0xFC70 # ARABIC LIGATURE TEH WITH REH FINAL FORM +0xF217 0xFC91 # ARABIC LIGATURE YEH WITH REH FINAL FORM +0xF218 0xFCB0 # ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM +0xF219 0xFD30 # ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM +0xF21A 0xFCCD # ARABIC LIGATURE LAM WITH HEH INITIAL FORM +0xF21C 0xFC44 # ARABIC LIGATURE LAM WITH YEH ISOLATED FORM +0xF21D 0xFC0A # ARABIC LIGATURE BEH WITH YEH ISOLATED FORM +0xF21E 0xFC10 # ARABIC LIGATURE TEH WITH YEH ISOLATED FORM +0xF21F 0xFC50 # ARABIC LIGATURE NOON WITH YEH ISOLATED FORM +0xF220 0x0020 # SPACE +0xF221 0x0021 # EXCLAMATION MARK +0xF222 0x0022 # QUOTATION MARK +0xF223 0x00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF224 0x00BB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK +0xF225 0x0025 # PERCENT SIGN +0xF226 0x00D7 # MULTIPLICATION SIGN +0xF227 0x00F7 # DIVISION SIGN +0xF228 0x0028 # LEFT PARENTHESIS +0xF229 0x0029 # RIGHT PARENTHESIS +0xF22A 0x002A # ASTERISK +0xF22B 0x002B # PLUS SIGN +0xF22C 0x060C # ARABIC COMMA +0xF22D 0x002D # HYPHEN-MINUS +0xF22E 0x002E # FULL STOP +0xF22F 0x002F # SOLIDUS +0xF230 0x0660 # ARABIC-INDIC DIGIT ZERO +0xF231 0x0661 # ARABIC-INDIC DIGIT ONE +0xF232 0x0662 # ARABIC-INDIC DIGIT TWO +0xF233 0x0663 # ARABIC-INDIC DIGIT THREE +0xF234 0x0664 # ARABIC-INDIC DIGIT FOUR +0xF235 0x0665 # ARABIC-INDIC DIGIT FIVE +0xF236 0x0666 # ARABIC-INDIC DIGIT SIX +0xF237 0x0667 # ARABIC-INDIC DIGIT SEVEN +0xF238 0x0668 # ARABIC-INDIC DIGIT EIGHT +0xF239 0x0669 # ARABIC-INDIC DIGIT NINE +0xF23A 0x003A # COLON +0xF23B 0x003B # SEMICOLON +0xF23B 0x061B # ARABIC SEMICOLON +0xF23C 0x201C # LEFT DOUBLE QUOTATION MARK +0xF23D 0x003D # EQUALS SIGN +0xF23E 0x201D # RIGHT DOUBLE QUOTATION MARK +0xF23F 0x003F # QUESTION MARK +0xF23F 0x061F # ARABIC QUESTION MARK +0xF241 0x0627 # ARABIC LETTER ALEF +0xF241 0xFE8D # ARABIC LETTER ALEF ISOLATED FORM +0xF242 0xFE8E # ARABIC LETTER ALEF FINAL FORM +0xF243 0x0623 # ARABIC LETTER ALEF WITH HAMZA ABOVE +0xF243 0xFE83 # ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF244 0xFE84 # ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM +0xF245 0x0622 # ARABIC LETTER ALEF WITH MADDA ABOVE +0xF245 0xFE81 # ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM +0xF246 0xFE82 # ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM +0xF247 0x0625 # ARABIC LETTER ALEF WITH HAMZA BELOW +0xF247 0xFE87 # ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM +0xF248 0xFE88 # ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM +0xF249 0xFE91 # ARABIC LETTER BEH INITIAL FORM +0xF24A 0xFE92 # ARABIC LETTER BEH MEDIAL FORM +0xF24B 0xFE90 # ARABIC LETTER BEH FINAL FORM +0xF24C 0x0628 # ARABIC LETTER BEH +0xF24C 0xFE8F # ARABIC LETTER BEH ISOLATED FORM +0xF24D 0xFE97 # ARABIC LETTER TEH INITIAL FORM +0xF24E 0xFE98 # ARABIC LETTER TEH MEDIAL FORM +0xF24F 0xFE96 # ARABIC LETTER TEH FINAL FORM +0xF250 0x062A # ARABIC LETTER TEH +0xF250 0xFE95 # ARABIC LETTER TEH ISOLATED FORM +0xF251 0xFE9B # ARABIC LETTER THEH INITIAL FORM +0xF252 0xFE9C # ARABIC LETTER THEH MEDIAL FORM +0xF253 0xFE9A # ARABIC LETTER THEH FINAL FORM +0xF254 0x062B # ARABIC LETTER THEH +0xF254 0xFE99 # ARABIC LETTER THEH ISOLATED FORM +0xF255 0xFE9F # ARABIC LETTER JEEM INITIAL FORM +0xF256 0xFEA0 # ARABIC LETTER JEEM MEDIAL FORM +0xF257 0xFE9E # ARABIC LETTER JEEM FINAL FORM +0xF258 0x062C # ARABIC LETTER JEEM +0xF258 0xFE9D # ARABIC LETTER JEEM ISOLATED FORM +0xF259 0xFEA3 # ARABIC LETTER HAH INITIAL FORM +0xF25A 0xFEA4 # ARABIC LETTER HAH MEDIAL FORM +0xF25B 0x005B # LEFT SQUARE BRACKET +0xF25C 0xFEA2 # ARABIC LETTER HAH FINAL FORM +0xF25D 0x005D # RIGHT SQUARE BRACKET +0xF25E 0x002C # COMMA +0xF25E 0x066B # ARABIC DECIMAL SEPARATOR +0xF25E 0x066C # ARABIC THOUSANDS SEPARATOR +0xF25F 0x0640 # ARABIC TATWEEL +0xF260 0x062D # ARABIC LETTER HAH +0xF260 0xFEA1 # ARABIC LETTER HAH ISOLATED FORM +0xF261 0xFEA7 # ARABIC LETTER KHAH INITIAL FORM +0xF262 0xFEA8 # ARABIC LETTER KHAH MEDIAL FORM +0xF263 0xFEA6 # ARABIC LETTER KHAH FINAL FORM +0xF264 0x062E # ARABIC LETTER KHAH +0xF264 0xFEA5 # ARABIC LETTER KHAH ISOLATED FORM +0xF265 0x062F # ARABIC LETTER DAL +0xF265 0xFEA9 # ARABIC LETTER DAL ISOLATED FORM +0xF266 0xFEAA # ARABIC LETTER DAL FINAL FORM +0xF267 0x0630 # ARABIC LETTER THAL +0xF267 0xFEAB # ARABIC LETTER THAL ISOLATED FORM +0xF268 0xFEAC # ARABIC LETTER THAL FINAL FORM +0xF269 0x0631 # ARABIC LETTER REH +0xF269 0xFEAD # ARABIC LETTER REH ISOLATED FORM +0xF26A 0xFEAE # ARABIC LETTER REH FINAL FORM +0xF26B 0x0632 # ARABIC LETTER ZAIN +0xF26B 0xFEAF # ARABIC LETTER ZAIN ISOLATED FORM +0xF26C 0xFEB0 # ARABIC LETTER ZAIN FINAL FORM +0xF26D 0xFEB3 # ARABIC LETTER SEEN INITIAL FORM +0xF26E 0xFEB4 # ARABIC LETTER SEEN MEDIAL FORM +0xF26F 0xFEB2 # ARABIC LETTER SEEN FINAL FORM +0xF270 0x0633 # ARABIC LETTER SEEN +0xF270 0xFEB1 # ARABIC LETTER SEEN ISOLATED FORM +0xF271 0xFEB7 # ARABIC LETTER SHEEN INITIAL FORM +0xF272 0xFEB8 # ARABIC LETTER SHEEN MEDIAL FORM +0xF273 0xFEB6 # ARABIC LETTER SHEEN FINAL FORM +0xF274 0x0634 # ARABIC LETTER SHEEN +0xF274 0xFEB5 # ARABIC LETTER SHEEN ISOLATED FORM +0xF275 0xFEBB # ARABIC LETTER SAD INITIAL FORM +0xF276 0xFEBC # ARABIC LETTER SAD MEDIAL FORM +0xF277 0xFEBA # ARABIC LETTER SAD FINAL FORM +0xF278 0x0635 # ARABIC LETTER SAD +0xF278 0xFEB9 # ARABIC LETTER SAD ISOLATED FORM +0xF279 0xFEBF # ARABIC LETTER DAD INITIAL FORM +0xF27A 0xFEC0 # ARABIC LETTER DAD MEDIAL FORM +0xF27B 0xFD3E # ORNATE LEFT PARENTHESIS +0xF27C 0xFEBE # ARABIC LETTER DAD FINAL FORM +0xF27D 0xFD3F # ORNATE RIGHT PARENTHESIS +0xF27E 0x0636 # ARABIC LETTER DAD +0xF27E 0xFEBD # ARABIC LETTER DAD ISOLATED FORM +0xF27F 0xFEC3 # ARABIC LETTER TAH INITIAL FORM +0xF280 0xFC9C # ARABIC LIGATURE BEH WITH JEEM INITIAL FORM +0xF281 0xFC9D # ARABIC LIGATURE BEH WITH HAH INITIAL FORM +0xF282 0xFC9E # ARABIC LIGATURE BEH WITH KHAH INITIAL FORM +0xF283 0xFCA1 # ARABIC LIGATURE TEH WITH JEEM INITIAL FORM +0xF284 0xFCA2 # ARABIC LIGATURE TEH WITH HAH INITIAL FORM +0xF285 0xFCA3 # ARABIC LIGATURE TEH WITH KHAH INITIAL FORM +0xF286 0xFCC9 # ARABIC LIGATURE LAM WITH JEEM INITIAL FORM +0xF287 0xFCCA # ARABIC LIGATURE LAM WITH HAH INITIAL FORM +0xF288 0xFCCB # ARABIC LIGATURE LAM WITH KHAH INITIAL FORM +0xF289 0xFCCE # ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM +0xF28A 0xFCCF # ARABIC LIGATURE MEEM WITH HAH INITIAL FORM +0xF28B 0xFCD0 # ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM +0xF28D 0xFCD2 # ARABIC LIGATURE NOON WITH JEEM INITIAL FORM +0xF28E 0xFCD3 # ARABIC LIGATURE NOON WITH HAH INITIAL FORM +0xF28F 0xFCDA # ARABIC LIGATURE YEH WITH JEEM INITIAL FORM +0xF290 0xFCDB # ARABIC LIGATURE YEH WITH HAH INITIAL FORM +0xF291 0xFCDC # ARABIC LIGATURE YEH WITH KHAH INITIAL FORM +0xF292 0xFC6D # ARABIC LIGATURE BEH WITH NOON FINAL FORM +0xF293 0xFC73 # ARABIC LIGATURE TEH WITH NOON FINAL FORM +0xF294 0xFC94 # ARABIC LIGATURE YEH WITH NOON FINAL FORM +0xF295 0xFC86 # ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM +0xF296 0xFC9F # ARABIC LIGATURE BEH WITH MEEM INITIAL FORM +0xF297 0xFCA4 # ARABIC LIGATURE TEH WITH MEEM INITIAL FORM +0xF298 0xFCD5 # ARABIC LIGATURE NOON WITH MEEM INITIAL FORM +0xF299 0xFCDD # ARABIC LIGATURE YEH WITH MEEM INITIAL FORM +0xF29A 0xFCA8 # ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM +0xF29B 0xFCAA # ARABIC LIGATURE HAH WITH MEEM INITIAL FORM +0xF29C 0xFCAC # ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM +0xF29D 0xFCCC # ARABIC LIGATURE LAM WITH MEEM INITIAL FORM +0xF29E 0xFCD1 # ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM +0xF29F 0xFC32 # ARABIC LIGATURE FEH WITH YEH ISOLATED FORM +0xF2A1 0xFEC2 # ARABIC LETTER TAH FINAL FORM +0xF2A2 0x0637 # ARABIC LETTER TAH +0xF2A2 0xFEC1 # ARABIC LETTER TAH ISOLATED FORM +0xF2A3 0x0638 # ARABIC LETTER ZAH +0xF2A3 0xFEC7 # ARABIC LETTER ZAH INITIAL FORM +0xF2A4 0xFEC8 # ARABIC LETTER ZAH MEDIAL FORM +0xF2A5 0xFEC6 # ARABIC LETTER ZAH FINAL FORM +0xF2A6 0xFEC5 # ARABIC LETTER ZAH ISOLATED FORM +0xF2A7 0xFECB # ARABIC LETTER AIN INITIAL FORM +0xF2A8 0xFECC # ARABIC LETTER AIN MEDIAL FORM +0xF2A9 0xFECA # ARABIC LETTER AIN FINAL FORM +0xF2AA 0x0639 # ARABIC LETTER AIN +0xF2AA 0xFEC9 # ARABIC LETTER AIN ISOLATED FORM +0xF2AB 0xFECF # ARABIC LETTER GHAIN INITIAL FORM +0xF2AC 0xFED0 # ARABIC LETTER GHAIN MEDIAL FORM +0xF2AD 0xFECE # ARABIC LETTER GHAIN FINAL FORM +0xF2AE 0x063A # ARABIC LETTER GHAIN +0xF2AE 0xFECD # ARABIC LETTER GHAIN ISOLATED FORM +0xF2AF 0xFED3 # ARABIC LETTER FEH INITIAL FORM +0xF2B0 0xFED4 # ARABIC LETTER FEH MEDIAL FORM +0xF2B1 0xFED2 # ARABIC LETTER FEH FINAL FORM +0xF2B2 0x0641 # ARABIC LETTER FEH +0xF2B2 0xFED1 # ARABIC LETTER FEH ISOLATED FORM +0xF2B3 0xFED7 # ARABIC LETTER QAF INITIAL FORM +0xF2B4 0xFED8 # ARABIC LETTER QAF MEDIAL FORM +0xF2B5 0xFED6 # ARABIC LETTER QAF FINAL FORM +0xF2B6 0x0642 # ARABIC LETTER QAF +0xF2B6 0xFED5 # ARABIC LETTER QAF ISOLATED FORM +0xF2B7 0xFEDB # ARABIC LETTER KAF INITIAL FORM +0xF2B8 0xFEDC # ARABIC LETTER KAF MEDIAL FORM +0xF2B9 0xFEDA # ARABIC LETTER KAF FINAL FORM +0xF2BA 0x0643 # ARABIC LETTER KAF +0xF2BA 0xFED9 # ARABIC LETTER KAF ISOLATED FORM +0xF2BB 0xFEDF # ARABIC LETTER LAM INITIAL FORM +0xF2BC 0xFEE0 # ARABIC LETTER LAM MEDIAL FORM +0xF2BD 0xFEDE # ARABIC LETTER LAM FINAL FORM +0xF2BE 0x0644 # ARABIC LETTER LAM +0xF2BE 0xFEDD # ARABIC LETTER LAM ISOLATED FORM +0xF2BF 0xFEE3 # ARABIC LETTER MEEM INITIAL FORM +0xF2C0 0xFEE4 # ARABIC LETTER MEEM MEDIAL FORM +0xF2C1 0xFEE2 # ARABIC LETTER MEEM FINAL FORM +0xF2C2 0x0645 # ARABIC LETTER MEEM +0xF2C2 0xFEE1 # ARABIC LETTER MEEM ISOLATED FORM +0xF2C3 0xFEE7 # ARABIC LETTER NOON INITIAL FORM +0xF2C4 0xFEE8 # ARABIC LETTER NOON MEDIAL FORM +0xF2C5 0xFEE6 # ARABIC LETTER NOON FINAL FORM +0xF2C6 0x0646 # ARABIC LETTER NOON +0xF2C6 0xFEE5 # ARABIC LETTER NOON ISOLATED FORM +0xF2C7 0xFEEB # ARABIC LETTER HEH INITIAL FORM +0xF2C8 0xFEEC # ARABIC LETTER HEH MEDIAL FORM +0xF2C9 0xFEEA # ARABIC LETTER HEH FINAL FORM +0xF2CA 0x0647 # ARABIC LETTER HEH +0xF2CA 0xFEE9 # ARABIC LETTER HEH ISOLATED FORM +0xF2CB 0x0648 # ARABIC LETTER WAW +0xF2CB 0xFEED # ARABIC LETTER WAW ISOLATED FORM +0xF2CC 0xFEEE # ARABIC LETTER WAW FINAL FORM +0xF2CD 0xFEF3 # ARABIC LETTER YEH INITIAL FORM +0xF2CE 0xFEF4 # ARABIC LETTER YEH MEDIAL FORM +0xF2CF 0xFEF2 # ARABIC LETTER YEH FINAL FORM +0xF2D0 0x064A # ARABIC LETTER YEH +0xF2D0 0xFEF1 # ARABIC LETTER YEH ISOLATED FORM +0xF2D1 0x0629 # ARABIC LETTER TEH MARBUTA +0xF2D1 0xFE93 # ARABIC LETTER TEH MARBUTA ISOLATED FORM +0xF2D2 0xFE94 # ARABIC LETTER TEH MARBUTA FINAL FORM +0xF2D3 0xFEF0 # ARABIC LETTER ALEF MAKSURA FINAL FORM +0xF2D4 0x0649 # ARABIC LETTER ALEF MAKSURA +0xF2D4 0xFEEF # ARABIC LETTER ALEF MAKSURA ISOLATED FORM +0xF2D5 0x0621 # ARABIC LETTER HAMZA +0xF2D6 0xFE8B # ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM +0xF2D7 0xFE8C # ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM +0xF2D8 0xFE8A # ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM +0xF2D9 0x0626 # ARABIC LETTER YEH WITH HAMZA ABOVE +0xF2D9 0xFE89 # ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM +0xF2DA 0x0624 # ARABIC LETTER WAW WITH HAMZA ABOVE +0xF2DA 0xFE85 # ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM +0xF2DB 0xFE86 # ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM +0xF2DC 0xFEFB # ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM +0xF2DD 0xFEFC # ARABIC LIGATURE LAM WITH ALEF FINAL FORM +0xF2DE 0xFEF7 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM +0xF2DF 0xFEF8 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM +0xF2E0 0xFEF5 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM +0xF2E1 0xFEF6 # ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM +0xF2E2 0xFEF9 # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM +0xF2E3 0xFEFA # ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM +0xF2E4 0x064E # ARABIC FATHA +0xF2E5 0x064F # ARABIC DAMMA +0xF2E6 0x0652 # ARABIC SUKUN +0xF2E7 0x064B # ARABIC FATHATAN +0xF2E8 0x064C # ARABIC DAMMATAN +0xF2E9 0x0651 # ARABIC SHADDA +0xF2EA 0x0650 # ARABIC KASRA +0xF2EB 0x064D # ARABIC KASRATAN +0xF2EC 0xFC60 # ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM +0xF2ED 0xFC61 # ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM +0xF2EF 0xFC5E # ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM +0xF2F0 0xFC62 # ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM +0xF2F1 0xFEC4 # ARABIC LETTER TAH MEDIAL FORM diff --git a/gfx/harfbuzz/src/Makefile.am b/gfx/harfbuzz/src/Makefile.am new file mode 100644 index 0000000000..ff6a6d6f37 --- /dev/null +++ b/gfx/harfbuzz/src/Makefile.am @@ -0,0 +1,620 @@ +# Process this file with automake to produce Makefile.in + +NULL = +SUBDIRS = +DIST_SUBDIRS = +BUILT_SOURCES = +EXTRA_DIST = +CLEANFILES = +DISTCLEANFILES = +MAINTAINERCLEANFILES = +DISTCHECK_CONFIGURE_FLAGS = --enable-introspection +TESTS = +check_PROGRAMS = + +EXTRA_DIST += harfbuzz.cc harfbuzz-subset.cc +EXTRA_DIST += meson.build +EXTRA_DIST += fix_get_types.py relative_to.py + +# Convenience targets: +lib: $(BUILT_SOURCES) libharfbuzz.la +libs: $(BUILT_SOURCES) $(lib_LTLIBRARIES) +tiny: + $(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Os -DHB_TINY $(CPPFLAGS)" libs +tinyz: + $(MAKE) $(AM_MAKEFLAGS) CPPFLAGS="-Oz -DHB_TINY $(CPPFLAGS)" libs + +lib_LTLIBRARIES = libharfbuzz.la + +include Makefile.sources + +HBCFLAGS = +HBLIBS = +HBNONPCLIBS = +HBDEPS = +HBSOURCES = $(HB_BASE_sources) +HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources) +HBHEADERS = $(HB_BASE_headers) + +if HAVE_PTHREAD +HBCFLAGS += $(PTHREAD_CFLAGS) +HBNONPCLIBS += $(PTHREAD_LIBS) +endif + +if HAVE_GLIB +HBCFLAGS += $(GLIB_CFLAGS) +HBLIBS += $(GLIB_LIBS) +HBDEPS += $(GLIB_DEPS) +HBSOURCES += $(HB_GLIB_sources) +HBHEADERS += $(HB_GLIB_headers) +HB_HAS_GLIB_DEF = define HB_HAS_GLIB 1 +else +HB_HAS_GLIB_DEF = undef HB_HAS_GLIB +endif + +if HAVE_FREETYPE +HBCFLAGS += $(FREETYPE_CFLAGS) +HBLIBS += $(FREETYPE_LIBS) +HBDEPS += $(FREETYPE_DEPS) +HBSOURCES += $(HB_FT_sources) +HBHEADERS += $(HB_FT_headers) +HB_HAS_FREETYPE_DEF = define HB_HAS_FREETYPE 1 +else +HB_HAS_FREETYPE_DEF = undef HB_HAS_FREETYPE +endif + +if HAVE_GRAPHITE2 +HBCFLAGS += $(GRAPHITE2_CFLAGS) +HBLIBS += $(GRAPHITE2_LIBS) +HBDEPS += $(GRAPHITE2_DEPS) +HBSOURCES += $(HB_GRAPHITE2_sources) +HBHEADERS += $(HB_GRAPHITE2_headers) +HB_HAS_GRAPHITE_DEF = define HB_HAS_GRAPHITE 1 +else +HB_HAS_GRAPHITE_DEF = undef HB_HAS_GRAPHITE +endif + +if HAVE_UNISCRIBE +HBCFLAGS += $(UNISCRIBE_CFLAGS) +HBNONPCLIBS += $(UNISCRIBE_LIBS) +HBSOURCES += $(HB_UNISCRIBE_sources) +HBHEADERS += $(HB_UNISCRIBE_headers) +HB_HAS_UNISCRIBE_DEF = define HB_HAS_UNISCRIBE 1 +else +HB_HAS_UNISCRIBE_DEF = undef HB_HAS_UNISCRIBE +endif + +if HAVE_DIRECTWRITE +HBCFLAGS += $(DIRECTWRITE_CXXFLAGS) +HBNONPCLIBS += $(DIRECTWRITE_LIBS) +HBSOURCES += $(HB_DIRECTWRITE_sources) +HBHEADERS += $(HB_DIRECTWRITE_headers) +HB_HAS_DIRECTWRITE_DEF = define HB_HAS_DIRECTWRITE 1 +else +HB_HAS_DIRECTWRITE_DEF = undef HB_HAS_DIRECTWRITE +endif + +if HAVE_GDI +HBCFLAGS += $(GDI_CXXFLAGS) +HBNONPCLIBS += $(GDI_LIBS) +HBSOURCES += $(HB_GDI_sources) +HBHEADERS += $(HB_GDI_headers) +HB_HAS_GDI_DEF = define HB_HAS_GDI 1 +else +HB_HAS_GDI_DEF = undef HB_HAS_GDI +endif + +if HAVE_CORETEXT +HBCFLAGS += $(CORETEXT_CFLAGS) +HBNONPCLIBS += $(CORETEXT_LIBS) +HBSOURCES += $(HB_CORETEXT_sources) +HBHEADERS += $(HB_CORETEXT_headers) +HB_HAS_CORETEXT_DEF = define HB_HAS_CORETEXT 1 +else +HB_HAS_CORETEXT_DEF = undef HB_HAS_CORETEXT +endif + +if HAVE_WASM +HBCFLAGS += $(WASM_CFLAGS) +HBNONPCLIBS += $(WASM_LIBS) +HBSOURCES += $(HB_WASM_sources) +HBHEADERS += $(HB_WASM_headers) +HB_HAS_WASM_DEF = define HB_HAS_WASM 1 +else +HB_HAS_WASM_DEF = undef HB_HAS_WASM +endif + + +BUILT_SOURCES += \ + hb-version.h + +$(srcdir)/hb-version.h: hb-version.h.in $(top_srcdir)/configure.ac + $(AM_V_GEN) $(SED) \ + -e 's/[@]HB_VERSION_MAJOR@/$(HB_VERSION_MAJOR)/' \ + -e 's/[@]HB_VERSION_MINOR@/$(HB_VERSION_MINOR)/' \ + -e 's/[@]HB_VERSION_MICRO@/$(HB_VERSION_MICRO)/' \ + -e 's/[@]HB_VERSION@/$(HB_VERSION)/' \ + "$<" > "$@" || ($(RM) "$@"; false) + +# Put the library together + +HBLIBS += $(HBNONPCLIBS) + +if OS_WIN32 +export_symbols = -export-symbols harfbuzz.def +harfbuzz_def_dependency = harfbuzz.def +export_symbols_subset = -export-symbols harfbuzz-subset.def +harfbuzz_subset_def_dependency = harfbuzz-subset.def +export_symbols_cairo = -export-symbols harfbuzz-cairo.def +harfbuzz_cairo_def_dependency = harfbuzz-cairo.def +export_symbols_icu = -export-symbols harfbuzz-icu.def +harfbuzz_icu_def_dependency = harfbuzz-icu.def +export_symbols_gobject = -export-symbols harfbuzz-gobject.def +harfbuzz_gobject_def_dependency = harfbuzz-gobject.def +chosen_linker = $(CXXLINK) +else +if WITH_LIBSTDCXX +chosen_linker = $(CXXLINK) +else +if HAVE_GCC +# Use a C linker for GCC, not C++; Don't link to libstdc++ +chosen_linker = $(LINK) +else +chosen_linker = $(CXXLINK) +endif +endif +endif + +@CODE_COVERAGE_RULES@ + +base_link_flags = $(AM_LDFLAGS) -lm -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined +libharfbuzz_la_LINK = $(chosen_linker) $(libharfbuzz_la_LDFLAGS) +libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) +libharfbuzz_la_CPPFLAGS = $(HBCFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_la_LDFLAGS = $(base_link_flags) $(export_symbols) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_la_LIBADD = $(HBLIBS) +EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency) +pkginclude_HEADERS = $(HBHEADERS) +nodist_pkginclude_HEADERS = +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = harfbuzz.pc +cmakedir = $(libdir)/cmake/harfbuzz +cmake_DATA = harfbuzz-config.cmake +EXTRA_DIST += hb-version.h.in hb-features.h.in harfbuzz.pc.in harfbuzz-config.cmake.in + +lib_LTLIBRARIES += libharfbuzz-subset.la +libharfbuzz_subset_la_LINK = $(chosen_linker) $(libharfbuzz_subset_la_LDFLAGS) +libharfbuzz_subset_la_SOURCES = $(HB_SUBSET_sources) +libharfbuzz_subset_la_CPPFLAGS = $(HBCFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_subset_la_LDFLAGS = $(base_link_flags) $(export_symbols_subset) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_subset_la_LIBADD = libharfbuzz.la +EXTRA_libharfbuzz_subset_la_DEPENDENCIES = $(harfbuzz_subset_def_dependency) +pkginclude_HEADERS += $(HB_SUBSET_headers) +pkgconfig_DATA += harfbuzz-subset.pc +EXTRA_DIST += harfbuzz-subset.pc.in + +harfbuzz-subset.cc: Makefile.sources + $(AM_V_GEN) \ + LANG=C; \ + for f in \ + $(HB_BASE_sources) \ + $(HB_SUBSET_sources) \ + ; do echo '#include "'$$f'"'; done | \ + sort -u | \ + grep '[.]cc"' > $(srcdir)/harfbuzz-subset.cc \ + || ($(RM) $(srcdir)/harfbuzz-subset.cc; false) +BUILT_SOURCES += harfbuzz-subset.cc + +lib_LTLIBRARIES += libharfbuzz-cairo.la +libharfbuzz_cairo_la_LINK = $(chosen_linker) $(libharfbuzz_cairo_la_LDFLAGS) +libharfbuzz_cairo_la_SOURCES = $(HB_CAIRO_sources) +libharfbuzz_cairo_la_CPPFLAGS = $(HBCFLAGS) $(CAIRO_CFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_cairo_la_LDFLAGS = $(base_link_flags) $(export_symbols_cairo) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_cairo_la_LIBADD = $(CAIRO_LIBS) libharfbuzz.la +EXTRA_libharfbuzz_cairo_la_DEPENDENCIES = $(harfbuzz_cairo_def_dependency) +pkginclude_HEADERS += $(HB_CAIRO_headers) +pkgconfig_DATA += harfbuzz-cairo.pc +EXTRA_DIST += harfbuzz-cairo.pc.in + +if HAVE_ICU +if HAVE_ICU_BUILTIN +HBCFLAGS += $(ICU_CFLAGS) +HBLIBS += $(ICU_LIBS) +HBSOURCES += $(HB_ICU_sources) +HBHEADERS += $(HB_ICU_headers) +HB_HAS_ICU_DEF = define HB_HAS_ICU 1 +else +lib_LTLIBRARIES += libharfbuzz-icu.la +libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources) +libharfbuzz_icu_la_CPPFLAGS = $(HBCFLAGS) $(ICU_CFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_icu_la_LDFLAGS = $(base_link_flags) $(export_symbols_icu) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la +EXTRA_libharfbuzz_icu_la_DEPENDENCIES = $(harfbuzz_icu_def_dependency) +pkginclude_HEADERS += $(HB_ICU_headers) +pkgconfig_DATA += harfbuzz-icu.pc +HB_HAS_ICU_DEF = undef HB_HAS_ICU +endif +endif +EXTRA_DIST += harfbuzz-icu.pc.in + +if HAVE_GOBJECT +lib_LTLIBRARIES += libharfbuzz-gobject.la +libharfbuzz_gobject_la_LINK = $(chosen_linker) $(libharfbuzz_gobject_la_LDFLAGS) +libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_DIST_sources) +nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_NODIST_sources) +libharfbuzz_gobject_la_CPPFLAGS = $(HBCFLAGS) $(GOBJECT_CFLAGS) $(CODE_COVERAGE_CFLAGS) +libharfbuzz_gobject_la_LDFLAGS = $(base_link_flags) $(CODE_COVERAGE_LDFLAGS) +libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la +EXTRA_libharfbuzz_gobject_la_DEPENDENCIES = $(harfbuzz_gobject_def_dependency) +pkginclude_HEADERS += $(HB_GOBJECT_DIST_headers) +nodist_pkginclude_HEADERS += $(HB_GOBJECT_NODIST_headers) +pkgconfig_DATA += harfbuzz-gobject.pc + +BUILT_SOURCES += \ + $(HB_GOBJECT_ENUM_sources) \ + $(HB_GOBJECT_ENUM_headers) \ + $(NULL) +DISTCLEANFILES += \ + $(HB_GOBJECT_ENUM_sources) \ + $(HB_GOBJECT_ENUM_headers) \ + $(NULL) +hb-gobject-enums.%: hb-gobject-enums.%.tmpl $(HBHEADERS) + $(AM_V_GEN) PYTHONIOENCODING=UTF-8 $(GLIB_MKENUMS) \ + --identifier-prefix hb_ --symbol-prefix hb_gobject \ + --template $^ | \ + sed 's/_t_get_type/_get_type/g; s/_T (/ (/g' > "$@" \ + || ($(RM) "$@"; false) +HB_HAS_GOBJECT_DEF = define HB_HAS_GOBJECT 1 +else +HB_HAS_GOBJECT_DEF = undef HB_HAS_GOBJECT +endif +EXTRA_DIST += \ + harfbuzz-gobject.pc.in \ + hb-gobject-enums.cc.tmpl \ + hb-gobject-enums.h.tmpl \ + $(NULL) + + +BUILT_SOURCES += \ + hb-features.h +DISTCLEANFILES += \ + hb-features.h + +hb-features.h: hb-features.h.in $(top_builddir)/config.status + $(AM_V_GEN) $(SED) \ + -e 's/mesondefine HB_HAS_CAIRO/$(HB_HAS_CAIRO_DEF)/' \ + -e 's/mesondefine HB_HAS_CORETEXT/$(HB_HAS_CORETEXT_DEF)/' \ + -e 's/mesondefine HB_HAS_DIRECTWRITE/$(HB_HAS_DIRECTWRITE_DEF)/' \ + -e 's/mesondefine HB_HAS_FREETYPE/$(HB_HAS_FREETYPE_DEF)/' \ + -e 's/mesondefine HB_HAS_GDI/$(HB_HAS_GDI_DEF)/' \ + -e 's/mesondefine HB_HAS_GDI/$(HB_HAS_GDI_DEF)/' \ + -e 's/mesondefine HB_HAS_GLIB/$(HB_HAS_GLIB_DEF)/' \ + -e 's/mesondefine HB_HAS_GOBJECT/$(HB_HAS_GOBJECT_DEF)/' \ + -e 's/mesondefine HB_HAS_GRAPHITE/$(HB_HAS_GRAPHITE_DEF)/' \ + -e 's/mesondefine HB_HAS_ICU/$(HB_HAS_ICU_DEF)/' \ + -e 's/mesondefine HB_HAS_UNISCRIBE/$(HB_HAS_UNISCRIBE_DEF)/' \ + -e 's/mesondefine HB_HAS_WASM/$(HB_HAS_WASM_DEF)/' \ + "$<" > "$@" || ($(RM) "$@"; false) + + +%.pc: %.pc.in $(top_builddir)/config.status + $(AM_V_GEN) \ + $(SED) -e 's@%prefix%@$(prefix)@g' \ + -e 's@%exec_prefix%@$(exec_prefix)@g' \ + -e 's@%libdir%@$(libdir)@g' \ + -e 's@%includedir%@$(includedir)@g' \ + -e 's@%libs_private%@$(HBNONPCLIBS)@g' \ + -e 's@%requires_private%@$(HBDEPS)@g' \ + -e 's@%VERSION%@$(VERSION)@g' \ + "$<" > "$@" \ + || ($(RM) "$@"; false) + +CLEANFILES += $(pkgconfig_DATA) + + +DEF_FILES = harfbuzz.def harfbuzz-subset.def harfbuzz-icu.def harfbuzz-deprecated-symbols.txt +if HAVE_GOBJECT +DEF_FILES += harfbuzz-gobject.def +endif +check: $(DEF_FILES) # For check-symbols.sh +CLEANFILES += $(DEF_FILES) +harfbuzz.def: $(top_builddir)/config.status +harfbuzz.def: $(HBHEADERS) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-subset.def: $(HB_SUBSET_headers) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-cairo.def: $(HB_CAIRO_headers) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-icu.def: $(HB_ICU_headers) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-gobject.def: $(HB_GOBJECT_headers) + $(AM_V_GEN) $(srcdir)/gen-def.py "$@" $^ +harfbuzz-deprecated-symbols.txt: $(srcdir)/hb-deprecated.h + $(AM_V_GEN) PLAIN_LIST=1 $(srcdir)/gen-def.py "$@" $^ + + +GENERATORS = \ + gen-arabic-joining-list.py \ + gen-arabic-table.py \ + gen-def.py \ + gen-emoji-table.py \ + gen-harfbuzzcc.py \ + gen-hb-version.py \ + gen-indic-table.py \ + gen-os2-unicode-ranges.py \ + gen-ragel-artifacts.py \ + gen-tag-table.py \ + gen-ucd-table.py \ + gen-use-table.py \ + gen-vowel-constraints.py \ + $(NULL) +EXTRA_DIST += $(GENERATORS) + +built-sources: $(BUILT_SOURCES) + +.PHONY: built-sources + +RAGEL_GENERATED = \ + $(patsubst %,$(srcdir)/%,$(HB_BASE_RAGEL_GENERATED_sources)) \ + $(NULL) +BUILT_SOURCES += $(RAGEL_GENERATED) +EXTRA_DIST += \ + $(HB_BASE_RAGEL_sources) \ + $(NULL) +# We decided to add ragel-generated files to git... +#MAINTAINERCLEANFILES += $(RAGEL_GENERATED) +$(srcdir)/%.hh: $(srcdir)/%.rl + $(AM_V_GEN)(cd $(srcdir) && $(RAGEL) -e -F1 -o "$*.hh" "$*.rl") \ + || ($(RM) "$@"; false) + +harfbuzz.cc: Makefile.sources + $(AM_V_GEN) \ + LANG=C; \ + for f in \ + $(HB_BASE_sources) \ + $(HB_GLIB_sources) \ + $(HB_FT_sources) \ + $(HB_GRAPHITE2_sources) \ + $(HB_UNISCRIBE_sources) \ + $(HB_GDI_sources) \ + $(HB_DIRECTWRITE_sources) \ + $(HB_CORETEXT_sources) \ + ; do echo '#include "'$$f'"'; done | \ + sort -u | \ + grep '[.]cc"' > $(srcdir)/harfbuzz.cc \ + || ($(RM) $(srcdir)/harfbuzz.cc; false) +BUILT_SOURCES += harfbuzz.cc + +noinst_PROGRAMS = \ + main \ + test \ + test-buffer-serialize \ + test-ot-meta \ + test-ot-name \ + test-ot-glyphname \ + test-gpos-size-params \ + test-gsub-get-alternates \ + test-gsub-would-substitute \ + test-use-table \ + $(NULL) +bin_PROGRAMS = + +main_SOURCES = main.cc +main_CPPFLAGS = $(HBCFLAGS) +main_LDADD = libharfbuzz.la $(HBLIBS) + +test_SOURCES = test.cc +test_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) +test_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) + +test_buffer_serialize_SOURCES = test-buffer-serialize.cc +test_buffer_serialize_CPPFLAGS = $(HBCFLAGS) +test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS) + +test_ot_meta_SOURCES = test-ot-meta.cc +test_ot_meta_CPPFLAGS = $(HBCFLAGS) +test_ot_meta_LDADD = libharfbuzz.la $(HBLIBS) + +test_ot_name_SOURCES = test-ot-name.cc +test_ot_name_CPPFLAGS = $(HBCFLAGS) +test_ot_name_LDADD = libharfbuzz.la $(HBLIBS) + +test_ot_glyphname_SOURCES = test-ot-glyphname.cc +test_ot_glyphname_CPPFLAGS = $(HBCFLAGS) +test_ot_glyphname_LDADD = libharfbuzz.la $(HBLIBS) + +test_use_table_SOURCES = test-use-table.cc +test_use_table_CPPFLAGS = $(HBCFLAGS) +test_use_table_LDADD = libharfbuzz.la $(HBLIBS) + +test_gpos_size_params_SOURCES = test-gpos-size-params.cc +test_gpos_size_params_CPPFLAGS = $(HBCFLAGS) +test_gpos_size_params_LDADD = libharfbuzz.la $(HBLIBS) + +test_gsub_get_alternates_SOURCES = test-gsub-get-alternates.cc +test_gsub_get_alternates_CPPFLAGS = $(HBCFLAGS) +test_gsub_get_alternates_LDADD = libharfbuzz.la $(HBLIBS) + +test_gsub_would_substitute_SOURCES = test-gsub-would-substitute.cc +test_gsub_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS) +test_gsub_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS) + +COMPILED_TESTS = \ + test-algs \ + test-array \ + test-bimap \ + test-iter \ + test-machinery \ + test-map \ + test-multimap \ + test-number \ + test-ot-tag \ + test-priority-queue \ + test-set \ + test-serialize \ + test-unicode-ranges \ + test-vector \ + test-repacker \ + test-classdef-graph \ + test-instancer-solver \ + test-tuple-varstore \ + test-item-varstore \ + $(NULL) +COMPILED_TESTS_CPPFLAGS = $(HBCFLAGS) -DMAIN -UNDEBUG +COMPILED_TESTS_LDADD = libharfbuzz.la $(HBLIBS) +check_PROGRAMS += $(COMPILED_TESTS) +TESTS += $(COMPILED_TESTS) + +test_algs_SOURCES = test-algs.cc hb-static.cc +test_algs_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_algs_LDADD = $(COMPILED_TESTS_LDADD) + +test_array_SOURCES = test-array.cc +test_array_CPPFLAGS = $(HBCFLAGS) +test_array_LDADD = libharfbuzz.la $(HBLIBS) + +test_bimap_SOURCES = test-bimap.cc hb-static.cc +test_bimap_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_bimap_LDADD = $(COMPILED_TESTS_LDADD) + +test_iter_SOURCES = test-iter.cc hb-static.cc +test_iter_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_iter_LDADD = $(COMPILED_TESTS_LDADD) + +test_machinery_SOURCES = test-machinery.cc hb-static.cc +test_machinery_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_machinery_LDADD = $(COMPILED_TESTS_LDADD) + +test_map_SOURCES = test-map.cc hb-static.cc +test_map_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_map_LDADD = $(COMPILED_TESTS_LDADD) + +test_multimap_SOURCES = test-multimap.cc hb-static.cc +test_multimap_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_multimap_LDADD = $(COMPILED_TESTS_LDADD) + +test_number_SOURCES = test-number.cc hb-number.cc +test_number_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_number_LDADD = $(COMPILED_TESTS_LDADD) + +test_ot_tag_SOURCES = hb-ot-tag.cc +test_ot_tag_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_ot_tag_LDADD = $(COMPILED_TESTS_LDADD) + +test_priority_queue_SOURCES = test-priority-queue.cc hb-static.cc +test_priority_queue_CPPFLAGS = $(HBCFLAGS) +test_priority_queue_LDADD = libharfbuzz.la $(HBLIBS) + +test_repacker_SOURCES = test-repacker.cc hb-static.cc graph/gsubgpos-context.cc +test_repacker_CPPFLAGS = $(HBCFLAGS) +test_repacker_LDADD = libharfbuzz.la libharfbuzz-subset.la $(HBLIBS) + +test_classdef_graph_SOURCES = graph/test-classdef-graph.cc hb-static.cc graph/gsubgpos-context.cc +test_classdef_graph_CPPFLAGS = $(HBCFLAGS) +test_classdef_graph_LDADD = libharfbuzz.la libharfbuzz-subset.la $(HBLIBS) + +test_set_SOURCES = test-set.cc hb-static.cc +test_set_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_set_LDADD = $(COMPILED_TESTS_LDADD) + +test_serialize_SOURCES = test-serialize.cc hb-static.cc +test_serialize_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_serialize_LDADD = $(COMPILED_TESTS_LDADD) + +test_unicode_ranges_SOURCES = test-unicode-ranges.cc +test_unicode_ranges_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_unicode_ranges_LDADD = $(COMPILED_TESTS_LDADD) + +test_vector_SOURCES = test-vector.cc hb-static.cc +test_vector_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_vector_LDADD = $(COMPILED_TESTS_LDADD) + +test_instancer_solver_SOURCES = test-subset-instancer-solver.cc hb-subset-instancer-solver.cc hb-static.cc +test_instancer_solver_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_instancer_solver_LDADD = $(COMPILED_TESTS_LDADD) + +test_tuple_varstore_SOURCES = test-tuple-varstore.cc hb-subset-instancer-solver.cc hb-static.cc +test_tuple_varstore_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_tuple_varstore_LDADD = $(COMPILED_TESTS_LDADD) + +test_item_varstore_SOURCES = test-item-varstore.cc hb-subset-instancer-solver.cc hb-static.cc +test_item_varstore_CPPFLAGS = $(COMPILED_TESTS_CPPFLAGS) +test_item_varstore_LDADD = $(COMPILED_TESTS_LDADD) + +dist_check_SCRIPTS = \ + check-c-linkage-decls.py \ + check-externs.py \ + check-header-guards.py \ + check-includes.py \ + check-static-inits.py \ + check-symbols.py \ + $(NULL) +TESTS += $(dist_check_SCRIPTS) + +if !WITH_LIBSTDCXX +dist_check_SCRIPTS += \ + check-libstdc++.py \ + $(NULL) +endif + +TESTS_ENVIRONMENT = \ + srcdir="$(srcdir)" \ + base_srcdir="$(srcdir)" \ + builddir="$(builddir)" \ + MAKE="$(MAKE) $(AM_MAKEFLAGS)" \ + HBSOURCES="$(HBSOURCES)" \ + HBHEADERS="$(HBHEADERS)" \ + LDD="$(LDD)" \ + NM="$(NM)" \ + OBJDUMP="$(OBJDUMP)" \ + OTOOL="$(OTOOL)" \ + $(NULL) + +if HAVE_INTROSPECTION + +-include $(INTROSPECTION_MAKEFILE) +INTROSPECTION_GIRS = HarfBuzz-0.0.gir # What does the 0 mean anyway?! +INTROSPECTION_SCANNER_ARGS = \ + -I$(srcdir) \ + --warn-all --verbose \ + --namespace=HarfBuzz \ + --nsversion=0.0 \ + --symbol-prefix=hb \ + --symbol-prefix=hb_gobject \ + --identifier-prefix=hb_ \ + --pkg-export=harfbuzz-gobject \ + --c-include=hb-gobject.h +INTROSPECTION_COMPILER_ARGS = --includedir=$(srcdir) +INTROSPECTION_SCANNER_ENV = CC="$(CC)" + +HarfBuzz-0.0.gir: libharfbuzz.la libharfbuzz-gobject.la +HarfBuzz_0_0_gir_INCLUDES = GObject-2.0 freetype2-2.0 +HarfBuzz_0_0_gir_CFLAGS = \ + $(INCLUDES) \ + $(HBCFLAGS) \ + -DHB_NO_SINGLE_HEADER_ERROR \ + -DHAVE_GOBJECT \ + -DHB_EXTERN= \ + $(NULL) +HarfBuzz_0_0_gir_LIBS = \ + libharfbuzz.la \ + libharfbuzz-gobject.la \ + $(NULL) +HarfBuzz_0_0_gir_FILES = \ + $(HBHEADERS) \ + $(HBSOURCES) \ + $(HB_GOBJECT_sources) \ + $(HB_GOBJECT_headers) \ + $(NULL) + +girdir = $(datadir)/gir-1.0 +gir_DATA = $(INTROSPECTION_GIRS) + +typelibdir = $(libdir)/girepository-1.0 +typelib_DATA = $(INTROSPECTION_GIRS:.gir=.typelib) + +CLEANFILES += $(gir_DATA) $(typelib_DATA) + +endif + +-include $(top_srcdir)/git.mk diff --git a/gfx/harfbuzz/src/Makefile.sources b/gfx/harfbuzz/src/Makefile.sources new file mode 100644 index 0000000000..fbbff5325e --- /dev/null +++ b/gfx/harfbuzz/src/Makefile.sources @@ -0,0 +1,416 @@ +# Base and default-included sources and headers + +HB_BASE_sources = \ + hb-aat-layout-ankr-table.hh \ + hb-aat-layout-bsln-table.hh \ + hb-aat-layout-common.hh \ + hb-aat-layout-feat-table.hh \ + hb-aat-layout-just-table.hh \ + hb-aat-layout-kerx-table.hh \ + hb-aat-layout-morx-table.hh \ + hb-aat-layout-opbd-table.hh \ + hb-aat-layout-trak-table.hh \ + hb-aat-layout.cc \ + hb-aat-layout.hh \ + hb-aat-ltag-table.hh \ + hb-aat-map.cc \ + hb-aat-map.hh \ + hb-algs.hh \ + hb-array.hh \ + hb-atomic.hh \ + hb-bimap.hh \ + hb-bit-page.hh \ + hb-bit-set.hh \ + hb-bit-set-invertible.hh \ + hb-blob.cc \ + hb-blob.hh \ + hb-buffer-serialize.cc \ + hb-buffer-verify.cc \ + hb-buffer.cc \ + hb-buffer.hh \ + hb-cache.hh \ + hb-cff-interp-common.hh \ + hb-cff-interp-cs-common.hh \ + hb-cff-interp-dict-common.hh \ + hb-cff1-interp-cs.hh \ + hb-cff2-interp-cs.hh \ + hb-common.cc \ + hb-config.hh \ + hb-debug.hh \ + hb-dispatch.hh \ + hb-draw.cc \ + hb-draw.hh \ + hb-face.cc \ + hb-face.hh \ + hb-face-builder.cc \ + hb-fallback-shape.cc \ + hb-font.cc \ + hb-font.hh \ + hb-iter.hh \ + hb-kern.hh \ + hb-limits.hh \ + hb-machinery.hh \ + hb-map.cc \ + hb-map.hh \ + hb-meta.hh \ + hb-ms-feature-ranges.hh \ + hb-multimap.hh \ + hb-mutex.hh \ + hb-null.hh \ + hb-number.cc \ + hb-number.hh \ + hb-object.hh \ + hb-open-file.hh \ + hb-open-type.hh \ + hb-ot-cff-common.hh \ + hb-ot-cff1-std-str.hh \ + hb-ot-cff1-table.cc \ + hb-ot-cff1-table.hh \ + hb-ot-cff2-table.cc \ + hb-ot-cff2-table.hh \ + hb-ot-cmap-table.hh \ + hb-ot-color.cc \ + hb-ot-face-table-list.hh \ + hb-ot-face.cc \ + hb-ot-face.hh \ + hb-ot-font.cc \ + hb-ot-gasp-table.hh \ + hb-ot-glyf-table.hh \ + hb-ot-hdmx-table.hh \ + hb-ot-head-table.hh \ + hb-ot-hhea-table.hh \ + hb-ot-hmtx-table.hh \ + hb-ot-kern-table.hh \ + hb-ot-layout-base-table.hh \ + hb-ot-layout-common.hh \ + hb-ot-layout-gdef-table.hh \ + hb-ot-layout-gpos-table.hh \ + hb-outline.hh \ + hb-outline.cc \ + hb-paint.cc \ + hb-paint.hh \ + hb-paint-extents.cc \ + hb-paint-extents.hh \ + hb-ot-layout-gsub-table.hh \ + OT/Color/CBDT/CBDT.hh \ + OT/Color/COLR/COLR.hh \ + OT/Color/CPAL/CPAL.hh \ + OT/Color/sbix/sbix.hh \ + OT/Color/svg/svg.hh \ + OT/glyf/glyf.hh \ + OT/glyf/glyf-helpers.hh \ + OT/glyf/loca.hh \ + OT/glyf/path-builder.hh \ + OT/glyf/Glyph.hh \ + OT/glyf/GlyphHeader.hh \ + OT/glyf/SimpleGlyph.hh \ + OT/glyf/coord-setter.hh \ + OT/glyf/composite-iter.hh \ + OT/glyf/CompositeGlyph.hh \ + OT/glyf/VarCompositeGlyph.hh \ + OT/glyf/SubsetGlyph.hh \ + OT/Layout/types.hh \ + OT/Layout/Common/Coverage.hh \ + OT/Layout/Common/CoverageFormat1.hh \ + OT/Layout/Common/CoverageFormat2.hh \ + OT/Layout/Common/RangeRecord.hh \ + OT/Layout/GDEF/GDEF.hh \ + OT/Layout/GPOS/AnchorFormat1.hh \ + OT/Layout/GPOS/AnchorFormat2.hh \ + OT/Layout/GPOS/AnchorFormat3.hh \ + OT/Layout/GPOS/Anchor.hh \ + OT/Layout/GPOS/AnchorMatrix.hh \ + OT/Layout/GPOS/ChainContextPos.hh \ + OT/Layout/GPOS/Common.hh \ + OT/Layout/GPOS/ContextPos.hh \ + OT/Layout/GPOS/CursivePosFormat1.hh \ + OT/Layout/GPOS/CursivePos.hh \ + OT/Layout/GPOS/ExtensionPos.hh \ + OT/Layout/GPOS/GPOS.hh \ + OT/Layout/GPOS/LigatureArray.hh \ + OT/Layout/GPOS/MarkArray.hh \ + OT/Layout/GPOS/MarkBasePosFormat1.hh \ + OT/Layout/GPOS/MarkBasePos.hh \ + OT/Layout/GPOS/MarkLigPosFormat1.hh \ + OT/Layout/GPOS/MarkLigPos.hh \ + OT/Layout/GPOS/MarkMarkPosFormat1.hh \ + OT/Layout/GPOS/MarkMarkPos.hh \ + OT/Layout/GPOS/MarkRecord.hh \ + OT/Layout/GPOS/PairPosFormat1.hh \ + OT/Layout/GPOS/PairPosFormat2.hh \ + OT/Layout/GPOS/PairPos.hh \ + OT/Layout/GPOS/PairSet.hh \ + OT/Layout/GPOS/PairValueRecord.hh \ + OT/Layout/GPOS/PosLookup.hh \ + OT/Layout/GPOS/PosLookupSubTable.hh \ + OT/Layout/GPOS/SinglePosFormat1.hh \ + OT/Layout/GPOS/SinglePosFormat2.hh \ + OT/Layout/GPOS/SinglePos.hh \ + OT/Layout/GPOS/ValueFormat.hh \ + OT/Layout/GSUB/AlternateSet.hh \ + OT/Layout/GSUB/AlternateSubstFormat1.hh \ + OT/Layout/GSUB/AlternateSubst.hh \ + OT/Layout/GSUB/ChainContextSubst.hh \ + OT/Layout/GSUB/Common.hh \ + OT/Layout/GSUB/ContextSubst.hh \ + OT/Layout/GSUB/ExtensionSubst.hh \ + OT/Layout/GSUB/GSUB.hh \ + OT/Layout/GSUB/Ligature.hh \ + OT/Layout/GSUB/LigatureSet.hh \ + OT/Layout/GSUB/LigatureSubstFormat1.hh \ + OT/Layout/GSUB/LigatureSubst.hh \ + OT/Layout/GSUB/MultipleSubstFormat1.hh \ + OT/Layout/GSUB/MultipleSubst.hh \ + OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh \ + OT/Layout/GSUB/ReverseChainSingleSubst.hh \ + OT/Layout/GSUB/Sequence.hh \ + OT/Layout/GSUB/SingleSubstFormat1.hh \ + OT/Layout/GSUB/SingleSubstFormat2.hh \ + OT/Layout/GSUB/SingleSubst.hh \ + OT/Layout/GSUB/SubstLookup.hh \ + OT/Layout/GSUB/SubstLookupSubTable.hh \ + OT/name/name.hh \ + hb-ot-layout-gsubgpos.hh \ + hb-ot-layout-jstf-table.hh \ + hb-ot-layout.cc \ + hb-ot-layout.hh \ + hb-ot-map.cc \ + hb-ot-map.hh \ + hb-ot-math-table.hh \ + hb-ot-math.cc \ + hb-ot-maxp-table.hh \ + hb-ot-meta-table.hh \ + hb-ot-meta.cc \ + hb-ot-metrics.cc \ + hb-ot-metrics.hh \ + hb-ot-name-language-static.hh \ + hb-ot-name-language.hh \ + hb-ot-name-table.hh \ + hb-ot-name.cc \ + hb-ot-os2-table.hh \ + hb-ot-os2-unicode-ranges.hh \ + hb-ot-post-macroman.hh \ + hb-ot-post-table.hh \ + hb-ot-shaper-arabic-fallback.hh \ + hb-ot-shaper-arabic-joining-list.hh \ + hb-ot-shaper-arabic-pua.hh \ + hb-ot-shaper-arabic-table.hh \ + hb-ot-shaper-arabic-win1256.hh \ + hb-ot-shaper-arabic.cc \ + hb-ot-shaper-arabic.hh \ + hb-ot-shaper-default.cc \ + hb-ot-shaper-hangul.cc \ + hb-ot-shaper-hebrew.cc \ + hb-ot-shaper-indic-table.cc \ + hb-ot-shaper-indic.cc \ + hb-ot-shaper-indic.hh \ + hb-ot-shaper-khmer.cc \ + hb-ot-shaper-myanmar.cc \ + hb-ot-shaper-syllabic.cc \ + hb-ot-shaper-syllabic.hh \ + hb-ot-shaper-thai.cc \ + hb-ot-shaper-use-table.hh \ + hb-ot-shaper-use.cc \ + hb-ot-shaper-vowel-constraints.cc \ + hb-ot-shaper-vowel-constraints.hh \ + hb-ot-shaper.hh \ + hb-ot-shape-fallback.cc \ + hb-ot-shape-fallback.hh \ + hb-ot-shape-normalize.cc \ + hb-ot-shape-normalize.hh \ + hb-ot-shape.cc \ + hb-ot-shape.hh \ + hb-ot-stat-table.hh \ + hb-ot-tag-table.hh \ + hb-ot-tag.cc \ + hb-ot-var-avar-table.hh \ + hb-ot-var-common.hh \ + hb-ot-var-cvar-table.hh \ + hb-ot-var-fvar-table.hh \ + hb-ot-var-gvar-table.hh \ + hb-ot-var-hvar-table.hh \ + hb-ot-var-mvar-table.hh \ + hb-ot-var.cc \ + hb-ot-vorg-table.hh \ + hb-pool.hh \ + hb-sanitize.hh \ + hb-serialize.hh \ + hb-set-digest.hh \ + hb-set.cc \ + hb-set.hh \ + hb-shape-plan.cc \ + hb-shape-plan.hh \ + hb-shape.cc \ + hb-shaper-impl.hh \ + hb-shaper-list.hh \ + hb-shaper.cc \ + hb-shaper.hh \ + hb-static.cc \ + hb-string-array.hh \ + hb-style.cc \ + hb-ucd-table.hh \ + hb-ucd.cc \ + hb-unicode-emoji-table.hh \ + hb-unicode.cc \ + hb-unicode.hh \ + hb-utf.hh \ + hb-vector.hh \ + hb-priority-queue.hh \ + hb.hh \ + $(NULL) + +HB_BASE_RAGEL_GENERATED_sources = \ + hb-buffer-deserialize-json.hh \ + hb-buffer-deserialize-text-glyphs.hh \ + hb-buffer-deserialize-text-unicode.hh \ + hb-number-parser.hh \ + hb-ot-shaper-indic-machine.hh \ + hb-ot-shaper-khmer-machine.hh \ + hb-ot-shaper-myanmar-machine.hh \ + hb-ot-shaper-use-machine.hh \ + $(NULL) +HB_BASE_RAGEL_sources = \ + hb-buffer-deserialize-json.rl \ + hb-buffer-deserialize-text-glyphs.rl \ + hb-buffer-deserialize-text-unicode.rl \ + hb-number-parser.rl \ + hb-ot-shaper-indic-machine.rl \ + hb-ot-shaper-khmer-machine.rl \ + hb-ot-shaper-myanmar-machine.rl \ + hb-ot-shaper-use-machine.rl \ + $(NULL) + +HB_BASE_headers = \ + hb-aat-layout.h \ + hb-aat.h \ + hb-blob.h \ + hb-buffer.h \ + hb-common.h \ + hb-cplusplus.hh \ + hb-deprecated.h \ + hb-draw.h \ + hb-face.h \ + hb-font.h \ + hb-map.h \ + hb-ot-color.h \ + hb-ot-deprecated.h \ + hb-ot-font.h \ + hb-ot-layout.h \ + hb-ot-math.h \ + hb-ot-meta.h \ + hb-ot-metrics.h \ + hb-ot-name.h \ + hb-ot-shape.h \ + hb-ot-var.h \ + hb-ot.h \ + hb-paint.h \ + hb-set.h \ + hb-shape-plan.h \ + hb-shape.h \ + hb-style.h \ + hb-unicode.h \ + hb-version.h \ + hb.h \ + $(NULL) + +# Optional Sources and Headers with external deps + +HB_FT_sources = hb-ft.cc hb-ft-colr.hh +HB_FT_headers = hb-ft.h + +HB_GLIB_sources = hb-glib.cc +HB_GLIB_headers = hb-glib.h + +HB_GRAPHITE2_sources = hb-graphite2.cc +HB_GRAPHITE2_headers = hb-graphite2.h + +# System-dependent sources and headers + +HB_CORETEXT_sources = hb-coretext.cc +HB_CORETEXT_headers = hb-coretext.h + +HB_DIRECTWRITE_sources = hb-directwrite.cc +HB_DIRECTWRITE_headers = hb-directwrite.h + +HB_GDI_sources = hb-gdi.cc +HB_GDI_headers = hb-gdi.h + +HB_UNISCRIBE_sources = hb-uniscribe.cc +HB_UNISCRIBE_headers = hb-uniscribe.h + +HB_ICU_sources = hb-icu.cc +HB_ICU_headers = hb-icu.h + +HB_WASM_sources = \ + hb-wasm-api.cc \ + hb-wasm-api.hh \ + hb-wasm-api-blob.hh \ + hb-wasm-api-buffer.hh \ + hb-wasm-api-common.hh \ + hb-wasm-api-face.hh \ + hb-wasm-api-font.hh \ + hb-wasm-api-shape.hh \ + hb-wasm-shape.cc \ + $(NULL) +HB_WASM_headers = hb-wasm-api.h + +# Sources for libharfbuzz-subset +HB_SUBSET_sources = \ + hb-number.cc \ + hb-number.hh \ + hb-ot-cff1-table.cc \ + hb-ot-cff2-table.cc \ + hb-ot-post-table-v2subset.hh \ + hb-static.cc \ + hb-subset-cff-common.cc \ + hb-subset-cff-common.hh \ + hb-subset-cff1.cc \ + hb-subset-cff2.cc \ + hb-subset-input.cc \ + hb-subset-input.hh \ + hb-subset-instancer-solver.hh \ + hb-subset-instancer-solver.cc \ + hb-subset-accelerator.hh \ + hb-subset-plan.cc \ + hb-subset-plan.hh \ + hb-subset-plan-member-list.hh \ + hb-subset-repacker.cc \ + hb-subset.cc \ + hb-subset.hh \ + hb-repacker.hh \ + graph/graph.hh \ + graph/gsubgpos-graph.hh \ + graph/gsubgpos-context.hh \ + graph/gsubgpos-context.cc \ + graph/coverage-graph.hh \ + graph/classdef-graph.hh \ + graph/pairpos-graph.hh \ + graph/markbasepos-graph.hh \ + graph/split-helpers.hh \ + graph/serialize.hh \ + OT/Color/COLR/colrv1-closure.hh \ + $(NULL) + +HB_SUBSET_headers = \ + hb-subset.h \ + hb-subset-repacker.h \ + $(NULL) + +HB_CAIRO_sources = \ + hb-cairo.cc \ + hb-cairo-utils.cc \ + hb-cairo-utils.hh \ + hb-static.cc \ + $(NULL) +HB_CAIRO_headers = \ + hb-cairo.h \ + $(NULL) + +HB_GOBJECT_DIST_sources = hb-gobject-structs.cc +HB_GOBJECT_DIST_headers = hb-gobject.h hb-gobject-structs.h +HB_GOBJECT_ENUM_sources = hb-gobject-enums.cc +HB_GOBJECT_ENUM_headers = hb-gobject-enums.h +HB_GOBJECT_NODIST_sources = $(HB_GOBJECT_ENUM_sources) +HB_GOBJECT_NODIST_headers = $(HB_GOBJECT_ENUM_headers) +HB_GOBJECT_sources = $(HB_GOBJECT_DIST_sources) $(HB_GOBJECT_NODIST_sources) +HB_GOBJECT_headers = $(HB_GOBJECT_DIST_headers) $(HB_GOBJECT_NODIST_headers) diff --git a/gfx/harfbuzz/src/OT/Color/CBDT/CBDT.hh b/gfx/harfbuzz/src/OT/Color/CBDT/CBDT.hh new file mode 100644 index 0000000000..bcf1848f49 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Color/CBDT/CBDT.hh @@ -0,0 +1,1031 @@ +/* + * 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<char> *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<char> *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<int> (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 <typename OffsetType> +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<OffsetType> embedded_offset; + embedded_offset = offset; + *size += sizeof (OffsetType); + auto *o = c->embed (embedded_offset); + return_trace ((bool) o); + } + + IndexSubtableHeader header; + UnsizedArrayOf<Offset<OffsetType>> + offsetArrayZ; + public: + DEFINE_SIZE_ARRAY (8, offsetArrayZ); +}; + +struct IndexSubtableFormat1 : IndexSubtableFormat1Or3<HBUINT32> {}; +struct IndexSubtableFormat3 : IndexSubtableFormat1Or3<HBUINT16> {}; + +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); + hb_barrier (); + 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<char> *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) && + hb_barrier () && + 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<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> *lookup, /* IN */ + const void *base, + unsigned int *start /* INOUT */) const + { + TRACE_SERIALIZE (this); + + auto *subtable = c->serializer->start_embed<IndexSubtable> (); + 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<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> *lookup, /* IN */ + const void *base, + unsigned int *start, /* INOUT */ + hb_vector_t<IndexSubtableRecord>* 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<IndexSubtable> 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<hb_pair_t<hb_codepoint_t, + const IndexSubtableRecord*>> *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<hb_codepoint_t, const IndexSubtableRecord*> (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<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> 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<IndexSubtableRecord> 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<hb_serialize_context_t::objidx_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<IndexSubtableRecord> 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) && + hb_barrier () && + 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<char> *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<IndexSubtableArray> + 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<HBUINT8> data; + public: + DEFINE_SIZE_ARRAY (9, data); +}; + +struct GlyphBitmapDataFormat18 +{ + BigGlyphMetrics glyphMetrics; + Array32Of<HBUINT8> data; + public: + DEFINE_SIZE_ARRAY (12, data); +}; + +struct GlyphBitmapDataFormat19 +{ + Array32Of<HBUINT8> 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) && + hb_barrier () && + likely (version.major == 2 || version.major == 3) && + hb_barrier () && + sizeTables.sanitize (c, this)); + } + + static bool + sink_cbdt (hb_subset_context_t *c, hb_vector_t<char>* 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<char> *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<BitmapSizeTable> 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<CBLC> (face); + this->cbdt = hb_sanitize_context_t ().reference_table<CBDT> (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<GlyphBitmapDataFormat17> (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<GlyphBitmapDataFormat18> (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<GlyphBitmapDataFormat17> (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<GlyphBitmapDataFormat18> (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<GlyphBitmapDataFormat19> (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> cblc; + hb_blob_ptr_t<CBDT> cbdt; + + unsigned int upem; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + likely (version.major == 2 || version.major == 3)); + } + + protected: + FixedVersion<> version; + UnsizedArrayOf<HBUINT8> 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<char> cbdt_prime; + + auto *cblc_prime = c->serializer->start_embed<CBLC> (); + 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<CBDT> (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/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh b/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh new file mode 100644 index 0000000000..b632a1d9eb --- /dev/null +++ b/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh @@ -0,0 +1,2497 @@ +/* + * 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<hb_paint_context_t> +{ + const char *get_name () { return "PAINT"; } + template <typename T> + 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<const COLR *> (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<hb_colrv1_closure_context_t> +{ + template <typename T> + 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<const COLR *> (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 <typename T> +struct Variable +{ + static constexpr bool is_variable = true; + + Variable<T>* 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 <typename T> +struct NoVariable +{ + static constexpr bool is_variable = false; + + static constexpr uint32_t varIdxBase = VarIdx::NO_VARIATION; + + NoVariable<T>* 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 <template<typename> 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<Var<ColorStop>> 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 <template<typename> 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<Var>> 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 <template<typename> 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<Var>> 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 <template<typename> 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<Var>> 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> 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 <template<typename> 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<Paint> src; /* Offset (from beginning of PaintTransform table) to Paint subtable. */ + Offset24To<Var<Affine2x3>> 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<Paint> 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<Paint> 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<Paint> 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<Paint> 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<Paint> 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<Paint> 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<Paint> 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<Paint> 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<Paint> 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<Paint> src; /* Offset (from beginning of PaintComposite table) to source Paint subtable. */ + CompositeMode mode; /* If mode is unrecognized use COMPOSITE_CLEAR */ + Offset24To<Paint> 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<ClipBoxFormat1> +{ + 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, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (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> 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<ClipRecord> clips; // Clip records, sorted by startGlyphID + public: + DEFINE_SIZE_ARRAY_SIZED (5, clips); +}; + +struct Paint +{ + + template <typename ...Ts> + 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<Ts> (ds)...))); + } + + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.paintformat2, std::forward<Ts> (ds)...)); + case 3: return_trace (c->dispatch (u.paintformat3, std::forward<Ts> (ds)...)); + case 4: return_trace (c->dispatch (u.paintformat4, std::forward<Ts> (ds)...)); + case 5: return_trace (c->dispatch (u.paintformat5, std::forward<Ts> (ds)...)); + case 6: return_trace (c->dispatch (u.paintformat6, std::forward<Ts> (ds)...)); + case 7: return_trace (c->dispatch (u.paintformat7, std::forward<Ts> (ds)...)); + case 8: return_trace (c->dispatch (u.paintformat8, std::forward<Ts> (ds)...)); + case 9: return_trace (c->dispatch (u.paintformat9, std::forward<Ts> (ds)...)); + case 10: return_trace (c->dispatch (u.paintformat10, std::forward<Ts> (ds)...)); + case 11: return_trace (c->dispatch (u.paintformat11, std::forward<Ts> (ds)...)); + case 12: return_trace (c->dispatch (u.paintformat12, std::forward<Ts> (ds)...)); + case 13: return_trace (c->dispatch (u.paintformat13, std::forward<Ts> (ds)...)); + case 14: return_trace (c->dispatch (u.paintformat14, std::forward<Ts> (ds)...)); + case 15: return_trace (c->dispatch (u.paintformat15, std::forward<Ts> (ds)...)); + case 16: return_trace (c->dispatch (u.paintformat16, std::forward<Ts> (ds)...)); + case 17: return_trace (c->dispatch (u.paintformat17, std::forward<Ts> (ds)...)); + case 18: return_trace (c->dispatch (u.paintformat18, std::forward<Ts> (ds)...)); + case 19: return_trace (c->dispatch (u.paintformat19, std::forward<Ts> (ds)...)); + case 20: return_trace (c->dispatch (u.paintformat20, std::forward<Ts> (ds)...)); + case 21: return_trace (c->dispatch (u.paintformat21, std::forward<Ts> (ds)...)); + case 22: return_trace (c->dispatch (u.paintformat22, std::forward<Ts> (ds)...)); + case 23: return_trace (c->dispatch (u.paintformat23, std::forward<Ts> (ds)...)); + case 24: return_trace (c->dispatch (u.paintformat24, std::forward<Ts> (ds)...)); + case 25: return_trace (c->dispatch (u.paintformat25, std::forward<Ts> (ds)...)); + case 26: return_trace (c->dispatch (u.paintformat26, std::forward<Ts> (ds)...)); + case 27: return_trace (c->dispatch (u.paintformat27, std::forward<Ts> (ds)...)); + case 28: return_trace (c->dispatch (u.paintformat28, std::forward<Ts> (ds)...)); + case 29: return_trace (c->dispatch (u.paintformat29, std::forward<Ts> (ds)...)); + case 30: return_trace (c->dispatch (u.paintformat30, std::forward<Ts> (ds)...)); + case 31: return_trace (c->dispatch (u.paintformat31, std::forward<Ts> (ds)...)); + case 32: return_trace (c->dispatch (u.paintformat32, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT8 format; + PaintColrLayers paintformat1; + NoVariable<PaintSolid> paintformat2; + Variable<PaintSolid> paintformat3; + NoVariable<PaintLinearGradient<NoVariable>> paintformat4; + Variable<PaintLinearGradient<Variable>> paintformat5; + NoVariable<PaintRadialGradient<NoVariable>> paintformat6; + Variable<PaintRadialGradient<Variable>> paintformat7; + NoVariable<PaintSweepGradient<NoVariable>> paintformat8; + Variable<PaintSweepGradient<Variable>> paintformat9; + PaintGlyph paintformat10; + PaintColrGlyph paintformat11; + PaintTransform<NoVariable> paintformat12; + PaintTransform<Variable> paintformat13; + NoVariable<PaintTranslate> paintformat14; + Variable<PaintTranslate> paintformat15; + NoVariable<PaintScale> paintformat16; + Variable<PaintScale> paintformat17; + NoVariable<PaintScaleAroundCenter> paintformat18; + Variable<PaintScaleAroundCenter> paintformat19; + NoVariable<PaintScaleUniform> paintformat20; + Variable<PaintScaleUniform> paintformat21; + NoVariable<PaintScaleUniformAroundCenter> paintformat22; + Variable<PaintScaleUniformAroundCenter> paintformat23; + NoVariable<PaintRotate> paintformat24; + Variable<PaintRotate> paintformat25; + NoVariable<PaintRotateAroundCenter> paintformat26; + Variable<PaintRotateAroundCenter> paintformat27; + NoVariable<PaintSkew> paintformat28; + Variable<PaintSkew> paintformat29; + NoVariable<PaintSkewAroundCenter> paintformat30; + Variable<PaintSkewAroundCenter> 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> paint; /* Offset (from beginning of BaseGlyphPaintRecord array) to Paint, + * Typically PaintColrLayers */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord> +{ + 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<BaseGlyphPaintRecord>::sanitize (c, this)); + } +}; + +struct LayerList : Array32OfOffset32To<Paint> +{ + 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<Paint>::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 false; + hb_barrier (); + + return (this+baseGlyphList).len > 0; + } + + 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<const LayerRecord> all_layers = (this+layersZ).as_array (numLayers); + hb_array_t<const LayerRecord> 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<COLR> (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> 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<const BaseGlyphRecord> baseGlyphs = (this+baseGlyphsZ).as_array (numBaseGlyphs); + hb_array_t<const LayerRecord> all_layers = (this+layersZ).as_array (numLayers); + + for (const BaseGlyphRecord record : baseGlyphs) + { + if (!glyphs->has (record.glyphId)) continue; + hb_array_t<const LayerRecord> 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_barrier (); + + 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) && + hb_barrier () && + (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) && + (this+layersZ).sanitize (c, numLayers) && + (version == 0 || + (hb_barrier () && + version == 1 && + baseGlyphList.sanitize (c, this) && + layerList.sanitize (c, this) && + clipList.sanitize (c, this) && + varIdxMap.sanitize (c, this) && + varStore.sanitize (c, this)))); + } + + template<typename BaseIterator, typename LayerIterator, + hb_requires (hb_is_iterator (BaseIterator)), + hb_requires (hb_is_iterator (LayerIterator))> + 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<BaseIterator> _ : + 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<LayerIterator>& _ : + 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<bool, BaseGlyphRecord> (false, Null (BaseGlyphRecord)); + BaseGlyphRecord new_record = {}; + new_record.glyphId = new_gid; + new_record.numLayers = old_record->numLayers; + return hb_pair_t<bool, BaseGlyphRecord> (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<LayerRecord> out_layers; + + if (unlikely (!old_record || + old_record->firstLayerIdx >= numLayers || + old_record->firstLayerIdx + old_record->numLayers > numLayers)) + return hb_pair_t<bool, hb_vector_t<LayerRecord>> (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<bool, hb_vector_t<LayerRecord>> (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<bool, hb_vector_t<LayerRecord>> (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<COLR> (); + 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<void> (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) + { + hb_barrier (); + + 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) + { + hb_barrier (); + + 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<SortedUnsizedArrayOf<BaseGlyphRecord>> + baseGlyphsZ; /* Offset to Base Glyph records. */ + NNOffset32To<UnsizedArrayOf<LayerRecord>> + layersZ; /* Offset to Layer Records. */ + HBUINT16 numLayers; /* Number of Layer Records. */ + // Version-1 additions + Offset32To<BaseGlyphList> baseGlyphList; + Offset32To<LayerList> layerList; + Offset32To<ClipList> clipList; // Offset to ClipList table (may be NULL) + Offset32To<DeltaSetIndexMap> varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL) + Offset32To<VariationStore> 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/gfx/harfbuzz/src/OT/Color/COLR/colrv1-closure.hh b/gfx/harfbuzz/src/OT/Color/COLR/colrv1-closure.hh new file mode 100644 index 0000000000..705863d4ad --- /dev/null +++ b/gfx/harfbuzz/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 <template<typename> class Var> +HB_INTERNAL void PaintTransform<Var>::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/gfx/harfbuzz/src/OT/Color/CPAL/CPAL.hh b/gfx/harfbuzz/src/OT/Color/CPAL/CPAL.hh new file mode 100644 index 0000000000..2821334db7 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Color/CPAL/CPAL.hh @@ -0,0 +1,358 @@ +/* + * 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<const NameID> 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<CPALV1Tail> (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<const NameID> 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<NameID> (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<UnsizedArrayOf<HBUINT32>> + paletteFlagsZ; /* Offset from the beginning of CPAL table to + * the Palette Type Array. Set to 0 if no array + * is provided. */ + NNOffset32To<UnsizedArrayOf<NameID>> + paletteLabelsZ; /* Offset from the beginning of CPAL table to + * the palette labels array. Set to 0 if no + * array is provided. */ + NNOffset32To<UnsizedArrayOf<NameID>> + 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<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords); + hb_array_t<const BGRAColor> 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) + { + hb_barrier (); + v1 ().collect_name_ids (this, numPalettes, numColors, color_index_map, nameids_to_retain); + } + } + + private: + const CPALV1Tail& v1 () const + { + if (version == 0) return Null (CPALV1Tail); + hb_barrier (); + return StructAfter<CPALV1Tail> (*this); + } + + public: + bool serialize (hb_serialize_context_t *c, + const hb_array_t<const HBUINT16> &color_record_indices, + const hb_array_t<const BGRAColor> &color_records, + const hb_vector_t<unsigned>& 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<HBUINT16> (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<BGRAColor> (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<unsigned> first_color_index_for_layer; + hb_map_t first_color_to_layer_index; + + const hb_array_t<const HBUINT16> 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<const BGRAColor> 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) + { + hb_barrier (); + 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) && + hb_barrier () && + (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<UnsizedArrayOf<BGRAColor>> + colorRecordsZ; /* Offset from the beginning of CPAL table to + * the first ColorRecord. */ + UnsizedArrayOf<HBUINT16> + 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/gfx/harfbuzz/src/OT/Color/sbix/sbix.hh b/gfx/harfbuzz/src/OT/Color/sbix/sbix.hh new file mode 100644 index 0000000000..51ae1a9c63 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Color/sbix/sbix.hh @@ -0,0 +1,448 @@ +/* + * 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<SBIXGlyph> (); + 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<HBUINT8> + 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<SBIXStrike> (); + 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<Offset32To<SBIXGlyph>> + 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<sbix> (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<PNGHeader>(); + + 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<sbix> table; + + unsigned int num_glyphs; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + hb_barrier () && + 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<Array32OfOffset32To<SBIXStrike>> (); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + hb_vector_t<Offset32To<SBIXStrike>*> new_strikes; + hb_vector_t<hb_serialize_context_t::objidx_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<SBIXStrike> + 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/gfx/harfbuzz/src/OT/Color/svg/svg.hh b/gfx/harfbuzz/src/OT/Color/svg/svg.hh new file mode 100644 index 0000000000..2e1f935109 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Color/svg/svg.hh @@ -0,0 +1,152 @@ +/* + * 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) && + hb_barrier () && + 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<UnsizedArrayOf<HBUINT8>> + 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<SVG> (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<SVG> table; + public: + DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t<SVG>)); + }; + + 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<SortedArray16Of<SVGDocumentIndexEntry>> + 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/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh b/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh new file mode 100644 index 0000000000..344e87afb3 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh @@ -0,0 +1,352 @@ +/* + * 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<typename Iterator> +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +struct Coverage +{ + + protected: + union { + HBUINT16 format; /* Format identifier */ + CoverageFormat1_3<SmallTypes> format1; + CoverageFormat2_4<SmallTypes> format2; +#ifndef HB_NO_BEYOND_64K + CoverageFormat1_3<MediumTypes>format3; + CoverageFormat2_4<MediumTypes>format4; +#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); + hb_barrier (); + 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 <typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> + 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<hb_codepoint_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 <typename set_t> + 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 <typename IterableOut, + hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))> + 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<iter_t, hb_codepoint_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<MediumTypes>::iter_t format4; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3<MediumTypes>::iter_t format3; +#endif + CoverageFormat2_4<SmallTypes>::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */ + CoverageFormat1_3<SmallTypes>::iter_t format1; + } u; + }; + iter_t iter () const { return iter_t (*this); } +}; + +template<typename Iterator> +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed<Coverage> ()->serialize (c, it); } + +} +} +} + +#endif // #ifndef OT_LAYOUT_COMMON_COVERAGE_HH diff --git a/gfx/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh b/gfx/harfbuzz/src/OT/Layout/Common/CoverageFormat1.hh new file mode 100644 index 0000000000..3f598d40ef --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct CoverageFormat1_3 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 1 */ + SortedArray16Of<typename Types::HBGlyphID> + 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 <typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> + 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 <typename IterableOut, + hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))> + 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 <typename set_t> + 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/gfx/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh b/gfx/harfbuzz/src/OT/Layout/Common/CoverageFormat2.hh new file mode 100644 index 0000000000..9c87542356 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct CoverageFormat2_4 +{ + friend struct Coverage; + + protected: + HBUINT16 coverageFormat; /* Format identifier--format = 2 */ + SortedArray16Of<RangeRecord<Types>> + 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<Types> &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 <typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> + 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<Types>::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<Types> &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 <typename IterableOut, + hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))> + 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 <typename set_t> + 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/gfx/harfbuzz/src/OT/Layout/Common/RangeRecord.hh b/gfx/harfbuzz/src/OT/Layout/Common/RangeRecord.hh new file mode 100644 index 0000000000..85aacace9a --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +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 <typename set_t> + 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 <typename Spec> +struct Null<OT::Layout::Common::RangeRecord<Spec>> { + static OT::Layout::Common::RangeRecord<Spec> const & get_null () { + return *reinterpret_cast<const OT::Layout::Common::RangeRecord<Spec> *> (_hb_Null_OT_RangeRecord); + } +}; + + +#endif // #ifndef OT_LAYOUT_COMMON_RANGERECORD_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh b/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh new file mode 100644 index 0000000000..14a9b5e5cd --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh @@ -0,0 +1,1067 @@ +/* + * 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<HBUINT16> +{ + 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<hb_codepoint_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> + coverage; /* Offset to Coverage table -- from + * beginning of AttachList table */ + Array16OfOffset16To<AttachPoint> + 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<unsigned, int> *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<Device> + 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, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (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); + hb_barrier (); + 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<CaretValue>& 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<CaretValue> + 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<hb_codepoint_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> + coverage; /* Offset to Coverage table--from + * beginning of LigCaretList table */ + Array16OfOffset16To<LigGlyph> + 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; } + + void collect_used_mark_sets (const hb_set_t& glyph_set, + hb_set_t& used_mark_sets /* OUT */) const + { + unsigned i = 0; + for (const auto &offset : coverage) + { + const auto &cov = this+offset; + if (cov.intersects (&glyph_set)) + used_mark_sets.add (i); + + i++; + } + } + + template <typename set_t> + void collect_coverage (hb_vector_t<set_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<Coverage>& offset : coverage.iter ()) + { + auto snap = c->serializer->snapshot (); + auto *o = out->coverage.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + //skip empty coverage + c->serializer->push (); + bool res = false; + if (offset) res = c->dispatch (this+offset); + if (!res) + { + c->serializer->pop_discard (); + c->serializer->revert (snap); + (out->coverage.len)--; + continue; + } + 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<Offset32To<Coverage>> + 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 <typename set_t> + void collect_coverage (hb_vector_t<set_t> &sets) const + { + switch (u.format) { + case 1: u.format1.collect_coverage (sets); return; + default:return; + } + } + + void collect_used_mark_sets (const hb_set_t& glyph_set, + hb_set_t& used_mark_sets /* OUT */) const + { + switch (u.format) { + case 1: u.format1.collect_used_mark_sets (glyph_set, used_mark_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); + hb_barrier (); + 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 <typename Types> +struct GDEFVersion1_2 +{ + friend struct GDEF; + + protected: + FixedVersion<>version; /* Version of the GDEF table--currently + * 0x00010003u */ + typename Types::template OffsetTo<ClassDef> + glyphClassDef; /* Offset to class definition table + * for glyph type--from beginning of + * GDEF header (may be Null) */ + typename Types::template OffsetTo<AttachList> + attachList; /* Offset to list of glyphs with + * attachment points--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo<LigCaretList> + ligCaretList; /* Offset to list of positioning points + * for ligature carets--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo<ClassDef> + markAttachClassDef; /* Offset to class definition table for + * mark attachment type--from beginning + * of GDEF header (may be Null) */ + typename Types::template OffsetTo<MarkGlyphSets> + markGlyphSetsDef; /* Offset to the table of mark set + * definitions--from beginning of GDEF + * header (may be NULL). Introduced + * in version 0x00010002. */ + Offset32To<VariationStore> + 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) && + hb_barrier () && + (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<unsigned, hb_pair_t<unsigned, int>>& 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->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->version.major = version.major; + out->version.minor = version.minor; + 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_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true); + + bool subset_markglyphsetsdef = false; + auto snapshot_version0 = c->serializer->snapshot (); + if (version.to_int () >= 0x00010002u) + { + if (unlikely (!c->serializer->embed (markGlyphSetsDef))) return_trace (false); + subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); + } + + bool subset_varstore = false; + auto snapshot_version2 = c->serializer->snapshot (); + if (version.to_int () >= 0x00010003u) + { + if (unlikely (!c->serializer->embed (varStore))) return_trace (false); + 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; + c->plan->has_gdef_varstore = true; + } else if (subset_markglyphsetsdef) { + out->version.minor = 2; + c->serializer->revert (snapshot_version2); + } else { + out->version.minor = 0; + c->serializer->revert (snapshot_version0); + } + + bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); + + 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); + hb_barrier (); + 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<GDEF> (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<GDEF> table; +#ifndef HB_NO_GDEF_CACHE + hb_vector_t<hb_set_digest_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<int>& normalized_coords, + bool calculate_delta, /* not pinned at default */ + bool no_variations, /* all axes pinned */ + hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *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<unsigned, int> (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<unsigned, int> (new_idx, delta)); + ++new_minor; + last_major = major; + } + var_store.destroy_cache (store_cache); + } + + protected: + union { + FixedVersion<> version; /* Version identifier */ + GDEFVersion1_2<SmallTypes> version1; +#ifndef HB_NO_BEYOND_64K + GDEFVersion1_2<MediumTypes> 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/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh new file mode 100644 index 0000000000..7802e397f4 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh @@ -0,0 +1,84 @@ +#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); + hb_barrier (); + 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<Anchor *> (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<Anchor *> (u.format1.copy (c->serializer)))); + } + return_trace (bool (reinterpret_cast<Anchor *> (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/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat1.hh new file mode 100644 index 0000000000..738cc31bbf --- /dev/null +++ b/gfx/harfbuzz/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<AnchorFormat1> (this); + if (!out) return_trace (out); + out->format = 1; + return_trace (out); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT1_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat2.hh new file mode 100644 index 0000000000..70b4d19f53 --- /dev/null +++ b/gfx/harfbuzz/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<AnchorFormat2> (this)); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_ANCHORFORMAT2_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh new file mode 100644 index 0000000000..b5422652c4 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorFormat3.hh @@ -0,0 +1,120 @@ +#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<Device> + xDeviceTable; /* Offset to Device table for X + * coordinate-- from beginning of + * Anchor table (may be NULL) */ + Offset16To<Device> + 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)) + { + hb_barrier (); + *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)) + { + hb_barrier (); + *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<unsigned, int> *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<unsigned, int> *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/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh new file mode 100644 index 0000000000..2557e9a723 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/AnchorMatrix.hh @@ -0,0 +1,87 @@ +#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<Offset16To<Anchor, AnchorMatrix>> + 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); + hb_barrier (); + 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); + + hb_barrier (); + 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); + hb_barrier (); + *found = !offset.is_null (); + return this+offset; + } + + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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 <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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; + for (const unsigned i : index_iter) + { + auto *offset = c->serializer->embed (matrixZ[i]); + if (!offset) return_trace (false); + offset->serialize_subset (c, matrixZ[i], this); + } + + return_trace (true); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_ANCHORMATRIX_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/ChainContextPos.hh new file mode 100644 index 0000000000..d551ac2a2b --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/OT/Layout/GPOS/Common.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/Common.hh new file mode 100644 index 0000000000..696d25d75c --- /dev/null +++ b/gfx/harfbuzz/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<typename Iterator, typename SrcLookup> +static void SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map, + unsigned new_format); + + +} +} +} + +#endif // OT_LAYOUT_GPOS_COMMON_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/ContextPos.hh new file mode 100644 index 0000000000..2a01eaa3a6 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh new file mode 100644 index 0000000000..0105a9b854 --- /dev/null +++ b/gfx/harfbuzz/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, typename ...Ts> + 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<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_CURSIVEPOS_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh new file mode 100644 index 0000000000..6b019ac513 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh @@ -0,0 +1,311 @@ +#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 struct CursivePosFormat1 *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 struct CursivePosFormat1 *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 struct CursivePosFormat1 *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<Anchor, struct CursivePosFormat1> + entryAnchor; /* Offset to EntryAnchor table--from + * beginning of CursivePos + * subtable--may be NULL */ + Offset16To<Anchor, struct CursivePosFormat1> + 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> + coverage; /* Offset to Coverage table--from + * beginning of subtable */ + Array16Of<EntryExitRecord> + 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_barrier (); + + 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); + } + hb_barrier (); + + 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 <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_subset_context_t *c, + Iterator it, + const struct CursivePosFormat1 *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<hb_codepoint_t, const EntryExitRecord&> p) -> hb_pair_t<hb_codepoint_t, const EntryExitRecord&> + { 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/gfx/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/ExtensionPos.hh new file mode 100644 index 0000000000..d1808adab4 --- /dev/null +++ b/gfx/harfbuzz/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<ExtensionPos> +{ + typedef struct PosLookupSubTable SubTable; +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_EXTENSIONPOS_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/GPOS.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/GPOS.hh new file mode 100644 index 0000000000..f4af98b25f --- /dev/null +++ b/gfx/harfbuzz/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<const PosLookup &> (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<PosLookup> (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize<PosLookup> (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<PosLookup> (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t<GPOS> 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/gfx/harfbuzz/src/OT/Layout/GPOS/LigatureArray.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/LigatureArray.hh new file mode 100644 index 0000000000..59cca40aad --- /dev/null +++ b/gfx/harfbuzz/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<LigatureAttach> +{ + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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/gfx/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkArray.hh new file mode 100644 index 0000000000..0887cc158b --- /dev/null +++ b/gfx/harfbuzz/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<MarkRecord> /* Array of MarkRecords--in Coverage order */ +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (Array16Of<MarkRecord>::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<MarkRecord>::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 <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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/gfx/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh new file mode 100644 index 0000000000..cd2fc7ccfd --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; +#ifndef HB_NO_BEYOND_64K + MarkBasePosFormat1_2<MediumTypes> format2; +#endif + } u; + + public: + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKBASEPOS_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkBasePosFormat1.hh new file mode 100644 index 0000000000..1b8f3c80a9 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct MarkBasePosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + markCoverage; /* Offset to MarkCoverage table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo<Coverage> + baseCoverage; /* Offset to BaseCoverage table--from + * beginning of MarkBasePos subtable */ + HBUINT16 classCount; /* Number of classes defined for marks */ + typename Types::template OffsetTo<MarkArray> + markArray; /* Offset to MarkArray table--from + * beginning of MarkBasePos subtable */ + typename Types::template OffsetTo<BaseArray> + 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<unsigned> 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<hb_codepoint_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<unsigned> 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/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh new file mode 100644 index 0000000000..739c325411 --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; +#ifndef HB_NO_BEYOND_64K + MarkLigPosFormat1_2<MediumTypes> format2; +#endif + } u; + + public: + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKLIGPOS_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPosFormat1.hh new file mode 100644 index 0000000000..d6bee277c7 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct MarkLigPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + markCoverage; /* Offset to Mark Coverage table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo<Coverage> + ligatureCoverage; /* Offset to Ligature Coverage + * table--from beginning of MarkLigPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo<MarkArray> + markArray; /* Offset to MarkArray table--from + * beginning of MarkLigPos subtable */ + typename Types::template OffsetTo<LigatureArray> + 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<unsigned> 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/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh new file mode 100644 index 0000000000..cddd2a3d50 --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; +#ifndef HB_NO_BEYOND_64K + MarkMarkPosFormat1_2<MediumTypes> format2; +#endif + } u; + + public: + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_MARKMARKPOS_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh new file mode 100644 index 0000000000..57eb782a95 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh @@ -0,0 +1,231 @@ +#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 <typename Types> +struct MarkMarkPosFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + mark1Coverage; /* Offset to Combining Mark1 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + typename Types::template OffsetTo<Coverage> + mark2Coverage; /* Offset to Combining Mark2 Coverage + * table--from beginning of MarkMarkPos + * subtable */ + HBUINT16 classCount; /* Number of defined mark classes */ + typename Types::template OffsetTo<MarkArray> + mark1Array; /* Offset to Mark1Array table--from + * beginning of MarkMarkPos subtable */ + typename Types::template OffsetTo<Mark2Array> + 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) && + hb_barrier () && + 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<unsigned> 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<hb_codepoint_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<unsigned> 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/gfx/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkRecord.hh new file mode 100644 index 0000000000..3d11c7773c --- /dev/null +++ b/gfx/harfbuzz/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<Anchor> + 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/gfx/harfbuzz/src/OT/Layout/GPOS/PairPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPos.hh new file mode 100644 index 0000000000..c13d4f4894 --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; + PairPosFormat2_4<SmallTypes> format2; +#ifndef HB_NO_BEYOND_64K + PairPosFormat1_3<MediumTypes> format3; + PairPosFormat2_4<MediumTypes> format4; +#endif + } u; + + public: + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOS_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh new file mode 100644 index 0000000000..ac2774a76f --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat1.hh @@ -0,0 +1,233 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT1_HH + +#include "PairSet.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template <typename Types> +struct PairPosFormat1_3 +{ + using PairSet = GPOS_impl::PairSet<Types>; + using PairValueRecord = GPOS_impl::PairValueRecord<Types>; + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + 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<typename Types::template OffsetTo<PairSet>> + 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); + hb_barrier (); + + 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<PairSet> &_) + { 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; + + hb_pair_t<unsigned, unsigned> newFormats = hb_pair (valueFormat[0], valueFormat[1]); + + if (c->plan->normalized_coords) + { + /* all device flags will be dropped when full instancing, no need to strip + * hints, also do not strip emtpy cause we don't compute the new default + * value during stripping */ + newFormats = compute_effective_value_formats (glyphset, false, false, &c->plan->layout_variation_idx_delta_map); + } + /* do not strip hints for VF */ + else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r')); + bool has_fvar = (blob != hb_blob_get_empty ()); + hb_blob_destroy (blob); + + bool strip = !has_fvar; + /* special case: strip hints when a VF has no GDEF varstore after + * subsetting*/ + if (has_fvar && !c->plan->has_gdef_varstore) + strip = true; + newFormats = compute_effective_value_formats (glyphset, strip, true); + } + + out->valueFormat[0] = newFormats.first; + out->valueFormat[1] = newFormats.second; + + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + + hb_zip (this+coverage, pairSet) + | hb_filter (glyphset, hb_first) + | hb_filter ([this, c, out] (const typename Types::template OffsetTo<PairSet>& _) + { + 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<unsigned, unsigned> compute_effective_value_formats (const hb_set_t& glyphset, + bool strip_hints, bool strip_empty, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) 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 (), strip_hints, strip_empty, &set, varidx_delta_map); + format2 = format2 | valueFormat[1].get_effective_format (record->get_values_2 (valueFormat[0]), strip_hints, strip_empty, &set, varidx_delta_map); + } + record = &StructAtOffset<const PairValueRecord> (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/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh new file mode 100644 index 0000000000..dd02da887d --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPosFormat2.hh @@ -0,0 +1,374 @@ +#ifndef OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH + +#include "ValueFormat.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +template <typename Types> +struct PairPosFormat2_4 : ValueBase +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo<Coverage> + 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<ClassDef> + classDef1; /* Offset to ClassDef table--from + * beginning of PairPos subtable--for + * the first glyph of the pair */ + typename Types::template OffsetTo<ClassDef> + 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<const Value> 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<unsigned, unsigned> newFormats = hb_pair (valueFormat1, valueFormat2); + + if (c->plan->normalized_coords) + { + /* in case of full instancing, all var device flags will be dropped so no + * need to strip hints here */ + newFormats = compute_effective_value_formats (klass1_map, klass2_map, false, false, &c->plan->layout_variation_idx_delta_map); + } + /* do not strip hints for VF */ + else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r')); + bool has_fvar = (blob != hb_blob_get_empty ()); + hb_blob_destroy (blob); + + bool strip = !has_fvar; + /* special case: strip hints when a VF has no GDEF varstore after + * subsetting*/ + if (has_fvar && !c->plan->has_gdef_varstore) + strip = true; + newFormats = compute_effective_value_formats (klass1_map, klass2_map, strip, true); + } + + out->valueFormat1 = newFormats.first; + out->valueFormat2 = newFormats.second; + + unsigned total_len = len1 + len2; + hb_vector_t<unsigned> 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<unsigned, unsigned> compute_effective_value_formats (const hb_map_t& klass1_map, + const hb_map_t& klass2_map, + bool strip_hints, bool strip_empty, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map = nullptr) 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], strip_hints, strip_empty, this, varidx_delta_map); + format2 = format2 | valueFormat2.get_effective_format (&values[idx + len1], strip_hints, strip_empty, this, varidx_delta_map); + } + + if (format1 == valueFormat1 && format2 == valueFormat2) + break; + } + + return hb_pair (format1, format2); + } +}; + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRPOSFORMAT2_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairSet.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairSet.hh new file mode 100644 index 0000000000..5560fab174 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/PairSet.hh @@ -0,0 +1,210 @@ +#ifndef OT_LAYOUT_GPOS_PAIRSET_HH +#define OT_LAYOUT_GPOS_PAIRSET_HH + +#include "PairValueRecord.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + + +template <typename Types> +struct PairSet : ValueBase +{ + template <typename Types2> + friend struct PairPosFormat1_3; + + using PairValueRecord = GPOS_impl::PairValueRecord<Types>; + + 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) && + hb_barrier () && + c->check_range (&firstPairValueRecord, + len, + closure->stride))) return_trace (false); + hb_barrier (); + + 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<const PairValueRecord> (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<const PairValueRecord> (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<const PairValueRecord> (record, record_size); + } + + out->len = num; + if (!num) c->serializer->revert (snap); + return_trace (num); + } +}; + + +} +} +} + +#endif // OT_LAYOUT_GPOS_PAIRSET_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairValueRecord.hh new file mode 100644 index 0000000000..d00618b763 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct PairValueRecord +{ + template <typename Types2> + 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 ValueBase *base; + const ValueFormat *valueFormats; + const ValueFormat *newFormats; + unsigned len1; /* valueFormats[0].get_len() */ + const hb_map_t *glyph_map; + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *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 ValueBase *base) const + { + unsigned record1_len = valueFormats[0].get_len (); + unsigned record2_len = valueFormats[1].get_len (); + const hb_array_t<const Value> 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/gfx/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PosLookup.hh new file mode 100644 index 0000000000..c4e57bb543 --- /dev/null +++ b/gfx/harfbuzz/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<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 <typename set_t> + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t<set_t> c (glyphs); + dispatch (&c); + } + + template <typename context_t> + static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index); + + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset<SubTable> (c); } + + bool sanitize (hb_sanitize_context_t *c) const + { return Lookup::sanitize<SubTable> (c); } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_POSLOOKUP_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PosLookupSubTable.hh new file mode 100644 index 0000000000..c19fbc323f --- /dev/null +++ b/gfx/harfbuzz/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, typename ...Ts> + 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<Ts> (ds)...)); + case Pair: return_trace (u.pair.dispatch (c, std::forward<Ts> (ds)...)); + case Cursive: return_trace (u.cursive.dispatch (c, std::forward<Ts> (ds)...)); + case MarkBase: return_trace (u.markBase.dispatch (c, std::forward<Ts> (ds)...)); + case MarkLig: return_trace (u.markLig.dispatch (c, std::forward<Ts> (ds)...)); + case MarkMark: return_trace (u.markMark.dispatch (c, std::forward<Ts> (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (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/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh new file mode 100644 index 0000000000..a0243a218c --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh @@ -0,0 +1,98 @@ +#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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + unsigned get_format (Iterator glyph_val_iter_pairs) + { + hb_array_t<const Value> 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<typename Iterator, + typename SrcLookup, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + const SrcLookup* src, + Iterator glyph_val_iter_pairs, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map, + unsigned newFormat) + { + if (unlikely (!c->extend_min (u.format))) return; + unsigned format = 2; + ValueFormat new_format; + new_format = newFormat; + + 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, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + + +template<typename Iterator, typename SrcLookup> +static void +SinglePos_serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map, + unsigned new_format) +{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_delta_map, new_format); } + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOS_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh new file mode 100644 index 0000000000..b2d151d446 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh @@ -0,0 +1,190 @@ +#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 : ValueBase +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + Offset16To<Coverage> + 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) && + hb_barrier () && + /* 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<typename Iterator, + typename SrcLookup, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *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<const Value>& _ : + 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); + + unsigned new_format = valueFormat; + + if (c->plan->normalized_coords) + { + new_format = valueFormat.get_effective_format (values.arrayZ, false, false, this, &c->plan->layout_variation_idx_delta_map); + } + /* do not strip hints for VF */ + else if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + { + hb_blob_t* blob = hb_face_reference_table (c->plan->source, HB_TAG ('f','v','a','r')); + bool has_fvar = (blob != hb_blob_get_empty ()); + hb_blob_destroy (blob); + + bool strip = !has_fvar; + /* special case: strip hints when a VF has no GDEF varstore after + * subsetting*/ + if (has_fvar && !c->plan->has_gdef_varstore) + strip = true; + new_format = valueFormat.get_effective_format (values.arrayZ, + strip, /* strip hints */ + true, /* strip empty */ + this, nullptr); + } + + 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, new_format); + return_trace (ret); + } +}; + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT1_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh new file mode 100644 index 0000000000..ae4a5ed756 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh @@ -0,0 +1,213 @@ +#ifndef OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH +#define OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH + +#include "Common.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +struct SinglePosFormat2 : ValueBase +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + Offset16To<Coverage> + 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<const Value> 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<typename Iterator, + typename SrcLookup, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + const SrcLookup *src, + Iterator it, + ValueFormat newFormat, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *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<const Value> _) + { 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); + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + unsigned compute_effective_format (const hb_face_t *face, + Iterator it, + bool is_instancing, bool strip_hints, + bool has_gdef_varstore, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const + { + hb_blob_t* blob = hb_face_reference_table (face, HB_TAG ('f','v','a','r')); + bool has_fvar = (blob != hb_blob_get_empty ()); + hb_blob_destroy (blob); + + unsigned new_format = 0; + if (is_instancing) + { + new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), false, false, this, varidx_delta_map); + } + /* do not strip hints for VF */ + else if (strip_hints) + { + bool strip = !has_fvar; + if (has_fvar && !has_gdef_varstore) + strip = true; + new_format = new_format | valueFormat.get_effective_format (+ it | hb_map (hb_second), strip, true, this, nullptr); + } + else + new_format = valueFormat; + + return new_format; + } + + 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<hb_codepoint_t, unsigned>& _) + { + return hb_pair (glyph_map[_.first], + values_array.sub_array (_.second * sub_length, + sub_length)); + }) + ; + + unsigned new_format = compute_effective_format (c->plan->source, it, + bool (c->plan->normalized_coords), + bool (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING), + c->plan->has_gdef_varstore, + &c->plan->layout_variation_idx_delta_map); + bool ret = bool (it); + SinglePos_serialize (c->serializer, this, it, &c->plan->layout_variation_idx_delta_map, new_format); + return_trace (ret); + } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GPOS_SINGLEPOSFORMAT2_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh new file mode 100644 index 0000000000..17f57db1f5 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GPOS/ValueFormat.hh @@ -0,0 +1,441 @@ +#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; + +struct ValueBase {}; // Dummy base class tag for OffsetTo<Value> bases. + +typedef UnsizedArrayOf<Value> 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<Device> xPlaDevice; /* Offset to Device table for + * horizontal placement--measured from + * beginning of PosTable (may be NULL) */ + Offset16To<Device> yPlaDevice; /* Offset to Device table for vertical + * placement--measured from beginning + * of PosTable (may be NULL) */ + Offset16To<Device> xAdvDevice; /* Offset to Device table for + * horizontal advance--measured from + * beginning of PosTable (may be NULL) */ + Offset16To<Device> 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<unsigned> get_device_table_indices () const { + unsigned i = 0; + hb_vector_t<unsigned> 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 ValueBase *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, bool strip_hints, bool strip_empty, const ValueBase *base, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const + { + unsigned int format = *this; + for (unsigned flag = xPlacement; flag <= yAdvDevice; flag = flag << 1) { + if (format & flag) + { + if (strip_hints && flag >= xPlaDevice) + { + format = format & ~flag; + values++; + continue; + } + if (varidx_delta_map && flag >= xPlaDevice) + { + update_var_flag (values++, (Flags) flag, &format, base, varidx_delta_map); + continue; + } + /* do not strip empty when instancing, cause we don't know whether the new + * default value is 0 or not */ + if (strip_empty) should_drop (*values, (Flags) flag, &format); + values++; + } + } + + return format; + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + unsigned int get_effective_format (Iterator it, bool strip_hints, bool strip_empty, const ValueBase *base, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const { + unsigned int new_format = 0; + + for (const hb_array_t<const Value>& values : it) + new_format = new_format | get_effective_format (&values, strip_hints, strip_empty, base, varidx_delta_map); + + return new_format; + } + + void copy_values (hb_serialize_context_t *c, + unsigned int new_format, + const ValueBase *base, + const Value *values, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *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<HBINT16 *> (c->copy (value)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c, + const ValueBase *base, + const hb_array_t<const Value>& 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++; + } + } + + private: + bool sanitize_value_devices (hb_sanitize_context_t *c, const ValueBase *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<Device, ValueBase>& get_device (Value* value) + { + return *static_cast<Offset16To<Device, ValueBase> *> (value); + } + static inline const Offset16To<Device, ValueBase>& get_device (const Value* value) + { + return *static_cast<const Offset16To<Device, ValueBase> *> (value); + } + static inline const Device& get_device (const Value* value, + bool *worked, + const ValueBase *base, + hb_sanitize_context_t &c) + { + if (worked) *worked |= bool (*value); + auto &offset = *static_cast<const Offset16To<Device> *> (value); + + if (unlikely (!offset.sanitize (&c, base))) + return Null(Device); + hb_barrier (); + + return base + offset; + } + + void add_delta_to_value (HBINT16 *value, + const ValueBase *base, + const Value *src_value, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const + { + if (!value) return; + unsigned varidx = (base + get_device (src_value)).get_variation_index (); + hb_pair_t<unsigned, int> *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 ValueBase *base, + const Value *src_value, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *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<const HBINT16 *> (value); + } + + public: + + bool has_device () const + { + unsigned int format = *this; + return (format & devices) != 0; + } + + bool sanitize_value (hb_sanitize_context_t *c, const ValueBase *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 ValueBase *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); + + hb_barrier (); + 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 ValueBase *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<const Value> (values, stride); + } + + return_trace (true); + } + + private: + + void should_drop (Value value, Flags flag, unsigned int* format) const + { + if (value) return; + *format = *format & ~flag; + } + + void update_var_flag (const Value* value, Flags flag, + unsigned int* format, const ValueBase *base, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map) const + { + if (*value) + { + unsigned varidx = (base + get_device (value)).get_variation_index (); + hb_pair_t<unsigned, int> *varidx_delta; + if (varidx_delta_map->has (varidx, &varidx_delta) && + varidx_delta->first != HB_OT_LAYOUT_NO_VARIATIONS_INDEX) + return; + } + *format = *format & ~flag; + } +}; + +} +} +} + +#endif // #ifndef OT_LAYOUT_GPOS_VALUEFORMAT_HH diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSet.hh new file mode 100644 index 0000000000..b4466119be --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct AlternateSet +{ + protected: + Array16Of<typename Types::HBGlyphID> + 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 <typename Iterator, + hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))> + 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/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh new file mode 100644 index 0000000000..04a052a783 --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; +#ifndef HB_NO_BEYOND_64K + AlternateSubstFormat1_2<MediumTypes> format2; +#endif + } u; + public: + + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (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<const HBGlyphID16> glyphs, + hb_array_t<const unsigned int> alternate_len_list, + hb_array_t<const HBGlyphID16> 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/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubstFormat1.hh new file mode 100644 index 0000000000..adec65d586 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct AlternateSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of<typename Types::template OffsetTo<AlternateSet<Types>>> + 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<Types> &_) { _.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<Types> &_) { _.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<const HBGlyphID16> glyphs, + hb_array_t<const unsigned int> alternate_len_list, + hb_array_t<const HBGlyphID16> 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<hb_codepoint_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/gfx/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ChainContextSubst.hh new file mode 100644 index 0000000000..08fd779f73 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/OT/Layout/GSUB/Common.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/Common.hh new file mode 100644 index 0000000000..b849494d88 --- /dev/null +++ b/gfx/harfbuzz/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<typename Iterator> +static void SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it); + +} +} +} + +#endif /* OT_LAYOUT_GSUB_COMMON_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ContextSubst.hh new file mode 100644 index 0000000000..9f8cb46b5e --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ExtensionSubst.hh new file mode 100644 index 0000000000..831a7dfa2d --- /dev/null +++ b/gfx/harfbuzz/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<ExtensionSubst> +{ + typedef struct SubstLookupSubTable SubTable; + bool is_reverse () const; +}; + +} +} +} + +#endif /* OT_LAYOUT_GSUB_EXTENSIONSUBST_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/GSUB.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/GSUB.hh new file mode 100644 index 0000000000..900cf603e4 --- /dev/null +++ b/gfx/harfbuzz/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<const SubstLookup &> (GSUBGPOS::get_lookup (i)); } + + bool subset (hb_subset_context_t *c) const + { + hb_subset_layout_context_t l (c, tableTag); + return GSUBGPOS::subset<SubstLookup> (&l); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (GSUBGPOS::sanitize<SubstLookup> (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<SubstLookup> (face, glyphs, lookup_indexes); } + + typedef GSUBGPOS::accelerator_t<GSUB> 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/gfx/harfbuzz/src/OT/Layout/GSUB/Ligature.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/Ligature.hh new file mode 100644 index 0000000000..402ed12ae2 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct Ligature +{ + public: + typename Types::HBGlyphID + ligGlyph; /* GlyphID of ligature to substitute */ + HeadlessArray16Of<typename Types::HBGlyphID> + 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 <typename Iterator, + hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))> + 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/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh new file mode 100644 index 0000000000..08665438c4 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct LigatureSet +{ + protected: + Array16OfOffset16To<Ligature<Types>> + 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<Types> &_) { 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<Types> &_) { + 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<Types> &_) { _.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<Types> &_) { _.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<Types> &_) { 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<const HBGlyphID16> ligatures, + hb_array_t<const unsigned int> component_count_list, + hb_array_t<const HBGlyphID16> &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/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh new file mode 100644 index 0000000000..18f6e35581 --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; +#ifndef HB_NO_BEYOND_64K + LigatureSubstFormat1_2<MediumTypes> format2; +#endif + } u; + + public: + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (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<const HBGlyphID16> first_glyphs, + hb_array_t<const unsigned int> ligature_per_first_glyph_count_list, + hb_array_t<const HBGlyphID16> ligatures_list, + hb_array_t<const unsigned int> component_count_list, + hb_array_t<const HBGlyphID16> 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/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh new file mode 100644 index 0000000000..5c7df97d13 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct LigatureSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of<typename Types::template OffsetTo<LigatureSet<Types>>> + 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<LigatureSet<Types>> &_) + { 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<Types> &_) { _.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<Types> &_) { _.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<const HBGlyphID16> first_glyphs, + hb_array_t<const unsigned int> ligature_per_first_glyph_count_list, + hb_array_t<const HBGlyphID16> ligatures_list, + hb_array_t<const unsigned int> component_count_list, + hb_array_t<const HBGlyphID16> 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<Types>& _) { + return _.intersects_lig_glyph (&glyphset); + }, hb_second) + | hb_map (hb_first) + | hb_sink (new_coverage); + + if (!c->serializer->push<Coverage> () + ->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/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh new file mode 100644 index 0000000000..742c8587ee --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; +#ifndef HB_NO_BEYOND_64K + MultipleSubstFormat1_2<MediumTypes> format2; +#endif + } u; + + public: + + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template<typename Iterator, + hb_requires (hb_is_sorted_iterator (Iterator))> + 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/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubstFormat1.hh new file mode 100644 index 0000000000..3b4bd11694 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct MultipleSubstFormat1_2 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of<typename Types::template OffsetTo<Sequence<Types>>> + 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<Types> &_) { _.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<Types> &_) { _.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<typename Iterator, + hb_requires (hb_is_sorted_iterator (Iterator))> + 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<hb_codepoint_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/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh new file mode 100644 index 0000000000..5ad463fea7 --- /dev/null +++ b/gfx/harfbuzz/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, typename ...Ts> + 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<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } +}; + +} +} +} + +#endif /* HB_OT_LAYOUT_GSUB_REVERSECHAINSINGLESUBST_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh new file mode 100644 index 0000000000..ec374f2f02 --- /dev/null +++ b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh @@ -0,0 +1,245 @@ +#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> + coverage; /* Offset to Coverage table--from + * beginning of table */ + Array16OfOffset16To<Coverage> + backtrack; /* Array of coverage tables + * in backtracking sequence, in glyph + * sequence order */ + Array16OfOffset16To<Coverage> + lookaheadX; /* Array of coverage tables + * in lookahead sequence, in glyph + * sequence order */ + Array16Of<HBGlyphID16> + 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); + hb_barrier (); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack); + if (!lookahead.sanitize (c, this)) + return_trace (false); + hb_barrier (); + const auto &substitute = StructAfter<decltype (substituteX)> (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<decltype (lookaheadX)> (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<decltype (lookaheadX)> (backtrack); + const auto &substitute = StructAfter<decltype (substituteX)> (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<decltype (lookaheadX)> (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<decltype (substituteX)> (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); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) + return_trace (false); /* No chaining to this type */ + + const auto &lookahead = StructAfter<decltype (lookaheadX)> (backtrack); + const auto &substitute = StructAfter<decltype (substituteX)> (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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool serialize_coverage_offset_array (hb_subset_context_t *c, Iterator it) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed<Array16OfOffset16To<Coverage>> (); + + if (unlikely (!c->serializer->allocate_size<HBUINT16> (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<typename Iterator, typename BacktrackIterator, typename LookaheadIterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_pair_t)), + hb_requires (hb_is_iterator (BacktrackIterator)), + hb_requires (hb_is_iterator (LookaheadIterator))> + 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<Array16Of<HBGlyphID16>> (); + 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<decltype (lookaheadX)> (backtrack); + const auto &substitute = StructAfter<decltype (substituteX)> (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<hb_codepoint_t, const HBGlyphID16 &> 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/gfx/harfbuzz/src/OT/Layout/GSUB/Sequence.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/Sequence.hh new file mode 100644 index 0000000000..a26cf8c6a6 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct Sequence +{ + protected: + Array16Of<typename Types::HBGlyphID> + 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 <typename Iterator, + hb_requires (hb_is_source_of (Iterator, hb_codepoint_t))> + 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/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh new file mode 100644 index 0000000000..181c9e52e5 --- /dev/null +++ b/gfx/harfbuzz/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<SmallTypes> format1; + SingleSubstFormat2_4<SmallTypes> format2; +#ifndef HB_NO_BEYOND_64K + SingleSubstFormat1_3<MediumTypes> format3; + SingleSubstFormat2_4<MediumTypes> format4; +#endif + } u; + + public: + + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + template<typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, + const hb_codepoint_pair_t))> + 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<typename Iterator> +static void +SingleSubst_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed<SingleSubst> ()->serialize (c, it); } + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SINGLESUBST_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat1.hh new file mode 100644 index 0000000000..850be86c04 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct SingleSubstFormat1_3 +{ + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + 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<typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> + 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/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubstFormat2.hh new file mode 100644 index 0000000000..9c651abe71 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Types> +struct SingleSubstFormat2_4 +{ + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of Substitution table */ + Array16Of<typename Types::HBGlyphID> + 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<typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, + hb_codepoint_pair_t))> + 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<hb_codepoint_t, const typename Types::HBGlyphID &> 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/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookup.hh new file mode 100644 index 0000000000..d49dcc0e0f --- /dev/null +++ b/gfx/harfbuzz/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<SubTable> (c); } + + const SubTable& get_subtable (unsigned int i) const + { return Lookup::get_subtable<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<hb_collect_glyphs_context_t>); + return dispatch (c); + } + + template <typename set_t> + void collect_coverage (set_t *glyphs) const + { + hb_collect_coverage_context_t<set_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<typename Glyphs, typename Substitutes, + hb_requires (hb_is_sorted_source_of (Glyphs, + const hb_codepoint_t) && + hb_is_source_of (Substitutes, + const hb_codepoint_t))> + 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<SubTable> ()->u.single.serialize (c, hb_zip (glyphs, substitutes))) + { + c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template<typename Iterator, + hb_requires (hb_is_sorted_iterator (Iterator))> + 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<SubTable> ()->u.multiple. + serialize (c, it)) + { + c->add_link (get_subtables<SubTable> ()[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<const HBGlyphID16> glyphs, + hb_array_t<const unsigned int> alternate_len_list, + hb_array_t<const HBGlyphID16> alternate_glyphs_list) + { + TRACE_SERIALIZE (this); + if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false); + + if (c->push<SubTable> ()->u.alternate. + serialize (c, + glyphs, + alternate_len_list, + alternate_glyphs_list)) + { + c->add_link (get_subtables<SubTable> ()[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<const HBGlyphID16> first_glyphs, + hb_array_t<const unsigned int> ligature_per_first_glyph_count_list, + hb_array_t<const HBGlyphID16> ligatures_list, + hb_array_t<const unsigned int> component_count_list, + hb_array_t<const HBGlyphID16> 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<SubTable> ()->u.ligature. + serialize (c, + first_glyphs, + ligature_per_first_glyph_count_list, + ligatures_list, + component_count_list, + component_list)) + { + c->add_link (get_subtables<SubTable> ()[0], c->pop_pack ()); + return_trace (true); + } + c->pop_discard (); + return_trace (false); + } + + template <typename context_t> + 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, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { return Lookup::dispatch<SubTable> (c, std::forward<Ts> (ds)...); } + + bool subset (hb_subset_context_t *c) const + { return Lookup::subset<SubTable> (c); } +}; + + +} +} +} + +#endif /* OT_LAYOUT_GSUB_SUBSTLOOKUP_HH */ diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SubstLookupSubTable.hh new file mode 100644 index 0000000000..a525fba039 --- /dev/null +++ b/gfx/harfbuzz/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, typename ...Ts> + 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<Ts> (ds)...)); + case Multiple: return_trace (u.multiple.dispatch (c, std::forward<Ts> (ds)...)); + case Alternate: return_trace (u.alternate.dispatch (c, std::forward<Ts> (ds)...)); + case Ligature: return_trace (u.ligature.dispatch (c, std::forward<Ts> (ds)...)); + case Context: return_trace (u.context.dispatch (c, std::forward<Ts> (ds)...)); + case ChainContext: return_trace (u.chainContext.dispatch (c, std::forward<Ts> (ds)...)); + case Extension: return_trace (u.extension.dispatch (c, std::forward<Ts> (ds)...)); + case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, std::forward<Ts> (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/gfx/harfbuzz/src/OT/Layout/types.hh b/gfx/harfbuzz/src/OT/Layout/types.hh new file mode 100644 index 0000000000..3840db0598 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Type, typename BaseType=void, bool has_null=true> + using OffsetTo = OT::Offset16To<Type, BaseType, has_null>; + template <typename Type> + using ArrayOf = OT::Array16Of<Type>; + template <typename Type> + using SortedArrayOf = OT::SortedArray16Of<Type>; +}; + +struct MediumTypes { + static constexpr unsigned size = 3; + using large_int = uint64_t; + using HBUINT = HBUINT24; + using HBGlyphID = HBGlyphID24; + using Offset = Offset24; + template <typename Type, typename BaseType=void, bool has_null=true> + using OffsetTo = OT::Offset24To<Type, BaseType, has_null>; + template <typename Type> + using ArrayOf = OT::Array24Of<Type>; + template <typename Type> + using SortedArrayOf = OT::SortedArray24Of<Type>; +}; + +} +} + +#endif /* OT_LAYOUT_TYPES_HH */ diff --git a/gfx/harfbuzz/src/OT/glyf/CompositeGlyph.hh b/gfx/harfbuzz/src/OT/glyf/CompositeGlyph.hh new file mode 100644 index 0000000000..60858a5a58 --- /dev/null +++ b/gfx/harfbuzz/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<const HBUINT8> (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<contour_point_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<contour_point_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<contour_point_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<const HBINT8> (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<HBINT16 *> (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<HBINT8 *> (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<CompositeGlyphRecord *> (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<const HBINT8> (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<const HBGlyphID24> (flags); + else +#endif + return StructAfter<const HBGlyphID16> (flags); + } + void set_gid (hb_codepoint_t gid) + { +#ifndef HB_NO_BEYOND_64K + if (flags & GID_IS_24BIT) + StructAfter<HBGlyphID24> (flags) = gid; + else +#endif + /* TODO assert? */ + StructAfter<HBGlyphID16> (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<char> (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<CompositeGlyphRecord>; + +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<CompositeGlyphRecord, GlyphHeader> (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<CompositeGlyphRecord &> (_).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<CompositeGlyphRecord &> ( + StructAfter<CompositeGlyphRecord, GlyphHeader> (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<const CompositeGlyphRecord *> (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/gfx/harfbuzz/src/OT/glyf/Glyph.hh b/gfx/harfbuzz/src/OT/glyf/Glyph.hh new file mode 100644 index 0000000000..5ea611948f --- /dev/null +++ b/gfx/harfbuzz/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<contour_point_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 <typename accelerator_t> + 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<int> coords = hb_array_t<int> (), + 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<contour_point_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<contour_point_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<contour_point_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<int> (); + + 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<GlyphHeader> ()), + gid (-1), + type(EMPTY) + {} + + Glyph (hb_bytes_t bytes_, + hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_), + header (bytes.as<GlyphHeader> ()), + 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/gfx/harfbuzz/src/OT/glyf/GlyphHeader.hh b/gfx/harfbuzz/src/OT/glyf/GlyphHeader.hh new file mode 100644 index 0000000000..a43b6691ab --- /dev/null +++ b/gfx/harfbuzz/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 <typename accelerator_t> + 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/gfx/harfbuzz/src/OT/glyf/SimpleGlyph.hh b/gfx/harfbuzz/src/OT/glyf/SimpleGlyph.hh new file mode 100644 index 0000000000..1d42cc2925 --- /dev/null +++ b/gfx/harfbuzz/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<HBUINT16> (&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<HBUINT16> (glyph - 2, 0) + 1; + unsigned int num_instructions = StructAtOffset<HBUINT16> (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<GlyphHeader &> (header); + (HBUINT16 &) StructAtOffset<HBUINT16> (&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<HBUINT16> (&bytes, flags_offset); + first_flag = (uint8_t) first_flag | FLAG_OVERLAP_SIMPLE; + } + + static bool read_flags (const HBUINT8 *&p /* IN/OUT */, + hb_array_t<contour_point_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<contour_point_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<HBUINT16> (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<HBUINT8> (&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<uint8_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<uint8_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<uint8_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/gfx/harfbuzz/src/OT/glyf/SubsetGlyph.hh b/gfx/harfbuzz/src/OT/glyf/SubsetGlyph.hh new file mode 100644 index 0000000000..8099d3c126 --- /dev/null +++ b/gfx/harfbuzz/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<CompositeGlyphRecord &> (_).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<VarCompositeGlyphRecord &> (_).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<CompositeGlyphRecord &> (*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/gfx/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh b/gfx/harfbuzz/src/OT/glyf/VarCompositeGlyph.hh new file mode 100644 index 0000000000..50cbece3ca --- /dev/null +++ b/gfx/harfbuzz/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<const contour_point_t> record_points, + hb_array_t<contour_point_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<contour_point_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<VarCompositeGlyphRecord>; + +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<VarCompositeGlyphRecord, GlyphHeader> (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/gfx/harfbuzz/src/OT/glyf/composite-iter.hh b/gfx/harfbuzz/src/OT/glyf/composite-iter.hh new file mode 100644 index 0000000000..d05701f3d1 --- /dev/null +++ b/gfx/harfbuzz/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 <typename CompositeGlyphRecord> +struct composite_iter_tmpl : hb_iter_with_fallback_t<composite_iter_tmpl<CompositeGlyphRecord>, + 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<CompositeGlyphRecord> (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/gfx/harfbuzz/src/OT/glyf/coord-setter.hh b/gfx/harfbuzz/src/OT/glyf/coord-setter.hh new file mode 100644 index 0000000000..cf05929362 --- /dev/null +++ b/gfx/harfbuzz/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<int> 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<int> get_coords () + { return coords.as_array (); } + + hb_vector_t<int> coords; +}; + + +} /* namespace glyf_impl */ +} /* namespace OT */ + +#endif /* OT_GLYF_COORD_SETTER_HH */ diff --git a/gfx/harfbuzz/src/OT/glyf/glyf-helpers.hh b/gfx/harfbuzz/src/OT/glyf/glyf-helpers.hh new file mode 100644 index 0000000000..d0a5a132f0 --- /dev/null +++ b/gfx/harfbuzz/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<typename IteratorIn, typename TypeOut, + hb_requires (hb_is_source_of (IteratorIn, unsigned int))> +static void +_write_loca (IteratorIn&& it, + const hb_sorted_vector_t<hb_codepoint_pair_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<head> (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<typename Iterator, + hb_requires (hb_is_source_of (Iterator, unsigned int))> +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/gfx/harfbuzz/src/OT/glyf/glyf.hh b/gfx/harfbuzz/src/OT/glyf/glyf.hh new file mode 100644 index 0000000000..6300cf4be0 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Iterator> + 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<unsigned> padded_offsets; + if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true))) + return_trace (false); + + hb_vector_t<glyf_impl::SubsetGlyph> 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 <glyf> (); + 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<glyf_impl::SubsetGlyph>& 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<glyf_impl::SubsetGlyph> &glyphs) const + { + for (auto &g : glyphs) + g.free_compiled_bytes (); + } + + protected: + UnsizedArrayOf<HBUINT8> + 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<glyf> (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<typename T> + 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> loca_table; + hb_blob_ptr_t<glyf> glyf_table; +}; + + +inline bool +glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan, + hb_font_t *font, + hb_vector_t<glyf_impl::SubsetGlyph>& 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<hb_variation_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/gfx/harfbuzz/src/OT/glyf/loca.hh b/gfx/harfbuzz/src/OT/glyf/loca.hh new file mode 100644 index 0000000000..4481cba8ed --- /dev/null +++ b/gfx/harfbuzz/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<HBUINT8> + 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/gfx/harfbuzz/src/OT/glyf/path-builder.hh b/gfx/harfbuzz/src/OT/glyf/path-builder.hh new file mode 100644 index 0000000000..f550524503 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/OT/name/name.hh b/gfx/harfbuzz/src/OT/name/name.hh new file mode 100644 index 0000000000..e2a25d4a0f --- /dev/null +++ b/gfx/harfbuzz/src/OT/name/name.hh @@ -0,0 +1,589 @@ +/* + * 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 <typename in_utf_t, typename out_utf_t> +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<hb_ot_name_record_ids_t, hb_bytes_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<hb_utf8_t, hb_utf16_be_t> (*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<hb_utf8_t, hb_utf16_be_t> (*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) && + hb_barrier () && + 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<UnsizedArrayOf<HBUINT8>> + 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 <typename Iterator, + hb_requires (hb_is_source_of (Iterator, const NameRecord &))> + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void *src_string_pool +#ifdef HB_EXPERIMENTAL_API + , const hb_vector_t<hb_ot_name_record_ids_t>& insert_name_records + , const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_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<NameRecord> 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<name> (); + +#ifdef HB_EXPERIMENTAL_API + const hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_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<hb_ot_name_record_ids_t, unsigned> 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<hb_ot_name_record_ids_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) && + hb_barrier () && + 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<name> (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<const NameRecord> 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<const NameRecord> 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<name> table; + hb_vector_t<hb_ot_name_entry_t> names; + }; + + public: + /* We only implement format 0 for now. */ + HBUINT16 format; /* Format selector (=0/1). */ + HBUINT16 count; /* Number of name records. */ + NNOffset16To<UnsizedArrayOf<HBUINT8>> + stringOffset; /* Offset to start of string storage (from start of table). */ + UnsizedArrayOf<NameRecord> + 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/gfx/harfbuzz/src/addTable.py b/gfx/harfbuzz/src/addTable.py new file mode 100644 index 0000000000..103f292dd6 --- /dev/null +++ b/gfx/harfbuzz/src/addTable.py @@ -0,0 +1,16 @@ +import sys +from fontTools.ttLib import TTFont +from fontTools.ttLib.tables.DefaultTable import DefaultTable + +if len(sys.argv) == 1: + print("usage: python addTable.py input.ttf output.ttf Wasm.bin") + sys.exit(1) + +font = TTFont(sys.argv[1]) + +wasm_table = DefaultTable("Wasm") +wasm_table.data = open(sys.argv[3], "rb").read() + +font["Wasm"] = wasm_table + +font.save(sys.argv[2]) diff --git a/gfx/harfbuzz/src/check-c-linkage-decls.py b/gfx/harfbuzz/src/check-c-linkage-decls.py new file mode 100755 index 0000000000..fe18eda897 --- /dev/null +++ b/gfx/harfbuzz/src/check-c-linkage-decls.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +import sys, os + +srcdir = os.getenv ('srcdir', os.path.dirname (__file__)) +base_srcdir = os.getenv ('base_srcdir', srcdir) + +os.chdir (srcdir) + +def removeprefix(s): + abs_path = os.path.join(base_srcdir, s) + return os.path.relpath(abs_path, srcdir) + + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] +HBSOURCES = [ + removeprefix(x) for x in os.getenv ('HBSOURCES', '').split () +] or [ + x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh')) +] +stat = 0 + +for x in HBHEADERS: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if ('HB_BEGIN_DECLS' not in content) or ('HB_END_DECLS' not in content): + print ('Ouch, file %s does not have HB_BEGIN_DECLS / HB_END_DECLS, but it should' % x) + stat = 1 + +for x in HBSOURCES: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if ('HB_BEGIN_DECLS' in content) or ('HB_END_DECLS' in content): + print ('Ouch, file %s has HB_BEGIN_DECLS / HB_END_DECLS, but it shouldn\'t' % x) + stat = 1 + +sys.exit (stat) diff --git a/gfx/harfbuzz/src/check-externs.py b/gfx/harfbuzz/src/check-externs.py new file mode 100755 index 0000000000..a64d2e5b4a --- /dev/null +++ b/gfx/harfbuzz/src/check-externs.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 + +import sys, os, re + +os.chdir (os.getenv ('srcdir', os.path.dirname (__file__))) + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] + +stat = 0 + +print ('Checking that all public symbols are exported with HB_EXTERN') +for x in HBHEADERS: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + for s in re.findall (r'\n.+\nhb_.+\n', content): + if not s.startswith ('\nHB_EXTERN '): + print ('failure on:', s) + stat = 1 + +sys.exit (stat) diff --git a/gfx/harfbuzz/src/check-header-guards.py b/gfx/harfbuzz/src/check-header-guards.py new file mode 100755 index 0000000000..35ae6bef62 --- /dev/null +++ b/gfx/harfbuzz/src/check-header-guards.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import sys, os, re + +srcdir = os.getenv ('srcdir', os.path.dirname (__file__)) +base_srcdir = os.getenv ('base_srcdir', srcdir) + +os.chdir (srcdir) + +def removeprefix(s): + abs_path = os.path.join(base_srcdir, s) + return os.path.relpath(abs_path, srcdir) + + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] +HBSOURCES = [ + removeprefix(x) for x in os.getenv ('HBSOURCES', '').split () +] or [ + x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh')) +] + + +stat = 0 + +for x in HBHEADERS + HBSOURCES: + if not x.endswith ('h') or x == 'hb-gobject-structs.h': continue + tag = x.upper ().replace ('.', '_').replace ('-', '_').replace(os.path.sep, '_').replace('/', '_') + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if len (re.findall (tag + r'\b', content)) != 3: + print ('Ouch, header file %s does not have correct preprocessor guards. Expected: %s' % (x, tag)) + stat = 1 + +sys.exit (stat) diff --git a/gfx/harfbuzz/src/check-includes.py b/gfx/harfbuzz/src/check-includes.py new file mode 100755 index 0000000000..fc95874b12 --- /dev/null +++ b/gfx/harfbuzz/src/check-includes.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import sys, os, re + +srcdir = os.getenv ('srcdir', os.path.dirname (__file__)) +base_srcdir = os.getenv ('base_srcdir', srcdir) + +os.chdir (srcdir) + +def removeprefix(s): + abs_path = os.path.join(base_srcdir, s) + return os.path.relpath(abs_path, srcdir) + +HBHEADERS = [os.path.basename (x) for x in os.getenv ('HBHEADERS', '').split ()] or \ + [x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith ('.h')] + +HBSOURCES = [ + removeprefix(x) for x in os.getenv ('HBSOURCES', '').split () +] or [ + x for x in os.listdir ('.') if x.startswith ('hb') and x.endswith (('.cc', '.hh')) +] + + +stat = 0 + +print ('Checking that public header files #include "hb-common.h" or "hb.h" first (or none)') +for x in HBHEADERS: + if x == 'hb.h' or x == 'hb-common.h': continue + with open (x, 'r', encoding='utf-8') as f: content = f.read () + first = re.findall (r'#.*include.*', content)[0] + if first not in ['#include "hb.h"', '#include "hb-common.h"']: + print ('failure on %s' % x) + stat = 1 + +print ('Checking that source files #include a private header first (or none)') +for x in HBSOURCES: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + includes = re.findall (r'#.*include.*', content) + if includes: + if not len (re.findall (r'".*\.hh"', includes[0])): + print ('failure on %s' % x) + stat = 1 + +print ('Checking that there is no #include <hb-*.h>') +for x in HBHEADERS + HBSOURCES: + with open (x, 'r', encoding='utf-8') as f: content = f.read () + if re.findall ('#.*include.*<.*hb', content): + print ('failure on %s' % x) + stat = 1 + +sys.exit (stat) diff --git a/gfx/harfbuzz/src/check-libstdc++.py b/gfx/harfbuzz/src/check-libstdc++.py new file mode 100755 index 0000000000..e70d5f80b2 --- /dev/null +++ b/gfx/harfbuzz/src/check-libstdc++.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import sys, os, shutil, subprocess + +os.chdir (os.getenv ('srcdir', os.path.dirname (__file__))) + +libs = os.getenv ('libs', '.libs') + +ldd = os.getenv ('LDD', shutil.which ('ldd')) +if not ldd: + otool = os.getenv ('OTOOL', shutil.which ('otool')) + if otool: + ldd = otool + ' -L' + else: + print ('check-libstdc++.py: \'ldd\' not found; skipping test') + sys.exit (77) + +stat = 0 +tested = False + +# harfbuzz-icu links to libstdc++ because icu does. +for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-gobject', 'harfbuzz-cairo']: + for suffix in ['so', 'dylib']: + so = os.path.join (libs, 'lib%s.%s' % (soname, suffix)) + if not os.path.exists (so): continue + + print ('Checking that we are not linking to libstdc++ or libc++ in %s' % so) + ldd_result = subprocess.check_output (ldd.split() + [so]) + if (b'libstdc++' in ldd_result) or (b'libc++' in ldd_result): + print ('Ouch, %s is linked to libstdc++ or libc++' % so) + stat = 1 + + tested = True + +if not tested: + print ('check-libstdc++.py: libharfbuzz shared library not found; skipping test') + sys.exit (77) + +sys.exit (stat) diff --git a/gfx/harfbuzz/src/check-static-inits.py b/gfx/harfbuzz/src/check-static-inits.py new file mode 100755 index 0000000000..c6e2db1ea6 --- /dev/null +++ b/gfx/harfbuzz/src/check-static-inits.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import sys, os, shutil, subprocess, glob, re + +builddir = os.getenv ('builddir', os.path.dirname (__file__)) +libs = os.getenv ('libs', '.libs') + +objdump = os.getenv ('OBJDUMP', shutil.which ('objdump')) +if not objdump: + print ('check-static-inits.py: \'ldd\' not found; skipping test') + sys.exit (77) + +if sys.version_info < (3, 5): + print ('check-static-inits.py: needs python 3.5 for recursive support in glob') + sys.exit (77) + +OBJS = glob.glob (os.path.join (builddir, libs, '**', '*hb*.o'), recursive=True) +if not OBJS: + print ('check-static-inits.py: object files not found; skipping test') + sys.exit (77) + +stat = 0 +tested = 0 + +for obj in OBJS: + result = subprocess.run(objdump.split () + ['-t', obj], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + if result.returncode: + if result.stderr.find (b'not recognized') != -1: + # https://github.com/harfbuzz/harfbuzz/issues/3019 + print ('objdump %s returned "not recognized", skipping' % obj) + continue + print ('objdump %s returned error:\n%s' % (obj, result.stderr.decode ('utf-8'))) + stat = 2 + + result = result.stdout.decode ('utf-8') + + # Checking that no object file has static initializers + for l in re.findall (r'^.*\.[cd]tors.*$', result, re.MULTILINE): + if not re.match (r'.*\b0+\b', l): + print ('Ouch, %s has static initializers/finalizers' % obj) + stat = 1 + + # Checking that no object file has lazy static C++ constructors/destructors or other such stuff + if ('__cxa_' in result) and ('__ubsan_handle' not in result): + print ('Ouch, %s has lazy static C++ constructors/destructors or other such stuff' % obj) + stat = 1 + + tested += 1 + +sys.exit (stat if tested else 77) diff --git a/gfx/harfbuzz/src/check-symbols.py b/gfx/harfbuzz/src/check-symbols.py new file mode 100755 index 0000000000..91bf8b0671 --- /dev/null +++ b/gfx/harfbuzz/src/check-symbols.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +import sys, os, shutil, subprocess, re, difflib + +os.environ['LC_ALL'] = 'C' # otherwise 'nm' prints in wrong order + +builddir = os.getenv ('builddir', os.path.dirname (__file__)) +libs = os.getenv ('libs', '.libs') + +IGNORED_SYMBOLS = '|'.join(['_fini', '_init', '_fdata', '_ftext', '_fbss', + '__bss_start', '__bss_start__', '__bss_end__', '_edata', '_end', '_bss_end__', + '__end__', '__gcov_.*', 'llvm_.*', 'flush_fn_list', 'writeout_fn_list', 'mangle_path', + 'lprofDirMode', 'reset_fn_list']) + +nm = os.getenv ('NM', shutil.which ('nm')) +if not nm: + print ('check-symbols.py: \'nm\' not found; skipping test') + sys.exit (77) + +cxxfilt = shutil.which ('c++filt') + +tested = False +stat = 0 + +for soname in ['harfbuzz', 'harfbuzz-subset', 'harfbuzz-icu', 'harfbuzz-gobject', 'harfbuzz-cairo']: + for suffix in ['so', 'dylib']: + so = os.path.join (builddir, libs, 'lib%s.%s' % (soname, suffix)) + if not os.path.exists (so): continue + + # On macOS, C symbols are prefixed with _ + symprefix = '_' if suffix == 'dylib' else '' + + EXPORTED_SYMBOLS = [s.split ()[2] + for s in re.findall (r'^.+ [BCDGIRSTu] .+$', subprocess.check_output (nm.split() + [so]).decode ('utf-8'), re.MULTILINE) + if not re.match (r'.* %s(%s)\b' % (symprefix, IGNORED_SYMBOLS), s)] + + # run again c++filt also if is available + if cxxfilt: + EXPORTED_SYMBOLS = subprocess.check_output ( + [cxxfilt], input='\n'.join (EXPORTED_SYMBOLS).encode () + ).decode ('utf-8').splitlines () + + prefix = (symprefix + os.path.basename (so)).replace ('libharfbuzz', 'hb').replace ('-', '_').split ('.')[0] + + print ('Checking that %s does not expose internal symbols' % so) + suspicious_symbols = [x for x in EXPORTED_SYMBOLS if not re.match (r'^%s(_|$)' % prefix, x)] + if suspicious_symbols: + print ('Ouch, internal symbols exposed:', suspicious_symbols) + stat = 1 + + def_path = os.path.join (builddir, soname + '.def') + if not os.path.exists (def_path): + print ('\'%s\' not found; skipping' % def_path) + else: + print ('Checking that %s has the same symbol list as %s' % (so, def_path)) + with open (def_path, 'r', encoding='utf-8') as f: def_file = f.read () + diff_result = list (difflib.context_diff ( + def_file.splitlines (), + ['EXPORTS'] + [re.sub ('^%shb' % symprefix, 'hb', x) for x in EXPORTED_SYMBOLS] + + # cheat: copy the last line from the def file! + [def_file.splitlines ()[-1]] + )) + + if diff_result: + print ('\n'.join (diff_result)) + stat = 1 + + tested = True + +if not tested: + print ('check-symbols.py: no shared libraries found; skipping test') + sys.exit (77) + +sys.exit (stat) diff --git a/gfx/harfbuzz/src/failing-alloc.c b/gfx/harfbuzz/src/failing-alloc.c new file mode 100644 index 0000000000..1bfb935b94 --- /dev/null +++ b/gfx/harfbuzz/src/failing-alloc.c @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#include <stdlib.h> +#include <stdio.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int alloc_state = 0; + +__attribute__((no_sanitize("integer"))) +static int fastrand () +{ + if (!alloc_state) return 1; + /* Based on https://software.intel.com/content/www/us/en/develop/articles/fast-random-number-generator-on-the-intel-pentiumr-4-processor.html */ + alloc_state = (214013 * alloc_state + 2531011); + return (alloc_state >> 16) & 0x7FFF; +} + +void* hb_malloc_impl (size_t size) +{ + return (fastrand () % 16) ? malloc (size) : NULL; +} + +void* hb_calloc_impl (size_t nmemb, size_t size) +{ + return (fastrand () % 16) ? calloc (nmemb, size) : NULL; +} + +void* hb_realloc_impl (void *ptr, size_t size) +{ + return (fastrand () % 16) ? realloc (ptr, size) : NULL; +} + +void hb_free_impl (void *ptr) +{ + return free (ptr); +} + +#ifdef __cplusplus +} +#endif diff --git a/gfx/harfbuzz/src/fix_get_types.py b/gfx/harfbuzz/src/fix_get_types.py new file mode 100755 index 0000000000..208b9dfcd6 --- /dev/null +++ b/gfx/harfbuzz/src/fix_get_types.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +import re +import argparse + +parser = argparse.ArgumentParser () +parser.add_argument ('input') +parser.add_argument ('output') +args = parser.parse_args () + +with open (args.input, 'r') as inp, open (args.output, 'w') as out: + for l in inp.readlines (): + l = re.sub ('_t_get_type', '_get_type', l) + l = re.sub ('_T \(', ' (', l) + out.write (l) diff --git a/gfx/harfbuzz/src/gen-arabic-joining-list.py b/gfx/harfbuzz/src/gen-arabic-joining-list.py new file mode 100755 index 0000000000..7ec7425fd8 --- /dev/null +++ b/gfx/harfbuzz/src/gen-arabic-joining-list.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-arabic-joining-table.py ArabicShaping.txt Scripts.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt +* https://unicode.org/Public/UCD/latest/ucd/Scripts.txt +""" + +import os.path, sys + +if len (sys.argv) != 3: + sys.exit (__doc__) + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[f.readline (), f.readline ()] for f in files] +while files[0].readline ().find ('##################') < 0: + pass + +def read (f): + mapping = {} + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + mapping[u] = t + + return mapping + +def read_joining_uu (f): + values = set () + for line in f: + + if line[0] == '#': + continue + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + if fields[2] in {'T', 'U'}: + continue + + values.add (int (fields[0], 16)) + + return sorted (values) + +def print_has_arabic_joining (scripts, joining_uu): + + print ("static bool") + print ("has_arabic_joining (hb_script_t script)") + print ("{") + print (" /* List of scripts that have data in arabic-table. */") + print (" switch ((int) script)") + print (" {") + + for script in sorted ({scripts[u] for u in joining_uu if scripts[u] not in {'Common', 'Inherited'}}): + print (" case HB_SCRIPT_{}:".format (script.upper ())) + + print (" return true;") + print () + print (" default:") + print (" return false;") + print (" }") + print ("}") + print () + +print ("/* == Start of generated function == */") +print ("/*") +print (" * The following function is generated by running:") +print (" *") +print (" * ./gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt") +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip ())) +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH") +print ("#define HB_OT_SHAPER_ARABIC_JOINING_LIST_HH") +print () + +print_has_arabic_joining (read (files[1]), read_joining_uu (files[0])) + +print () +print ("#endif /* HB_OT_SHAPER_ARABIC_JOINING_LIST_HH */") +print () +print ("/* == End of generated function == */") diff --git a/gfx/harfbuzz/src/gen-arabic-pua.py b/gfx/harfbuzz/src/gen-arabic-pua.py new file mode 100755 index 0000000000..4cf290087d --- /dev/null +++ b/gfx/harfbuzz/src/gen-arabic-pua.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-arabic-pua.py +""" + +import packTab + + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * ./gen-arabic-pua.py") +print (" *") +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_ARABIC_PUA_HH") +print ("#define HB_OT_SHAPER_ARABIC_PUA_HH") +print () + +code = packTab.Code('_hb_arabic') + +for p in ("ArabicPUASimplified.txt", "ArabicPUATraditional.txt"): + with open (p, encoding='utf-8') as f: + fields = [l.split('\t') for l in f if l[:1] != '#'] + data = {int(fs[1], 16):int(fs[0], 16) for fs in fields} + sol = packTab.pack_table(data, compression=9) + sol.genCode(code, f'pua_{p[9:13].lower()}_map') + +code.print_c(linkage='static inline') + +print () +print ("#endif /* HB_OT_SHAPER_ARABIC_PUA_HH */") +print () +print ("/* == End of generated table == */") diff --git a/gfx/harfbuzz/src/gen-arabic-table.py b/gfx/harfbuzz/src/gen-arabic-table.py new file mode 100755 index 0000000000..8278d7d69c --- /dev/null +++ b/gfx/harfbuzz/src/gen-arabic-table.py @@ -0,0 +1,358 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt +* https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt +* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt +""" + +import os.path, sys + +if len (sys.argv) != 4: + sys.exit (__doc__) + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[files[0].readline (), files[0].readline ()], [files[2].readline (), files[2].readline ()]] +headers.append (["UnicodeData.txt does not have a header."]) +while files[0].readline ().find ('##################') < 0: + pass + +blocks = {} +def read_blocks(f): + global blocks + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + blocks[u] = t + +def print_joining_table(f): + + values = {} + for line in f: + + if line[0] == '#': + continue + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + u = int (fields[0], 16) + + if fields[3] in ["ALAPH", "DALATH RISH"]: + value = "JOINING_GROUP_" + fields[3].replace(' ', '_') + else: + value = "JOINING_TYPE_" + fields[2] + values[u] = value + + short_value = {} + for value in sorted (set ([v for v in values.values ()] + ['JOINING_TYPE_X'])): + short = ''.join(x[0] for x in value.split('_')[2:]) + assert short not in short_value.values() + short_value[value] = short + + print () + for value,short in short_value.items(): + print ("#define %s %s" % (short, value)) + + uu = sorted(values.keys()) + num = len(values) + all_blocks = set([blocks[u] for u in uu]) + + last = -100000 + ranges = [] + for u in uu: + if u - last <= 1+16*5: + ranges[-1][-1] = u + else: + ranges.append([u,u]) + last = u + + print () + print ("static const uint8_t joining_table[] =") + print ("{") + last_block = None + offset = 0 + for start,end in ranges: + + print () + print ("#define joining_offset_0x%04xu %d" % (start, offset)) + + for u in range(start, end+1): + + block = blocks.get(u, last_block) + value = values.get(u, "JOINING_TYPE_X") + + if block != last_block or u == start: + if u != start: + print () + if block in all_blocks: + print ("\n /* %s */" % block) + else: + print ("\n /* FILLER */") + last_block = block + if u % 32 != 0: + print () + print (" /* %04X */" % (u//32*32), " " * (u % 32), end="") + + if u % 32 == 0: + print () + print (" /* %04X */ " % u, end="") + print ("%s," % short_value[value], end="") + print () + + offset += end - start + 1 + print () + occupancy = num * 100. / offset + print ("}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)) + print () + + page_bits = 12 + print () + print ("static unsigned int") + print ("joining_type (hb_codepoint_t u)") + print ("{") + print (" switch (u >> %d)" % page_bits) + print (" {") + pages = set([u>>page_bits for u in [s for s,e in ranges]+[e for s,e in ranges]]) + for p in sorted(pages): + print (" case 0x%0Xu:" % p) + for (start,end) in ranges: + if p not in [start>>page_bits, end>>page_bits]: continue + offset = "joining_offset_0x%04xu" % start + print (" if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return joining_table[u - 0x%04Xu + %s];" % (start, end, start, offset)) + print (" break;") + print ("") + print (" default:") + print (" break;") + print (" }") + print (" return X;") + print ("}") + print () + for value,short in short_value.items(): + print ("#undef %s" % (short)) + print () + +LIGATURES = ( + 0xF2EE, 0xFC08, 0xFC0E, 0xFC12, 0xFC32, 0xFC3F, 0xFC40, 0xFC41, 0xFC42, + 0xFC44, 0xFC4E, 0xFC5E, 0xFC60, 0xFC61, 0xFC62, 0xFC6A, 0xFC6D, 0xFC6F, + 0xFC70, 0xFC73, 0xFC75, 0xFC86, 0xFC8F, 0xFC91, 0xFC94, 0xFC9C, 0xFC9D, + 0xFC9E, 0xFC9F, 0xFCA1, 0xFCA2, 0xFCA3, 0xFCA4, 0xFCA8, 0xFCAA, 0xFCAC, + 0xFCB0, 0xFCC9, 0xFCCA, 0xFCCB, 0xFCCC, 0xFCCD, 0xFCCE, 0xFCCF, 0xFCD0, + 0xFCD1, 0xFCD2, 0xFCD3, 0xFCD5, 0xFCDA, 0xFCDB, 0xFCDC, 0xFCDD, 0xFD30, + 0xFD88, 0xFEF5, 0xFEF6, 0xFEF7, 0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, + 0xF201, 0xF211, 0xF2EE, +) + +def print_shaping_table(f): + + shapes = {} + ligatures = {} + names = {} + lines = f.readlines() + lines += [ + "F201;PUA ARABIC LIGATURE LELLAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 0644 0647;;;;N;;;;;", + "F211;PUA ARABIC LIGATURE LAM WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062C;;;;N;;;;;", + "F2EE;PUA ARABIC LIGATURE SHADDA WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B 0651;;;;N;;;;;", + ] + for line in lines: + + fields = [x.strip () for x in line.split (';')] + if fields[5][0:1] != '<': + continue + + items = fields[5].split (' ') + shape, items = items[0][1:-1], tuple (int (x, 16) for x in items[1:]) + c = int (fields[0], 16) + + if not shape in ['initial', 'medial', 'isolated', 'final']: + continue + + if len (items) != 1: + # Mark ligatures start with space and are in visual order, so we + # remove the space and reverse the items. + if items[0] == 0x0020: + items = items[:0:-1] + shape = None + # We only care about a subset of ligatures + if c not in LIGATURES: + continue + + # Save ligature + names[c] = fields[1] + if items not in ligatures: + ligatures[items] = {} + ligatures[items][shape] = c + else: + # Save shape + if items[0] not in names: + names[items[0]] = fields[1] + else: + names[items[0]] = os.path.commonprefix ([names[items[0]], fields[1]]).strip () + if items[0] not in shapes: + shapes[items[0]] = {} + shapes[items[0]][shape] = c + + print () + print ("static const uint16_t shaping_table[][4] =") + print ("{") + + keys = shapes.keys () + min_u, max_u = min (keys), max (keys) + for u in range (min_u, max_u + 1): + s = [shapes[u][shape] if u in shapes and shape in shapes[u] else 0 + for shape in ['initial', 'medial', 'final', 'isolated']] + value = ', '.join ("0x%04Xu" % c for c in s) + print (" {%s}, /* U+%04X %s */" % (value, u, names[u] if u in names else "")) + + print ("};") + print () + print ("#define SHAPING_TABLE_FIRST 0x%04Xu" % min_u) + print ("#define SHAPING_TABLE_LAST 0x%04Xu" % max_u) + print () + + ligas_2 = {} + ligas_3 = {} + ligas_mark_2 = {} + for key in ligatures.keys (): + for shape in ligatures[key]: + c = ligatures[key][shape] + if len(key) == 3: + if shape == 'isolated': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial'], shapes[key[2]]['final']) + elif shape == 'final': + liga = (shapes[key[0]]['medial'], shapes[key[1]]['medial'], shapes[key[2]]['final']) + elif shape == 'initial': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial'], shapes[key[2]]['medial']) + else: + raise Exception ("Unexpected shape", shape) + if liga[0] not in ligas_3: + ligas_3[liga[0]] = [] + ligas_3[liga[0]].append ((liga[1], liga[2], c)) + elif len(key) == 2: + if shape is None: + liga = key + if liga[0] not in ligas_mark_2: + ligas_mark_2[liga[0]] = [] + ligas_mark_2[liga[0]].append ((liga[1], c)) + continue + elif shape == 'isolated': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['final']) + elif shape == 'final': + liga = (shapes[key[0]]['medial'], shapes[key[1]]['final']) + elif shape == 'initial': + liga = (shapes[key[0]]['initial'], shapes[key[1]]['medial']) + else: + raise Exception ("Unexpected shape", shape) + if liga[0] not in ligas_2: + ligas_2[liga[0]] = [] + ligas_2[liga[0]].append ((liga[1], c)) + else: + raise Exception ("Unexpected number of ligature components", key) + max_i = max (len (ligas_2[l]) for l in ligas_2) + print () + print ("static const struct ligature_set_t {") + print (" uint16_t first;") + print (" struct ligature_pairs_t {") + print (" uint16_t components[1];") + print (" uint16_t ligature;") + print (" } ligatures[%d];" % max_i) + print ("} ligature_table[] =") + print ("{") + for first in sorted (ligas_2.keys ()): + + print (" { 0x%04Xu, {" % (first)) + for liga in ligas_2[first]: + print (" { {0x%04Xu}, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]])) + print (" }},") + + print ("};") + print () + + max_i = max (len (ligas_mark_2[l]) for l in ligas_mark_2) + print () + print ("static const struct ligature_mark_set_t {") + print (" uint16_t first;") + print (" struct ligature_pairs_t {") + print (" uint16_t components[1];") + print (" uint16_t ligature;") + print (" } ligatures[%d];" % max_i) + print ("} ligature_mark_table[] =") + print ("{") + for first in sorted (ligas_mark_2.keys ()): + + print (" { 0x%04Xu, {" % (first)) + for liga in ligas_mark_2[first]: + print (" { {0x%04Xu}, 0x%04Xu }, /* %s */" % (liga[0], liga[1], names[liga[1]])) + print (" }},") + + print ("};") + print () + + max_i = max (len (ligas_3[l]) for l in ligas_3) + print () + print ("static const struct ligature_3_set_t {") + print (" uint16_t first;") + print (" struct ligature_triplets_t {") + print (" uint16_t components[2];") + print (" uint16_t ligature;") + print (" } ligatures[%d];" % max_i) + print ("} ligature_3_table[] =") + print ("{") + for first in sorted (ligas_3.keys ()): + + print (" { 0x%04Xu, {" % (first)) + for liga in ligas_3[first]: + print (" { {0x%04Xu, 0x%04Xu}, 0x%04Xu}, /* %s */" % (liga[0], liga[1], liga[2], names[liga[2]])) + print (" }},") + + print ("};") + print () + + + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt") +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip())) +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH") +print ("#define HB_OT_SHAPER_ARABIC_TABLE_HH") +print () + +read_blocks (files[2]) +print_joining_table (files[0]) +print_shaping_table (files[1]) + +print () +print ("#endif /* HB_OT_SHAPER_ARABIC_TABLE_HH */") +print () +print ("/* == End of generated table == */") diff --git a/gfx/harfbuzz/src/gen-def.py b/gfx/harfbuzz/src/gen-def.py new file mode 100755 index 0000000000..c34976a067 --- /dev/null +++ b/gfx/harfbuzz/src/gen-def.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +"usage: gen-def.py harfbuzz.def hb.h [hb-blob.h hb-buffer.h ...]" + +import os, re, sys + +if len (sys.argv) < 3: + sys.exit(__doc__) + +output_file = sys.argv[1] +header_paths = sys.argv[2:] + +headers_content = [] +for h in header_paths: + if h.endswith (".h"): + with open (h, encoding='utf-8') as f: headers_content.append (f.read ()) + +symbols = sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re.M)) +if '--experimental-api' not in sys.argv: + # Move these to harfbuzz-sections.txt when got stable + experimental_symbols = \ +"""hb_shape_justify +hb_subset_repack_or_fail +hb_subset_input_override_name_table +hb_subset_input_set_axis_range +""".splitlines () + symbols = [x for x in symbols if x not in experimental_symbols] +symbols = "\n".join (symbols) + +result = symbols if os.getenv ('PLAIN_LIST', '') else """EXPORTS +%s +LIBRARY lib%s-0.dll""" % (symbols, output_file.replace ('src/', '').replace ('.def', '')) + +with open (output_file, "w") as f: f.write (result) diff --git a/gfx/harfbuzz/src/gen-emoji-table.py b/gfx/harfbuzz/src/gen-emoji-table.py new file mode 100755 index 0000000000..42a3fb8de5 --- /dev/null +++ b/gfx/harfbuzz/src/gen-emoji-table.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-emoji-table.py emoji-data.txt emoji-test.txt + +Input file: +* https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt +* https://www.unicode.org/Public/emoji/latest/emoji-test.txt +""" + +import sys +from collections import OrderedDict +import packTab + +if len (sys.argv) != 3: + sys.exit (__doc__) + +f = open(sys.argv[1]) +header = [f.readline () for _ in range(10)] + +ranges = OrderedDict() +for line in f.readlines(): + line = line.strip() + if not line or line[0] == '#': + continue + rang, typ = [s.strip() for s in line.split('#')[0].split(';')[:2]] + + rang = [int(s, 16) for s in rang.split('..')] + if len(rang) > 1: + start, end = rang + else: + start = end = rang[0] + + if typ not in ranges: + ranges[typ] = [] + if ranges[typ] and ranges[typ][-1][1] == start - 1: + ranges[typ][-1] = (ranges[typ][-1][0], end) + else: + ranges[typ].append((start, end)) + + + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following tables are generated by running:") +print (" *") +print (" * ./gen-emoji-table.py emoji-data.txt") +print (" *") +print (" * on file with this header:") +print (" *") +for l in header: + print (" * %s" % (l.strip())) +print (" */") +print () +print ("#ifndef HB_UNICODE_EMOJI_TABLE_HH") +print ("#define HB_UNICODE_EMOJI_TABLE_HH") +print () +print ('#include "hb-unicode.hh"') +print () + +for typ, s in ranges.items(): + if typ != "Extended_Pictographic": continue + + arr = dict() + for start,end in s: + for i in range(start, end + 1): + arr[i] = 1 + + sol = packTab.pack_table(arr, 0, compression=9) + code = packTab.Code('_hb_emoji') + sol.genCode(code, 'is_'+typ) + code.print_c(linkage='static inline') + print() + +print () +print ("#endif /* HB_UNICODE_EMOJI_TABLE_HH */") +print () +print ("/* == End of generated table == */") + + +# Generate test file. +sequences = [] +with open(sys.argv[2]) as f: + for line in f.readlines(): + if "#" in line: + line = line[:line.index("#")] + if ";" in line: + line = line[:line.index(";")] + line = line.strip() + line = line.split(" ") + if len(line) < 2: + continue + sequences.append(line) + +with open("../test/shape/data/in-house/tests/emoji-clusters.tests", "w") as f: + for sequence in sequences: + f.write("../fonts/AdobeBlank2.ttf;--no-glyph-names --no-positions --font-funcs=ot") + f.write(";" + ",".join(sequence)) + f.write(";[" + "|".join("1=0" for c in sequence) + "]\n") diff --git a/gfx/harfbuzz/src/gen-harfbuzzcc.py b/gfx/harfbuzz/src/gen-harfbuzzcc.py new file mode 100755 index 0000000000..227384043e --- /dev/null +++ b/gfx/harfbuzz/src/gen-harfbuzzcc.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +"This tool is intended to be used from meson" + +import os, sys, shutil + +if len (sys.argv) < 3: + sys.exit (__doc__) + +OUTPUT = sys.argv[1] +CURRENT_SOURCE_DIR = sys.argv[2] + +# make sure input files are unique +sources = sorted(set(sys.argv[3:])) + +with open (OUTPUT, "wb") as f: + f.write ("".join ('#include "{}"\n'.format (os.path.relpath (os.path.abspath (x), CURRENT_SOURCE_DIR)) for x in sources if x.endswith (".cc")).encode ()) + +# copy it also to the source tree, but only if it has changed +baseline_filename = os.path.join (CURRENT_SOURCE_DIR, os.path.basename (OUTPUT)) +with open(baseline_filename, "rb") as baseline: + with open(OUTPUT, "rb") as generated: + if baseline.read() != generated.read(): + shutil.copyfile (OUTPUT, baseline_filename) diff --git a/gfx/harfbuzz/src/gen-hb-version.py b/gfx/harfbuzz/src/gen-hb-version.py new file mode 100755 index 0000000000..06018edfcf --- /dev/null +++ b/gfx/harfbuzz/src/gen-hb-version.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +"This tool is intended to be used from meson" + +import os, sys, shutil, re + +if len (sys.argv) < 4: + sys.exit(__doc__) + +version = sys.argv[1] +major, minor, micro = version.split (".") + +OUTPUT = sys.argv[2] +INPUT = sys.argv[3] +CURRENT_SOURCE_DIR = os.path.dirname(INPUT) + +try: + with open (OUTPUT, "r", encoding='utf-8') as old_output: + for line in old_output: + old_version = re.match (r"#define HB_VERSION_STRING \"(\d.\d.\d)\"", line) + if old_version and old_version[1] == version: + sys.exit () +except IOError: + pass + +with open (INPUT, "r", encoding='utf-8') as template: + with open (OUTPUT, "wb") as output: + output.write (template.read () + .replace ("@HB_VERSION_MAJOR@", major) + .replace ("@HB_VERSION_MINOR@", minor) + .replace ("@HB_VERSION_MICRO@", micro) + .replace ("@HB_VERSION@", version) + .encode ()) + +# copy it also to the source tree, but only if it has changed +baseline_filename = os.path.join (CURRENT_SOURCE_DIR, os.path.basename (OUTPUT)) +with open(baseline_filename, "rb") as baseline: + with open(OUTPUT, "rb") as generated: + if baseline.read() != generated.read(): + shutil.copyfile (OUTPUT, baseline_filename) diff --git a/gfx/harfbuzz/src/gen-indic-table.py b/gfx/harfbuzz/src/gen-indic-table.py new file mode 100755 index 0000000000..4ef970265d --- /dev/null +++ b/gfx/harfbuzz/src/gen-indic-table.py @@ -0,0 +1,699 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt +""" + +import sys + +if len (sys.argv) != 4: + sys.exit (__doc__) + +ALLOWED_SINGLES = [0x00A0, 0x25CC] +ALLOWED_BLOCKS = [ + 'Basic Latin', + 'Latin-1 Supplement', + 'Devanagari', + 'Bengali', + 'Gurmukhi', + 'Gujarati', + 'Oriya', + 'Tamil', + 'Telugu', + 'Kannada', + 'Malayalam', + 'Myanmar', + 'Khmer', + 'Vedic Extensions', + 'General Punctuation', + 'Superscripts and Subscripts', + 'Devanagari Extended', + 'Myanmar Extended-B', + 'Myanmar Extended-A', +] + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[f.readline () for i in range (2)] for f in files] + +unicode_data = [{} for _ in files] +for i, f in enumerate (files): + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1] + + for u in range (start, end + 1): + unicode_data[i][u] = t + +# Merge data into one dict: +defaults = ('Other', 'Not_Applicable', 'No_Block') +combined = {} +for i,d in enumerate (unicode_data): + for u,v in d.items (): + if i == 2 and not u in combined: + continue + if not u in combined: + combined[u] = list (defaults) + combined[u][i] = v +combined = {k:v for k,v in combined.items() if k in ALLOWED_SINGLES or v[2] in ALLOWED_BLOCKS} + + +# Convert categories & positions types + +categories = { + 'indic' : [ + 'X', + 'C', + 'V', + 'N', + 'H', + 'ZWNJ', + 'ZWJ', + 'M', + 'SM', + 'A', + 'VD', + 'PLACEHOLDER', + 'DOTTEDCIRCLE', + 'RS', + 'MPst', + 'Repha', + 'Ra', + 'CM', + 'Symbol', + 'CS', + ], + 'khmer' : [ + 'VAbv', + 'VBlw', + 'VPre', + 'VPst', + + 'Robatic', + 'Xgroup', + 'Ygroup', + ], + 'myanmar' : [ + 'VAbv', + 'VBlw', + 'VPre', + 'VPst', + + 'IV', + 'As', + 'DB', + 'GB', + 'MH', + 'MR', + 'MW', + 'MY', + 'PT', + 'VS', + 'ML', + ], +} + +category_map = { + 'Other' : 'X', + 'Avagraha' : 'Symbol', + 'Bindu' : 'SM', + 'Brahmi_Joining_Number' : 'PLACEHOLDER', # Don't care. + 'Cantillation_Mark' : 'A', + 'Consonant' : 'C', + 'Consonant_Dead' : 'C', + 'Consonant_Final' : 'CM', + 'Consonant_Head_Letter' : 'C', + 'Consonant_Initial_Postfixed' : 'C', # TODO + 'Consonant_Killer' : 'M', # U+17CD only. + 'Consonant_Medial' : 'CM', + 'Consonant_Placeholder' : 'PLACEHOLDER', + 'Consonant_Preceding_Repha' : 'Repha', + 'Consonant_Prefixed' : 'X', # Don't care. + 'Consonant_Subjoined' : 'CM', + 'Consonant_Succeeding_Repha' : 'CM', + 'Consonant_With_Stacker' : 'CS', + 'Gemination_Mark' : 'SM', # https://github.com/harfbuzz/harfbuzz/issues/552 + 'Invisible_Stacker' : 'H', + 'Joiner' : 'ZWJ', + 'Modifying_Letter' : 'X', + 'Non_Joiner' : 'ZWNJ', + 'Nukta' : 'N', + 'Number' : 'PLACEHOLDER', + 'Number_Joiner' : 'PLACEHOLDER', # Don't care. + 'Pure_Killer' : 'M', # Is like a vowel matra. + 'Register_Shifter' : 'RS', + 'Syllable_Modifier' : 'SM', + 'Tone_Letter' : 'X', + 'Tone_Mark' : 'N', + 'Virama' : 'H', + 'Visarga' : 'SM', + 'Vowel' : 'V', + 'Vowel_Dependent' : 'M', + 'Vowel_Independent' : 'V', +} +position_map = { + 'Not_Applicable' : 'END', + + 'Left' : 'PRE_C', + 'Top' : 'ABOVE_C', + 'Bottom' : 'BELOW_C', + 'Right' : 'POST_C', + + # These should resolve to the position of the last part of the split sequence. + 'Bottom_And_Right' : 'POST_C', + 'Left_And_Right' : 'POST_C', + 'Top_And_Bottom' : 'BELOW_C', + 'Top_And_Bottom_And_Left' : 'BELOW_C', + 'Top_And_Bottom_And_Right' : 'POST_C', + 'Top_And_Left' : 'ABOVE_C', + 'Top_And_Left_And_Right' : 'POST_C', + 'Top_And_Right' : 'POST_C', + + 'Overstruck' : 'AFTER_MAIN', + 'Visual_order_left' : 'PRE_M', +} + +category_overrides = { + + # These are the variation-selectors. They only appear in the Myanmar grammar + # but are not Myanmar-specific + 0xFE00: 'VS', + 0xFE01: 'VS', + 0xFE02: 'VS', + 0xFE03: 'VS', + 0xFE04: 'VS', + 0xFE05: 'VS', + 0xFE06: 'VS', + 0xFE07: 'VS', + 0xFE08: 'VS', + 0xFE09: 'VS', + 0xFE0A: 'VS', + 0xFE0B: 'VS', + 0xFE0C: 'VS', + 0xFE0D: 'VS', + 0xFE0E: 'VS', + 0xFE0F: 'VS', + + # These appear in the OT Myanmar spec, but are not Myanmar-specific + 0x2015: 'PLACEHOLDER', + 0x2022: 'PLACEHOLDER', + 0x25FB: 'PLACEHOLDER', + 0x25FC: 'PLACEHOLDER', + 0x25FD: 'PLACEHOLDER', + 0x25FE: 'PLACEHOLDER', + + + # Indic + + 0x0930: 'Ra', # Devanagari + 0x09B0: 'Ra', # Bengali + 0x09F0: 'Ra', # Bengali + 0x0A30: 'Ra', # Gurmukhi No Reph + 0x0AB0: 'Ra', # Gujarati + 0x0B30: 'Ra', # Oriya + 0x0BB0: 'Ra', # Tamil No Reph + 0x0C30: 'Ra', # Telugu Reph formed only with ZWJ + 0x0CB0: 'Ra', # Kannada + 0x0D30: 'Ra', # Malayalam No Reph, Logical Repha + + # The following act more like the Bindus. + 0x0953: 'SM', + 0x0954: 'SM', + + # U+0A40 GURMUKHI VOWEL SIGN II may be preceded by U+0A02 GURMUKHI SIGN BINDI. + 0x0A40: 'MPst', + + # The following act like consonants. + 0x0A72: 'C', + 0x0A73: 'C', + 0x1CF5: 'C', + 0x1CF6: 'C', + + # TODO: The following should only be allowed after a Visarga. + # For now, just treat them like regular tone marks. + 0x1CE2: 'A', + 0x1CE3: 'A', + 0x1CE4: 'A', + 0x1CE5: 'A', + 0x1CE6: 'A', + 0x1CE7: 'A', + 0x1CE8: 'A', + + # TODO: The following should only be allowed after some of + # the nasalization marks, maybe only for U+1CE9..U+1CF1. + # For now, just treat them like tone marks. + 0x1CED: 'A', + + # The following take marks in standalone clusters, similar to Avagraha. + 0xA8F2: 'Symbol', + 0xA8F3: 'Symbol', + 0xA8F4: 'Symbol', + 0xA8F5: 'Symbol', + 0xA8F6: 'Symbol', + 0xA8F7: 'Symbol', + 0x1CE9: 'Symbol', + 0x1CEA: 'Symbol', + 0x1CEB: 'Symbol', + 0x1CEC: 'Symbol', + 0x1CEE: 'Symbol', + 0x1CEF: 'Symbol', + 0x1CF0: 'Symbol', + 0x1CF1: 'Symbol', + + 0x0A51: 'M', # https://github.com/harfbuzz/harfbuzz/issues/524 + + # According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil, + # so the Indic shaper needs to know their categories. + 0x11301: 'SM', + 0x11302: 'SM', + 0x11303: 'SM', + 0x1133B: 'N', + 0x1133C: 'N', + + 0x0AFB: 'N', # https://github.com/harfbuzz/harfbuzz/issues/552 + 0x0B55: 'N', # https://github.com/harfbuzz/harfbuzz/issues/2849 + + 0x09FC: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/1613 + 0x0C80: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/623 + 0x0D04: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/pull/3511 + + 0x25CC: 'DOTTEDCIRCLE', + + + # Khmer + + 0x179A: 'Ra', + + 0x17CC: 'Robatic', + 0x17C9: 'Robatic', + 0x17CA: 'Robatic', + + 0x17C6: 'Xgroup', + 0x17CB: 'Xgroup', + 0x17CD: 'Xgroup', + 0x17CE: 'Xgroup', + 0x17CF: 'Xgroup', + 0x17D0: 'Xgroup', + 0x17D1: 'Xgroup', + + 0x17C7: 'Ygroup', + 0x17C8: 'Ygroup', + 0x17DD: 'Ygroup', + 0x17D3: 'Ygroup', # Just guessing. Uniscribe doesn't categorize it. + + 0x17D9: 'PLACEHOLDER', # https://github.com/harfbuzz/harfbuzz/issues/2384 + + + # Myanmar + + # https://docs.microsoft.com/en-us/typography/script-development/myanmar#analyze + + 0x104E: 'C', # The spec says C, IndicSyllableCategory says Consonant_Placeholder + + 0x1004: 'Ra', + 0x101B: 'Ra', + 0x105A: 'Ra', + + 0x1032: 'A', + 0x1036: 'A', + + 0x103A: 'As', + + #0x1040: 'D0', # XXX The spec says D0, but Uniscribe doesn't seem to do. + + 0x103E: 'MH', + 0x1060: 'ML', + 0x103C: 'MR', + 0x103D: 'MW', + 0x1082: 'MW', + 0x103B: 'MY', + 0x105E: 'MY', + 0x105F: 'MY', + + 0x1063: 'PT', + 0x1064: 'PT', + 0x1069: 'PT', + 0x106A: 'PT', + 0x106B: 'PT', + 0x106C: 'PT', + 0x106D: 'PT', + 0xAA7B: 'PT', + + 0x1038: 'SM', + 0x1087: 'SM', + 0x1088: 'SM', + 0x1089: 'SM', + 0x108A: 'SM', + 0x108B: 'SM', + 0x108C: 'SM', + 0x108D: 'SM', + 0x108F: 'SM', + 0x109A: 'SM', + 0x109B: 'SM', + 0x109C: 'SM', + + 0x104A: 'PLACEHOLDER', +} +position_overrides = { + + 0x0A51: 'BELOW_C', # https://github.com/harfbuzz/harfbuzz/issues/524 + + 0x0B01: 'BEFORE_SUB', # Oriya Bindu is BeforeSub in the spec. +} + +def matra_pos_left(u, block): + return "PRE_M" +def matra_pos_right(u, block): + if block == 'Devanagari': return 'AFTER_SUB' + if block == 'Bengali': return 'AFTER_POST' + if block == 'Gurmukhi': return 'AFTER_POST' + if block == 'Gujarati': return 'AFTER_POST' + if block == 'Oriya': return 'AFTER_POST' + if block == 'Tamil': return 'AFTER_POST' + if block == 'Telugu': return 'BEFORE_SUB' if u <= 0x0C42 else 'AFTER_SUB' + if block == 'Kannada': return 'BEFORE_SUB' if u < 0x0CC3 or u > 0x0CD6 else 'AFTER_SUB' + if block == 'Malayalam': return 'AFTER_POST' + return 'AFTER_SUB' +def matra_pos_top(u, block): + # BENG and MLYM don't have top matras. + if block == 'Devanagari': return 'AFTER_SUB' + if block == 'Gurmukhi': return 'AFTER_POST' # Deviate from spec + if block == 'Gujarati': return 'AFTER_SUB' + if block == 'Oriya': return 'AFTER_MAIN' + if block == 'Tamil': return 'AFTER_SUB' + if block == 'Telugu': return 'BEFORE_SUB' + if block == 'Kannada': return 'BEFORE_SUB' + return 'AFTER_SUB' +def matra_pos_bottom(u, block): + if block == 'Devanagari': return 'AFTER_SUB' + if block == 'Bengali': return 'AFTER_SUB' + if block == 'Gurmukhi': return 'AFTER_POST' + if block == 'Gujarati': return 'AFTER_POST' + if block == 'Oriya': return 'AFTER_SUB' + if block == 'Tamil': return 'AFTER_POST' + if block == 'Telugu': return 'BEFORE_SUB' + if block == 'Kannada': return 'BEFORE_SUB' + if block == 'Malayalam': return 'AFTER_POST' + return "AFTER_SUB" +def indic_matra_position(u, pos, block): # Reposition matra + if pos == 'PRE_C': return matra_pos_left(u, block) + if pos == 'POST_C': return matra_pos_right(u, block) + if pos == 'ABOVE_C': return matra_pos_top(u, block) + if pos == 'BELOW_C': return matra_pos_bottom(u, block) + assert (False) + +def position_to_category(pos): + if pos == 'PRE_C': return 'VPre' + if pos == 'ABOVE_C': return 'VAbv' + if pos == 'BELOW_C': return 'VBlw' + if pos == 'POST_C': return 'VPst' + assert(False) + + +defaults = (category_map[defaults[0]], position_map[defaults[1]], defaults[2]) + +indic_data = {} +for k, (cat, pos, block) in combined.items(): + cat = category_map[cat] + pos = position_map[pos] + indic_data[k] = (cat, pos, block) + +for k,new_cat in category_overrides.items(): + (cat, pos, _) = indic_data.get(k, defaults) + indic_data[k] = (new_cat, pos, unicode_data[2][k]) + +# We only expect position for certain types +positioned_categories = ('CM', 'SM', 'RS', 'H', 'M', 'MPst') +for k, (cat, pos, block) in indic_data.items(): + if cat not in positioned_categories: + pos = 'END' + indic_data[k] = (cat, pos, block) + +# Position overrides are more complicated + +# Keep in sync with CONSONANT_FLAGS in the shaper +consonant_categories = ('C', 'CS', 'Ra','CM', 'V', 'PLACEHOLDER', 'DOTTEDCIRCLE') +matra_categories = ('M', 'MPst') +smvd_categories = ('SM', 'VD', 'A', 'Symbol') +for k, (cat, pos, block) in indic_data.items(): + if cat in consonant_categories: + pos = 'BASE_C' + elif cat in matra_categories: + if block.startswith('Khmer') or block.startswith('Myanmar'): + cat = position_to_category(pos) + else: + pos = indic_matra_position(k, pos, block) + elif cat in smvd_categories: + pos = 'SMVD'; + indic_data[k] = (cat, pos, block) + +for k,new_pos in position_overrides.items(): + (cat, pos, _) = indic_data.get(k, defaults) + indic_data[k] = (cat, new_pos, unicode_data[2][k]) + + +values = [{_: 1} for _ in defaults] +for vv in indic_data.values(): + for i,v in enumerate(vv): + values[i][v] = values[i].get (v, 0) + 1 + + + + +# Move the outliers NO-BREAK SPACE and DOTTED CIRCLE out +singles = {} +for u in ALLOWED_SINGLES: + singles[u] = indic_data[u] + del indic_data[u] + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt") +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip())) +print (" */") +print () +print ('#include "hb.hh"') +print () +print ('#ifndef HB_NO_OT_SHAPE') +print () +print ('#include "hb-ot-shaper-indic.hh"') +print () +print ('#pragma GCC diagnostic push') +print ('#pragma GCC diagnostic ignored "-Wunused-macros"') +print () + +# Print categories +for shaper in categories: + print ('#include "hb-ot-shaper-%s-machine.hh"' % shaper) +print () +done = {} +for shaper, shaper_cats in categories.items(): + print ('/* %s */' % shaper) + for cat in shaper_cats: + v = shaper[0].upper() + if cat not in done: + print ("#define OT_%s %s_Cat(%s)" % (cat, v, cat)) + done[cat] = v + else: + print ('static_assert (OT_%s == %s_Cat(%s), "");' % (cat, v, cat)) +print () + +# Shorten values +short = [{ + "Repha": 'Rf', + "PLACEHOLDER": 'GB', + "DOTTEDCIRCLE": 'DC', + "VPst": 'VR', + "VPre": 'VL', + "Robatic": 'Rt', + "Xgroup": 'Xg', + "Ygroup": 'Yg', + "As": 'As', +},{ + "END": 'X', + "BASE_C": 'C', + "ABOVE_C": 'T', + "BELOW_C": 'B', + "POST_C": 'R', + "PRE_C": 'L', + "PRE_M": 'LM', + "AFTER_MAIN": 'A', + "AFTER_SUB": 'AS', + "BEFORE_SUB": 'BS', + "AFTER_POST": 'AP', + "SMVD": 'SM', +}] +all_shorts = [{},{}] + +# Add some of the values, to make them more readable, and to avoid duplicates + +for i in range (2): + for v,s in short[i].items (): + all_shorts[i][s] = v + +what = ["OT", "POS"] +what_short = ["_OT", "_POS"] +cat_defs = [] +for i in range (2): + vv = sorted (values[i].keys ()) + for v in vv: + v_no_and = v.replace ('_And_', '_') + if v in short[i]: + s = short[i][v] + else: + s = ''.join ([c for c in v_no_and if ord ('A') <= ord (c) <= ord ('Z')]) + if s in all_shorts[i]: + raise Exception ("Duplicate short value alias", v, all_shorts[i][s]) + all_shorts[i][s] = v + short[i][v] = s + cat_defs.append ((what_short[i] + '_' + s, what[i] + '_' + (v.upper () if i else v), str (values[i][v]), v)) + +maxlen_s = max ([len (c[0]) for c in cat_defs]) +maxlen_l = max ([len (c[1]) for c in cat_defs]) +maxlen_n = max ([len (c[2]) for c in cat_defs]) +for s in what_short: + print () + for c in [c for c in cat_defs if s in c[0]]: + print ("#define %s %s /* %s chars; %s */" % + (c[0].ljust (maxlen_s), c[1].ljust (maxlen_l), c[2].rjust (maxlen_n), c[3])) +print () +print ('#pragma GCC diagnostic pop') +print () +print ("#define INDIC_COMBINE_CATEGORIES(S,M) ((S) | ((M) << 8))") +print () +print ("#define _(S,M) INDIC_COMBINE_CATEGORIES (%s_##S, %s_##M)" % tuple(what_short)) +print () +print () + +total = 0 +used = 0 +last_block = None +def print_block (block, start, end, data): + global total, used, last_block + if block and block != last_block: + print () + print () + print (" /* %s */" % block) + num = 0 + assert start % 8 == 0 + assert (end+1) % 8 == 0 + for u in range (start, end+1): + if u % 8 == 0: + print () + print (" /* %04X */" % u, end="") + if u in data: + num += 1 + d = data.get (u, defaults) + print ("%9s" % ("_(%s,%s)," % (short[0][d[0]], short[1][d[1]])), end="") + + total += end - start + 1 + used += num + if block: + last_block = block + +uu = sorted (indic_data) + +last = -100000 +num = 0 +offset = 0 +starts = [] +ends = [] +print ("static const uint16_t indic_table[] = {") +for u in uu: + if u <= last: + continue + block = indic_data[u][2] + + start = u//8*8 + end = start+1 + while end in uu and block == indic_data[end][2]: + end += 1 + end = (end-1)//8*8 + 7 + + if start != last + 1: + if start - last <= 1+16*2: + print_block (None, last+1, start-1, indic_data) + else: + if last >= 0: + ends.append (last + 1) + offset += ends[-1] - starts[-1] + print () + print () + print ("#define indic_offset_0x%04xu %d" % (start, offset)) + starts.append (start) + + print_block (block, start, end, indic_data) + last = end +ends.append (last + 1) +offset += ends[-1] - starts[-1] +print () +print () +occupancy = used * 100. / total +page_bits = 12 +print ("}; /* Table items: %d; occupancy: %d%% */" % (offset, occupancy)) +print () +print ("uint16_t") +print ("hb_indic_get_categories (hb_codepoint_t u)") +print ("{") +print (" switch (u >> %d)" % page_bits) +print (" {") +pages = set ([u>>page_bits for u in starts+ends+list (singles.keys ())]) +for p in sorted(pages): + print (" case 0x%0Xu:" % p) + for u,d in singles.items (): + if p != u>>page_bits: continue + print (" if (unlikely (u == 0x%04Xu)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]])) + for (start,end) in zip (starts, ends): + if p not in [start>>page_bits, end>>page_bits]: continue + offset = "indic_offset_0x%04xu" % start + print (" if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)) + print (" break;") + print ("") +print (" default:") +print (" break;") +print (" }") +print (" return _(X,X);") +print ("}") +print () +print ("#undef _") +print ("#undef INDIC_COMBINE_CATEGORIES") +for i in range (2): + print () + vv = sorted (values[i].keys ()) + for v in vv: + print ("#undef %s_%s" % + (what_short[i], short[i][v])) +print () +print ('#endif') +print () +print ("/* == End of generated table == */") + +# Maintain at least 50% occupancy in the table */ +if occupancy < 50: + raise Exception ("Table too sparse, please investigate: ", occupancy) diff --git a/gfx/harfbuzz/src/gen-os2-unicode-ranges.py b/gfx/harfbuzz/src/gen-os2-unicode-ranges.py new file mode 100755 index 0000000000..b1a34d478f --- /dev/null +++ b/gfx/harfbuzz/src/gen-os2-unicode-ranges.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +"""Generates the code for a sorted unicode range array as used in hb-ot-os2-unicode-ranges.hh +Input is a tab separated list of unicode ranges from the otspec +(https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur). +""" + +import re +import sys + + +print ("""static OS2Range _hb_os2_unicode_ranges[] = +{""") + +args = sys.argv[1:] +input_file = args[0] + +with open (input_file, mode="r", encoding="utf-8") as f: + + all_ranges = [] + current_bit = 0 + while True: + line = f.readline().strip() + if not line: + break + fields = re.split(r'\t+', line) + if len(fields) == 3: + current_bit = fields[0] + fields = fields[1:] + elif len(fields) > 3: + raise Exception("bad input :(.") + + name = fields[0] + ranges = re.split("-", fields[1]) + if len(ranges) != 2: + raise Exception("bad input :(.") + + v = tuple((int(ranges[0], 16), int(ranges[1], 16), int(current_bit), name)) + all_ranges.append(v) + +all_ranges = sorted(all_ranges, key=lambda t: t[0]) + +for ranges in all_ranges: + start = ("0x%X" % ranges[0]).rjust(8) + end = ("0x%X" % ranges[1]).rjust(8) + bit = ("%s" % ranges[2]).rjust(3) + + print (" {%s, %s, %s}, // %s" % (start, end, bit, ranges[3])) + +print ("""};""") diff --git a/gfx/harfbuzz/src/gen-ragel-artifacts.py b/gfx/harfbuzz/src/gen-ragel-artifacts.py new file mode 100755 index 0000000000..8bbb375bfb --- /dev/null +++ b/gfx/harfbuzz/src/gen-ragel-artifacts.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +"This tool is intended to be used from meson" + +import os, os.path, sys, subprocess, shutil + +ragel = sys.argv[1] +if not ragel: + sys.exit ('You have to install ragel if you are going to develop HarfBuzz itself') + +if len (sys.argv) < 4: + sys.exit (__doc__) + +OUTPUT = sys.argv[2] +CURRENT_SOURCE_DIR = sys.argv[3] +INPUT = sys.argv[4] + +outdir = os.path.dirname (OUTPUT) +shutil.copy (INPUT, outdir) +rl = os.path.basename (INPUT) +hh = rl.replace ('.rl', '.hh') +subprocess.Popen (ragel.split() + ['-e', '-F1', '-o', hh, rl], cwd=outdir).wait () + +# copy it also to src/ +shutil.copyfile (os.path.join (outdir, hh), os.path.join (CURRENT_SOURCE_DIR, hh)) diff --git a/gfx/harfbuzz/src/gen-tag-table.py b/gfx/harfbuzz/src/gen-tag-table.py new file mode 100755 index 0000000000..7e15c08c56 --- /dev/null +++ b/gfx/harfbuzz/src/gen-tag-table.py @@ -0,0 +1,1216 @@ +#!/usr/bin/env python3 + +"""Generator of the mapping from OpenType tags to BCP 47 tags and vice +versa. + +It creates a ``const LangTag[]``, matching the tags from the OpenType +languages system tag list to the language subtags of the BCP 47 language +subtag registry, with some manual adjustments. The mappings are +supplemented with macrolanguages' sublanguages and retired codes' +replacements, according to BCP 47 and some manual additions where BCP 47 +omits a retired code entirely. + +Also generated is a function, ``hb_ot_ambiguous_tag_to_language``, +intended for use by ``hb_ot_tag_to_language``. It maps OpenType tags +back to BCP 47 tags. Ambiguous OpenType tags (those that correspond to +multiple BCP 47 tags) are listed here, except when the alphabetically +first BCP 47 tag happens to be the chosen disambiguated tag. In that +case, the fallback behavior will choose the right tag anyway. + +usage: ./gen-tag-table.py languagetags language-subtag-registry + +Input files: +* https://docs.microsoft.com/en-us/typography/opentype/spec/languagetags +* https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry +""" + +import collections +import html +from html.parser import HTMLParser +import itertools +import re +import sys +import unicodedata + +if len (sys.argv) != 3: + sys.exit (__doc__) + +def expect (condition, message=None): + if not condition: + if message is None: + raise AssertionError + raise AssertionError (message) + +def write (s): + sys.stdout.flush () + sys.stdout.buffer.write (s.encode ('utf-8')) + +DEFAULT_LANGUAGE_SYSTEM = '' + +# from https://www-01.sil.org/iso639-3/iso-639-3.tab +ISO_639_3_TO_1 = { + 'aar': 'aa', + 'abk': 'ab', + 'afr': 'af', + 'aka': 'ak', + 'amh': 'am', + 'ara': 'ar', + 'arg': 'an', + 'asm': 'as', + 'ava': 'av', + 'ave': 'ae', + 'aym': 'ay', + 'aze': 'az', + 'bak': 'ba', + 'bam': 'bm', + 'bel': 'be', + 'ben': 'bn', + 'bis': 'bi', + 'bod': 'bo', + 'bos': 'bs', + 'bre': 'br', + 'bul': 'bg', + 'cat': 'ca', + 'ces': 'cs', + 'cha': 'ch', + 'che': 'ce', + 'chu': 'cu', + 'chv': 'cv', + 'cor': 'kw', + 'cos': 'co', + 'cre': 'cr', + 'cym': 'cy', + 'dan': 'da', + 'deu': 'de', + 'div': 'dv', + 'dzo': 'dz', + 'ell': 'el', + 'eng': 'en', + 'epo': 'eo', + 'est': 'et', + 'eus': 'eu', + 'ewe': 'ee', + 'fao': 'fo', + 'fas': 'fa', + 'fij': 'fj', + 'fin': 'fi', + 'fra': 'fr', + 'fry': 'fy', + 'ful': 'ff', + 'gla': 'gd', + 'gle': 'ga', + 'glg': 'gl', + 'glv': 'gv', + 'grn': 'gn', + 'guj': 'gu', + 'hat': 'ht', + 'hau': 'ha', + 'hbs': 'sh', + 'heb': 'he', + 'her': 'hz', + 'hin': 'hi', + 'hmo': 'ho', + 'hrv': 'hr', + 'hun': 'hu', + 'hye': 'hy', + 'ibo': 'ig', + 'ido': 'io', + 'iii': 'ii', + 'iku': 'iu', + 'ile': 'ie', + 'ina': 'ia', + 'ind': 'id', + 'ipk': 'ik', + 'isl': 'is', + 'ita': 'it', + 'jav': 'jv', + 'jpn': 'ja', + 'kal': 'kl', + 'kan': 'kn', + 'kas': 'ks', + 'kat': 'ka', + 'kau': 'kr', + 'kaz': 'kk', + 'khm': 'km', + 'kik': 'ki', + 'kin': 'rw', + 'kir': 'ky', + 'kom': 'kv', + 'kon': 'kg', + 'kor': 'ko', + 'kua': 'kj', + 'kur': 'ku', + 'lao': 'lo', + 'lat': 'la', + 'lav': 'lv', + 'lim': 'li', + 'lin': 'ln', + 'lit': 'lt', + 'ltz': 'lb', + 'lub': 'lu', + 'lug': 'lg', + 'mah': 'mh', + 'mal': 'ml', + 'mar': 'mr', + 'mkd': 'mk', + 'mlg': 'mg', + 'mlt': 'mt', + 'mol': 'mo', + 'mon': 'mn', + 'mri': 'mi', + 'msa': 'ms', + 'mya': 'my', + 'nau': 'na', + 'nav': 'nv', + 'nbl': 'nr', + 'nde': 'nd', + 'ndo': 'ng', + 'nep': 'ne', + 'nld': 'nl', + 'nno': 'nn', + 'nob': 'nb', + 'nor': 'no', + 'nya': 'ny', + 'oci': 'oc', + 'oji': 'oj', + 'ori': 'or', + 'orm': 'om', + 'oss': 'os', + 'pan': 'pa', + 'pli': 'pi', + 'pol': 'pl', + 'por': 'pt', + 'pus': 'ps', + 'que': 'qu', + 'roh': 'rm', + 'ron': 'ro', + 'run': 'rn', + 'rus': 'ru', + 'sag': 'sg', + 'san': 'sa', + 'sin': 'si', + 'slk': 'sk', + 'slv': 'sl', + 'sme': 'se', + 'smo': 'sm', + 'sna': 'sn', + 'snd': 'sd', + 'som': 'so', + 'sot': 'st', + 'spa': 'es', + 'sqi': 'sq', + 'srd': 'sc', + 'srp': 'sr', + 'ssw': 'ss', + 'sun': 'su', + 'swa': 'sw', + 'swe': 'sv', + 'tah': 'ty', + 'tam': 'ta', + 'tat': 'tt', + 'tel': 'te', + 'tgk': 'tg', + 'tgl': 'tl', + 'tha': 'th', + 'tir': 'ti', + 'ton': 'to', + 'tsn': 'tn', + 'tso': 'ts', + 'tuk': 'tk', + 'tur': 'tr', + 'twi': 'tw', + 'uig': 'ug', + 'ukr': 'uk', + 'urd': 'ur', + 'uzb': 'uz', + 'ven': 've', + 'vie': 'vi', + 'vol': 'vo', + 'wln': 'wa', + 'wol': 'wo', + 'xho': 'xh', + 'yid': 'yi', + 'yor': 'yo', + 'zha': 'za', + 'zho': 'zh', + 'zul': 'zu', +} + +class LanguageTag (object): + """A BCP 47 language tag. + + Attributes: + subtags (List[str]): The list of subtags in this tag. + grandfathered (bool): Whether this tag is grandfathered. If + ``true``, the entire lowercased tag is the ``language`` + and the other subtag fields are empty. + language (str): The language subtag. + script (str): The script subtag. + region (str): The region subtag. + variant (str): The variant subtag. + + Args: + tag (str): A BCP 47 language tag. + + """ + def __init__ (self, tag): + global bcp_47 + self.subtags = tag.lower ().split ('-') + self.grandfathered = tag.lower () in bcp_47.grandfathered + if self.grandfathered: + self.language = tag.lower () + self.script = '' + self.region = '' + self.variant = '' + else: + self.language = self.subtags[0] + self.script = self._find_first (lambda s: len (s) == 4 and s[0] > '9', self.subtags) + self.region = self._find_first (lambda s: len (s) == 2 and s[0] > '9' or len (s) == 3 and s[0] <= '9', self.subtags[1:]) + self.variant = self._find_first (lambda s: len (s) > 4 or len (s) == 4 and s[0] <= '9', self.subtags) + + def __str__(self): + return '-'.join(self.subtags) + + def __repr__ (self): + return 'LanguageTag(%r)' % str(self) + + @staticmethod + def _find_first (function, sequence): + try: + return next (iter (filter (function, sequence))) + except StopIteration: + return None + + def is_complex (self): + """Return whether this tag is too complex to represent as a + ``LangTag`` in the generated code. + + Complex tags need to be handled in + ``hb_ot_tags_from_complex_language``. + + Returns: + Whether this tag is complex. + """ + return not (len (self.subtags) == 1 + or self.grandfathered + and len (self.subtags[1]) != 3 + and ot.from_bcp_47[self.subtags[0]] == ot.from_bcp_47[self.language]) + + def get_group (self): + """Return the group into which this tag should be categorized in + ``hb_ot_tags_from_complex_language``. + + The group is the first letter of the tag, or ``'und'`` if this tag + should not be matched in a ``switch`` statement in the generated + code. + + Returns: + This tag's group. + """ + return ('und' + if (self.language == 'und' + or self.variant in bcp_47.prefixes and len (bcp_47.prefixes[self.variant]) == 1) + else self.language[0]) + +class OpenTypeRegistryParser (HTMLParser): + """A parser for the OpenType language system tag registry. + + Attributes: + header (str): The "last updated" line of the registry. + names (Mapping[str, str]): A map of language system tags to the + names they are given in the registry. + ranks (DefaultDict[str, int]): A map of language system tags to + numbers. If a single BCP 47 tag corresponds to multiple + OpenType tags, the tags are ordered in increasing order by + rank. The rank is based on the number of BCP 47 tags + associated with a tag, though it may be manually modified. + to_bcp_47 (DefaultDict[str, AbstractSet[str]]): A map of + OpenType language system tags to sets of BCP 47 tags. + from_bcp_47 (DefaultDict[str, AbstractSet[str]]): ``to_bcp_47`` + inverted. Its values start as unsorted sets; + ``sort_languages`` converts them to sorted lists. + from_bcp_47_uninherited (Optional[Dict[str, AbstractSet[str]]]): + A copy of ``from_bcp_47``. It starts as ``None`` and is + populated at the beginning of the first call to + ``inherit_from_macrolanguages``. + + """ + def __init__ (self): + HTMLParser.__init__ (self) + self.header = '' + self.names = {} + self.ranks = collections.defaultdict (int) + self.to_bcp_47 = collections.defaultdict (set) + self.from_bcp_47 = collections.defaultdict (set) + self.from_bcp_47_uninherited = None + # Whether the parser is in a <td> element + self._td = False + # Whether the parser is after a <br> element within the current <tr> element + self._br = False + # The text of the <td> elements of the current <tr> element. + self._current_tr = [] + + def handle_starttag (self, tag, attrs): + if tag == 'br': + self._br = True + elif tag == 'meta': + for attr, value in attrs: + if attr == 'name' and value == 'updated_at': + self.header = self.get_starttag_text () + break + elif tag == 'td': + self._td = True + self._current_tr.append ('') + elif tag == 'tr': + self._br = False + self._current_tr = [] + + def handle_endtag (self, tag): + if tag == 'td': + self._td = False + elif tag == 'tr' and self._current_tr: + expect (2 <= len (self._current_tr) <= 3) + name = self._current_tr[0].strip () + tag = self._current_tr[1].strip ("\t\n\v\f\r '") + rank = 0 + if len (tag) > 4: + expect (tag.endswith (' (deprecated)'), 'ill-formed OpenType tag: %s' % tag) + name += ' (deprecated)' + tag = tag.split (' ')[0] + rank = 1 + self.names[tag] = re.sub (' languages$', '', name) + if not self._current_tr[2]: + return + iso_codes = self._current_tr[2].strip () + self.to_bcp_47[tag].update (ISO_639_3_TO_1.get (code, code) for code in iso_codes.replace (' ', '').split (',')) + rank += 2 * len (self.to_bcp_47[tag]) + self.ranks[tag] = rank + + def handle_data (self, data): + if self._td and not self._br: + self._current_tr[-1] += data + + def handle_charref (self, name): + self.handle_data (html.unescape ('&#%s;' % name)) + + def handle_entityref (self, name): + self.handle_data (html.unescape ('&%s;' % name)) + + def parse (self, filename): + """Parse the OpenType language system tag registry. + + Args: + filename (str): The file name of the registry. + """ + with open (filename, encoding='utf-8') as f: + self.feed (f.read ()) + expect (self.header) + for tag, iso_codes in self.to_bcp_47.items (): + for iso_code in iso_codes: + self.from_bcp_47[iso_code].add (tag) + + def add_language (self, bcp_47_tag, ot_tag): + """Add a language as if it were in the registry. + + Args: + bcp_47_tag (str): A BCP 47 tag. If the tag is more than just + a language subtag, and if the language subtag is a + macrolanguage, then new languages are added corresponding + to the macrolanguages' individual languages with the + remainder of the tag appended. + ot_tag (str): An OpenType language system tag. + """ + global bcp_47 + self.to_bcp_47[ot_tag].add (bcp_47_tag) + self.from_bcp_47[bcp_47_tag].add (ot_tag) + if bcp_47_tag.lower () not in bcp_47.grandfathered: + try: + [macrolanguage, suffix] = bcp_47_tag.split ('-', 1) + if macrolanguage in bcp_47.macrolanguages: + s = set () + for language in bcp_47.macrolanguages[macrolanguage]: + if language.lower () not in bcp_47.grandfathered: + s.add ('%s-%s' % (language, suffix)) + bcp_47.macrolanguages['%s-%s' % (macrolanguage, suffix)] = s + except ValueError: + pass + + @staticmethod + def _remove_language (tag_1, dict_1, dict_2): + for tag_2 in dict_1.pop (tag_1): + dict_2[tag_2].remove (tag_1) + if not dict_2[tag_2]: + del dict_2[tag_2] + + def remove_language_ot (self, ot_tag): + """Remove an OpenType tag from the registry. + + Args: + ot_tag (str): An OpenType tag. + """ + self._remove_language (ot_tag, self.to_bcp_47, self.from_bcp_47) + + def remove_language_bcp_47 (self, bcp_47_tag): + """Remove a BCP 47 tag from the registry. + + Args: + bcp_47_tag (str): A BCP 47 tag. + """ + self._remove_language (bcp_47_tag, self.from_bcp_47, self.to_bcp_47) + + def inherit_from_macrolanguages (self): + """Copy mappings from macrolanguages to individual languages. + + If a BCP 47 tag for an individual mapping has no OpenType + mapping but its macrolanguage does, the mapping is copied to + the individual language. For example, als (Tosk Albanian) has no + explicit mapping, so it inherits from sq (Albanian) the mapping + to SQI. + + However, if an OpenType tag maps to a BCP 47 macrolanguage and + some but not all of its individual languages, the mapping is not + inherited from the macrolanguage to the missing individual + languages. For example, INUK (Nunavik Inuktitut) is mapped to + ike (Eastern Canadian Inuktitut) and iu (Inuktitut) but not to + ikt (Inuinnaqtun, which is an individual language of iu), so + this method does not add a mapping from ikt to INUK. + + If a BCP 47 tag for a macrolanguage has no OpenType mapping but + some of its individual languages do, their mappings are copied + to the macrolanguage. + """ + global bcp_47 + first_time = self.from_bcp_47_uninherited is None + if first_time: + self.from_bcp_47_uninherited = dict (self.from_bcp_47) + for macrolanguage, languages in dict (bcp_47.macrolanguages).items (): + ot_macrolanguages = { + ot_macrolanguage for ot_macrolanguage in self.from_bcp_47_uninherited.get (macrolanguage, set ()) + } + blocked_ot_macrolanguages = set () + if 'retired code' not in bcp_47.scopes.get (macrolanguage, ''): + for ot_macrolanguage in ot_macrolanguages: + round_trip_macrolanguages = { + l for l in self.to_bcp_47[ot_macrolanguage] + if 'retired code' not in bcp_47.scopes.get (l, '') + } + round_trip_languages = { + l for l in languages + if 'retired code' not in bcp_47.scopes.get (l, '') + } + intersection = round_trip_macrolanguages & round_trip_languages + if intersection and intersection != round_trip_languages: + blocked_ot_macrolanguages.add (ot_macrolanguage) + if ot_macrolanguages: + for ot_macrolanguage in ot_macrolanguages: + if ot_macrolanguage not in blocked_ot_macrolanguages: + for language in languages: + self.add_language (language, ot_macrolanguage) + if not blocked_ot_macrolanguages: + self.ranks[ot_macrolanguage] += 1 + elif first_time: + for language in languages: + if language in self.from_bcp_47_uninherited: + ot_macrolanguages |= self.from_bcp_47_uninherited[language] + else: + ot_macrolanguages.clear () + if not ot_macrolanguages: + break + for ot_macrolanguage in ot_macrolanguages: + self.add_language (macrolanguage, ot_macrolanguage) + + def sort_languages (self): + """Sort the values of ``from_bcp_47`` in ascending rank order.""" + for language, tags in self.from_bcp_47.items (): + self.from_bcp_47[language] = sorted (tags, + key=lambda t: (self.ranks[t] + rank_delta (language, t), t)) + +ot = OpenTypeRegistryParser () + +class BCP47Parser (object): + """A parser for the BCP 47 subtag registry. + + Attributes: + header (str): The "File-Date" line of the registry. + names (Mapping[str, str]): A map of subtags to the names they + are given in the registry. Each value is a + ``'\\n'``-separated list of names. + scopes (Mapping[str, str]): A map of language subtags to strings + suffixed to language names, including suffixes to explain + language scopes. + macrolanguages (DefaultDict[str, AbstractSet[str]]): A map of + language subtags to the sets of language subtags which + inherit from them. See + ``OpenTypeRegistryParser.inherit_from_macrolanguages``. + prefixes (DefaultDict[str, AbstractSet[str]]): A map of variant + subtags to their prefixes. + grandfathered (AbstractSet[str]): The set of grandfathered tags, + normalized to lowercase. + + """ + def __init__ (self): + self.header = '' + self.names = {} + self.scopes = {} + self.macrolanguages = collections.defaultdict (set) + self.prefixes = collections.defaultdict (set) + self.grandfathered = set () + + def parse (self, filename): + """Parse the BCP 47 subtag registry. + + Args: + filename (str): The file name of the registry. + """ + with open (filename, encoding='utf-8') as f: + subtag_type = None + subtag = None + deprecated = False + has_preferred_value = False + line_buffer = '' + for line in itertools.chain (f, ['']): + line = line.rstrip () + if line.startswith (' '): + line_buffer += line[1:] + continue + line, line_buffer = line_buffer, line + if line.startswith ('Type: '): + subtag_type = line.split (' ')[1] + deprecated = False + has_preferred_value = False + elif line.startswith ('Subtag: ') or line.startswith ('Tag: '): + subtag = line.split (' ')[1] + if subtag_type == 'grandfathered': + self.grandfathered.add (subtag.lower ()) + elif line.startswith ('Description: '): + description = line.split (' ', 1)[1].replace (' (individual language)', '') + description = re.sub (' (\(family\)|\((individual |macro)language\)|languages)$', '', + description) + if subtag in self.names: + self.names[subtag] += '\n' + description + else: + self.names[subtag] = description + elif subtag_type == 'language' or subtag_type == 'grandfathered': + if line.startswith ('Scope: '): + scope = line.split (' ')[1] + if scope == 'macrolanguage': + scope = ' [macrolanguage]' + elif scope == 'collection': + scope = ' [collection]' + else: + continue + self.scopes[subtag] = scope + elif line.startswith ('Deprecated: '): + self.scopes[subtag] = ' (retired code)' + self.scopes.get (subtag, '') + deprecated = True + elif deprecated and line.startswith ('Comments: see '): + # If a subtag is split into multiple replacement subtags, + # it essentially represents a macrolanguage. + for language in line.replace (',', '').split (' ')[2:]: + self._add_macrolanguage (subtag, language) + elif line.startswith ('Preferred-Value: '): + # If a subtag is deprecated in favor of a single replacement subtag, + # it is either a dialect or synonym of the preferred subtag. Either + # way, it is close enough to the truth to consider the replacement + # the macrolanguage of the deprecated language. + has_preferred_value = True + macrolanguage = line.split (' ')[1] + self._add_macrolanguage (macrolanguage, subtag) + elif not has_preferred_value and line.startswith ('Macrolanguage: '): + self._add_macrolanguage (line.split (' ')[1], subtag) + elif subtag_type == 'variant': + if line.startswith ('Deprecated: '): + self.scopes[subtag] = ' (retired code)' + self.scopes.get (subtag, '') + elif line.startswith ('Prefix: '): + self.prefixes[subtag].add (line.split (' ')[1]) + elif line.startswith ('File-Date: '): + self.header = line + expect (self.header) + + def _add_macrolanguage (self, macrolanguage, language): + global ot + if language not in ot.from_bcp_47: + for l in self.macrolanguages.get (language, set ()): + self._add_macrolanguage (macrolanguage, l) + if macrolanguage not in ot.from_bcp_47: + for ls in list (self.macrolanguages.values ()): + if macrolanguage in ls: + ls.add (language) + return + self.macrolanguages[macrolanguage].add (language) + + def remove_extra_macrolanguages (self): + """Make every language have at most one macrolanguage.""" + inverted = collections.defaultdict (list) + for macrolanguage, languages in self.macrolanguages.items (): + for language in languages: + inverted[language].append (macrolanguage) + for language, macrolanguages in inverted.items (): + if len (macrolanguages) > 1: + macrolanguages.sort (key=lambda ml: len (self.macrolanguages[ml])) + biggest_macrolanguage = macrolanguages.pop () + for macrolanguage in macrolanguages: + self._add_macrolanguage (biggest_macrolanguage, macrolanguage) + + def _get_name_piece (self, subtag): + """Return the first name of a subtag plus its scope suffix. + + Args: + subtag (str): A BCP 47 subtag. + + Returns: + The name form of ``subtag``. + """ + return self.names[subtag].split ('\n')[0] + self.scopes.get (subtag, '') + + def get_name (self, lt): + """Return the names of the subtags in a language tag. + + Args: + lt (LanguageTag): A BCP 47 language tag. + + Returns: + The name form of ``lt``. + """ + name = self._get_name_piece (lt.language) + if lt.script: + name += '; ' + self._get_name_piece (lt.script.title ()) + if lt.region: + name += '; ' + self._get_name_piece (lt.region.upper ()) + if lt.variant: + name += '; ' + self._get_name_piece (lt.variant) + return name + +bcp_47 = BCP47Parser () + +ot.parse (sys.argv[1]) +bcp_47.parse (sys.argv[2]) + +ot.add_language ('ary', 'MOR') + +ot.add_language ('ath', 'ATH') + +ot.add_language ('bai', 'BML') + +ot.ranks['BAL'] = ot.ranks['KAR'] + 1 + +ot.add_language ('ber', 'BBR') + +ot.remove_language_ot ('PGR') +ot.add_language ('el-polyton', 'PGR') + +bcp_47.macrolanguages['et'] = {'ekk'} + +bcp_47.names['flm'] = 'Falam Chin' +bcp_47.scopes['flm'] = ' (retired code)' +bcp_47.macrolanguages['flm'] = {'cfm'} + +ot.ranks['FNE'] = ot.ranks['TNE'] + 1 + +ot.add_language ('und-fonipa', 'IPPH') + +ot.add_language ('und-fonnapa', 'APPH') + +ot.remove_language_ot ('IRT') +ot.add_language ('ga-Latg', 'IRT') + +ot.add_language ('hy-arevmda', 'HYE') + +ot.remove_language_ot ('KGE') +ot.add_language ('und-Geok', 'KGE') + +bcp_47.macrolanguages['id'] = {'in'} + +bcp_47.macrolanguages['ijo'] = {'ijc'} + +ot.add_language ('kht', 'KHN') +ot.names['KHN'] = ot.names['KHT'] + ' (Microsoft fonts)' +ot.ranks['KHN'] = ot.ranks['KHT'] + 1 + +ot.ranks['LCR'] = ot.ranks['MCR'] + 1 + +ot.names['MAL'] = 'Malayalam Traditional' +ot.ranks['MLR'] += 1 + +bcp_47.names['mhv'] = 'Arakanese' +bcp_47.scopes['mhv'] = ' (retired code)' + +ot.add_language ('mnw-TH', 'MONT') + +ot.add_language ('no', 'NOR') + +ot.add_language ('oc-provenc', 'PRO') + +ot.remove_language_ot ('QUZ') +ot.add_language ('qu', 'QUZ') +ot.add_language ('qub', 'QWH') +ot.add_language ('qud', 'QVI') +ot.add_language ('qug', 'QVI') +ot.add_language ('qul', 'QUH') +ot.add_language ('qup', 'QVI') +ot.add_language ('qur', 'QWH') +ot.add_language ('qus', 'QUH') +ot.add_language ('quw', 'QVI') +ot.add_language ('qux', 'QWH') +ot.add_language ('qva', 'QWH') +ot.add_language ('qvh', 'QWH') +ot.add_language ('qvj', 'QVI') +ot.add_language ('qvl', 'QWH') +ot.add_language ('qvm', 'QWH') +ot.add_language ('qvn', 'QWH') +ot.add_language ('qvo', 'QVI') +ot.add_language ('qvp', 'QWH') +ot.add_language ('qvw', 'QWH') +ot.add_language ('qvz', 'QVI') +ot.add_language ('qwa', 'QWH') +ot.add_language ('qws', 'QWH') +ot.add_language ('qxa', 'QWH') +ot.add_language ('qxc', 'QWH') +ot.add_language ('qxh', 'QWH') +ot.add_language ('qxl', 'QVI') +ot.add_language ('qxn', 'QWH') +ot.add_language ('qxo', 'QWH') +ot.add_language ('qxr', 'QVI') +ot.add_language ('qxt', 'QWH') +ot.add_language ('qxw', 'QWH') + +bcp_47.macrolanguages['ro-MD'].add ('mo') + +ot.remove_language_ot ('SYRE') +ot.remove_language_ot ('SYRJ') +ot.remove_language_ot ('SYRN') +ot.add_language ('und-Syre', 'SYRE') +ot.add_language ('und-Syrj', 'SYRJ') +ot.add_language ('und-Syrn', 'SYRN') + +bcp_47.names['xst'] = "Silt'e" +bcp_47.scopes['xst'] = ' (retired code)' +bcp_47.macrolanguages['xst'] = {'stv', 'wle'} + +ot.add_language ('xwo', 'TOD') + +ot.remove_language_ot ('ZHH') +ot.remove_language_ot ('ZHP') +ot.remove_language_ot ('ZHT') +ot.remove_language_ot ('ZHTM') +bcp_47.macrolanguages['zh'].remove ('lzh') +bcp_47.macrolanguages['zh'].remove ('yue') +ot.add_language ('zh-Hant-MO', 'ZHH') +ot.add_language ('zh-Hant-MO', 'ZHTM') +ot.add_language ('zh-Hant-HK', 'ZHH') +ot.add_language ('zh-Hans', 'ZHS') +ot.add_language ('zh-Hant', 'ZHT') +ot.add_language ('zh-HK', 'ZHH') +ot.add_language ('zh-MO', 'ZHH') +ot.add_language ('zh-MO', 'ZHTM') +ot.add_language ('zh-TW', 'ZHT') +ot.add_language ('lzh', 'ZHT') +ot.add_language ('lzh-Hans', 'ZHS') +ot.add_language ('yue', 'ZHH') +ot.add_language ('yue-Hans', 'ZHS') + +bcp_47.macrolanguages['zom'] = {'yos'} + +def rank_delta (bcp_47, ot): + """Return a delta to apply to a BCP 47 tag's rank. + + Most OpenType tags have a constant rank, but a few have ranks that + depend on the BCP 47 tag. + + Args: + bcp_47 (str): A BCP 47 tag. + ot (str): An OpenType tag to. + + Returns: + A number to add to ``ot``'s rank when sorting ``bcp_47``'s + OpenType equivalents. + """ + if bcp_47 == 'ak' and ot == 'AKA': + return -1 + if bcp_47 == 'tw' and ot == 'TWI': + return -1 + return 0 + +disambiguation = { + 'ALT': 'alt', + 'ARK': 'rki', + 'ATH': 'ath', + 'BHI': 'bhb', + 'BLN': 'bjt', + 'BTI': 'beb', + 'CCHN': 'cco', + 'CMR': 'swb', + 'CPP': 'crp', + 'CRR': 'crx', + 'DUJ': 'dwu', + 'ECR': 'crj', + 'HAL': 'cfm', + 'HND': 'hnd', + 'HYE': 'hyw', + 'KIS': 'kqs', + 'KUI': 'uki', + 'LRC': 'bqi', + 'NDB': 'nd', + 'NIS': 'njz', + 'PLG': 'pce', + 'PRO': 'pro', + 'QIN': 'bgr', + 'QUH': 'quh', + 'QVI': 'qvi', + 'QWH': 'qwh', + 'SIG': 'stv', + 'SRB': 'sr', + 'SXT': 'xnj', + 'ZHH': 'zh-HK', + 'ZHS': 'zh-Hans', + 'ZHT': 'zh-Hant', + 'ZHTM': 'zh-MO', +} + +ot.inherit_from_macrolanguages () +bcp_47.remove_extra_macrolanguages () +ot.inherit_from_macrolanguages () +ot.names[DEFAULT_LANGUAGE_SYSTEM] = '*/' +ot.ranks[DEFAULT_LANGUAGE_SYSTEM] = max (ot.ranks.values ()) + 1 +for tricky_ot_tag in filter (lambda tag: re.match ('[A-Z]{3}$', tag), ot.names): + possible_bcp_47_tag = tricky_ot_tag.lower () + if possible_bcp_47_tag in bcp_47.names and not ot.from_bcp_47[possible_bcp_47_tag]: + ot.add_language (possible_bcp_47_tag, DEFAULT_LANGUAGE_SYSTEM) + bcp_47.macrolanguages[possible_bcp_47_tag] = set () +ot.sort_languages () + +print ('/* == Start of generated table == */') +print ('/*') +print (' * The following table is generated by running:') +print (' *') +print (' * %s languagetags language-subtag-registry' % sys.argv[0]) +print (' *') +print (' * on files with these headers:') +print (' *') +print (' * %s' % ot.header.strip ()) +print (' * %s' % bcp_47.header) +print (' */') +print () +print ('#ifndef HB_OT_TAG_TABLE_HH') +print ('#define HB_OT_TAG_TABLE_HH') +print () + +def hb_tag (tag): + """Convert a tag to ``HB_TAG`` form. + + Args: + tag (str): An OpenType tag. + + Returns: + A snippet of C++ representing ``tag``. + """ + if tag == DEFAULT_LANGUAGE_SYSTEM: + return 'HB_TAG_NONE\t ' + return "HB_TAG('%s','%s','%s','%s')" % tuple (('%-4s' % tag)[:4]) + +def get_variant_set (name): + """Return a set of variant language names from a name. + + Args: + name (str): A list of language names from the BCP 47 registry, + joined on ``'\\n'``. + + Returns: + A set of normalized language names. + """ + return set (unicodedata.normalize ('NFD', n.replace ('\u2019', "'")) + .encode ('ASCII', 'ignore') + .strip () + for n in re.split ('[\n(),]', name) if n) + +def language_name_intersection (a, b): + """Return the names in common between two language names. + + Args: + a (str): A list of language names from the BCP 47 registry, + joined on ``'\\n'``. + b (str): A list of language names from the BCP 47 registry, + joined on ``'\\n'``. + + Returns: + The normalized language names shared by ``a`` and ``b``. + """ + return get_variant_set (a).intersection (get_variant_set (b)) + +def get_matching_language_name (intersection, candidates): + return next (iter (c for c in candidates if not intersection.isdisjoint (get_variant_set (c)))) + +def same_tag (bcp_47_tag, ot_tags): + return len (bcp_47_tag) == 3 and len (ot_tags) == 1 and bcp_47_tag == ot_tags[0].lower () + +for language_len in (2, 3): + if language_len == 3: + print ('#ifndef HB_NO_LANGUAGE_LONG') + print ('static const LangTag ot_languages%d[] = {' % language_len) + for language, tags in sorted (ot.from_bcp_47.items ()): + if language == '' or '-' in language: + continue + if len(language) != language_len: continue + commented_out = same_tag (language, tags) + for i, tag in enumerate (tags, start=1): + print ('%s{%s,\t%s},' % ('/*' if commented_out else ' ', hb_tag (language), hb_tag (tag)), end='') + if commented_out: + print ('*/', end='') + print ('\t/* ', end='') + bcp_47_name = bcp_47.names.get (language, '') + bcp_47_name_candidates = bcp_47_name.split ('\n') + ot_name = ot.names[tag] + scope = bcp_47.scopes.get (language, '') + if tag == DEFAULT_LANGUAGE_SYSTEM: + write (f'{bcp_47_name_candidates[0]}{scope} != {ot.names[language.upper ()]}') + else: + intersection = language_name_intersection (bcp_47_name, ot_name) + if not intersection: + write ('%s%s -> %s' % (bcp_47_name_candidates[0], scope, ot_name)) + else: + name = get_matching_language_name (intersection, bcp_47_name_candidates) + bcp_47.names[language] = name + write ('%s%s' % (name if len (name) > len (ot_name) else ot_name, scope)) + print (' */') + print ('};') + if language_len == 3: + print ('#endif') + print () + +print ('/**') +print (' * hb_ot_tags_from_complex_language:') +print (' * @lang_str: a BCP 47 language tag to convert.') +print (' * @limit: a pointer to the end of the substring of @lang_str to consider for') +print (' * conversion.') +print (' * @count: maximum number of language tags to retrieve (IN) and actual number of') +print (' * language tags retrieved (OUT). If no tags are retrieved, it is not modified.') +print (' * @tags: array of size at least @language_count to store the language tag') +print (' * results') +print (' *') +print (' * Converts a multi-subtag BCP 47 language tag to language tags.') +print (' *') +print (' * Return value: Whether any language systems were retrieved.') +print (' **/') +print ('static inline bool') +print ('hb_ot_tags_from_complex_language (const char *lang_str,') +print ('\t\t\t\t const char *limit,') +print ('\t\t\t\t unsigned int *count /* IN/OUT */,') +print ('\t\t\t\t hb_tag_t *tags /* OUT */)') +print ('{') + +def print_subtag_matches (subtag, string, new_line): + if subtag: + if new_line: + print () + print ('\t&& ', end='') + print ('subtag_matches (%s, limit, "-%s", %i)' % (string, subtag, 1 + len (subtag)), end='') + +complex_tags = collections.defaultdict (list) +for initial, group in itertools.groupby ((lt_tags for lt_tags in [ + (LanguageTag (language), tags) + for language, tags in sorted (ot.from_bcp_47.items (), + key=lambda i: (-len (i[0]), i[0])) + ] if lt_tags[0].is_complex ()), + key=lambda lt_tags: lt_tags[0].get_group ()): + complex_tags[initial] += group + +# Calculate the min length of the subtags outside the switch +min_subtag_len = 100 +for initial, items in sorted (complex_tags.items ()): + if initial != 'und': + continue + for lt, tags in items: + if not tags: + continue + subtag_len = 0 + subtag_len += 1 + len (lt.script) if lt.script is not None else 0 + subtag_len += 1 + len (lt.region) if lt.region is not None else 0 + subtag_len += 1 + len (lt.variant) if lt.variant is not None else 0 + min_subtag_len = min(subtag_len, min_subtag_len) + +print (' if (limit - lang_str >= %d)' % (min_subtag_len + 2)) +print (' {') +print (" const char *p = strchr (lang_str, '-');") +print (" if (!p || p >= limit || limit - p < %i) goto out;" % min_subtag_len) +for initial, items in sorted (complex_tags.items ()): + if initial != 'und': + continue + for lt, tags in items: + if not tags: + continue + if lt.variant in bcp_47.prefixes: + expect (next (iter (bcp_47.prefixes[lt.variant])) == lt.language, + '%s is not a valid prefix of %s' % (lt.language, lt.variant)) + print (' if (', end='') + print_subtag_matches (lt.script, 'p', False) + print_subtag_matches (lt.region, 'p', False) + print_subtag_matches (lt.variant, 'p', False) + print (')') + print (' {') + write (' /* %s */' % bcp_47.get_name (lt)) + print () + if len (tags) == 1: + write (' tags[0] = %s; /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]])) + print () + print (' *count = 1;') + else: + print (' hb_tag_t possible_tags[] = {') + for tag in tags: + write (' %s, /* %s */' % (hb_tag (tag), ot.names[tag])) + print () + print (' };') + print (' for (i = 0; i < %s && i < *count; i++)' % len (tags)) + print ('\ttags[i] = possible_tags[i];') + print (' *count = i;') + print (' return true;') + print (' }') +print (' }') +print ('out:') + +print (' switch (lang_str[0])') +print (' {') +for initial, items in sorted (complex_tags.items ()): + if initial == 'und': + continue + print (" case '%s':" % initial) + for lt, tags in items: + if not tags: + continue + print (' if (', end='') + script = lt.script + region = lt.region + if lt.grandfathered: + print ('0 == strcmp (&lang_str[1], "%s")' % lt.language[1:], end='') + else: + string_literal = lt.language[1:] + '-' + if script: + string_literal += script + script = None + if region: + string_literal += '-' + region + region = None + if string_literal[-1] == '-': + print ('0 == strncmp (&lang_str[1], "%s", %i)' % (string_literal, len (string_literal)), end='') + else: + print ('lang_matches (&lang_str[1], limit, "%s", %i)' % (string_literal, len (string_literal)), end='') + print_subtag_matches (script, 'lang_str', True) + print_subtag_matches (region, 'lang_str', True) + print_subtag_matches (lt.variant, 'lang_str', True) + print (')') + print (' {') + write (' /* %s */' % bcp_47.get_name (lt)) + print () + if len (tags) == 1: + write (' tags[0] = %s; /* %s */' % (hb_tag (tags[0]), ot.names[tags[0]])) + print () + print (' *count = 1;') + else: + print (' unsigned int i;') + print (' hb_tag_t possible_tags[] = {') + for tag in tags: + write ('\t%s, /* %s */' % (hb_tag (tag), ot.names[tag])) + print () + print (' };') + print (' for (i = 0; i < %s && i < *count; i++)' % len (tags)) + print ('\ttags[i] = possible_tags[i];') + print (' *count = i;') + print (' return true;') + print (' }') + print (' break;') + +print (' }') +print (' return false;') +print ('}') +print () +print ('/**') +print (' * hb_ot_ambiguous_tag_to_language') +print (' * @tag: A language tag.') +print (' *') +print (' * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to') +print (' * many language tags) and the best tag is not the alphabetically first, or if') +print (' * the best tag consists of multiple subtags, or if the best tag does not appear') +print (' * in #ot_languages.') +print (' *') +print (' * Return value: The #hb_language_t corresponding to the BCP 47 language tag,') +print (' * or #HB_LANGUAGE_INVALID if @tag is not ambiguous.') +print (' **/') +print ('static inline hb_language_t') +print ('hb_ot_ambiguous_tag_to_language (hb_tag_t tag)') +print ('{') +print (' switch (tag)') +print (' {') + +def verify_disambiguation_dict (): + """Verify and normalize ``disambiguation``. + + ``disambiguation`` is a map of ambiguous OpenType language system + tags to the particular BCP 47 tags they correspond to. This function + checks that all its keys really are ambiguous and that each key's + value is valid for that key. It checks that no ambiguous tag is + missing, except when it can figure out which BCP 47 tag is the best + by itself. + + It modifies ``disambiguation`` to remove keys whose values are the + same as those that the fallback would return anyway, and to add + ambiguous keys whose disambiguations it determined automatically. + + Raises: + AssertionError: Verification failed. + """ + global bcp_47 + global disambiguation + global ot + for ot_tag, bcp_47_tags in ot.to_bcp_47.items (): + if ot_tag == DEFAULT_LANGUAGE_SYSTEM: + primary_tags = [] + else: + primary_tags = list (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot.from_bcp_47.get (t)[0] == ot_tag) + if len (primary_tags) == 1: + expect (ot_tag not in disambiguation, 'unnecessary disambiguation for OT tag: %s' % ot_tag) + if '-' in primary_tags[0]: + disambiguation[ot_tag] = primary_tags[0] + else: + first_tag = sorted (t for t in bcp_47_tags if t not in bcp_47.grandfathered and ot_tag in ot.from_bcp_47.get (t))[0] + if primary_tags[0] != first_tag: + disambiguation[ot_tag] = primary_tags[0] + elif len (primary_tags) == 0: + expect (ot_tag not in disambiguation, 'There is no possible valid disambiguation for %s' % ot_tag) + else: + original_languages = [t for t in primary_tags if t in ot.from_bcp_47_uninherited and 'retired code' not in bcp_47.scopes.get (t, '')] + if len (original_languages) == 1: + macrolanguages = original_languages + else: + macrolanguages = [t for t in primary_tags if bcp_47.scopes.get (t) == ' [macrolanguage]'] + if len (macrolanguages) != 1: + macrolanguages = list (t for t in primary_tags if bcp_47.scopes.get (t) == ' [collection]') + if len (macrolanguages) != 1: + macrolanguages = list (t for t in primary_tags if 'retired code' not in bcp_47.scopes.get (t, '')) + if len (macrolanguages) != 1: + expect (ot_tag in disambiguation, 'ambiguous OT tag: %s %s' % (ot_tag, str (macrolanguages))) + expect (disambiguation[ot_tag] in bcp_47_tags, + '%s is not a valid disambiguation for %s' % (disambiguation[ot_tag], ot_tag)) + elif ot_tag not in disambiguation: + disambiguation[ot_tag] = macrolanguages[0] + different_bcp_47_tags = sorted (t for t in bcp_47_tags if not same_tag (t, ot.from_bcp_47.get (t))) + if different_bcp_47_tags and disambiguation[ot_tag] == different_bcp_47_tags[0] and '-' not in disambiguation[ot_tag]: + del disambiguation[ot_tag] + for ot_tag in disambiguation.keys (): + expect (ot_tag in ot.to_bcp_47, 'unknown OT tag: %s' % ot_tag) + +verify_disambiguation_dict () +for ot_tag, bcp_47_tag in sorted (disambiguation.items ()): + write (' case %s: /* %s */' % (hb_tag (ot_tag), ot.names[ot_tag])) + print () + write (' return hb_language_from_string (\"%s\", -1); /* %s */' % (bcp_47_tag, bcp_47.get_name (LanguageTag (bcp_47_tag)))) + print () + +print (' default:') +print (' return HB_LANGUAGE_INVALID;') +print (' }') +print ('}') + +print () +print ('#endif /* HB_OT_TAG_TABLE_HH */') +print () +print ('/* == End of generated table == */') + diff --git a/gfx/harfbuzz/src/gen-ucd-table.py b/gfx/harfbuzz/src/gen-ucd-table.py new file mode 100755 index 0000000000..d85ae4faa8 --- /dev/null +++ b/gfx/harfbuzz/src/gen-ucd-table.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 + +"""usage: ./gen-ucd-table ucd.nounihan.grouped.xml [/path/to/hb-common.h] + +Input file: +* https://unicode.org/Public/UCD/latest/ucdxml/ucd.nounihan.grouped.zip +""" + +import sys, re +import logging +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) + +if len (sys.argv) not in (2, 3): + sys.exit (__doc__) + +# https://github.com/harfbuzz/packtab +import packTab +import packTab.ucdxml + +logging.info('Loading UCDXML...') +ucdxml = packTab.ucdxml.load_ucdxml(sys.argv[1]) +ucd = packTab.ucdxml.ucdxml_get_repertoire(ucdxml) + +hb_common_h = 'hb-common.h' if len (sys.argv) < 3 else sys.argv[2] + +logging.info('Preparing data tables...') + + +# This is how the data is encoded: +# +# General_Category (gc), Canonical_Combining_Class (ccc), +# and Script (sc) are encoded as integers. +# +# Mirroring character (bmg) is encoded as difference from +# the original character. +# +# Composition & Decomposition (dm) are encoded elaborately, +# as discussed below. + +gc = [u['gc'] for u in ucd] +ccc = [int(u['ccc']) for u in ucd] +bmg = [int(v, 16) - int(u) if v else 0 for u,v in enumerate(u['bmg'] for u in ucd)] +sc = [u['sc'] for u in ucd] + + +# Prepare Compose / Decompose data +# +# This code is very dense. See hb_ucd_compose() / hb_ucd_decompose() for the logic. + +dm = {i:tuple(int(v, 16) for v in u['dm'].split()) for i,u in enumerate(ucd) + if u['dm'] != '#' and u['dt'] == 'can' and not (0xAC00 <= i < 0xAC00+11172)} +ce = {i for i,u in enumerate(ucd) if u['Comp_Ex'] == 'Y'} + +assert not any(v for v in dm.values() if len(v) not in (1,2)) +dm1 = sorted(set(v for v in dm.values() if len(v) == 1)) +assert all((v[0] >> 16) in (0,2) for v in dm1) +dm1_p0_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 0] +dm1_p2_array = ['0x%04Xu' % (v[0] & 0xFFFF) for v in dm1 if (v[0] >> 16) == 2] +dm1_order = {v:i+1 for i,v in enumerate(dm1)} + +dm2 = sorted((v+(i if i not in ce and not ccc[i] else 0,), v) + for i,v in dm.items() if len(v) == 2) + +filt = lambda v: ((v[0] & 0xFFFFF800) == 0x0000 and + (v[1] & 0xFFFFFF80) == 0x0300 and + (v[2] & 0xFFF0C000) == 0x0000) +dm2_u32_array = [v for v in dm2 if filt(v[0])] +dm2_u64_array = [v for v in dm2 if not filt(v[0])] +assert dm2_u32_array + dm2_u64_array == dm2 +dm2_u32_array = ["HB_CODEPOINT_ENCODE3_11_7_14 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u32_array] +dm2_u64_array = ["HB_CODEPOINT_ENCODE3 (0x%04Xu, 0x%04Xu, 0x%04Xu)" % v[0] for v in dm2_u64_array] + +l = 1 + len(dm1_p0_array) + len(dm1_p2_array) +dm2_order = {v[1]:i+l for i,v in enumerate(dm2)} + +dm_order = {None: 0} +dm_order.update(dm1_order) +dm_order.update(dm2_order) + + +# Prepare General_Category / Script mapping arrays + +gc_order = dict() +for i,v in enumerate(('Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', + 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', + 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',)): + gc_order[i] = v + gc_order[v] = i + +sc_order = dict() +sc_array = [] +sc_re = re.compile(r"\b(HB_SCRIPT_[_A-Z]*).*HB_TAG [(]'(.)','(.)','(.)','(.)'[)]") +for line in open(hb_common_h): + m = sc_re.search (line) + if not m: continue + name = m.group(1) + tag = ''.join(m.group(i) for i in range(2, 6)) + i = len(sc_array) + sc_order[tag] = i + sc_order[i] = tag + sc_array.append(name) + + +# Write out main data + +DEFAULT = 'DEFAULT' +COMPACT = 'COMPACT' +SLOPPY = 'SLOPPY' + +compression_level = { + DEFAULT: 5, + COMPACT: 9, + SLOPPY: 9, +} + +logging.info('Generating output...') +print("/* == Start of generated table == */") +print("/*") +print(" * The following table is generated by running:") +print(" *") +print(" * ./gen-ucd-table.py ucd.nounihan.grouped.xml") +print(" *") +print(" * on file with this description:", ucdxml.description) +print(" */") +print() +print("#ifndef HB_UCD_TABLE_HH") +print("#define HB_UCD_TABLE_HH") +print() +print('#include "hb.hh"') +print() + + +# Write mapping data + +code = packTab.Code('_hb_ucd') +sc_array, _ = code.addArray('hb_script_t', 'sc_map', sc_array) +dm1_p0_array, _ = code.addArray('uint16_t', 'dm1_p0_map', dm1_p0_array) +dm1_p2_array, _ = code.addArray('uint16_t', 'dm1_p2_map', dm1_p2_array) +dm2_u32_array, _ = code.addArray('uint32_t', 'dm2_u32_map', dm2_u32_array) +dm2_u64_array, _ = code.addArray('uint64_t', 'dm2_u64_map', dm2_u64_array) +code.print_c(linkage='static inline') + +datasets = [ + ('gc', gc, 'Cn', gc_order), + ('ccc', ccc, 0, None), + ('bmg', bmg, 0, None), + ('sc', sc, 'Zzzz', sc_order), + ('dm', dm, None, dm_order), +] + + +# Write main data + +for step in (DEFAULT, COMPACT, SLOPPY): + compression = compression_level[step] + logging.info(' Compression=%d:' % compression) + print() + if step == DEFAULT: + print('#ifndef HB_OPTIMIZE_SIZE') + elif step == COMPACT: + print('#elif !defined(HB_NO_UCD_UNASSIGNED)') + elif step == SLOPPY: + print('#else') + else: + assert False + print() + + if step == SLOPPY: + for i in range(len(gc)): + if (i % 128) and gc[i] == 'Cn': + gc[i] = gc[i - 1] + for i in range(len(gc) - 2, -1, -1): + if ((i + 1) % 128) and gc[i] == 'Cn': + gc[i] = gc[i + 1] + for i in range(len(sc)): + if (i % 128) and sc[i] == 'Zzzz': + sc[i] = sc[i - 1] + for i in range(len(sc) - 2, -1, -1): + if ((i + 1) % 128) and sc[i] == 'Zzzz': + sc[i] = sc[i + 1] + + + code = packTab.Code('_hb_ucd') + + for name,data,default,mapping in datasets: + sol = packTab.pack_table(data, default, mapping=mapping, compression=compression) + logging.info(' Dataset=%-8s FullCost=%d' % (name, sol.fullCost)) + sol.genCode(code, name) + + code.print_c(linkage='static inline') + + print() + + +print('#endif') +print() + +print() +print("#endif /* HB_UCD_TABLE_HH */") +print() +print("/* == End of generated table == */") +logging.info('Done.') diff --git a/gfx/harfbuzz/src/gen-use-table.py b/gfx/harfbuzz/src/gen-use-table.py new file mode 100755 index 0000000000..0012e5db04 --- /dev/null +++ b/gfx/harfbuzz/src/gen-use-table.py @@ -0,0 +1,520 @@ +#!/usr/bin/env python3 +# flake8: noqa: F821 + +"""usage: ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt + +Input files: +* https://unicode.org/Public/UCD/latest/ucd/IndicSyllabicCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/IndicPositionalCategory.txt +* https://unicode.org/Public/UCD/latest/ucd/ArabicShaping.txt +* https://unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt +* https://unicode.org/Public/UCD/latest/ucd/UnicodeData.txt +* https://unicode.org/Public/UCD/latest/ucd/Blocks.txt +* https://unicode.org/Public/UCD/latest/ucd/Scripts.txt +* ms-use/IndicSyllabicCategory-Additional.txt +* ms-use/IndicPositionalCategory-Additional.txt +""" + +import logging +logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO) + + +import sys + +if len (sys.argv) != 10: + sys.exit (__doc__) + +DISABLED_SCRIPTS = { + 'Arabic', + 'Lao', + 'Samaritan', + 'Syriac', + 'Thai', +} + +files = [open (x, encoding='utf-8') for x in sys.argv[1:]] + +headers = [[f.readline () for i in range (2)] for j,f in enumerate(files) if j != 4] +for j in range(7, 9): + for line in files[j]: + line = line.rstrip() + if not line: + break + headers[j - 1].append(line) +headers.append (["UnicodeData.txt does not have a header."]) + +unicode_data = [{} for _ in files] +values = [{} for _ in files] +for i, f in enumerate (files): + for line in f: + + j = line.find ('#') + if j >= 0: + line = line[:j] + + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + + t = fields[1 if i not in [2, 4] else 2] + + if i == 2: + t = 'jt_' + t + elif i == 3 and t != 'Default_Ignorable_Code_Point': + continue + elif i == 7 and t == 'Consonant_Final_Modifier': + # TODO: https://github.com/MicrosoftDocs/typography-issues/issues/336 + t = 'Syllable_Modifier' + elif i == 8 and t == 'NA': + t = 'Not_Applicable' + + i0 = i if i < 7 else i - 7 + for u in range (start, end + 1): + unicode_data[i0][u] = t + values[i0][t] = values[i0].get (t, 0) + end - start + 1 + +defaults = ('Other', 'Not_Applicable', 'jt_X', '', 'Cn', 'No_Block', 'Unknown') + +# Merge data into one dict: +for i,v in enumerate (defaults): + values[i][v] = values[i].get (v, 0) + 1 +combined = {} +for i,d in enumerate (unicode_data): + for u,v in d.items (): + if not u in combined: + if i >= 4: + continue + combined[u] = list (defaults) + combined[u][i] = v +combined = {k: v for k, v in combined.items() if v[6] not in DISABLED_SCRIPTS} + + +property_names = [ + # General_Category + 'Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', + 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', + 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs', + # Indic_Syllabic_Category + 'Other', + 'Bindu', + 'Visarga', + 'Avagraha', + 'Nukta', + 'Virama', + 'Pure_Killer', + 'Invisible_Stacker', + 'Vowel_Independent', + 'Vowel_Dependent', + 'Vowel', + 'Consonant_Placeholder', + 'Consonant', + 'Consonant_Dead', + 'Consonant_With_Stacker', + 'Consonant_Prefixed', + 'Consonant_Preceding_Repha', + 'Consonant_Succeeding_Repha', + 'Consonant_Subjoined', + 'Consonant_Medial', + 'Consonant_Final', + 'Consonant_Head_Letter', + 'Consonant_Initial_Postfixed', + 'Modifying_Letter', + 'Tone_Letter', + 'Tone_Mark', + 'Gemination_Mark', + 'Cantillation_Mark', + 'Register_Shifter', + 'Syllable_Modifier', + 'Consonant_Killer', + 'Non_Joiner', + 'Joiner', + 'Number_Joiner', + 'Number', + 'Brahmi_Joining_Number', + 'Symbol_Modifier', + 'Hieroglyph', + 'Hieroglyph_Joiner', + 'Hieroglyph_Mark_Begin', + 'Hieroglyph_Mark_End', + 'Hieroglyph_Mirror', + 'Hieroglyph_Modifier', + 'Hieroglyph_Segment_Begin', + 'Hieroglyph_Segment_End', + # Indic_Positional_Category + 'Not_Applicable', + 'Right', + 'Left', + 'Visual_Order_Left', + 'Left_And_Right', + 'Top', + 'Bottom', + 'Top_And_Bottom', + 'Top_And_Bottom_And_Left', + 'Top_And_Right', + 'Top_And_Left', + 'Top_And_Left_And_Right', + 'Bottom_And_Left', + 'Bottom_And_Right', + 'Top_And_Bottom_And_Right', + 'Overstruck', + # Joining_Type + 'jt_C', + 'jt_D', + 'jt_L', + 'jt_R', + 'jt_T', + 'jt_U', + 'jt_X', +] + +class PropertyValue(object): + def __init__(self, name_): + self.name = name_ + def __str__(self): + return self.name + def __eq__(self, other): + return self.name == (other if isinstance(other, str) else other.name) + def __ne__(self, other): + return not (self == other) + def __hash__(self): + return hash(str(self)) + +property_values = {} + +for name in property_names: + value = PropertyValue(name) + assert value not in property_values + assert value not in globals() + property_values[name] = value +globals().update(property_values) + + +def is_BASE(U, UISC, UDI, UGC, AJT): + return (UISC in [Number, Consonant, Consonant_Head_Letter, + Tone_Letter, + Vowel_Independent, + ] or + # TODO: https://github.com/MicrosoftDocs/typography-issues/issues/484 + AJT in [jt_C, jt_D, jt_L, jt_R] and UISC != Joiner or + (UGC == Lo and UISC in [Avagraha, Bindu, Consonant_Final, Consonant_Medial, + Consonant_Subjoined, Vowel, Vowel_Dependent])) +def is_BASE_NUM(U, UISC, UDI, UGC, AJT): + return UISC == Brahmi_Joining_Number +def is_BASE_OTHER(U, UISC, UDI, UGC, AJT): + if UISC == Consonant_Placeholder: return True + return U in [0x2015, 0x2022, 0x25FB, 0x25FC, 0x25FD, 0x25FE] +def is_CGJ(U, UISC, UDI, UGC, AJT): + # Also includes VARIATION_SELECTOR and ZWJ + return UISC == Joiner or UDI and UGC in [Mc, Me, Mn] +def is_CONS_FINAL(U, UISC, UDI, UGC, AJT): + return ((UISC == Consonant_Final and UGC != Lo) or + UISC == Consonant_Succeeding_Repha) +def is_CONS_FINAL_MOD(U, UISC, UDI, UGC, AJT): + return UISC == Syllable_Modifier +def is_CONS_MED(U, UISC, UDI, UGC, AJT): + # Consonant_Initial_Postfixed is new in Unicode 11; not in the spec. + return (UISC == Consonant_Medial and UGC != Lo or + UISC == Consonant_Initial_Postfixed) +def is_CONS_MOD(U, UISC, UDI, UGC, AJT): + return UISC in [Nukta, Gemination_Mark, Consonant_Killer] +def is_CONS_SUB(U, UISC, UDI, UGC, AJT): + return UISC == Consonant_Subjoined and UGC != Lo +def is_CONS_WITH_STACKER(U, UISC, UDI, UGC, AJT): + return UISC == Consonant_With_Stacker +def is_HALANT(U, UISC, UDI, UGC, AJT): + return UISC == Virama and not is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UDI, UGC, AJT) +def is_HALANT_OR_VOWEL_MODIFIER(U, UISC, UDI, UGC, AJT): + # Split off of HALANT + return U == 0x0DCA +def is_HALANT_NUM(U, UISC, UDI, UGC, AJT): + return UISC == Number_Joiner +def is_HIEROGLYPH(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph +def is_HIEROGLYPH_JOINER(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph_Joiner +def is_HIEROGLYPH_MIRROR(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph_Mirror +def is_HIEROGLYPH_MOD(U, UISC, UDI, UGC, AJT): + return UISC == Hieroglyph_Modifier +def is_HIEROGLYPH_SEGMENT_BEGIN(U, UISC, UDI, UGC, AJT): + return UISC in [Hieroglyph_Mark_Begin, Hieroglyph_Segment_Begin] +def is_HIEROGLYPH_SEGMENT_END(U, UISC, UDI, UGC, AJT): + return UISC in [Hieroglyph_Mark_End, Hieroglyph_Segment_End] +def is_INVISIBLE_STACKER(U, UISC, UDI, UGC, AJT): + # Split off of HALANT + return (UISC == Invisible_Stacker + and not is_SAKOT(U, UISC, UDI, UGC, AJT) + ) +def is_ZWNJ(U, UISC, UDI, UGC, AJT): + return UISC == Non_Joiner +def is_OTHER(U, UISC, UDI, UGC, AJT): + # Also includes BASE_IND and SYM + return ((UGC == Po or UISC in [Consonant_Dead, Joiner, Modifying_Letter, Other]) + and not is_BASE(U, UISC, UDI, UGC, AJT) + and not is_BASE_OTHER(U, UISC, UDI, UGC, AJT) + and not is_CGJ(U, UISC, UDI, UGC, AJT) + and not is_SYM_MOD(U, UISC, UDI, UGC, AJT) + and not is_Word_Joiner(U, UISC, UDI, UGC, AJT) + ) +def is_REPHA(U, UISC, UDI, UGC, AJT): + return UISC in [Consonant_Preceding_Repha, Consonant_Prefixed] +def is_SAKOT(U, UISC, UDI, UGC, AJT): + # Split off of HALANT + return U == 0x1A60 +def is_SYM_MOD(U, UISC, UDI, UGC, AJT): + return UISC == Symbol_Modifier +def is_VOWEL(U, UISC, UDI, UGC, AJT): + return (UISC == Pure_Killer or + UGC != Lo and UISC in [Vowel, Vowel_Dependent]) +def is_VOWEL_MOD(U, UISC, UDI, UGC, AJT): + return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or + UGC != Lo and UISC == Bindu) +def is_Word_Joiner(U, UISC, UDI, UGC, AJT): + # Also includes Rsv + return (UDI and U not in [0x115F, 0x1160, 0x3164, 0xFFA0, 0x1BCA0, 0x1BCA1, 0x1BCA2, 0x1BCA3] + and UISC == Other + and not is_CGJ(U, UISC, UDI, UGC, AJT) + ) or UGC == Cn + +use_mapping = { + 'B': is_BASE, + 'N': is_BASE_NUM, + 'GB': is_BASE_OTHER, + 'CGJ': is_CGJ, + 'F': is_CONS_FINAL, + 'FM': is_CONS_FINAL_MOD, + 'M': is_CONS_MED, + 'CM': is_CONS_MOD, + 'SUB': is_CONS_SUB, + 'CS': is_CONS_WITH_STACKER, + 'H': is_HALANT, + 'HVM': is_HALANT_OR_VOWEL_MODIFIER, + 'HN': is_HALANT_NUM, + 'IS': is_INVISIBLE_STACKER, + 'G': is_HIEROGLYPH, + 'HM': is_HIEROGLYPH_MOD, + 'HR': is_HIEROGLYPH_MIRROR, + 'J': is_HIEROGLYPH_JOINER, + 'SB': is_HIEROGLYPH_SEGMENT_BEGIN, + 'SE': is_HIEROGLYPH_SEGMENT_END, + 'ZWNJ': is_ZWNJ, + 'O': is_OTHER, + 'R': is_REPHA, + 'Sk': is_SAKOT, + 'SM': is_SYM_MOD, + 'V': is_VOWEL, + 'VM': is_VOWEL_MOD, + 'WJ': is_Word_Joiner, +} + +use_positions = { + 'F': { + 'Abv': [Top], + 'Blw': [Bottom], + 'Pst': [Right], + }, + 'M': { + 'Abv': [Top], + 'Blw': [Bottom, Bottom_And_Left, Bottom_And_Right], + 'Pst': [Right], + 'Pre': [Left, Top_And_Bottom_And_Left], + }, + 'CM': { + 'Abv': [Top], + 'Blw': [Bottom, Overstruck], + }, + 'V': { + 'Abv': [Top, Top_And_Bottom, Top_And_Bottom_And_Right, Top_And_Right], + 'Blw': [Bottom, Overstruck, Bottom_And_Right], + 'Pst': [Right], + 'Pre': [Left, Top_And_Left, Top_And_Left_And_Right, Left_And_Right], + }, + 'VM': { + 'Abv': [Top], + 'Blw': [Bottom, Overstruck], + 'Pst': [Right], + 'Pre': [Left], + }, + 'SM': { + 'Abv': [Top], + 'Blw': [Bottom], + }, + 'H': None, + 'HM': None, + 'HR': None, + 'HVM': None, + 'IS': None, + 'B': None, + 'FM': { + 'Abv': [Top], + 'Blw': [Bottom], + 'Pst': [Not_Applicable], + }, + 'R': None, + 'SUB': None, +} + +def map_to_use(data): + out = {} + items = use_mapping.items() + for U, (UISC, UIPC, AJT, UDI, UGC, UBlock, _) in data.items(): + + # Resolve Indic_Syllabic_Category + + # TODO: These don't have UISC assigned in Unicode 13.0.0, but have UIPC + if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark + + # Tibetan: + # TODO: These don't have UISC assigned in Unicode 13.0.0, but have UIPC + if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent + + # TODO: U+1CED should only be allowed after some of + # the nasalization marks, maybe only for U+1CE9..U+1CF1. + if U == 0x1CED: UISC = Tone_Mark + + values = [k for k,v in items if v(U, UISC, UDI, UGC, AJT)] + assert len(values) == 1, "%s %s %s %s %s %s" % (hex(U), UISC, UDI, UGC, AJT, values) + USE = values[0] + + # Resolve Indic_Positional_Category + + # TODO: https://github.com/harfbuzz/harfbuzz/pull/1037 + # and https://github.com/harfbuzz/harfbuzz/issues/1631 + if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top + + assert (UIPC in [Not_Applicable, Visual_Order_Left] or U == 0x0F7F or + USE in use_positions), "%s %s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UDI, UGC, AJT) + + pos_mapping = use_positions.get(USE, None) + if pos_mapping: + values = [k for k,v in pos_mapping.items() if v and UIPC in v] + assert len(values) == 1, "%s %s %s %s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UDI, UGC, AJT, values) + USE = USE + values[0] + + out[U] = (USE, UBlock) + return out + +use_data = map_to_use(combined) + +print ("/* == Start of generated table == */") +print ("/*") +print (" * The following table is generated by running:") +print (" *") +print (" * {} IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt".format (sys.argv[0])) +print (" *") +print (" * on files with these headers:") +print (" *") +for h in headers: + for l in h: + print (" * %s" % (l.strip())) +print (" */") +print () +print ("#ifndef HB_OT_SHAPER_USE_TABLE_HH") +print ("#define HB_OT_SHAPER_USE_TABLE_HH") +print () +print ('#include "hb.hh"') +print () +print ('#include "hb-ot-shaper-use-machine.hh"') +print () + +total = 0 +used = 0 +last_block = None +def print_block (block, start, end, use_data): + global total, used, last_block + if block and block != last_block: + print () + print () + print (" /* %s */" % block) + if start % 16: + print (' ' * (20 + (start % 16 * 6)), end='') + num = 0 + assert start % 8 == 0 + assert (end+1) % 8 == 0 + for u in range (start, end+1): + if u % 16 == 0: + print () + print (" /* %04X */" % u, end='') + if u in use_data: + num += 1 + d = use_data.get (u) + if d is not None: + d = d[0] + elif u in unicode_data[4]: + d = 'O' + else: + d = 'WJ' + print ("%6s," % d, end='') + + total += end - start + 1 + used += num + if block: + last_block = block + +uu = sorted (use_data.keys ()) + +last = -100000 +num = 0 +offset = 0 +starts = [] +ends = [] +print ('#pragma GCC diagnostic push') +print ('#pragma GCC diagnostic ignored "-Wunused-macros"') +for k,v in sorted(use_mapping.items()): + if k in use_positions and use_positions[k]: continue + print ("#define %s USE(%s) /* %s */" % (k, k, v.__name__[3:])) +for k,v in sorted(use_positions.items()): + if not v: continue + for suf in v.keys(): + tag = k + suf + print ("#define %s USE(%s)" % (tag, tag)) +print ('#pragma GCC diagnostic pop') +print ("") + + +import packTab +data = {u:v[0] for u,v in use_data.items()} + +DEFAULT = 5 +COMPACT = 9 +for compression in (DEFAULT, COMPACT): + + logging.info(' Compression=%d:' % compression) + print() + if compression == DEFAULT: + print('#ifndef HB_OPTIMIZE_SIZE') + elif compression == COMPACT: + print('#else') + else: + assert False + print() + + code = packTab.Code('hb_use') + sol = packTab.pack_table(data, compression=compression, default='O') + logging.info(' FullCost=%d' % (sol.fullCost)) + sol.genCode(code, f'get_category') + code.print_c(linkage='static inline') + print () + +print('#endif') + +print () +for k in sorted(use_mapping.keys()): + if k in use_positions and use_positions[k]: continue + print ("#undef %s" % k) +for k,v in sorted(use_positions.items()): + if not v: continue + for suf in v.keys(): + tag = k + suf + print ("#undef %s" % tag) +print () +print () +print ("#endif /* HB_OT_SHAPER_USE_TABLE_HH */") +print ("/* == End of generated table == */") diff --git a/gfx/harfbuzz/src/gen-vowel-constraints.py b/gfx/harfbuzz/src/gen-vowel-constraints.py new file mode 100755 index 0000000000..3c1f6211eb --- /dev/null +++ b/gfx/harfbuzz/src/gen-vowel-constraints.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python3 + +"""Generator of the function to prohibit certain vowel sequences. + +It creates ``_hb_preprocess_text_vowel_constraints``, which inserts dotted +circles into sequences prohibited by the USE script development spec. +This function should be used as the ``preprocess_text`` of an +``hb_ot_shaper_t``. + +usage: ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt + +Input file: +* https://unicode.org/Public/UCD/latest/ucd/Scripts.txt +""" + +import collections +def write (s): + sys.stdout.flush () + sys.stdout.buffer.write (s.encode ('utf-8')) +import sys + +if len (sys.argv) != 3: + sys.exit (__doc__) + +with open (sys.argv[2], encoding='utf-8') as f: + scripts_header = [f.readline () for i in range (2)] + scripts = {} + script_order = {} + for line in f: + j = line.find ('#') + if j >= 0: + line = line[:j] + fields = [x.strip () for x in line.split (';')] + if len (fields) == 1: + continue + uu = fields[0].split ('..') + start = int (uu[0], 16) + if len (uu) == 1: + end = start + else: + end = int (uu[1], 16) + script = fields[1] + for u in range (start, end + 1): + scripts[u] = script + if script not in script_order: + script_order[script] = start + +class ConstraintSet (object): + """A set of prohibited code point sequences. + + Args: + constraint (List[int]): A prohibited code point sequence. + + """ + def __init__ (self, constraint): + # Either a list or a dictionary. As a list of code points, it + # represents a prohibited code point sequence. As a dictionary, + # it represents a set of prohibited sequences, where each item + # represents the set of prohibited sequences starting with the + # key (a code point) concatenated with any of the values + # (ConstraintSets). + self._c = constraint + + def add (self, constraint): + """Add a constraint to this set.""" + if not constraint: + return + first = constraint[0] + rest = constraint[1:] + if isinstance (self._c, list): + if constraint == self._c[:len (constraint)]: + self._c = constraint + elif self._c != constraint[:len (self._c)]: + self._c = {self._c[0]: ConstraintSet (self._c[1:])} + if isinstance (self._c, dict): + if first in self._c: + self._c[first].add (rest) + else: + self._c[first] = ConstraintSet (rest) + + @staticmethod + def _indent (depth): + return (' ' * depth).replace (' ', '\t') + + def __str__ (self, index=0, depth=4): + s = [] + indent = self._indent (depth) + if isinstance (self._c, list): + if len (self._c) == 0: + assert index == 2, 'Cannot use `matched` for this constraint; the general case has not been implemented' + s.append ('{}matched = true;\n'.format (indent)) + elif len (self._c) == 1: + assert index == 1, 'Cannot use `matched` for this constraint; the general case has not been implemented' + s.append ('{}matched = 0x{:04X}u == buffer->cur ({}).codepoint;\n'.format (indent, next (iter (self._c)), index or '')) + else: + s.append ('{}if (0x{:04X}u == buffer->cur ({}).codepoint &&\n'.format (indent, self._c[0], index or '')) + if index: + s.append ('{}buffer->idx + {} < count &&\n'.format (self._indent (depth + 2), index + 1)) + for i, cp in enumerate (self._c[1:], start=1): + s.append ('{}0x{:04X}u == buffer->cur ({}).codepoint{}\n'.format ( + self._indent (depth + 2), cp, index + i, ')' if i == len (self._c) - 1 else ' &&')) + s.append ('{}{{\n'.format (indent)) + for i in range (index): + s.append ('{}(void) buffer->next_glyph ();\n'.format (self._indent (depth + 1))) + s.append ('{}matched = true;\n'.format (self._indent (depth + 1))) + s.append ('{}}}\n'.format (indent)) + else: + s.append ('{}switch (buffer->cur ({}).codepoint)\n'.format(indent, index or '')) + s.append ('{}{{\n'.format (indent)) + cases = collections.defaultdict (set) + for first, rest in sorted (self._c.items ()): + cases[rest.__str__ (index + 1, depth + 2)].add (first) + for body, labels in sorted (cases.items (), key=lambda b_ls: sorted (b_ls[1])[0]): + for i, cp in enumerate (sorted (labels)): + if i % 4 == 0: + s.append (self._indent (depth + 1)) + else: + s.append (' ') + s.append ('case 0x{:04X}u:{}'.format (cp, '\n' if i % 4 == 3 else '')) + if len (labels) % 4 != 0: + s.append ('\n') + s.append (body) + s.append ('{}break;\n'.format (self._indent (depth + 2))) + s.append ('{}}}\n'.format (indent)) + return ''.join (s) + +constraints = {} +with open (sys.argv[1], encoding='utf-8') as f: + constraints_header = [] + while True: + line = f.readline ().strip () + if line == '#': + break + constraints_header.append(line) + for line in f: + j = line.find ('#') + if j >= 0: + line = line[:j] + constraint = [int (cp, 16) for cp in line.split (';')[0].split ()] + if not constraint: continue + assert 2 <= len (constraint), 'Prohibited sequence is too short: {}'.format (constraint) + script = scripts[constraint[0]] + if script in constraints: + constraints[script].add (constraint) + else: + constraints[script] = ConstraintSet (constraint) + assert constraints, 'No constraints found' + +print ('/* == Start of generated functions == */') +print ('/*') +print (' * The following functions are generated by running:') +print (' *') +print (' * %s ms-use/IndicShapingInvalidCluster.txt Scripts.txt' % sys.argv[0]) +print (' *') +print (' * on files with these headers:') +print (' *') +for line in constraints_header: + print (' * %s' % line.strip ()) +print (' *') +for line in scripts_header: + print (' * %s' % line.strip ()) +print (' */') + +print () +print ('#include "hb.hh"') +print () +print ('#ifndef HB_NO_OT_SHAPE') +print () +print ('#include "hb-ot-shaper-vowel-constraints.hh"') +print () +print ('static void') +print ('_output_dotted_circle (hb_buffer_t *buffer)') +print ('{') +print (' (void) buffer->output_glyph (0x25CCu);') +print (' _hb_glyph_info_reset_continuation (&buffer->prev());') +print ('}') +print () +print ('static void') +print ('_output_with_dotted_circle (hb_buffer_t *buffer)') +print ('{') +print (' _output_dotted_circle (buffer);') +print (' (void) buffer->next_glyph ();') +print ('}') +print () + +print ('void') +print ('_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,') +print ('\t\t\t\t hb_buffer_t *buffer,') +print ('\t\t\t\t hb_font_t *font HB_UNUSED)') +print ('{') +print ('#ifdef HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS') +print (' return;') +print ('#endif') +print (' if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)') +print (' return;') +print () +print (' /* UGLY UGLY UGLY business of adding dotted-circle in the middle of') +print (' * vowel-sequences that look like another vowel. Data for each script') +print (' * collected from the USE script development spec.') +print (' *') +print (' * https://github.com/harfbuzz/harfbuzz/issues/1019') +print (' */') +print (' buffer->clear_output ();') +print (' unsigned int count = buffer->len;') +print (' switch ((unsigned) buffer->props.script)') +print (' {') + +for script, constraints in sorted (constraints.items (), key=lambda s_c: script_order[s_c[0]]): + print (' case HB_SCRIPT_{}:'.format (script.upper ())) + print (' for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)') + print (' {') + print ('\tbool matched = false;') + write (str (constraints)) + print ('\t(void) buffer->next_glyph ();') + print ('\tif (matched) _output_with_dotted_circle (buffer);') + print (' }') + print (' break;') + print () + +print (' default:') +print (' break;') +print (' }') +print (' buffer->sync ();') +print ('}') + +print () +print () +print ('#endif') +print ('/* == End of generated functions == */') diff --git a/gfx/harfbuzz/src/graph/classdef-graph.hh b/gfx/harfbuzz/src/graph/classdef-graph.hh new file mode 100644 index 0000000000..9cf845a82d --- /dev/null +++ b/gfx/harfbuzz/src/graph/classdef-graph.hh @@ -0,0 +1,225 @@ +/* + * 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<SmallTypes> +{ + 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<SmallTypes>::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + return vertex_len >= min_size + classValue.get_size () - classValue.len.get_size (); + } +}; + +struct ClassDefFormat2 : public OT::ClassDefFormat2_4<SmallTypes> +{ + 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<SmallTypes>::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + return vertex_len >= min_size + rangeRecord.get_size () - rangeRecord.len.get_size (); + } +}; + +struct ClassDef : public OT::ClassDef +{ + template<typename It> + 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<typename It> + 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; + hb_barrier (); + 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<typename It> + 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<unsigned, unsigned> num_ranges_per_class; + hb_hashmap_t<unsigned, hb_set_t> glyphs_per_class; +}; + + +} + +#endif // GRAPH_CLASSDEF_GRAPH_HH diff --git a/gfx/harfbuzz/src/graph/coverage-graph.hh b/gfx/harfbuzz/src/graph/coverage-graph.hh new file mode 100644 index 0000000000..61ca063e34 --- /dev/null +++ b/gfx/harfbuzz/src/graph/coverage-graph.hh @@ -0,0 +1,161 @@ +/* + * 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<SmallTypes> +{ + 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<SmallTypes>::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + return vertex_len >= min_size + glyphArray.get_size () - glyphArray.len.get_size (); + } +}; + +struct CoverageFormat2 : public OT::Layout::Common::CoverageFormat2_4<SmallTypes> +{ + 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<SmallTypes>::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + 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<unsigned, unsigned> 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<typename It> + 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<typename It> + 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; + hb_barrier (); + 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/gfx/harfbuzz/src/graph/graph.hh b/gfx/harfbuzz/src/graph/graph.hh new file mode 100644 index 0000000000..26ad00bdd9 --- /dev/null +++ b/gfx/harfbuzz/src/graph/graph.hh @@ -0,0 +1,1519 @@ +/* + * 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<unsigned, unsigned> 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<unsigned, unsigned> + position_to_index_map () const + { + hb_hashmap_t<unsigned, unsigned> 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<unsigned>& id_map) + { + if (single_parent != (unsigned) -1) + { + assert (single_parent < id_map.length); + single_parent = id_map[single_parent]; + return true; + } + + hb_hashmap_t<unsigned, unsigned> 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<hb_serialize_context_t::object_t::link_t>& this_links, + const hb_vector_t<hb_serialize_context_t::object_t::link_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 <typename T> + 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<typename T> + 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<typename T> + 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<int64_t> queue; + queue.alloc (vertices_.length); + hb_vector_t<vertex_t> &sorted_graph = vertices_scratch_; + if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; + hb_vector_t<unsigned> id_map; + if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; + + hb_vector_t<unsigned> 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 <typename T, typename ...Ts> + vertex_and_table_t<T> as_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index<T> (index_for_offset (parent, offset), std::forward<Ts>(ds)...); + } + + template <typename T, typename ...Ts> + vertex_and_table_t<T> as_mutable_table (unsigned parent, const void* offset, Ts... ds) + { + return as_table_from_index<T> (mutable_index_for_offset (parent, offset), std::forward<Ts>(ds)...); + } + + template <typename T, typename ...Ts> + vertex_and_table_t<T> as_table_from_index (unsigned index, Ts... ds) + { + if (index >= vertices_.length) + return vertex_and_table_t<T> (); + + vertex_and_table_t<T> r; + r.vertex = &vertices_[index]; + r.table = (T*) r.vertex->obj.head; + r.index = index; + if (!r.table) + return vertex_and_table_t<T> (); + + if (!r.table->sanitize (*(r.vertex), std::forward<Ts>(ds)...)) + return vertex_and_table_t<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<typename O> + 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<int64_t> queue; + queue.alloc (count); + queue.insert (0, vertices_.length - 1); + + hb_vector_t<bool> 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<typename Iterator, hb_requires (hb_is_iterator (Iterator))> + 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<unsigned>& id_map, + hb_vector_t<vertex_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<vertex_t> vertices_; + hb_vector_t<vertex_t> vertices_scratch_; + private: + bool parents_invalid; + bool distance_invalid; + bool positions_invalid; + bool successful; + hb_vector_t<unsigned> num_roots_for_space_; + hb_vector_t<char*> buffers; +}; + +} + +#endif // GRAPH_GRAPH_HH diff --git a/gfx/harfbuzz/src/graph/gsubgpos-context.cc b/gfx/harfbuzz/src/graph/gsubgpos-context.cc new file mode 100644 index 0000000000..d66eb49cfd --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/graph/gsubgpos-context.hh b/gfx/harfbuzz/src/graph/gsubgpos-context.hh new file mode 100644 index 0000000000..b25d538fe3 --- /dev/null +++ b/gfx/harfbuzz/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<unsigned, graph::Lookup*> lookups; + hb_hashmap_t<unsigned, unsigned> 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/gfx/harfbuzz/src/graph/gsubgpos-graph.hh b/gfx/harfbuzz/src/graph/gsubgpos-graph.hh new file mode 100644 index 0000000000..0f6d5662e0 --- /dev/null +++ b/gfx/harfbuzz/src/graph/gsubgpos-graph.hh @@ -0,0 +1,435 @@ +/* + * 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<typename T> +struct ExtensionFormat1 : public OT::ExtensionFormat1<T> +{ + 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<T>::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; + hb_barrier (); + 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<hb_pair_t<unsigned, hb_vector_t<unsigned>>> 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<OT::Layout::GSUB_impl::ExtensionSubst>* extension = + (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) + 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<unsigned> new_sub_tables; + switch (type) + { + case 2: + new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break; + case 4: + new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break; + default: + break; + } + if (new_sub_tables.in_error ()) return false; + if (!new_sub_tables) continue; + hb_pair_t<unsigned, hb_vector_t<unsigned>>* 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<typename T> + hb_vector_t<unsigned> 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<unsigned> (); + + 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<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& 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<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& 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<OT::Layout::GSUB_impl::ExtensionSubst>::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<OT::Layout::GSUB_impl::ExtensionSubst>* extension = + (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) 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 <typename T> +struct LookupList : public OT::LookupList<T> +{ + bool sanitize (const graph_t::vertex_t& vertex) const + { + int64_t vertex_len = vertex.obj.tail - vertex.obj.head; + if (vertex_len < OT::LookupList<T>::min_size) return false; + hb_barrier (); + return vertex_len >= OT::LookupList<T>::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; + hb_barrier (); + + 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; + hb_barrier (); + return len >= get_size (); + } + + void find_lookups (graph_t& graph, + hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */) + { + switch (u.version.major) { + case 1: find_lookups<SmallTypes> (graph, lookups); break; +#ifndef HB_NO_BEYOND_64K + case 2: find_lookups<MediumTypes> (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<typename Types> + void find_lookups (graph_t& graph, + hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */) + { + unsigned lookup_list_idx = get_lookup_list_index (graph); + const LookupList<Types>* lookupList = + (const LookupList<Types>*) 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/gfx/harfbuzz/src/graph/markbasepos-graph.hh b/gfx/harfbuzz/src/graph/markbasepos-graph.hh new file mode 100644 index 0000000000..fb4166128a --- /dev/null +++ b/gfx/harfbuzz/src/graph/markbasepos-graph.hh @@ -0,0 +1,518 @@ +/* + * 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; + hb_barrier (); + + 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; + hb_barrier (); + + return vertex_len >= get_size (); + } + + bool shrink (gsubgpos_graph_context_t& c, + const hb_hashmap_t<unsigned, unsigned>& 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<unsigned, unsigned>& 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<SmallTypes> +{ + 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<unsigned> 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<SmallTypes>::min_size + + MarkArray::min_size + + AnchorMatrix::min_size + + c.graph.vertices_[base_coverage_id].table_size (); + + hb_vector_t<class_info_t> class_to_info = get_class_info (c, this_index); + + unsigned class_count = classCount; + auto base_array = c.graph.as_table<AnchorMatrix> (this_index, + &baseArray, + class_count); + if (!base_array) return hb_vector_t<unsigned> (); + unsigned base_count = base_array.table->rows; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t<unsigned> 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_t> (split_context, split_points); + } + + private: + + struct class_info_t { + hb_set_t marks; + hb_vector_t<unsigned> child_indices; + }; + + struct split_context_t { + gsubgpos_graph_context_t& c; + MarkBasePosFormat1* thiz; + unsigned this_index; + hb_vector_t<class_info_t> class_to_info; + hb_hashmap_t<unsigned, unsigned> 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<class_info_t> get_class_info (gsubgpos_graph_context_t& c, + unsigned this_index) + { + hb_vector_t<class_info_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<class_info_t>(); + + auto mark_array = c.graph.as_table<MarkArray> (this_index, &markArray); + if (!mark_array) return hb_vector_t<class_info_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<Coverage> (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<AnchorMatrix> (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<MarkArray> (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<SmallTypes>::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<Coverage> (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 <MarkArray> (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<AnchorMatrix> (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<unsigned> 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<unsigned> (); + } + } + + 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; + hb_barrier (); + + 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/gfx/harfbuzz/src/graph/pairpos-graph.hh b/gfx/harfbuzz/src/graph/pairpos-graph.hh new file mode 100644 index 0000000000..f7f74b18c9 --- /dev/null +++ b/gfx/harfbuzz/src/graph/pairpos-graph.hh @@ -0,0 +1,650 @@ +/* + * 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<SmallTypes> +{ + 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<SmallTypes>::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + + return vertex_len >= + min_size + pairSet.get_size () - pairSet.len.get_size(); + } + + hb_vector_t<unsigned> 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<SmallTypes>::min_size; + + unsigned partial_coverage_size = 4; + unsigned accumulated = base_size; + hb_vector_t<unsigned> 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_t> (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<Coverage> (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<unsigned, unsigned> 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<SmallTypes>::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<SmallTypes> +{ + bool sanitize (graph_t::vertex_t& vertex) const + { + size_t vertex_len = vertex.table_size (); + unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size; + if (vertex_len < min_size) return false; + hb_barrier (); + + const unsigned class1_count = class1Count; + return vertex_len >= + min_size + class1_count * get_class1_record_size (); + } + + hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, + unsigned parent_index, + unsigned this_index) + { + const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::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<unsigned> split_points; + + hb_hashmap_t<unsigned, unsigned> device_tables = get_all_device_tables (c, this_index); + hb_vector_t<unsigned> format1_device_table_indices = valueFormat1.get_device_table_indices (); + hb_vector_t<unsigned> 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_t> (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<unsigned, unsigned>& device_tables; + const hb_vector_t<unsigned>& format1_device_table_indices; + const hb_vector_t<unsigned>& 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<SmallTypes>::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<unsigned>& 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<Coverage> (split_context.this_index, &this->coverage); + if (!coverage) return false; + + auto class_def_1 = + graph.as_mutable_table<ClassDef> (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<unsigned, unsigned> + 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<unsigned, unsigned>& device_tables, + const hb_vector_t<unsigned> 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<unsigned> 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<unsigned> (); + } + } + + 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; + hb_barrier (); + + 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/gfx/harfbuzz/src/graph/serialize.hh b/gfx/harfbuzz/src/graph/serialize.hh new file mode 100644 index 0000000000..06e4bf44d8 --- /dev/null +++ b/gfx/harfbuzz/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<overflow_record_t>* overflows = nullptr) +{ + if (overflows) overflows->resize (0); + graph.update_positions (); + + hb_hashmap_t<overflow_record_t*, bool> 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<overflow_record_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 <typename O> 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<O>* offset = reinterpret_cast<OT::Offset<O>*> (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<OT::HBINT32> (link, head, c); + } else { + serialize_link_of_type<OT::HBUINT32> (link, head, c); + } + return; + case 2: + if (link.is_signed) + { + serialize_link_of_type<OT::HBINT16> (link, head, c); + } else { + serialize_link_of_type<OT::HBUINT16> (link, head, c); + } + return; + case 3: + serialize_link_of_type<OT::HBUINT24> (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<char> 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<void> (); + 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 <char> (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/gfx/harfbuzz/src/graph/split-helpers.hh b/gfx/harfbuzz/src/graph/split-helpers.hh new file mode 100644 index 0000000000..61fd7c2d2f --- /dev/null +++ b/gfx/harfbuzz/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<typename Context> +HB_INTERNAL +hb_vector_t<unsigned> actuate_subtable_split (Context& split_context, + const hb_vector_t<unsigned>& split_points) +{ + hb_vector_t<unsigned> 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/gfx/harfbuzz/src/graph/test-classdef-graph.cc b/gfx/harfbuzz/src/graph/test-classdef-graph.cc new file mode 100644 index 0000000000..266be5e2d4 --- /dev/null +++ b/gfx/harfbuzz/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_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/gfx/harfbuzz/src/harfbuzz-cairo.pc.in b/gfx/harfbuzz/src/harfbuzz-cairo.pc.in new file mode 100644 index 0000000000..df97ff1512 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-cairo.pc.in @@ -0,0 +1,12 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz cairo integration +Description: HarfBuzz cairo integration +Version: %VERSION% + +Requires: harfbuzz = %VERSION% +Libs: -L${libdir} -lharfbuzz-cairo +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz-config.cmake.in b/gfx/harfbuzz/src/harfbuzz-config.cmake.in new file mode 100644 index 0000000000..6abe2d62dc --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-config.cmake.in @@ -0,0 +1,32 @@ +@PACKAGE_INIT@ + +set_and_check(HARFBUZZ_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@") + +# Add the libraries. +add_library(harfbuzz::harfbuzz @HB_LIBRARY_TYPE@ IMPORTED) +set_target_properties(harfbuzz::harfbuzz PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz@HB_LIB_SUFFIX@") + +add_library(harfbuzz::icu @HB_LIBRARY_TYPE@ IMPORTED) +set_target_properties(harfbuzz::icu PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" + INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-icu@HB_LIB_SUFFIX@") + +add_library(harfbuzz::subset @HB_LIBRARY_TYPE@ IMPORTED) +set_target_properties(harfbuzz::subset PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" + INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-subset@HB_LIB_SUFFIX@") + +# Only add the gobject library if it was built. +if (@HB_HAVE_GOBJECT@) + add_library(harfbuzz::gobject @HB_LIBRARY_TYPE@ IMPORTED) + set_target_properties(harfbuzz::gobject PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "@PACKAGE_INCLUDE_INSTALL_DIR@" + INTERFACE_LINK_LIBRARIES "harfbuzz::harfbuzz" + IMPORTED_LOCATION "@PACKAGE_CMAKE_INSTALL_LIBDIR@/@HB_LIB_PREFIX@harfbuzz-gobject@HB_LIB_SUFFIX@") +endif () + +check_required_components(harfbuzz) diff --git a/gfx/harfbuzz/src/harfbuzz-gobject.pc.in b/gfx/harfbuzz/src/harfbuzz-gobject.pc.in new file mode 100644 index 0000000000..7008360190 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-gobject.pc.in @@ -0,0 +1,12 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library GObject integration +Version: %VERSION% + +Requires: harfbuzz gobject-2.0 glib-2.0 +Libs: -L${libdir} -lharfbuzz-gobject +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz-icu.pc.in b/gfx/harfbuzz/src/harfbuzz-icu.pc.in new file mode 100644 index 0000000000..949869a356 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-icu.pc.in @@ -0,0 +1,13 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library ICU integration +Version: %VERSION% + +Requires: harfbuzz +Requires.private: icu-uc +Libs: -L${libdir} -lharfbuzz-icu +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz-subset.cc b/gfx/harfbuzz/src/harfbuzz-subset.cc new file mode 100644 index 0000000000..c0e23b3eb8 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/harfbuzz-subset.pc.in b/gfx/harfbuzz/src/harfbuzz-subset.pc.in new file mode 100644 index 0000000000..ca13c70ef3 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz-subset.pc.in @@ -0,0 +1,12 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz subsetter +Description: HarfBuzz font subsetter +Version: %VERSION% + +Requires: harfbuzz = %VERSION% +Libs: -L${libdir} -lharfbuzz-subset +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/harfbuzz.cc b/gfx/harfbuzz/src/harfbuzz.cc new file mode 100644 index 0000000000..26e2bc1450 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/harfbuzz.pc.in b/gfx/harfbuzz/src/harfbuzz.pc.in new file mode 100644 index 0000000000..661251c2d4 --- /dev/null +++ b/gfx/harfbuzz/src/harfbuzz.pc.in @@ -0,0 +1,13 @@ +prefix=%prefix% +exec_prefix=%exec_prefix% +libdir=%libdir% +includedir=%includedir% + +Name: harfbuzz +Description: HarfBuzz text shaping library +Version: %VERSION% + +Libs: -L${libdir} -lharfbuzz +Libs.private: -lm %libs_private% +Requires.private: %requires_private% +Cflags: -I${includedir}/harfbuzz diff --git a/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh b/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh new file mode 100644 index 0000000000..dbb38b1bc0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-ankr-table.hh @@ -0,0 +1,99 @@ +/* + * 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<Anchor> 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<GlyphAnchors> *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) && + hb_barrier () && + 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<Lookup<NNOffset16To<GlyphAnchors>>> + lookupTable; /* Offset to the table's lookup table */ + NNOffset32To<HBUINT8> + anchorData; /* Offset to the glyph data table */ + + public: + DEFINE_SIZE_STATIC (12); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_ANKR_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-aat-layout-bsln-table.hh b/gfx/harfbuzz/src/hb-aat-layout-bsln-table.hh new file mode 100644 index 0000000000..8e42fab2e0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-bsln-table.hh @@ -0,0 +1,159 @@ +/* + * 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<HBUINT16> + 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<HBUINT16> + 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); + hb_barrier (); + + 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/gfx/harfbuzz/src/hb-aat-layout-common.hh b/gfx/harfbuzz/src/hb-aat-layout-common.hh new file mode 100644 index 0000000000..05dd58c6df --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-common.hh @@ -0,0 +1,923 @@ +/* + * 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<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY> +{ + const char *get_name () { return "APPLY"; } + template <typename T> + 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<hb_aat_map_t::range_flags_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<hb_blob_t *> (&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 <typename T> struct Lookup; + +template <typename T> +struct LookupFormat0 +{ + friend struct Lookup<T>; + + 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<T> + arrayZ; /* Array of lookup values, indexed by glyph index. */ + public: + DEFINE_SIZE_UNBOUNDED (2); +}; + + +template <typename T> +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 <typename T> +struct LookupFormat2 +{ + friend struct Lookup<T>; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentSingle<T> *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<LookupSegmentSingle<T>> + 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 <typename T> +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) && + hb_barrier () && + first <= last && + valuesZ.sanitize (c, base, last - first + 1)); + } + template <typename ...Ts> + bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + first <= last && + valuesZ.sanitize (c, base, last - first + 1, std::forward<Ts> (ds)...)); + } + + HBGlyphID16 last; /* Last GlyphID in this segment */ + HBGlyphID16 first; /* First GlyphID in this segment */ + NNOffset16To<UnsizedArrayOf<T>> + valuesZ; /* A 16-bit offset from the start of + * the table to the data. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template <typename T> +struct LookupFormat4 +{ + friend struct Lookup<T>; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSegmentArray<T> *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<LookupSegmentArray<T>> + 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 <typename T> +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 <typename T> +struct LookupFormat6 +{ + friend struct Lookup<T>; + + private: + const T* get_value (hb_codepoint_t glyph_id) const + { + const LookupSingle<T> *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<LookupSingle<T>> + entries; /* The actual entries, sorted by glyph index. */ + public: + DEFINE_SIZE_ARRAY (8, entries); +}; + +template <typename T> +struct LookupFormat8 +{ + friend struct Lookup<T>; + + 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<T> + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (6, valueArrayZ); +}; + +template <typename T> +struct LookupFormat10 +{ + friend struct Lookup<T>; + + 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) && + hb_barrier () && + 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<HBUINT8> + valueArrayZ; /* The lookup values (indexed by the glyph index + * minus the value of firstGlyph). */ + public: + DEFINE_SIZE_ARRAY (8, valueArrayZ); +}; + +template <typename T> +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); + hb_barrier (); + 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); + hb_barrier (); + 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<T> format0; + LookupFormat2<T> format2; + LookupFormat4<T> format4; + LookupFormat6<T> format6; + LookupFormat8<T> format8; + LookupFormat10<T> format10; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; +DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); + +enum { DELETED_GLYPH = 0xFFFF }; + +/* + * (Extended) State Table + */ + +template <typename T> +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<void> +{ + // 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 <typename Types, typename Extra> +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<Extra> *get_entries () const + { return (this+entryTable).arrayZ; } + + const Entry<Extra> &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<Extra> *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) && + hb_barrier () && + nClasses >= 4 /* Ensure pre-defined classes fit. */ && + classTable.sanitize (c, this)))) return_trace (false); + + const HBUSHORT *states = (this+stateArrayTable).arrayZ; + const Entry<Extra> *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<Extra> *stop = &entries[num_entries]; + for (const Entry<Extra> *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<ClassType, HBUINT> + classTable; /* Offset to the class table. */ + NNOffsetTo<UnsizedArrayOf<HBUSHORT>, HBUINT> + stateArrayTable;/* Offset to the state array. */ + NNOffsetTo<UnsizedArrayOf<Entry<Extra>>, HBUINT> + entryTable; /* Offset to the entry array. */ + + public: + DEFINE_SIZE_STATIC (4 * sizeof (HBUINT)); +}; + +template <typename HBUCHAR> +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<HBUCHAR> 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<HBUINT8> ClassTypeNarrow; + typedef ClassTable<HBUINT16> ClassTypeWide; + + template <typename T> + 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 <typename T> + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base, + const T *array) + { + return offsetToIndex (offset, base, array); + } + template <typename T> + 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<HBUINT16> ClassTypeNarrow; + typedef Lookup<HBUINT16> ClassTypeWide; + + template <typename T> + static unsigned int offsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } + template <typename T> + static unsigned int byteOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset / 2; + } + template <typename T> + static unsigned int wordOffsetToIndex (unsigned int offset, + const void *base HB_UNUSED, + const T *array HB_UNUSED) + { + return offset; + } +}; + +template <typename Types, typename EntryData> +struct StateTableDriver +{ + using StateTableT = StateTable<Types, EntryData>; + using EntryT = Entry<EntryData>; + + StateTableDriver (const StateTableT &machine_, + hb_buffer_t *buffer_, + hb_face_t *face_) : + machine (machine_), + buffer (buffer_), + num_glyphs (face_->get_num_glyphs ()) {} + + template <typename context_t> + 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/gfx/harfbuzz/src/hb-aat-layout-feat-table.hh b/gfx/harfbuzz/src/hb-aat-layout-feat-table.hh new file mode 100644 index 0000000000..4fbec332eb --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-feat-table.hh @@ -0,0 +1,224 @@ +/* + * 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) && + hb_barrier () && + (base+settingTableZ).sanitize (c, nSettings))); + } + + protected: + HBUINT16 feature; /* Feature type. */ + HBUINT16 nSettings; /* The number of records in the setting name array. */ + NNOffset32To<UnsizedArrayOf<SettingName>> + 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) && + hb_barrier () && + 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<FeatureName> + namesZ; /* The feature name array. */ + public: + DEFINE_SIZE_ARRAY (12, namesZ); +}; + +} /* namespace AAT */ + +#endif /* HB_AAT_LAYOUT_FEAT_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-aat-layout-just-table.hh b/gfx/harfbuzz/src/hb-aat-layout-just-table.hh new file mode 100644 index 0000000000..ee08da172e --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-just-table.hh @@ -0,0 +1,420 @@ +/* + * 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<HBUINT16> + 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); + hb_barrier (); + + 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); + hb_barrier (); + + unsigned int offset = min_size; + for (unsigned int i = 0; i < count; i++) + { + const ActionSubrecord& subrecord = StructAtOffset<ActionSubrecord> (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<WidthDeltaPair> 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<ObsoleteTypes> + morphHeader; /* Metamorphosis-style subtable header. */ + StateTable<ObsoleteTypes, EntryData> + 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<JustificationCategory> + justClassTable; /* Offset to the justification category state table. */ + Offset16To<WidthDeltaCluster> + 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<PostcompensationActionChain> + 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<Offset16To<WidthDeltaCluster>> + 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) && + hb_barrier () && + 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<JustificationHeader> + 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<JustificationHeader> + vertData; /* ditto, vertical */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh b/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh new file mode 100644 index 0000000000..0de54e0a02 --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-kerx-table.hh @@ -0,0 +1,1014 @@ +/* + * 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<FWORD> (base, offset); + if (unlikely (!c->sanitizer.check_array (pv, tupleCount))) return 0; + hb_barrier (); + 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 <typename KernSubTableHeader> +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<accelerator_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<KernPair, typename KernSubTableHeader::Types::HBUINT> + pairs; /* Sorted kern records. */ + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 16, pairs); +}; + + +template <bool extended> +struct Format1Entry; + +template <> +struct Format1Entry<true> +{ + 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<EntryData> &entry) + { return entry.data.kernActionIndex != 0xFFFF; } + + static unsigned int kernActionIndex (const Entry<EntryData> &entry) + { return entry.data.kernActionIndex; } +}; +template <> +struct Format1Entry<false> +{ + 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<EntryData> &entry) + { return entry.flags & Offset; } + + static unsigned int kernActionIndex (const Entry<EntryData> &entry) + { return entry.flags & Offset; } +}; + +template <typename KernSubTableHeader> +struct KerxSubTableFormat1 +{ + typedef typename KernSubTableHeader::Types Types; + typedef typename Types::HBUINT HBUINT; + + typedef Format1Entry<Types::extended> 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<Types, EntryData> *driver HB_UNUSED, + const Entry<EntryData> &entry) + { return Format1EntryT::performAction (entry); } + void transition (StateTableDriver<Types, EntryData> *driver, + const Entry<EntryData> &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_barrier (); + + 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<FWORD> &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<Types, EntryData> 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<Types, EntryData> machine; + NNOffsetTo<UnsizedArrayOf<FWORD>, HBUINT> kernAction; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 5 * sizeof (HBUINT)); +}; + +template <typename KernSubTableHeader> +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<FWORD> &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; + hb_barrier (); + + 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<accelerator_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) && + hb_barrier () && + c->check_range (this, array))); + } + + protected: + KernSubTableHeader header; + HBUINT rowWidth; /* The width, in bytes, of a row in the table. */ + NNOffsetTo<typename Types::ClassTypeWide, HBUINT> + leftClassTable; /* Offset from beginning of this subtable to + * left-hand class table. */ + NNOffsetTo<typename Types::ClassTypeWide, HBUINT> + rightClassTable;/* Offset from beginning of this subtable to + * right-hand class table. */ + NNOffsetTo<UnsizedArrayOf<FWORD>, 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 <typename KernSubTableHeader> +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<Types, EntryData> *driver HB_UNUSED, + const Entry<EntryData> &entry) + { return entry.data.ankrActionIndex != 0xFFFF; } + void transition (StateTableDriver<Types, EntryData> *driver, + const Entry<EntryData> &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; + hb_barrier (); + 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; + hb_barrier (); + 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; + hb_barrier (); + 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<Types, EntryData> 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<Types, EntryData> machine; + HBUINT32 flags; + public: + DEFINE_SIZE_STATIC (KernSubTableHeader::static_size + 20); +}; + +template <typename KernSubTableHeader> +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<FWORD32> (&(this+t.array), offset * sizeof (FWORD32)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + hb_barrier (); + 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<FWORD> (&(this+t.array), offset * sizeof (FWORD)); + if (unlikely (!v->sanitize (&c->sanitizer))) return 0; + hb_barrier (); + 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<accelerator_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) && + hb_barrier () && + (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<Lookup<HBUINT32>> rowIndexTable; + NNOffset32To<Lookup<HBUINT32>> columnIndexTable; + NNOffset32To<UnsizedArrayOf<FWORD32>> array; + } l; + struct Short + { + NNOffset32To<Lookup<HBUINT16>> rowIndexTable; + NNOffset32To<Lookup<HBUINT16>> columnIndexTable; + NNOffset32To<UnsizedArrayOf<FWORD>> array; + } s; + } u; + NNOffset32To<UnsizedArrayOf<FWORD>> 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, typename ...Ts> + 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<Ts> (ds)...)); + case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); + case 6: return_trace (c->dispatch (u.format6, std::forward<Ts> (ds)...)); + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(u.header.sanitize (c) && + hb_barrier () && + 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<KerxSubTableHeader> format0; + KerxSubTableFormat1<KerxSubTableHeader> format1; + KerxSubTableFormat2<KerxSubTableHeader> format2; + KerxSubTableFormat4<KerxSubTableHeader> format4; + KerxSubTableFormat6<KerxSubTableHeader> format6; + } u; + public: + DEFINE_SIZE_MIN (12); +}; + + +/* + * The 'kerx' Table + */ + +template <typename T> +struct KerxTable +{ + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const T* thiz () const { return static_cast<const T *> (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<SubTable> (*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<SubTable> (*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<SubTable> (*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<SubTable> (*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) && + hb_barrier () && + (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); + hb_barrier (); + /* 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<SubTable> (*st); + } + + return_trace (true); + } +}; + +struct kerx : KerxTable<kerx> +{ + friend struct KerxTable<kerx>; + + 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/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh b/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh new file mode 100644 index 0000000000..06c9334b37 --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -0,0 +1,1229 @@ +/* + * 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 <typename Types> +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<Types, EntryData> *driver HB_UNUSED, + const Entry<EntryData> &entry) + { + return (entry.flags & Verb) && start < end; + } + void transition (StateTableDriver<Types, EntryData> *driver, + const Entry<EntryData> &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<Types, EntryData> 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<Types, EntryData> machine; + public: + DEFINE_SIZE_STATIC (16); +}; + +template <typename Types> +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<Types, EntryData> *driver, + const Entry<EntryData> &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<Types, EntryData> *driver, + const Entry<EntryData> &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<HBGlyphID16> &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<HBGlyphID16> &subs_old = (const UnsizedArrayOf<HBGlyphID16> &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!(replacement->sanitize (&c->sanitizer) && + hb_barrier () && + *replacement)) + replacement = nullptr; + } + if (replacement) + { + buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); + 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<HBGlyphID16> &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<HBGlyphID16> &subs_old = (const UnsizedArrayOf<HBGlyphID16> &) subs; + replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)]; + if (!(replacement->sanitize (&c->sanitizer) && + hb_barrier () && + *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<Lookup<HBGlyphID16>, HBUINT, void, false> &subs; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver<Types, EntryData> 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); + hb_barrier (); + + if (!Types::extended) + return_trace (substitutionTables.sanitize (c, this, 0)); + + unsigned int num_lookups = 0; + + const Entry<EntryData> *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<Types, EntryData> + machine; + NNOffsetTo<UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false>, HBUINT> + substitutionTables; + public: + DEFINE_SIZE_STATIC (20); +}; + + +template <bool extended> +struct LigatureEntry; + +template <> +struct LigatureEntry<true> +{ + 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<EntryData> &entry) + { return entry.flags & PerformAction; } + + static unsigned int ligActionIndex (const Entry<EntryData> &entry) + { return entry.data.ligActionIndex; } +}; +template <> +struct LigatureEntry<false> +{ + 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<EntryData> &entry) + { return entry.flags & Offset; } + + static unsigned int ligActionIndex (const Entry<EntryData> &entry) + { return entry.flags & Offset; } +}; + + +template <typename Types> +struct LigatureSubtable +{ + typedef typename Types::HBUINT HBUINT; + + typedef LigatureEntry<Types::extended> 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<Types, EntryData> *driver HB_UNUSED, + const Entry<EntryData> &entry) + { + return LigatureEntryT::performAction (entry); + } + void transition (StateTableDriver<Types, EntryData> *driver, + const Entry<EntryData> &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; + hb_barrier (); + 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; + hb_barrier (); + 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_barrier (); + 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<HBUINT32> &ligAction; + const UnsizedArrayOf<HBUINT16> &component; + const UnsizedArrayOf<HBGlyphID16> &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<Types, EntryData> 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) && + hb_barrier () && + ligAction && component && ligature); + } + + protected: + StateTable<Types, EntryData> + machine; + NNOffsetTo<UnsizedArrayOf<HBUINT32>, HBUINT> + ligAction; /* Offset to the ligature action table. */ + NNOffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT> + component; /* Offset to the component table. */ + NNOffsetTo<UnsizedArrayOf<HBGlyphID16>, HBUINT> + ligature; /* Offset to the actual ligature lists. */ + public: + DEFINE_SIZE_STATIC (28); +}; + +template <typename Types> +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<HBGlyphID16> substitute; + public: + DEFINE_SIZE_MIN (2); +}; + +template <typename Types> +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<Types, EntryData> *driver HB_UNUSED, + const Entry<EntryData> &entry) + { + return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && + (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); + } + void transition (StateTableDriver<Types, EntryData> *driver, + const Entry<EntryData> &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; + hb_barrier (); + + 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; + hb_barrier (); + + 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<HBGlyphID16> &insertionAction; + }; + + bool apply (hb_aat_apply_context_t *c) const + { + TRACE_APPLY (this); + + driver_context_t dc (this, c); + + StateTableDriver<Types, EntryData> 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) && + hb_barrier () && + insertionAction); + } + + protected: + StateTable<Types, EntryData> + machine; + NNOffsetTo<UnsizedArrayOf<HBGlyphID16>, 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 <typename Types> +struct ChainSubtable +{ + typedef typename Types::HBUINT HBUINT; + + template <typename T> + 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, typename ...Ts> + 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<Ts> (ds)...)); + case Contextual: return_trace (c->dispatch (u.contextual, std::forward<Ts> (ds)...)); + case Ligature: return_trace (c->dispatch (u.ligature, std::forward<Ts> (ds)...)); + case Noncontextual: return_trace (c->dispatch (u.noncontextual, std::forward<Ts> (ds)...)); + case Insertion: return_trace (c->dispatch (u.insertion, std::forward<Ts> (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) && + hb_barrier () && + 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<Types> rearrangement; + ContextualSubtable<Types> contextual; + LigatureSubtable<Types> ligature; + NoncontextualSubtable<Types> noncontextual; + InsertionSubtable<Types> insertion; + } u; + public: + DEFINE_SIZE_MIN (2 * sizeof (HBUINT) + 4); +}; + +template <typename Types> +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<Types> *subtable = &StructAfter<ChainSubtable<Types>> (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<Types>::AllDirections) && + HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != + bool (subtable->get_coverage() & ChainSubtable<Types>::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<Types>::Logical ? + bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) : + bool (subtable->get_coverage () & ChainSubtable<Types>::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<ChainSubtable<Types>> (*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) && + hb_barrier () && + length >= min_size && + c->check_range (this, length))) + return_trace (false); + + if (!c->check_array (featureZ.arrayZ, featureCount)) + return_trace (false); + + const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount)); + unsigned int count = subtableCount; + for (unsigned int i = 0; i < count; i++) + { + if (!subtable->sanitize (c)) + return_trace (false); + hb_barrier (); + subtable = &StructAfter<ChainSubtable<Types>> (*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<Feature> 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 <typename Types, hb_tag_t TAG> +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<Types> *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<Types>> (*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<Types> *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<Types>> (*chain); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(version.sanitize (c) && + hb_barrier () && + version && + chainCount.sanitize (c))) + return_trace (false); + + const Chain<Types> *chain = &firstChain; + unsigned int count = chainCount; + for (unsigned int i = 0; i < count; i++) + { + if (!chain->sanitize (c, version)) + return_trace (false); + hb_barrier (); + chain = &StructAfter<Chain<Types>> (*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<Types> firstChain; /* Chains. */ + + public: + DEFINE_SIZE_MIN (8); +}; + +struct morx : mortmorx<ExtendedTypes, HB_AAT_TAG_morx> {}; +struct mort : mortmorx<ObsoleteTypes, HB_AAT_TAG_mort> {}; + + +} /* namespace AAT */ + + +#endif /* HB_AAT_LAYOUT_MORX_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-aat-layout-opbd-table.hh b/gfx/harfbuzz/src/hb-aat-layout-opbd-table.hh new file mode 100644 index 0000000000..9840d3a554 --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-opbd-table.hh @@ -0,0 +1,174 @@ +/* + * 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<OpticalBounds> *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<Offset16To<OpticalBounds>> + 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<OpticalBounds> *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<Offset16To<OpticalBounds>> + 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); + hb_barrier (); + + 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/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh b/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh new file mode 100644 index 0000000000..345a236e95 --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-layout-trak-table.hh @@ -0,0 +1,232 @@ +/* + * 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<UnsizedArrayOf<FWORD>> + 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<const F16DOT16> 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<const F16DOT16> 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) && + hb_barrier () && + 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<UnsizedArrayOf<F16DOT16>> + sizeTable; /* Offset from start of the tracking table to + * Array[nSizes] of size values.. */ + UnsizedArrayOf<TrackTableEntry> + 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) && + hb_barrier () && + 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<TrackData> + horizData; /* Offset from start of tracking table to TrackData + * for horizontal text (or 0 if none). */ + Offset16To<TrackData> + 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/gfx/harfbuzz/src/hb-aat-layout.cc b/gfx/harfbuzz/src/hb-aat-layout.cc new file mode 100644 index 0000000000..5e4cea2224 --- /dev/null +++ b/gfx/harfbuzz/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>Note: does not examine the `GSUB` table.</note> + * + * 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<AAT::morx> (); + 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<AAT::mort> (); + 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>Note: does not examine the `GPOS` table.</note> + * + * 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::kerx> (); + + 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/gfx/harfbuzz/src/hb-aat-layout.h b/gfx/harfbuzz/src/hb-aat-layout.h new file mode 100644 index 0000000000..c682a2f6d7 --- /dev/null +++ b/gfx/harfbuzz/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 <hb-aat.h> 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_CURSIVE_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_CURSIVE_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/gfx/harfbuzz/src/hb-aat-layout.hh b/gfx/harfbuzz/src/hb-aat-layout.hh new file mode 100644 index 0000000000..15c382aa92 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-aat-ltag-table.hh b/gfx/harfbuzz/src/hb-aat-ltag-table.hh new file mode 100644 index 0000000000..c974025d44 --- /dev/null +++ b/gfx/harfbuzz/src/hb-aat-ltag-table.hh @@ -0,0 +1,95 @@ +/* + * 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) && + hb_barrier () && + (base+tag).sanitize (c, length)); + } + + protected: + NNOffset16To<UnsizedArrayOf<HBUINT8>> + 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) && + hb_barrier () && + version >= 1 && + tagRanges.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Table version; currently 1 */ + HBUINT32 flags; /* Table flags; currently none defined */ + Array32Of<FTStringRange> + tagRanges; /* Range for each tag's string */ + public: + DEFINE_SIZE_ARRAY (12, tagRanges); +}; + +} /* namespace AAT */ + + +#endif /* HB_AAT_LTAG_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-aat-map.cc b/gfx/harfbuzz/src/hb-aat-map.cc new file mode 100644 index 0000000000..5bdb8004f2 --- /dev/null +++ b/gfx/harfbuzz/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_event_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<feature_info_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/gfx/harfbuzz/src/hb-aat-map.hh b/gfx/harfbuzz/src/hb-aat-map.hh new file mode 100644 index 0000000000..cb22ffee42 --- /dev/null +++ b/gfx/harfbuzz/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<hb_sorted_vector_t<range_flags_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<feature_range_t> features; + hb_sorted_vector_t<feature_info_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/gfx/harfbuzz/src/hb-aat.h b/gfx/harfbuzz/src/hb-aat.h new file mode 100644 index 0000000000..c14313d1e2 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-algs.hh b/gfx/harfbuzz/src/hb-algs.hh new file mode 100644 index 0000000000..ea97057165 --- /dev/null +++ b/gfx/harfbuzz/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 <algorithm> +#include <initializer_list> +#include <functional> +#include <new> + +/* + * 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 <typename Type, int Bytes = sizeof (Type)> +struct BEInt; +template <typename Type> +struct BEInt<Type, 1> +{ + public: + BEInt () = default; + constexpr BEInt (Type V) : v {uint8_t (V)} {} + constexpr operator Type () const { return v; } + private: uint8_t v; +}; +template <typename Type> +struct BEInt<Type, 2> +{ + 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 <typename Type> +struct BEInt<Type, 3> +{ + static_assert (!std::is_signed<Type>::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 <typename Type> +struct BEInt<Type, 4> +{ + 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 <typename T> constexpr auto + operator () (T&& v) const HB_AUTO_RETURN ( std::forward<T> (v) ) +} +HB_FUNCOBJ (hb_identity); +struct +{ + /* Like identity(), but only retains lvalue-references. Rvalues are returned as rvalues. */ + template <typename T> constexpr T& + operator () (T& v) const { return v; } + + template <typename T> constexpr hb_remove_reference<T> + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_lidentity); +struct +{ + /* Like identity(), but always returns rvalue. */ + template <typename T> constexpr hb_remove_reference<T> + operator () (T&& v) const { return v; } +} +HB_FUNCOBJ (hb_ridentity); + +struct +{ + template <typename T> constexpr bool + operator () (T&& v) const { return bool (std::forward<T> (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 <typename T> 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 <typename T, + hb_enable_if (std::is_integral<T>::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 <typename T, + hb_enable_if (std::is_integral<T>::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 <typename T, + hb_enable_if (std::is_floating_point<T>::value)> constexpr auto + impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, fasthash32 (std::addressof (v), sizeof (T), 0xf437ffe6)) + + template <typename T> constexpr auto + impl (const T& v, hb_priority<0>) const HB_RETURN (uint32_t, std::hash<hb_decay<decltype (hb_deref (v))>>{} (hb_deref (v))) + + public: + + template <typename T> 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 <typename Appl, typename T, typename ...Ts> auto + impl (Appl&& a, hb_priority<2>, T &&v, Ts&&... ds) const HB_AUTO_RETURN + ((hb_deref (std::forward<T> (v)).*std::forward<Appl> (a)) (std::forward<Ts> (ds)...)) + + /* Pointer-to-member. */ + template <typename Appl, typename T> auto + impl (Appl&& a, hb_priority<1>, T &&v) const HB_AUTO_RETURN + ((hb_deref (std::forward<T> (v))).*std::forward<Appl> (a)) + + /* Operator(). */ + template <typename Appl, typename ...Ts> auto + impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN + (hb_deref (std::forward<Appl> (a)) (std::forward<Ts> (ds)...)) + + public: + + template <typename Appl, typename ...Ts> auto + operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN + ( + impl (std::forward<Appl> (a), + hb_prioritize, + std::forward<Ts> (ds)...) + ) +} +HB_FUNCOBJ (hb_invoke); + +template <unsigned Pos, typename Appl, typename V> +struct hb_partial_t +{ + hb_partial_t (Appl a, V v) : a (a), v (v) {} + + static_assert (Pos > 0, ""); + + template <typename ...Ts, + unsigned P = Pos, + hb_enable_if (P == 1)> auto + operator () (Ts&& ...ds) -> decltype (hb_invoke (hb_declval (Appl), + hb_declval (V), + hb_declval (Ts)...)) + { + return hb_invoke (std::forward<Appl> (a), + std::forward<V> (v), + std::forward<Ts> (ds)...); + } + template <typename T0, typename ...Ts, + unsigned P = Pos, + hb_enable_if (P == 2)> 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<Appl> (a), + std::forward<T0> (d0), + std::forward<V> (v), + std::forward<Ts> (ds)...); + } + + private: + hb_reference_wrapper<Appl> a; + V v; +}; +template <unsigned Pos=1, typename Appl, typename V> +auto hb_partial (Appl&& a, V&& v) HB_AUTO_RETURN +(( hb_partial_t<Pos, Appl, V> (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 <typename _T> \ + decltype(auto) operator () (_T&& _v) const \ + { return hb_partial<Pos> (this, std::forward<_T> (_v)); } \ + static_assert (true, "") +#else +/* https://github.com/harfbuzz/harfbuzz/issues/1724 */ +#define HB_PARTIALIZE(Pos) \ + template <typename _T> \ + auto operator () (_T&& _v) const HB_AUTO_RETURN \ + (hb_partial<Pos> (+this, std::forward<_T> (_v))) \ + static_assert (true, "") +#endif + + +struct +{ + private: + + template <typename Pred, typename Val> auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_deref (std::forward<Pred> (p)).has (std::forward<Val> (v)) + ) + + template <typename Pred, typename Val> auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward<Pred> (p), + std::forward<Val> (v)) + ) + + public: + + template <typename Pred, typename Val> auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward<Pred> (p), + std::forward<Val> (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_has); + +struct +{ + private: + + template <typename Pred, typename Val> auto + impl (Pred&& p, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_has (std::forward<Pred> (p), + std::forward<Val> (v)) + ) + + template <typename Pred, typename Val> auto + impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward<Pred> (p) == std::forward<Val> (v) + ) + + public: + + template <typename Pred, typename Val> auto + operator () (Pred&& p, Val &&v) const HB_RETURN (bool, + impl (std::forward<Pred> (p), + std::forward<Val> (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_match); + +struct +{ + private: + + template <typename Proj, typename Val> auto + impl (Proj&& f, Val &&v, hb_priority<2>) const HB_AUTO_RETURN + ( + hb_deref (std::forward<Proj> (f)).get (std::forward<Val> (v)) + ) + + template <typename Proj, typename Val> auto + impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN + ( + hb_invoke (std::forward<Proj> (f), + std::forward<Val> (v)) + ) + + template <typename Proj, typename Val> auto + impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward<Proj> (f)[std::forward<Val> (v)] + ) + + public: + + template <typename Proj, typename Val> auto + operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN + ( + impl (std::forward<Proj> (f), + std::forward<Val> (v), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_get); + +struct +{ + private: + + template <typename T1, typename T2> auto + impl (T1&& v1, T2 &&v2, hb_priority<3>) const HB_AUTO_RETURN + ( + std::forward<T2> (v2).cmp (std::forward<T1> (v1)) == 0 + ) + + template <typename T1, typename T2> auto + impl (T1&& v1, T2 &&v2, hb_priority<2>) const HB_AUTO_RETURN + ( + std::forward<T1> (v1).cmp (std::forward<T2> (v2)) == 0 + ) + + template <typename T1, typename T2> auto + impl (T1&& v1, T2 &&v2, hb_priority<1>) const HB_AUTO_RETURN + ( + std::forward<T1> (v1) == std::forward<T2> (v2) + ) + + template <typename T1, typename T2> auto + impl (T1&& v1, T2 &&v2, hb_priority<0>) const HB_AUTO_RETURN + ( + std::forward<T2> (v2) == std::forward<T1> (v1) + ) + + public: + + template <typename T1, typename T2> auto + operator () (T1&& v1, T2 &&v2) const HB_AUTO_RETURN + ( + impl (std::forward<T1> (v1), + std::forward<T2> (v2), + hb_prioritize) + ) +} +HB_FUNCOBJ (hb_equal); + +struct +{ + template <typename T> void + operator () (T& a, T& b) const + { + using std::swap; // allow ADL + swap (a, b); + } +} +HB_FUNCOBJ (hb_swap); + + +template <typename T1, typename T2> +struct hb_pair_t +{ + typedef T1 first_t; + typedef T2 second_t; + typedef hb_pair_t<T1, T2> pair_t; + + template <typename U1 = T1, typename U2 = T2, + hb_enable_if (std::is_default_constructible<U1>::value && + std::is_default_constructible<U2>::value)> + hb_pair_t () : first (), second () {} + hb_pair_t (T1 a, T2 b) : first (std::forward<T1> (a)), second (std::forward<T2> (b)) {} + + template <typename Q1, typename Q2, + hb_enable_if (hb_is_convertible (T1, Q1) && + hb_is_convertible (T2, Q2))> + operator hb_pair_t<Q1, Q2> () { return hb_pair_t<Q1, Q2> (first, second); } + + hb_pair_t<T1, T2> reverse () const + { return hb_pair_t<T1, T2> (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 <typename T1, typename T2> static inline hb_pair_t<T1, T2> +hb_pair (T1&& a, T2&& b) { return hb_pair_t<T1, T2> (a, b); } + +typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t; + +struct +{ + template <typename Pair> constexpr typename Pair::first_t + operator () (const Pair& pair) const { return pair.first; } +} +HB_FUNCOBJ (hb_first); + +struct +{ + template <typename Pair> 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<T> 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 <typename T, typename T2> constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a <= b ? a : b) +} +HB_FUNCOBJ (hb_min); +struct +{ + template <typename T, typename T2> constexpr auto + operator () (T&& a, T2&& b) const HB_AUTO_RETURN + (a >= b ? a : b) +} +HB_FUNCOBJ (hb_max); +struct +{ + template <typename T, typename T2, typename T3> constexpr auto + operator () (T&& x, T2&& min, T3&& max) const HB_AUTO_RETURN + (hb_min (hb_max (std::forward<T> (x), std::forward<T2> (min)), std::forward<T3> (max))) +} +HB_FUNCOBJ (hb_clamp); + +/* + * Bithacks. + */ + +/* Return the number of 1 bits in v. */ +template <typename T> +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> ((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 <typename T> +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> ((uint64_t) (v >> shift)) + shift : + hb_bit_storage<uint64_t> ((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 <typename T> +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> ((uint64_t) v) : + hb_bit_storage<uint64_t> ((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 <typename Type, unsigned int n> +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 <typename T> static inline bool +hb_in_range (T u, T lo, T hi) +{ + static_assert (!std::is_signed<T>::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 <typename T> static inline bool +hb_in_ranges (T u, T lo1, T hi1) +{ + return hb_in_range (u, lo1, hi1); +} +template <typename T, typename ...Ts> static inline bool +hb_in_ranges (T u, T lo1, T hi1, Ts... ds) +{ + return hb_in_range<T> (u, lo1, hi1) || hb_in_ranges<T> (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 <typename K, typename V, typename ...Ts> +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 <typename V, typename K, typename ...Ts> +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 <typename V, typename K> +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<K, V>) +{ + 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 <typename V, typename K, typename ...Ts> +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 <typename ...Ts> +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 <typename ...Ts> +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<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));} + printf("\n"); */ + + if(nel < 10) { + /* Insertion sort for arbitrarily small inputs */ + char *pi, *pj; + for(pi = b+w; pi < end; pi += w) { + for(pj = pi; pj > b && sort_r_cmpswap(pj-w,pj,w,compar,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<nel; i++) {printf("%4i", *(int*)(b + i*sizeof(int)));} + printf("\n");*/ + + sort_r_simple(b, (pl-ple)/w, w, compar, ds...); + sort_r_simple(end-(pre-pr), (pre-pr)/w, w, compar, ds...); + } +} + +static inline void +hb_qsort (void *base, size_t nel, size_t width, + int (*compar)(const void *_a, const void *_b)) +{ +#if defined(__OPTIMIZE_SIZE__) && !defined(HB_USE_INTERNAL_QSORT) + qsort (base, nel, width, compar); +#else + sort_r_simple (base, nel, width, compar); +#endif +} + +static inline void +hb_qsort (void *base, size_t nel, size_t width, + int (*compar)(const void *_a, const void *_b, void *_arg), + void *arg) +{ +#ifdef HAVE_GNU_QSORT_R + qsort_r (base, nel, width, compar, arg); +#else + sort_r_simple (base, nel, width, compar, arg); +#endif +} + + +template <typename T, typename T2, typename T3 = int> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> constexpr auto + operator () (const T &a, const T &b) const HB_AUTO_RETURN (a | ~b) +} +HB_FUNCOBJ (hb_bitwise_ge); +struct +{ + template <typename T> constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (~a) +} +HB_FUNCOBJ (hb_bitwise_neg); + +struct +{ HB_PARTIALIZE(2); + template <typename T, typename T2> constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a + b) +} +HB_FUNCOBJ (hb_add); +struct +{ HB_PARTIALIZE(2); + template <typename T, typename T2> constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a - b) +} +HB_FUNCOBJ (hb_sub); +struct +{ HB_PARTIALIZE(2); + template <typename T, typename T2> constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (b - a) +} +HB_FUNCOBJ (hb_rsub); +struct +{ HB_PARTIALIZE(2); + template <typename T, typename T2> constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a * b) +} +HB_FUNCOBJ (hb_mul); +struct +{ HB_PARTIALIZE(2); + template <typename T, typename T2> constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a / b) +} +HB_FUNCOBJ (hb_div); +struct +{ HB_PARTIALIZE(2); + template <typename T, typename T2> constexpr auto + operator () (const T &a, const T2 &b) const HB_AUTO_RETURN (a % b) +} +HB_FUNCOBJ (hb_mod); +struct +{ + template <typename T> constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (+a) +} +HB_FUNCOBJ (hb_pos); +struct +{ + template <typename T> constexpr auto + operator () (const T &a) const HB_AUTO_RETURN (-a) +} +HB_FUNCOBJ (hb_neg); +struct +{ + template <typename T> constexpr auto + operator () (T &a) const HB_AUTO_RETURN (++a) +} +HB_FUNCOBJ (hb_inc); +struct +{ + template <typename T> 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 <typename func_t> +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/gfx/harfbuzz/src/hb-array.hh b/gfx/harfbuzz/src/hb-array.hh new file mode 100644 index 0000000000..9037179bc5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-array.hh @@ -0,0 +1,504 @@ +/* + * 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 <typename Type> +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 <typename Type> +struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&> +{ + static constexpr bool realloc_move = true; + + /* + * 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 <unsigned int length_> + constexpr hb_array_t (Type (&array_)[length_]) : hb_array_t (array_, length_) {} + + template <typename U, + hb_enable_if (hb_is_cr_convertible(U, Type))> + constexpr hb_array_t (const hb_array_t<U> &o) : + hb_iter_with_fallback_t<hb_array_t, Type&> (), + arrayZ (o.arrayZ), length (o.length), backwards_length (o.backwards_length) {} + template <typename U, + hb_enable_if (hb_is_cr_convertible(U, Type))> + hb_array_t& operator = (const hb_array_t<U> &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<const Type> () { return hb_array_t<const Type> (arrayZ, length); } + template <typename T> 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 <typename T> + Type *lsearch (const T &x, Type *not_found = nullptr) + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template <typename T> + const Type *lsearch (const T &x, const Type *not_found = nullptr) const + { + unsigned i; + return lfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template <typename T> + 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<Type> 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<Type> (*this); + } + hb_sorted_array_t<Type> 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<Type> (*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 <typename T, + unsigned P = sizeof (Type), + hb_enable_if (P == 1)> + const T *as () const + { return length < hb_min_size (T) ? &Null (T) : reinterpret_cast<const T *> (arrayZ); } + + template <typename T, + unsigned P = sizeof (Type), + hb_enable_if (P == 1)> + 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 <typename hb_serialize_context_t, + typename U = Type, + hb_enable_if (!(sizeof (U) < sizeof (long long) && hb_is_trivially_copy_assignable(hb_decay<Type>)))> + 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 <typename hb_serialize_context_t, + typename U = Type, + hb_enable_if (sizeof (U) < sizeof (long long) && hb_is_trivially_copy_assignable(hb_decay<Type>))> + 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 <typename hb_sanitize_context_t> + 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 <typename T> inline hb_array_t<T> +hb_array () +{ return hb_array_t<T> (); } +template <typename T> inline hb_array_t<T> +hb_array (T *array, unsigned int length) +{ return hb_array_t<T> (array, length); } +template <typename T, unsigned int length_> inline hb_array_t<T> +hb_array (T (&array_)[length_]) +{ return hb_array_t<T> (array_); } + +template <typename Type> +struct hb_sorted_array_t : + hb_array_t<Type>, + hb_iter_t<hb_sorted_array_t<Type>, Type&> +{ + typedef hb_iter_t<hb_sorted_array_t, Type&> 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<Type> (array_, length_) {} + template <unsigned int length_> + constexpr hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {} + + template <typename U, + hb_enable_if (hb_is_cr_convertible(U, Type))> + constexpr hb_sorted_array_t (const hb_array_t<U> &o) : + hb_array_t<Type> (o), + hb_iter_t<hb_sorted_array_t, Type&> () {} + template <typename U, + hb_enable_if (hb_is_cr_convertible(U, Type))> + hb_sorted_array_t& operator = (const hb_array_t<U> &o) + { hb_array_t<Type> (*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<Type> *) (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 <typename T> + Type *bsearch (const T &x, Type *not_found = nullptr) + { + unsigned int i; + return bfind (x, &i) ? &this->arrayZ[i] : not_found; + } + template <typename T> + 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 <typename T> + 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 <typename T, typename ...Ts> + 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<T, Type, Ts...>, + std::forward<Ts> (ds)...); + } +}; +template <typename T> inline hb_sorted_array_t<T> +hb_sorted_array (T *array, unsigned int length) +{ return hb_sorted_array_t<T> (array, length); } +template <typename T, unsigned int length_> inline hb_sorted_array_t<T> +hb_sorted_array (T (&array_)[length_]) +{ return hb_sorted_array_t<T> (array_); } + +template <typename T> +inline bool hb_array_t<T>::operator == (const hb_array_t<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<const char>::operator == (const hb_array_t<const char> &o) const +{ + if (o.length != this->length) return false; + return 0 == hb_memcmp (arrayZ, o.arrayZ, length); +} +template <> +inline bool hb_array_t<const unsigned char>::operator == (const hb_array_t<const unsigned char> &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<const char>::hash () const +{ + // https://github.com/harfbuzz/harfbuzz/pull/4228 + return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */); +} + +template <> +inline uint32_t hb_array_t<const unsigned char>::hash () const +{ + // https://github.com/harfbuzz/harfbuzz/pull/4228 + return fasthash32(arrayZ, length, 0xf437ffe6 /* magic? */); +} +#endif + + +typedef hb_array_t<const char> hb_bytes_t; +typedef hb_array_t<const unsigned char> hb_ubytes_t; + + + +#endif /* HB_ARRAY_HH */ diff --git a/gfx/harfbuzz/src/hb-atomic.hh b/gfx/harfbuzz/src/hb-atomic.hh new file mode 100644 index 0000000000..366fb32b7d --- /dev/null +++ b/gfx/harfbuzz/src/hb-atomic.hh @@ -0,0 +1,228 @@ +/* + * 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 <chris@chris-wilson.co.uk> + * 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 <atomic> + +#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<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) +#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> *> (AI)->store ((V), std::memory_order_release)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast<std::atomic<std::decay<decltype (*(AI))>::type> const *> (AI)->load (std::memory_order_acquire)) + +#define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast<std::atomic<void*> *> (P)->store ((V), std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast<std::atomic<void*> const *> (P)->load (std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get(P) (reinterpret_cast<std::atomic<void*> *> (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<std::atomic<const void*> *> (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 +static inline void _hb_compiler_memory_r_barrier () { asm volatile("": : :"memory"); } +#elif !defined(_MSC_VER) +#include <atomic> +#define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire) +#else +static inline void _hb_compiler_memory_r_barrier () {} +#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 <typename P> +struct hb_atomic_ptr_t +{ + typedef hb_remove_pointer<P> 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 <typename C> operator C * () const { return get_acquire (); } + + T *v = nullptr; +}; + +static inline bool hb_barrier () +{ + _hb_compiler_memory_r_barrier (); + return true; +} + + +#endif /* HB_ATOMIC_HH */ diff --git a/gfx/harfbuzz/src/hb-bimap.hh b/gfx/harfbuzz/src/hb-bimap.hh new file mode 100644 index 0000000000..f541472544 --- /dev/null +++ b/gfx/harfbuzz/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 <hb_codepoint_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<hb_codepoint_t> back_map; + + public: + auto keys () const HB_AUTO_RETURN (+ back_map.iter()) +}; + +#endif /* HB_BIMAP_HH */ diff --git a/gfx/harfbuzz/src/hb-bit-page.hh b/gfx/harfbuzz/src/hb-bit-page.hh new file mode 100644 index 0000000000..869c678957 --- /dev/null +++ b/gfx/harfbuzz/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 <typename elt_t, unsigned int byte_size> +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 <typename Op> + 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 <typename Op> + 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<const elt_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<elt_t, PAGE_BITS / 8> 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/gfx/harfbuzz/src/hb-bit-set-invertible.hh b/gfx/harfbuzz/src/hb-bit-set-invertible.hh new file mode 100644 index 0000000000..2626251807 --- /dev/null +++ b/gfx/harfbuzz/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 <typename T> + 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 <typename T> + void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template <typename T> + 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 <typename T> + bool add_sorted_array (const hb_sorted_array_t<const 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 <typename Op> + 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<iter_t, hb_codepoint_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 (likely (l)) l--; } + void __prev__ () { s->previous (&v); l++; } + 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/gfx/harfbuzz/src/hb-bit-set.hh b/gfx/harfbuzz/src/hb-bit-set.hh new file mode 100644 index 0000000000..1dbcce5cbd --- /dev/null +++ b/gfx/harfbuzz/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_t> page_map; + hb_vector_t<page_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<typename Type> + 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 Type*> ((const char *) P + offset); +#pragma GCC diagnostic pop + } + + template <typename T> + 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<T> (array, stride); + count--; + } + while (count && (g = *array, start <= g && g < end)); + } + } + + template <typename T> + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (true, array, count, stride); } + template <typename T> + void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); } + + template <typename T> + void del_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { set_array (false, array, count, stride); } + template <typename T> + void del_array (const hb_array_t<const T>& arr) { del_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template <typename T> + 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<T> (array, stride); + count--; + } + while (count && (g = *array, g < end)); + } + return true; + } + + template <typename T> + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (true, array, count, stride); } + template <typename T> + bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); } + + template <typename T> + bool del_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return set_sorted_array (false, array, count, stride); } + template <typename T> + bool del_sorted_array (const hb_sorted_array_t<const 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<unsigned> 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<unsigned>& 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<unsigned>& workspace, + unsigned int length) + { + assert(workspace.length == pages.length); + hb_vector_t<unsigned>& 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<unsigned>& 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<unsigned> 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 <typename Op> + 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 <typename Op> + void process (const Op& op, const hb_bit_set_t &other) + { + process_ (op_<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<page_map.length && size; i++) + { + uint32_t base = major_start (page_map[i].major); + unsigned int n = pages[page_map[i].index].write_inverted (base, start_page_value, out, size, &next_value); + out += n; + size -= n; + start_page_value = 0; + } + while (next_value < HB_SET_VALUE_INVALID && size) { + *out++ = next_value++; + size--; + } + return initial_size - size; + } + + bool has_population () const { return population != UINT_MAX; } + unsigned int get_population () const + { + if (has_population ()) + return population; + + unsigned int pop = 0; + unsigned int count = pages.length; + for (unsigned int i = 0; i < count; i++) + pop += pages[i].get_population (); + + population = pop; + return pop; + } + hb_codepoint_t get_min () const + { + unsigned count = pages.length; + for (unsigned i = 0; i < count; i++) + { + const auto& map = page_map[i]; + const auto& page = pages[map.index]; + + if (!page.is_empty ()) + return map.major * page_t::PAGE_BITS + page.get_min (); + } + return INVALID; + } + hb_codepoint_t get_max () const + { + unsigned count = pages.length; + for (signed i = count - 1; 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<iter_t, hb_codepoint_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/gfx/harfbuzz/src/hb-blob.cc b/gfx/harfbuzz/src/hb-blob.cc new file mode 100644 index 0000000000..265effba03 --- /dev/null +++ b/gfx/harfbuzz/src/hb-blob.cc @@ -0,0 +1,775 @@ +/* + * Copyright © 2009 Red Hat, 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-blob.hh" + +#ifdef HAVE_SYS_MMAN_H +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#include <sys/mman.h> +#endif /* HAVE_SYS_MMAN_H */ + + +/** + * 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. + **/ + + +/** + * hb_blob_create: (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. + * + * Return value: New blob, or the empty blob if something failed or if @length is + * zero. Destroy with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create (const char *data, + unsigned int length, + hb_memory_mode_t mode, + void *user_data, + hb_destroy_func_t destroy) +{ + 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<hb_blob_t> ())) + { + if (destroy) + destroy (user_data); + return nullptr; + } + + blob->data = data; + blob->length = length; + blob->mode = mode; + + blob->user_data = user_data; + blob->destroy = destroy; + + if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { + blob->mode = HB_MEMORY_MODE_READONLY; + if (!blob->try_make_writable ()) + { + hb_blob_destroy (blob); + return nullptr; + } + } + + return blob; +} + +static void +_hb_blob_destroy (void *data) +{ + hb_blob_destroy ((hb_blob_t *) data); +} + +/** + * hb_blob_create_sub_blob: + * @parent: Parent blob. + * @offset: Start offset of sub-blob within @parent, in bytes. + * @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 + * 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. + * + * Makes @parent immutable. + * + * Return value: New blob, or the empty blob if something failed or if + * @length is zero or @offset is beyond the end of @parent's data. Destroy + * with hb_blob_destroy(). + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_create_sub_blob (hb_blob_t *parent, + unsigned int offset, + unsigned int length) +{ + hb_blob_t *blob; + + if (!length || !parent || offset >= parent->length) + return hb_blob_get_empty (); + + hb_blob_make_immutable (parent); + + blob = hb_blob_create (parent->data + offset, + hb_min (length, parent->length - offset), + HB_MEMORY_MODE_READONLY, + hb_blob_reference (parent), + _hb_blob_destroy); + + 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: + * + * Returns the singleton empty blob. + * + * See TODO:link object types for more information. + * + * Return value: (transfer full): The empty blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_get_empty () +{ + return const_cast<hb_blob_t *> (&Null (hb_blob_t)); +} + +/** + * hb_blob_reference: (skip) + * @blob: a blob. + * + * Increases the reference count on @blob. + * + * See TODO:link object types for more information. + * + * Return value: @blob. + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_blob_reference (hb_blob_t *blob) +{ + return hb_object_reference (blob); +} + +/** + * hb_blob_destroy: (skip) + * @blob: a blob. + * + * 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. + * + * See TODO:link object types for more information. + * + * Since: 0.9.2 + **/ +void +hb_blob_destroy (hb_blob_t *blob) +{ + if (!hb_object_destroy (blob)) return; + + hb_free (blob); +} + +/** + * hb_blob_set_user_data: (skip) + * @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 + * + * Attaches a user-data key/data pair to the specified blob. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (blob, key, data, destroy, replace); +} + +/** + * hb_blob_get_user_data: (skip) + * @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): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_blob_get_user_data (const hb_blob_t *blob, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (blob, key); +} + + +/** + * hb_blob_make_immutable: + * @blob: a blob + * + * Makes a blob immutable. + * + * Since: 0.9.2 + **/ +void +hb_blob_make_immutable (hb_blob_t *blob) +{ + if (hb_object_is_immutable (blob)) + return; + + hb_object_make_immutable (blob); +} + +/** + * hb_blob_is_immutable: + * @blob: a blob. + * + * Tests whether a blob is immutable. + * + * 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 hb_object_is_immutable (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. + * + * Since: 0.9.2 + **/ +unsigned int +hb_blob_get_length (hb_blob_t *blob) +{ + return blob->length; +} + +/** + * hb_blob_get_data: + * @blob: a blob. + * @length: (out): The length in bytes of the data retrieved + * + * Fetches the data from a blob. + * + * Returns: (nullable) (transfer none) (array length=length): the byte data of @blob. + * + * Since: 0.9.2 + **/ +const char * +hb_blob_get_data (hb_blob_t *blob, unsigned int *length) +{ + if (length) + *length = blob->length; + + return blob->data; +} + +/** + * hb_blob_get_data_writable: + * @blob: a blob. + * @length: (out): output length of the writable data. + * + * Tries to make blob data writable (possibly copying it) and + * return pointer to data. + * + * Fails if blob has been made immutable, or if memory allocation + * fails. + * + * Returns: (transfer none) (array length=length): Writable blob data, + * or `NULL` if failed. + * + * Since: 0.9.2 + **/ +char * +hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) +{ + if (hb_object_is_immutable (blob) || + !blob->try_make_writable ()) + { + if (length) *length = 0; + return nullptr; + } + + if (length) *length = blob->length; + return const_cast<char *> (blob->data); +} + + +bool +hb_blob_t::try_make_writable_inplace_unix () +{ +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) + uintptr_t pagesize = -1, mask, length; + const char *addr; + +#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); +#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) + pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); +#elif defined(HAVE_GETPAGESIZE) + pagesize = (uintptr_t) getpagesize (); +#endif + + if ((uintptr_t) -1L == pagesize) { + DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno)); + return false; + } + DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize); + + mask = ~(pagesize-1); + 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, this, "mprotect failed: %s", strerror (errno)); + return false; + } + + this->mode = HB_MEMORY_MODE_WRITABLE; + + DEBUG_MSG_FUNC (BLOB, this, + "successfully made [%p..%p] (%lu bytes) writable\n", + addr, addr+length, (unsigned long) length); + return true; +#else + return false; +#endif +} + +bool +hb_blob_t::try_make_writable_inplace () +{ + DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n"); + + if (this->try_make_writable_inplace_unix ()) + return true; + + DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n"); + + /* Failed to make writable inplace, mark that */ + this->mode = HB_MEMORY_MODE_READONLY; + return false; +} + +bool +hb_blob_t::try_make_writable () +{ + if (unlikely (!length)) + mode = HB_MEMORY_MODE_WRITABLE; + + if (this->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ()) + return true; + + if (this->mode == HB_MEMORY_MODE_WRITABLE) + return true; + + + DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data); + + char *new_data; + + new_data = (char *) hb_malloc (this->length); + if (unlikely (!new_data)) + return false; + + DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); + + 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 <sys/paths.h> +# endif +# include <sys/types.h> +# include <sys/stat.h> +# include <fcntl.h> +#endif + +#ifdef _WIN32 +# include <windows.h> +#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/gfx/harfbuzz/src/hb-blob.h b/gfx/harfbuzz/src/hb-blob.h new file mode 100644 index 0000000000..db50067e16 --- /dev/null +++ b/gfx/harfbuzz/src/hb-blob.h @@ -0,0 +1,160 @@ +/* + * Copyright © 2009 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): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_BLOB_H +#define HB_BLOB_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * 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, @HB_MEMORY_MODE_DUPLICATE should be used + * such that HarfBuzz makes a copy immediately, + * + * - Use @HB_MEMORY_MODE_READONLY otherwise, unless you really really + * really know what you are doing, + * + * - @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 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, + HB_MEMORY_MODE_WRITABLE, + 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 * +hb_blob_create (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_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 + * modify the parent data as that data may be + * shared among multiple sub-blobs. + */ +HB_EXTERN hb_blob_t * +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); + +HB_EXTERN hb_blob_t * +hb_blob_reference (hb_blob_t *blob); + +HB_EXTERN void +hb_blob_destroy (hb_blob_t *blob); + +HB_EXTERN hb_bool_t +hb_blob_set_user_data (hb_blob_t *blob, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_blob_get_user_data (const hb_blob_t *blob, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_blob_make_immutable (hb_blob_t *blob); + +HB_EXTERN hb_bool_t +hb_blob_is_immutable (hb_blob_t *blob); + + +HB_EXTERN unsigned int +hb_blob_get_length (hb_blob_t *blob); + +HB_EXTERN const char * +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/gfx/harfbuzz/src/hb-blob.hh b/gfx/harfbuzz/src/hb-blob.hh new file mode 100644 index 0000000000..b1b3b94d3d --- /dev/null +++ b/gfx/harfbuzz/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 <typename Type> + const Type* as () const { return as_bytes ().as<Type> (); } + + 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 <typename P> +struct hb_blob_ptr_t +{ + typedef hb_remove_pointer<P> 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 <typename C> operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + const T * get () const { return b->as<T> (); } + 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<hb_blob_t> b; +}; + + +#endif /* HB_BLOB_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh b/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh new file mode 100644 index 0000000000..1deaaafd87 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-json.hh @@ -0,0 +1,795 @@ + +#line 1 "hb-buffer-deserialize-json.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_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_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, 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, 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, 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[] = { + 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, 3, 3, 3, + 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, 8, 9, 1, + 1, 1, 1, 1, 1, 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, 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, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 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, 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, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 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, 1, 1, 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, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 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, 28, 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, 1, + 1, 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, 1, 1, 1, 1, 29, + 1, 1, 1, 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, 35, 1, 1, 1, + 1, 1, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 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, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 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, 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, + 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, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 43, + 1, 1, 1, 1, 1, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 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, 1, 49, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 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, 50, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 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, 1, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 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, 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, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 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, 1, + 59, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 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, 60, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 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, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 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, 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, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 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, 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, + 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, 74, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 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, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 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, 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, 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 = 56; +static const int deserialize_json_error = 0; + +static const int deserialize_json_en_main = 1; + + +#line 111 "hb-buffer-deserialize-json.rl" + + +static hb_bool_t +_hb_buffer_deserialize_json (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, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + *end_ptr = ++p; + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + +#line 559 "hb-buffer-deserialize-json.hh" + { + cs = deserialize_json_start; + } + +#line 564 "hb-buffer-deserialize-json.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_json_trans_keys + (cs<<1); + _inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs]; + + _slen = _deserialize_json_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=(*p) && + (*p) <= _keys[1] ? + (*p) - _keys[0] : _slen ]; + + cs = _deserialize_json_trans_targs[_trans]; + + if ( _deserialize_json_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _deserialize_json_trans_actions[_trans] ) { + case 1: +#line 38 "hb-buffer-deserialize-json.rl" + { + 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 (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 2: +#line 51 "hb-buffer-deserialize-json.rl" + { + tok = p; +} + break; + 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+1, p - tok - 2, /* Skip "" */ + &info.codepoint)) + return false; +} + break; + case 20: +#line 66 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.codepoint)) return false; } + break; + case 8: +#line 67 "hb-buffer-deserialize-json.rl" + { if (!parse_uint (tok, p, &info.cluster )) return false; } + break; + case 10: +#line 68 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_offset )) return false; } + break; + case 12: +#line 69 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.y_offset )) return false; } + break; + case 3: +#line 70 "hb-buffer-deserialize-json.rl" + { if (!parse_int (tok, p, &pos.x_advance)) return false; } + break; + case 6: +#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 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 (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 9: +#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 (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 11: +#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 (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 13: +#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 (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 4: +#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 (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + case 7: +#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 (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + break; + 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: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + _out: {} + } + +#line 137 "hb-buffer-deserialize-json.rl" + + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl b/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl new file mode 100644 index 0000000000..b12dd0f1a1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-json.rl @@ -0,0 +1,144 @@ +/* + * 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_JSON_HH +#define HB_BUFFER_DESERIALIZE_JSON_HH + +#include "hb.hh" + +%%{ + +machine deserialize_json; +alphtype unsigned char; +write data; + +action clear_item { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} + +action add_item { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + +action tok { + tok = p; +} + +action ensure_glyphs { if (unlikely (!buffer->ensure_glyphs ())) return false; } +action ensure_unicode { if (unlikely (!buffer->ensure_unicode ())) return false; } + +action parse_glyph_name { + /* TODO Unescape \" and \\ if found. */ + if (!hb_font_glyph_from_string (font, + tok+1, p - tok - 2, /* Skip "" */ + &info.codepoint)) + return false; +} + +action parse_codepoint { if (!parse_uint (tok, p, &info.codepoint)) return false; } +action parse_cluster { if (!parse_uint (tok, p, &info.cluster )) return false; } +action parse_x_offset { if (!parse_int (tok, p, &pos.x_offset )) return false; } +action parse_y_offset { if (!parse_int (tok, p, &pos.y_offset )) return false; } +action parse_x_advance { if (!parse_int (tok, p, &pos.x_advance)) return false; } +action parse_y_advance { if (!parse_int (tok, p, &pos.y_advance)) return false; } +action parse_glyph_flags{ if (!parse_uint (tok, p, &info.mask )) return false; } + +unum = '0' | [1-9] digit*; +num = '-'? unum; + +comma = space* ',' space*; +colon = space* ':' space*; + +codepoint = unum; +glyph_name = '"' ([^\\"] | '\\' [\\"])* '"'; + +parse_glyph_name = (glyph_name >tok %parse_glyph_name); +parse_codepoint = (codepoint >tok %parse_codepoint); + +glyph = "\"g\"" colon (parse_glyph_name | parse_codepoint); +unicode = "\"u\"" colon parse_codepoint; +cluster = "\"cl\"" colon (unum >tok %parse_cluster); +xoffset = "\"dx\"" colon (num >tok %parse_x_offset); +yoffset = "\"dy\"" colon (num >tok %parse_y_offset); +xadvance= "\"ax\"" colon (num >tok %parse_x_advance); +yadvance= "\"ay\"" colon (num >tok %parse_y_advance); +glyphflags="\"fl\"" colon (unum >tok %parse_glyph_flags); + +element = glyph @ensure_glyphs + | unicode @ensure_unicode + | cluster + | xoffset + | yoffset + | xadvance + | yadvance + | glyphflags; +item = + ( '{' space* element (comma element)* space* '}') + >clear_item + @add_item + ; + +main := space* item (comma item)* space* (','|']'); + +}%% + +static hb_bool_t +_hb_buffer_deserialize_json (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, nullptr); + + while (p < pe && ISSPACE (*p)) + p++; + if (p < pe && *p == (buffer->len ? ',' : '[')) + *end_ptr = ++p; + + const char *tok = nullptr; + int cs; + hb_glyph_info_t info = {0}; + hb_glyph_position_t pos = {0}; + %%{ + write init; + write exec; + }%% + + *end_ptr = p; + + return p == pe && *(p-1) != ']'; +} + +#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */ diff --git a/gfx/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh b/gfx/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh new file mode 100644 index 0000000000..ea81273b31 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-buffer-deserialize-text-glyphs.rl b/gfx/harfbuzz/src/hb-buffer-deserialize-text-glyphs.rl new file mode 100644 index 0000000000..21db14b568 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-text-glyphs.rl @@ -0,0 +1,150 @@ +/* + * 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" + +%%{ + +machine deserialize_text_glyphs; +alphtype unsigned char; +write data; + +action clear_item { + hb_memset (&info, 0, sizeof (info)); + hb_memset (&pos , 0, sizeof (pos )); +} + +action add_item { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + +action tok { + tok = p; +} + +action parse_glyph { + /* TODO Unescape delimiters. */ + if (!hb_font_glyph_from_string (font, + tok, p - tok, + &info.codepoint)) + return false; +} + +action parse_cluster { if (!parse_uint (tok, p, &info.cluster )) return false; } +action parse_x_offset { if (!parse_int (tok, p, &pos.x_offset )) return false; } +action parse_y_offset { if (!parse_int (tok, p, &pos.y_offset )) return false; } +action parse_x_advance { if (!parse_int (tok, p, &pos.x_advance)) return false; } +action parse_y_advance { if (!parse_int (tok, p, &pos.y_advance)) return false; } +action parse_glyph_flags{ if (!parse_uint (tok, p, &info.mask )) return false; } + +unum = '0' | [1-9] digit*; +num = '-'? unum; + +glyph_id = unum; +glyph_name = ([^\\\]=@+,#|] | '\\' [\\\]=@+,|]) *; + +glyph = (glyph_id | glyph_name) >tok %parse_glyph; +cluster = '=' (unum >tok %parse_cluster); +offsets = '@' (num >tok %parse_x_offset) ',' (num >tok %parse_y_offset ); +advances= '+' (num >tok %parse_x_advance) (',' (num >tok %parse_y_advance))?; +glyphflags= '#' (unum >tok %parse_glyph_flags); + +glyph_item = + ( + glyph + cluster? + offsets? + advances? + glyphflags? + ) + >clear_item + %add_item + ; + +glyphs = glyph_item (space* '|' space* glyph_item)* space*; + +main := space* glyphs; + +}%% + +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}; + %%{ + write init; + write exec; + }%% + + 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/gfx/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh b/gfx/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh new file mode 100644 index 0000000000..a8cdf67e73 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-buffer-deserialize-text-unicode.rl b/gfx/harfbuzz/src/hb-buffer-deserialize-text-unicode.rl new file mode 100644 index 0000000000..92873b804f --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-deserialize-text-unicode.rl @@ -0,0 +1,129 @@ +/* + * 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" + +%%{ + +machine deserialize_text_unicode; +alphtype unsigned char; +write data; + +action clear_item { + hb_memset (&info, 0, sizeof (info)); +} + +action add_item { + buffer->add_info (info); + if (unlikely (!buffer->successful)) + return false; + if (buffer->have_positions) + buffer->pos[buffer->len - 1] = pos; + *end_ptr = p; +} + +action tok { + tok = p; +} + +action parse_hexdigits {if (!parse_hex (tok, p, &info.codepoint )) return false; } + +action parse_cluster { if (!parse_uint (tok, p, &info.cluster )) return false; } + +unum = '0' | [1-9] digit*; +num = '-'? unum; + +cluster = '=' (unum >tok %parse_cluster); + +unicode = [Uu] '+'? xdigit+ >tok %parse_hexdigits; + +unicode_item = + ( + unicode + cluster? + ) + >clear_item + %add_item + ; + +unicodes = unicode_item (space* '|' space* unicode_item)* space*; + +main := space* unicodes; + +}%% + +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}; + %%{ + write init; + write exec; + }%% + + 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/gfx/harfbuzz/src/hb-buffer-serialize.cc b/gfx/harfbuzz/src/hb-buffer-serialize.cc new file mode 100644 index 0000000000..16f189519b --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer-serialize.cc @@ -0,0 +1,872 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_BUFFER_SERIALIZE + +#include "hb-buffer.hh" + + +static const char *_hb_buffer_serialize_formats[] = { + "text", + "json", + nullptr +}; + +/** + * hb_buffer_serialize_list_formats: + * + * Returns a list of supported buffer serialization formats. + * + * Return value: (transfer none): + * A string array of buffer serialization formats. Should not be freed. + * + * Since: 0.9.7 + **/ +const char ** +hb_buffer_serialize_list_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 + * + * 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: + * The parsed #hb_buffer_serialize_format_t. + * + * Since: 0.9.7 + **/ +hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len) +{ + /* Upper-case it. */ + return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020u); +} + +/** + * 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 + * #hb_buffer_serialize_format_t. + * + * Return value: (transfer none): + * 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 ((unsigned) format) + { + 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; + } +} + +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) +{ + 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); + + *buf_consumed = 0; + hb_position_t x = 0, y = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + +#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END + + if (i) + *p++ = ','; + else + *p++ = '['; + + *p++ = '{'; + + APPEND ("\"g\":"); + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES)) + { + char g[128]; + hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g)); + *p++ = '"'; + for (char *q = g; *q; q++) + { + if (unlikely (*q == '"' || *q == '\\')) + *p++ = '\\'; + *p++ = *q; + } + *p++ = '"'; + } + else + 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)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + 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 += 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 += 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) + { + 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; +} + +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) +{ + 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); + + *buf_consumed = 0; + hb_position_t x = 0, y = 0; + for (unsigned int i = start; i < end; i++) + { + char b[1024]; + char *p = b; + + /* In the following code, we know b is large enough that no overflow can happen. */ + + 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 += 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), "=%u", info[i].cluster)); + } + + if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + { + 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)); + + 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 += 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 += 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) + { + 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; + *buf = '\0'; + } else + return i - start; + } + return end - start; +} + +/** + * hb_buffer_serialize_glyphs: + * @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 glyph content, + * 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 glyphs will look something like: + * + * ``` + * [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 + * #HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES flag is set. Then, + * - If #HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS is not set, `=` then #hb_glyph_info_t.cluster. + * - 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>` + * + * ## json + * A machine-readable, structured format. + * The serialized glyphs will look something like: + * + * ``` + * [{"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) +{ + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + if (buf_size) + *buf = '\0'; + + buffer->assert_glyphs (); + + if (!buffer->have_positions) + flags |= HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS; + + if (unlikely (start == end)) + return 0; + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_serialize_glyphs_text (buffer, start, end, + 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); + + default: + case HB_BUFFER_SERIALIZE_FORMAT_INVALID: + return 0; + + } +} + +/** + * 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: + * + * ``` + * <U+0651=0|U+0628=1> + * ``` + * + * - 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) +{ + end = hb_clamp (end, start, buffer->len); + start = hb_min (start, end); + + unsigned int sconsumed; + if (!buf_consumed) + buf_consumed = &sconsumed; + *buf_consumed = 0; + if (buf_size) + *buf = '\0'; + + 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 bool +parse_uint (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 */))) + return false; + + *pv = v; + return true; +} + +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; + return true; +} + +#include "hb-buffer-deserialize-json.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): 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: `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 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; + + 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); + + if (!buf_len) + { + *end_ptr = buf; + return false; + } + + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); + + if (!font) + font = hb_font_get_empty (); + + switch (format) + { + case HB_BUFFER_SERIALIZE_FORMAT_TEXT: + return _hb_buffer_deserialize_text_glyphs (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; + + } +} + + +/** + * 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/gfx/harfbuzz/src/hb-buffer-verify.cc b/gfx/harfbuzz/src/hb-buffer-verify.cc new file mode 100644 index 0000000000..15a53919de --- /dev/null +++ b/gfx/harfbuzz/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<char> 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/gfx/harfbuzz/src/hb-buffer.cc b/gfx/harfbuzz/src/hb-buffer.cc new file mode 100644 index 0000000000..934c6c2129 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer.cc @@ -0,0 +1,2222 @@ +/* + * 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 + */ + +#include "hb-buffer.hh" +#include "hb-utf.hh" + + +/** + * SECTION: hb-buffer + * @title: hb-buffer + * @short_description: Input and output buffers + * @include: hb.h + * + * 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. + * @b: second #hb_segment_properties_t to compare. + * + * Checks the equality of two #hb_segment_properties_t's. + * + * Return value: + * `true` if all properties of @a equal those of @b, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b) +{ + return a->direction == b->direction && + a->script == b->script && + a->language == b->language && + a->reserved1 == b->reserved1 && + a->reserved2 == b->reserved2; + +} + +/** + * hb_segment_properties_hash: + * @p: #hb_segment_properties_t to hash. + * + * Creates a hash representing @p. + * + * Return value: + * A hash of @p. + * + * Since: 0.9.7 + **/ +unsigned int +hb_segment_properties_hash (const hb_segment_properties_t *p) +{ + 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: + * + * There are two info pointers: info and out_info. They always have + * the same allocated size, but different lengths. + * + * 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, 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 + * current contents (out_len entries) are copied to the new place. + * + * This should all remain transparent to the user. sync() then + * switches info over to out_info and does housekeeping. + */ + + + +/* Internal API */ + +bool +hb_buffer_t::enlarge (unsigned int size) +{ + if (unlikely (!successful)) + return false; + if (unlikely (size > max_len)) + { + successful = false; + return false; + } + + unsigned int new_allocated = allocated; + hb_glyph_position_t *new_pos = nullptr; + hb_glyph_info_t *new_info = nullptr; + bool separate_out = out_info != info; + + if (unlikely (hb_unsigned_mul_overflows (size, sizeof (info[0])))) + goto done; + + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 32; + + unsigned new_bytes; + if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]), &new_bytes))) + goto done; + + 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)) + successful = false; + + if (likely (new_pos)) + pos = new_pos; + + if (likely (new_info)) + info = new_info; + + out_info = separate_out ? (hb_glyph_info_t *) pos : info; + if (likely (successful)) + allocated = new_allocated; + + return likely (successful); +} + +bool +hb_buffer_t::make_room_for (unsigned int num_in, + unsigned int num_out) +{ + if (unlikely (!ensure (out_len + num_out))) return false; + + if (out_info == info && + out_len + num_out > idx + num_in) + { + assert (have_output); + + out_info = (hb_glyph_info_t *) pos; + hb_memcpy (out_info, info, out_len * sizeof (out_info[0])); + } + + return true; +} + +bool +hb_buffer_t::shift_forward (unsigned int count) +{ + assert (have_output); + if (unlikely (!ensure (len + count))) return false; + + memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0])); + if (idx + count > len) + { + /* Under memory failure we might expose this area. At least + * 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; + + return true; +} + +hb_buffer_t::scratch_buffer_t * +hb_buffer_t::get_scratch_buffer (unsigned int *size) +{ + have_output = false; + have_positions = false; + + out_len = 0; + out_info = info; + + assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0); + *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t); + return (scratch_buffer_t *) (void *) pos; +} + + + +/* HarfBuzz-Internal API */ + +void +hb_buffer_t::similar (const hb_buffer_t &src) +{ + hb_unicode_funcs_destroy (unicode); + 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 () +{ + content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT; + props = default_props; + + successful = true; + shaping_failed = false; + have_output = false; + have_positions = false; + + idx = 0; + len = 0; + out_len = 0; + out_info = info; + + 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) +{ + hb_glyph_info_t *glyph; + + if (unlikely (!ensure (len + 1))) return; + + glyph = &info[len]; + + hb_memset (glyph, 0, sizeof (*glyph)); + glyph->codepoint = codepoint; + glyph->mask = 0; + glyph->cluster = cluster; + + len++; +} + +void +hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info) +{ + if (unlikely (!ensure (len + 1))) return; + + info[len] = glyph_info; + + len++; +} + + +void +hb_buffer_t::clear_output () +{ + have_output = true; + have_positions = false; + + idx = 0; + out_len = 0; + out_info = info; +} + +void +hb_buffer_t::clear_positions () +{ + have_output = false; + have_positions = true; + + out_len = 0; + out_info = info; + + hb_memset (pos, 0, sizeof (pos[0]) * len); +} + +bool +hb_buffer_t::sync () +{ + bool ret = false; + + assert (have_output); + + assert (idx <= len); + + if (unlikely (!successful || !next_glyphs (len - idx))) + goto reset; + + if (out_info != info) + { + pos = (hb_glyph_position_t *) info; + info = out_info; + } + len = out_len; + ret = true; + +reset: + have_output = false; + out_len = 0; + out_info = info; + idx = 0; + + return ret; +} + +int +hb_buffer_t::sync_so_far () +{ + bool had_output = have_output; + unsigned out_i = out_len; + unsigned i = idx; + unsigned old_idx = idx; + + if (sync ()) + idx = out_i; + else + idx = i; + + if (had_output) + { + have_output = true; + out_len = idx; + } + + assert (idx <= len); + + return idx - old_idx; +} + +bool +hb_buffer_t::move_to (unsigned int i) +{ + if (!have_output) + { + assert (i <= len); + idx = i; + return true; + } + if (unlikely (!successful)) + return false; + + assert (i <= out_len + (len - idx)); + + if (out_len < i) + { + unsigned int count = i - out_len; + if (unlikely (!make_room_for (count, count))) return false; + + memmove (out_info + out_len, info + idx, count * sizeof (out_info[0])); + idx += count; + out_len += count; + } + else if (out_len > i) + { + /* Tricky part: rewinding... */ + unsigned int count = out_len - i; + + /* This will blow in our face if memory allocation fails later + * 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); + + idx -= count; + out_len -= count; + memmove (info + idx, out_info + out_len, count * sizeof (out_info[0])); + } + + return true; +} + + +void +hb_buffer_t::set_masks (hb_mask_t value, + hb_mask_t mask, + unsigned int cluster_start, + unsigned int cluster_end) +{ + if (!mask) + return; + + hb_mask_t not_mask = ~mask; + value &= mask; + + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (cluster_start <= info[i].cluster && info[i].cluster < cluster_end) + info[i].mask = (info[i].mask & not_mask) | value; +} + +void +hb_buffer_t::merge_clusters_impl (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + unsafe_to_break (start, end); + return; + } + + unsigned int cluster = info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = hb_min (cluster, info[i].cluster); + + /* Extend end */ + if (cluster != info[end - 1].cluster) + while (end < len && info[end - 1].cluster == info[end].cluster) + end++; + + /* Extend 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 && 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); + + for (unsigned int i = start; i < end; i++) + set_cluster (info[i], cluster); +} +void +hb_buffer_t::merge_out_clusters (unsigned int start, + unsigned int end) +{ + if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + return; + + if (unlikely (end - start < 2)) + return; + + unsigned int cluster = out_info[start].cluster; + + for (unsigned int i = start + 1; i < end; i++) + cluster = hb_min (cluster, out_info[i].cluster); + + /* Extend start */ + while (start && out_info[start - 1].cluster == out_info[start].cluster) + start--; + + /* Extend end */ + while (end < out_len && out_info[end - 1].cluster == out_info[end].cluster) + end++; + + /* If we hit the end of out-buffer, continue in buffer. */ + if (end == out_len) + for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++) + set_cluster (info[i], cluster); + + for (unsigned int i = start; i < end; i++) + set_cluster (out_info[i], cluster); +} +void +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) || + (out_len && cluster == out_info[out_len - 1].cluster)) + { + /* Cluster survives; do nothing. */ + goto done; + } + + if (out_len) + { + /* Merge cluster backward. */ + if (cluster < out_info[out_len - 1].cluster) + { + unsigned int mask = info[idx].mask; + unsigned int old_cluster = out_info[out_len - 1].cluster; + for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--) + set_cluster (out_info[i - 1], cluster, mask); + } + goto done; + } + + if (idx + 1 < len) + { + /* Merge cluster forward. */ + merge_clusters (idx, idx + 2); + goto done; + } + +done: + skip_glyph (); +} + +void +hb_buffer_t::delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)) +{ + /* 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++) + { + 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++; + } + len = j; +} + +void +hb_buffer_t::guess_segment_properties () +{ + assert_unicode (); + + /* If script is set to INVALID, guess from buffer contents */ + if (props.script == HB_SCRIPT_INVALID) { + for (unsigned int i = 0; i < len; i++) { + hb_script_t script = unicode->script (info[i].codepoint); + if (likely (script != HB_SCRIPT_COMMON && + script != HB_SCRIPT_INHERITED && + script != HB_SCRIPT_UNKNOWN)) { + props.script = script; + break; + } + } + } + + /* 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 */ + if (props.language == HB_LANGUAGE_INVALID) { + /* TODO get_default_for_script? using $LANGUAGE */ + props.language = hb_language_get_default (); + } +} + + +/* Public API */ + +DEFINE_NULL_INSTANCE (hb_buffer_t) = +{ + HB_OBJECT_HEADER_STATIC, + + const_cast<hb_unicode_funcs_t *> (&_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: + * + * 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 + * be allocated, a special #hb_buffer_t object will be returned on which + * hb_buffer_allocation_successful() returns `false`. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_create () +{ + hb_buffer_t *buffer; + + if (!(buffer = hb_object_create<hb_buffer_t> ())) + return hb_buffer_get_empty (); + + buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; + buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT; + + buffer->reset (); + + return buffer; +} + +/** + * 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 () +{ + return const_cast<hb_buffer_t *> (&Null (hb_buffer_t)); +} + +/** + * hb_buffer_reference: (skip) + * @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. + * + * Return value: (transfer full): + * The referenced #hb_buffer_t. + * + * Since: 0.9.2 + **/ +hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer) +{ + return hb_object_reference (buffer); +} + +/** + * hb_buffer_destroy: (skip) + * @buffer: An #hb_buffer_t + * + * Deallocate the @buffer. + * Decreases the reference count on @buffer by one. If the result is zero, then + * @buffer and all associated resources are freed. See hb_buffer_reference(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_destroy (hb_buffer_t *buffer) +{ + if (!hb_object_destroy (buffer)) return; + + hb_unicode_funcs_destroy (buffer->unicode); + + hb_free (buffer->info); + hb_free (buffer->pos); +#ifndef HB_NO_BUFFER_MESSAGE + if (buffer->message_destroy) + buffer->message_destroy (buffer->message_data); +#endif + + hb_free (buffer); +} + +/** + * hb_buffer_set_user_data: (skip) + * @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: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (buffer, key, data, destroy, replace); +} + +/** + * hb_buffer_get_user_data: (skip) + * @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: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_buffer_get_user_data (const hb_buffer_t *buffer, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (buffer, key); +} + + +/** + * hb_buffer_set_content_type: + * @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 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 + **/ +void +hb_buffer_set_content_type (hb_buffer_t *buffer, + hb_buffer_content_type_t content_type) +{ + buffer->content_type = content_type; +} + +/** + * hb_buffer_get_content_type: + * @buffer: An #hb_buffer_t + * + * 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 + * + * Since: 0.9.5 + **/ +hb_buffer_content_type_t +hb_buffer_get_content_type (const hb_buffer_t *buffer) +{ + return buffer->content_type; +} + + +/** + * hb_buffer_set_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 + **/ +void +hb_buffer_set_unicode_funcs (hb_buffer_t *buffer, + hb_unicode_funcs_t *unicode_funcs) +{ + 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; +} + +/** + * hb_buffer_get_unicode_funcs: + * @buffer: An #hb_buffer_t + * + * Fetches the Unicode-functions structure of a buffer. + * + * Return value: The Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_buffer_get_unicode_funcs (const hb_buffer_t *buffer) +{ + return buffer->unicode; +} + +/** + * hb_buffer_set_direction: + * @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 + * setting @buffer direction, and it controls the visual direction for the + * output glyphs; for RTL direction the glyphs will be reversed. Many layout + * features depend on the proper setting of the direction, for example, + * reversing RTL text before shaping, then shaping with LTR direction is not + * the same as keeping the text in logical order and shaping with RTL + * direction. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_direction (hb_buffer_t *buffer, + hb_direction_t direction) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.direction = direction; +} + +/** + * hb_buffer_get_direction: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_direction() + * + * Return value: + * The direction of the @buffer. + * + * Since: 0.9.2 + **/ +hb_direction_t +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. + * + * Sets the script of @buffer to @script. + * + * Script is crucial for choosing the proper shaping behaviour for scripts that + * require it (e.g. Arabic) and the which OpenType features defined in the font + * to be applied. + * + * 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. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_script (hb_buffer_t *buffer, + hb_script_t script) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.script = script; +} + +/** + * hb_buffer_get_script: + * @buffer: An #hb_buffer_t + * + * Fetches the script of @buffer. + * + * Return value: + * The #hb_script_t of the @buffer + * + * Since: 0.9.2 + **/ +hb_script_t +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 + * + * Sets the language of @buffer to @language. + * + * Languages are crucial for selecting which OpenType feature to apply to the + * buffer which can result in applying language-specific behaviour. Languages + * 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 BCP 47 language tags to + * #hb_language_t. + * + * Since: 0.9.2 + **/ +void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props.language = language; +} + +/** + * hb_buffer_get_language: + * @buffer: An #hb_buffer_t + * + * See hb_buffer_set_language(). + * + * Return value: (transfer none): + * The #hb_language_t of the buffer. Must not be freed by the caller. + * + * Since: 0.9.2 + **/ +hb_language_t +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 + * + * Sets the segment properties of the buffer, a shortcut for calling + * hb_buffer_set_direction(), hb_buffer_set_script() and + * hb_buffer_set_language() individually. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_segment_properties (hb_buffer_t *buffer, + const hb_segment_properties_t *props) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->props = *props; +} + +/** + * hb_buffer_get_segment_properties: + * @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 (const hb_buffer_t *buffer, + hb_segment_properties_t *props) +{ + *props = buffer->props; +} + + +/** + * hb_buffer_set_flags: + * @buffer: An #hb_buffer_t + * @flags: The buffer flags to set + * + * Sets @buffer flags to @flags. See #hb_buffer_flags_t. + * + * Since: 0.9.7 + **/ +void +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->flags = flags; +} + +/** + * hb_buffer_get_flags: + * @buffer: An #hb_buffer_t + * + * Fetches the #hb_buffer_flags_t of @buffer. + * + * Return value: + * The @buffer flags + * + * Since: 0.9.7 + **/ +hb_buffer_flags_t +hb_buffer_get_flags (const hb_buffer_t *buffer) +{ + return buffer->flags; +} + +/** + * hb_buffer_set_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) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->cluster_level = cluster_level; +} + +/** + * hb_buffer_get_cluster_level: + * @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: The cluster level of @buffer + * + * Since: 0.9.42 + **/ +hb_buffer_cluster_level_t +hb_buffer_get_cluster_level (const hb_buffer_t *buffer) +{ + return buffer->cluster_level; +} + + +/** + * hb_buffer_set_replacement_codepoint: + * @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. + * + * Since: 0.9.31 + **/ +void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return; + + buffer->replacement = replacement; +} + +/** + * hb_buffer_get_replacement_codepoint: + * @buffer: An #hb_buffer_t + * + * 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 + * + * Since: 0.9.31 + **/ +hb_codepoint_t +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer) +{ + return buffer->replacement; +} + + +/** + * hb_buffer_set_invisible_glyph: + * @buffer: An #hb_buffer_t + * @invisible: the invisible #hb_codepoint_t + * + * 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: 2.0.0 + **/ +void +hb_buffer_set_invisible_glyph (hb_buffer_t *buffer, + hb_codepoint_t invisible) +{ + 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 + * + * Similar to hb_buffer_reset(), but does not clear the Unicode functions and + * the replacement code point. + * + * Since: 0.9.11 + **/ +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. + * + * Pre allocates memory for @buffer to fit at least @size number of items. + * + * Return value: + * `true` if @buffer memory allocation succeeded, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_pre_allocate (hb_buffer_t *buffer, unsigned int size) +{ + return buffer->ensure (size); +} + +/** + * hb_buffer_allocation_successful: + * @buffer: An #hb_buffer_t + * + * Check if allocating memory for the buffer succeeded. + * + * Return value: + * `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->successful; +} + +/** + * hb_buffer_add: + * @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 + * the client wants, they are usually used to refer to the index of the + * character in the input text stream and are output in + * #hb_glyph_info_t.cluster field. + * + * This function does not check the validity of @codepoint, it is up to the + * caller to ensure it is a valid Unicode code point. + * + * Since: 0.9.7 + **/ +void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster) +{ + buffer->add (codepoint, cluster); + buffer->clear_context (1); +} + +/** + * hb_buffer_set_length: + * @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. + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length) +{ + if (unlikely (hb_object_is_immutable (buffer))) + return length == 0; + + if (unlikely (!buffer->ensure (length))) + return false; + + /* Wipe the new space */ + if (length > buffer->len) { + hb_memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len)); + if (buffer->have_positions) + hb_memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len)); + } + + buffer->len = length; + + if (!length) + { + buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID; + buffer->clear_context (0); + } + buffer->clear_context (1); + + return true; +} + +/** + * hb_buffer_get_length: + * @buffer: An #hb_buffer_t + * + * Returns the number of items in the buffer. + * + * Return value: + * The @buffer length. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +unsigned int +hb_buffer_get_length (const hb_buffer_t *buffer) +{ + return buffer->len; +} + +/** + * hb_buffer_get_glyph_infos: + * @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. + * + * Return value: (transfer none) (array length=length): + * The @buffer glyph information array. + * The value valid as long as buffer has not been modified. + * + * Since: 0.9.2 + **/ +hb_glyph_info_t * +hb_buffer_get_glyph_infos (hb_buffer_t *buffer, + unsigned int *length) +{ + if (length) + *length = buffer->len; + + return (hb_glyph_info_t *) buffer->info; +} + +/** + * hb_buffer_get_glyph_positions: + * @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. + * + * Since: 0.9.2 + **/ +hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length) +{ + 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 + * + * Returns glyph flags encoded within a #hb_glyph_info_t. + * + * Return value: + * The #hb_glyph_flags_t encoded within @info + * + * Since: 1.5.0 + **/ +hb_glyph_flags_t +(hb_glyph_info_get_glyph_flags) (const hb_glyph_info_t *info) +{ + return hb_glyph_info_get_glyph_flags (info); +} + +/** + * hb_buffer_reverse: + * @buffer: An #hb_buffer_t + * + * Reverses buffer contents. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse (hb_buffer_t *buffer) +{ + buffer->reverse (); +} + +/** + * hb_buffer_reverse_range: + * @buffer: An #hb_buffer_t + * @start: start index + * @end: end index + * + * Reverses buffer contents between @start and @end. + * + * Since: 0.9.41 + **/ +void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + buffer->reverse_range (start, end); +} + +/** + * hb_buffer_reverse_clusters: + * @buffer: An #hb_buffer_t + * + * Reverses buffer clusters. That is, the buffer contents are + * reversed, then each cluster (consecutive items having the + * same cluster number) are reversed again. + * + * Since: 0.9.2 + **/ +void +hb_buffer_reverse_clusters (hb_buffer_t *buffer) +{ + buffer->reverse_clusters (); +} + +/** + * hb_buffer_guess_segment_properties: + * @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. + * + * 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. + * + * 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), + * 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 + **/ +void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer) +{ + buffer->guess_segment_properties (); +} + +template <typename utf_t> +static inline void +hb_buffer_add_utf (hb_buffer_t *buffer, + const typename utf_t::codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + typedef typename utf_t::codepoint_t T; + const hb_codepoint_t replacement = buffer->replacement; + + buffer->assert_unicode (); + + if (unlikely (hb_object_is_immutable (buffer))) + return; + + if (text_length == -1) + text_length = utf_t::strlen (text); + + if (item_length == -1) + item_length = text_length - item_offset; + + 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 + * provide pre-context in one add_utf() call, then provide + * text in a follow-up call. See: + * + * https://bugzilla.mozilla.org/show_bug.cgi?id=801410#c13 + */ + if (!buffer->len && item_offset > 0) + { + /* Add pre-context */ + buffer->clear_context (0); + const T *prev = text + item_offset; + const T *start = text; + while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + prev = utf_t::prev (prev, start, &u, replacement); + buffer->context[0][buffer->context_len[0]++] = u; + } + } + + const T *next = text + item_offset; + const T *end = next + item_length; + while (next < end) + { + hb_codepoint_t u; + const T *old_next = next; + next = utf_t::next (next, end, &u, replacement); + buffer->add (u, old_next - (const T *) text); + } + + /* Add post-context */ + buffer->clear_context (1); + end = text + text_length; + while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH) + { + hb_codepoint_t u; + next = utf_t::next (next, end, &u, replacement); + buffer->context[1][buffer->context_len[1]++] = u; + } + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE; +} + +/** + * hb_buffer_add_utf8: + * @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). + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-8 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf8_t> (buffer, (const uint8_t *) text, text_length, item_offset, item_length); +} + +/** + * 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) + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-16 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf16_t> (buffer, text, text_length, item_offset, item_length); +} + +/** + * 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) + * + * See hb_buffer_add_codepoints(). + * + * Replaces invalid UTF-32 characters with the @buffer replacement code point, + * see hb_buffer_set_replacement_codepoint(). + * + * Since: 0.9.2 + **/ +void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf32_t> (buffer, text, text_length, item_offset, item_length); +} + +/** + * hb_buffer_add_latin1: + * @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) + * + * Similar to hb_buffer_add_codepoints(), but allows only access to first 256 + * Unicode code points that can fit in 8-bit strings. + * + * <note>Has nothing to do with non-Unicode Latin-1 encoding.</note> + * + * Since: 0.9.39 + **/ +void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_latin1_t> (buffer, text, text_length, item_offset, item_length); +} + +/** + * 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. + * @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). + * + * Appends characters from @text array to @buffer. The @item_offset is the + * position of the first character from @text that will be appended, and + * @item_length is the number of character. When shaping part of a larger text + * (e.g. a run of text from a paragraph), instead of passing just the substring + * corresponding to the run, it is preferable to pass the whole + * paragraph and specify the run start and length as @item_offset and + * @item_length, respectively, to give HarfBuzz the full context to be able, + * for example, to do cross-run Arabic shaping or properly handle combining + * 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 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 + **/ +void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length) +{ + hb_buffer_add_utf<hb_utf32_novalidate_t> (buffer, text, text_length, item_offset, item_length); +} + + +/** + * hb_buffer_append: + * @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 @HB_FEATURE_GLOBAL_END to copy to end of buffer. + * + * Append (part of) contents of another buffer to this buffer. + * + * Since: 1.5.0 + **/ +HB_EXTERN void +hb_buffer_append (hb_buffer_t *buffer, + const hb_buffer_t *source, + unsigned int start, + unsigned int end) +{ + assert (!buffer->have_output && !source->have_output); + assert (buffer->have_positions == source->have_positions || + !buffer->len || !source->len); + assert (buffer->content_type == source->content_type || + !buffer->len || !source->len); + + if (end > source->len) + end = source->len; + if (start > end) + start = end; + if (start == end) + return; + + if (buffer->len + (end - start) < buffer->len) /* Overflows. */ + { + buffer->successful = false; + return; + } + + unsigned int orig_len = buffer->len; + hb_buffer_set_length (buffer, buffer->len + (end - start)); + if (unlikely (!buffer->successful)) + return; + + 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) + 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]; + } +} + + +static int +compare_info_codepoint (const hb_glyph_info_t *pa, + const hb_glyph_info_t *pb) +{ + return (int) pb->codepoint - (int) pa->codepoint; +} + +static inline void +normalize_glyphs_cluster (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool backward) +{ + hb_glyph_position_t *pos = buffer->pos; + + /* Total cluster advance */ + hb_position_t total_x_advance = 0, total_y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + total_x_advance += pos[i].x_advance; + total_y_advance += pos[i].y_advance; + } + + hb_position_t x_advance = 0, y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + pos[i].x_offset += x_advance; + pos[i].y_offset += y_advance; + + x_advance += pos[i].x_advance; + y_advance += pos[i].y_advance; + + pos[i].x_advance = 0; + pos[i].y_advance = 0; + } + + if (backward) + { + /* Transfer all cluster advance to the last glyph. */ + pos[end - 1].x_advance = total_x_advance; + pos[end - 1].y_advance = total_y_advance; + + hb_stable_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); + } else { + /* Transfer all cluster advance to the first glyph. */ + pos[start].x_advance += total_x_advance; + pos[start].y_advance += total_y_advance; + for (unsigned int i = start + 1; i < end; i++) { + pos[i].x_offset -= total_x_advance; + pos[i].y_offset -= total_y_advance; + } + hb_stable_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + } +} + +/** + * hb_buffer_normalize_glyphs: + * @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. + * + * <note>This has nothing to do with Unicode normalization.</note> + * + * Since: 0.9.2 + **/ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer) +{ + assert (buffer->have_positions); + + buffer->assert_glyphs (); + + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + foreach_cluster (buffer, start, end) + normalize_glyphs_cluster (buffer, start, end, backward); +} + +void +hb_buffer_t::sort (unsigned int start, unsigned int end, int(*compar)(const hb_glyph_info_t *, const hb_glyph_info_t *)) +{ + assert (!have_positions); + for (unsigned int i = start + 1; i < end; i++) + { + unsigned int j = i; + while (j > start && compar (&info[j - 1], &info[i]) > 0) + j--; + if (i == j) + continue; + /* Move item i to occupy place for item j, shift what's in between. */ + merge_clusters (j, i + 1); + { + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (hb_glyph_info_t)); + info[j] = t; + } + } +} + + +/* + * Comparing buffers. + */ + +/** + * 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 + * callers if just comparing two buffers is needed. + * + * Since: 1.5.0 + **/ +hb_buffer_diff_flags_t +hb_buffer_diff (hb_buffer_t *buffer, + hb_buffer_t *reference, + hb_codepoint_t dottedcircle_glyph, + unsigned int position_fuzz) +{ + if (buffer->content_type != reference->content_type && buffer->len && reference->len) + return HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH; + + hb_buffer_diff_flags_t result = HB_BUFFER_DIFF_FLAG_EQUAL; + bool contains = dottedcircle_glyph != (hb_codepoint_t) -1; + + unsigned int count = reference->len; + + if (buffer->len != count) + { + /* + * we can't compare glyph-by-glyph, but we do want to know if there + * are .notdef or dottedcircle glyphs present in the reference buffer + */ + const hb_glyph_info_t *info = reference->info; + unsigned int i; + for (i = 0; i < count; i++) + { + if (contains && info[i].codepoint == dottedcircle_glyph) + 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_LENGTH_MISMATCH; + return hb_buffer_diff_flags_t (result); + } + + if (!count) + return hb_buffer_diff_flags_t (result); + + const hb_glyph_info_t *buf_info = buffer->info; + const hb_glyph_info_t *ref_info = reference->info; + for (unsigned int i = 0; i < count; i++) + { + if (buf_info->codepoint != ref_info->codepoint) + 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 ^ 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; + if (contains && ref_info->codepoint == 0) + result |= HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT; + buf_info++; + ref_info++; + } + + if (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) + { + assert (buffer->have_positions); + const hb_glyph_position_t *buf_pos = buffer->pos; + const hb_glyph_position_t *ref_pos = reference->pos; + 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) + { + result |= HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH; + break; + } + buf_pos++; + ref_pos++; + } + } + + return result; +} + + +/* + * Debugging. + */ + +#ifndef HB_NO_BUFFER_MESSAGE +/** + * hb_buffer_set_message_func: + * @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 + **/ +void +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); + + if (func) { + buffer->message_func = func; + buffer->message_data = user_data; + buffer->message_destroy = destroy; + } else { + buffer->message_func = nullptr; + buffer->message_data = nullptr; + 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); + bool ret = (bool) this->message_func (this, font, buf, this->message_data); + + message_depth--; + + return ret; +} +#endif diff --git a/gfx/harfbuzz/src/hb-buffer.h b/gfx/harfbuzz/src/hb-buffer.h new file mode 100644 index 0000000000..3573127ff0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-buffer.h @@ -0,0 +1,799 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2004,2007,2009 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_BUFFER_H +#define HB_BUFFER_H + +#include "hb-common.h" +#include "hb-unicode.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +/** + * hb_glyph_info_t: + * @codepoint: either a Unicode code point (before shaping) or a glyph index + * (after shaping). + * @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 + * @cluster value, if they resulted from the same character (e.g. one + * to many glyph substitution), and when more than one character gets + * merged in the same glyph (e.g. many to one glyph substitution) the + * #hb_glyph_info_t will have the smallest cluster value of them. + * By default some characters are merged into the same cluster + * (e.g. combining marks have the same cluster as their bases) + * even if they are separate glyphs, hb_buffer_set_cluster_level() + * allow selecting more fine-grained cluster handling. + * + * 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; + /*< private >*/ + hb_mask_t mask; + /*< public >*/ + uint32_t cluster; + + /*< private >*/ + hb_var_int_t var1; + 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_CONCAT = 0x00000002, + HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL = 0x00000004, + + HB_GLYPH_FLAG_DEFINED = 0x00000007 /* OR of all defined flags */ +} hb_glyph_flags_t; + +HB_EXTERN hb_glyph_flags_t +hb_glyph_info_get_glyph_flags (const hb_glyph_info_t *info); + +#define hb_glyph_info_get_glyph_flags(info) \ + ((hb_glyph_flags_t) ((unsigned int) (info)->mask & HB_GLYPH_FLAG_DEFINED)) + + +/** + * hb_glyph_position_t: + * @x_advance: how much the line advances after drawing this glyph when setting + * text in horizontal direction. + * @y_advance: how much the line advances after drawing this glyph when setting + * text in vertical direction. + * @x_offset: how much the glyph moves on the X-axis before drawing it, this + * should not affect how much the line advances. + * @y_offset: how much the glyph moves on the Y-axis before drawing it, this + * should not affect how much the line advances. + * + * The #hb_glyph_position_t is the structure that holds the positions of the + * glyph in both horizontal and vertical directions. All positions in + * #hb_glyph_position_t are relative to the current point. + * + */ +typedef struct hb_glyph_position_t { + hb_position_t x_advance; + hb_position_t y_advance; + hb_position_t x_offset; + hb_position_t y_offset; + + /*< private >*/ + hb_var_int_t var; +} hb_glyph_position_t; + +/** + * hb_segment_properties_t: + * @direction: the #hb_direction_t of the buffer, see hb_buffer_set_direction(). + * @script: the #hb_script_t of the buffer, see hb_buffer_set_script(). + * @language: the #hb_language_t of the buffer, see hb_buffer_set_language(). + * + * The structure that holds various text properties of an #hb_buffer_t. Can be + * set and retrieved using hb_buffer_set_segment_properties() and + * hb_buffer_get_segment_properties(), respectively. + */ +typedef struct hb_segment_properties_t { + hb_direction_t direction; + hb_script_t script; + hb_language_t language; + /*< private >*/ + void *reserved1; + 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, \ + (void *) 0, \ + (void *) 0} + +HB_EXTERN hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b); + +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); + + +/** + * hb_buffer_t: + * + * The main structure holding the input text and its properties before shaping, + * and output glyphs and their information after shaping. + */ + +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); + +HB_EXTERN hb_buffer_t * +hb_buffer_reference (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_destroy (hb_buffer_t *buffer); + +HB_EXTERN hb_bool_t +hb_buffer_set_user_data (hb_buffer_t *buffer, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_buffer_get_user_data (const hb_buffer_t *buffer, + hb_user_data_key_t *key); + + +/** + * hb_buffer_content_type_t: + * @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, + HB_BUFFER_CONTENT_TYPE_UNICODE, + HB_BUFFER_CONTENT_TYPE_GLYPHS +} hb_buffer_content_type_t; + +HB_EXTERN void +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 (const hb_buffer_t *buffer); + + +HB_EXTERN void +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 (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 (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 (const hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_set_language (hb_buffer_t *buffer, + hb_language_t language); + + +HB_EXTERN hb_language_t +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 (const hb_buffer_t *buffer, + hb_segment_properties_t *props); + +HB_EXTERN void +hb_buffer_guess_segment_properties (hb_buffer_t *buffer); + + +/** + * hb_buffer_flags_t: + * @HB_BUFFER_FLAG_DEFAULT: the default buffer flag. + * @HB_BUFFER_FLAG_BOT: flag indicating that special handling of the beginning + * of text paragraph can be applied to this buffer. Should usually + * be set, unless you are passing to the buffer only part + * 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_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 (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 + */ +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_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 +hb_buffer_set_flags (hb_buffer_t *buffer, + hb_buffer_flags_t flags); + +HB_EXTERN hb_buffer_flags_t +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 { + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES = 0, + HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS = 1, + HB_BUFFER_CLUSTER_LEVEL_CHARACTERS = 2, + HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES +} hb_buffer_cluster_level_t; + +HB_EXTERN void +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 (const hb_buffer_t *buffer); + +/** + * HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT: + * + * The default code point for replacing invalid characters in a given encoding. + * Set to U+FFFD REPLACEMENT CHARACTER. + * + * Since: 0.9.31 + */ +#define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu + +HB_EXTERN void +hb_buffer_set_replacement_codepoint (hb_buffer_t *buffer, + hb_codepoint_t replacement); + +HB_EXTERN hb_codepoint_t +hb_buffer_get_replacement_codepoint (const hb_buffer_t *buffer); + +HB_EXTERN void +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); + + +HB_EXTERN hb_bool_t +hb_buffer_allocation_successful (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse (hb_buffer_t *buffer); + +HB_EXTERN void +hb_buffer_reverse_range (hb_buffer_t *buffer, + unsigned int start, unsigned int end); + +HB_EXTERN void +hb_buffer_reverse_clusters (hb_buffer_t *buffer); + + +/* Filling the buffer in */ + +HB_EXTERN void +hb_buffer_add (hb_buffer_t *buffer, + hb_codepoint_t codepoint, + unsigned int cluster); + +HB_EXTERN void +hb_buffer_add_utf8 (hb_buffer_t *buffer, + const char *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf16 (hb_buffer_t *buffer, + const uint16_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_utf32 (hb_buffer_t *buffer, + const uint32_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_latin1 (hb_buffer_t *buffer, + const uint8_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_add_codepoints (hb_buffer_t *buffer, + const hb_codepoint_t *text, + int text_length, + unsigned int item_offset, + int item_length); + +HB_EXTERN void +hb_buffer_append (hb_buffer_t *buffer, + const hb_buffer_t *source, + unsigned int start, + unsigned int end); + +HB_EXTERN hb_bool_t +hb_buffer_set_length (hb_buffer_t *buffer, + unsigned int length); + +HB_EXTERN unsigned int +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); + +HB_EXTERN hb_glyph_position_t * +hb_buffer_get_glyph_positions (hb_buffer_t *buffer, + unsigned int *length); + +HB_EXTERN hb_bool_t +hb_buffer_has_positions (hb_buffer_t *buffer); + + +HB_EXTERN void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer); + + +/* + * Serialize + */ + +/** + * hb_buffer_serialize_flags_t: + * @HB_BUFFER_SERIALIZE_FLAG_DEFAULT: serialize glyph names, clusters and positions. + * @HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS: do not serialize glyph cluster. + * @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(). + * + * Since: 0.9.20 + */ +typedef enum { /*< flags >*/ + HB_BUFFER_SERIALIZE_FLAG_DEFAULT = 0x00000000u, + HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS = 0x00000001u, + 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_NO_ADVANCES = 0x00000020u, + + HB_BUFFER_SERIALIZE_FLAG_DEFINED = 0x0000003Fu +} hb_buffer_serialize_flags_t; + +/** + * hb_buffer_serialize_format_t: + * @HB_BUFFER_SERIALIZE_FORMAT_TEXT: a human-readable, plain text format. + * @HB_BUFFER_SERIALIZE_FORMAT_JSON: a machine-readable JSON format. + * @HB_BUFFER_SERIALIZE_FORMAT_INVALID: invalid format. + * + * The buffer serialization and de-serialization format used in + * hb_buffer_serialize_glyphs() and hb_buffer_deserialize_glyphs(). + * + * Since: 0.9.2 + */ +typedef enum { + HB_BUFFER_SERIALIZE_FORMAT_TEXT = HB_TAG('T','E','X','T'), + HB_BUFFER_SERIALIZE_FORMAT_JSON = HB_TAG('J','S','O','N'), + HB_BUFFER_SERIALIZE_FORMAT_INVALID = HB_TAG_NONE +} hb_buffer_serialize_format_t; + +HB_EXTERN hb_buffer_serialize_format_t +hb_buffer_serialize_format_from_string (const char *str, int len); + +HB_EXTERN const char * +hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format); + +HB_EXTERN const char ** +hb_buffer_serialize_list_formats (void); + +HB_EXTERN 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); + +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, + int buf_len, + const char **end_ptr, + 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, + + /* Buffers with different content_type cannot be meaningfully compared + * in any further detail. */ + HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH = 0x0001, + + /* For buffers with differing length, the per-glyph comparison is not + * attempted, though we do still scan reference for dottedcircle / .notdef + * glyphs. */ + HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH = 0x0002, + + /* We want to know if dottedcircle / .notdef glyphs are present in the + * reference, as we may not care so much about other differences in this + * case. */ + HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT = 0x0004, + HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT = 0x0008, + + /* 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. */ + HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH = 0x0010, + HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH = 0x0020, + HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH = 0x0040, + HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH = 0x0080 + +} hb_buffer_diff_flags_t; + +/* Compare the contents of two buffers, report types of differences. */ +HB_EXTERN hb_buffer_diff_flags_t +hb_buffer_diff (hb_buffer_t *buffer, + hb_buffer_t *reference, + hb_codepoint_t dottedcircle_glyph, + unsigned int position_fuzz); + + +/* + * 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, + void *user_data); + +HB_EXTERN void +hb_buffer_set_message_func (hb_buffer_t *buffer, + hb_buffer_message_func_t func, + void *user_data, hb_destroy_func_t destroy); + + +HB_END_DECLS + +#endif /* HB_BUFFER_H */ diff --git a/gfx/harfbuzz/src/hb-buffer.hh b/gfx/harfbuzz/src/hb-buffer.hh new file mode 100644 index 0000000000..f04ad58f11 --- /dev/null +++ b/gfx/harfbuzz/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<<end) - (1u<<start); + assert (0 == (allocated_var_bits & bits)); + allocated_var_bits |= bits; + } + bool try_allocate_var (unsigned int start, unsigned int count) + { + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u<<end) - (1u<<start); + if (allocated_var_bits & bits) + return false; + allocated_var_bits |= bits; + return true; + } + void deallocate_var (unsigned int start, unsigned int count) + { + unsigned int end = start + count; + assert (end <= 8); + unsigned int bits = (1u<<end) - (1u<<start); + assert (bits == (allocated_var_bits & bits)); + allocated_var_bits &= ~bits; + } + void assert_var (unsigned int start, unsigned int count) + { + unsigned int end = start + count; + assert (end <= 8); + HB_UNUSED unsigned int bits = (1u<<end) - (1u<<start); + assert (bits == (allocated_var_bits & bits)); + } + void deallocate_var_all () + { + allocated_var_bits = 0; + } + + hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; } + hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; } + + hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; } + hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; } + + hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; } + hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; } + + hb_set_digest_t digest () const + { + hb_set_digest_t d; + d.init (); + d.add_array (&info[0].codepoint, len, sizeof (info[0])); + return d; + } + + HB_INTERNAL void similar (const hb_buffer_t &src); + HB_INTERNAL void reset (); + HB_INTERNAL void clear (); + + /* Called around shape() */ + HB_INTERNAL void enter (); + HB_INTERNAL void leave (); + +#ifndef HB_NO_BUFFER_VERIFY + HB_INTERNAL +#endif + bool verify (hb_buffer_t *text_buffer, + hb_font_t *font, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shapers) +#ifndef HB_NO_BUFFER_VERIFY + ; +#else + { return true; } +#endif + + unsigned int backtrack_len () const { return have_output ? out_len : idx; } + unsigned int lookahead_len () const { return len - idx; } + uint8_t next_serial () { return ++serial ? serial : ++serial; } + + HB_INTERNAL void add (hb_codepoint_t codepoint, + unsigned int cluster); + HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info); + + void reverse_range (unsigned start, unsigned end) + { + hb_array_t<hb_glyph_info_t> (info, len).reverse (start, end); + if (have_positions) + hb_array_t<hb_glyph_position_t> (pos, len).reverse (start, end); + } + void reverse () { reverse_range (0, len); } + + template <typename FuncType> + 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 <typename FuncType> + 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 <typename T> + 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/gfx/harfbuzz/src/hb-cache.hh b/gfx/harfbuzz/src/hb-cache.hh new file mode 100644 index 0000000000..6d8a54cf10 --- /dev/null +++ b/gfx/harfbuzz/src/hb-cache.hh @@ -0,0 +1,99 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_CACHE_HH +#define HB_CACHE_HH + +#include "hb.hh" + + +/* 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 <unsigned int key_bits=16, + unsigned int value_bits=8 + 32 - key_bits, + unsigned int cache_bits=8, + bool thread_safe=true> +struct hb_cache_t +{ + using item_t = typename std::conditional<thread_safe, + typename std::conditional<key_bits + value_bits - cache_bits <= 16, + hb_atomic_short_t, + hb_atomic_int_t>::type, + typename std::conditional<key_bits + value_bits - cache_bits <= 16, + short, + int>::type + >::type; + + static_assert ((key_bits >= cache_bits), ""); + static_assert ((key_bits + value_bits <= cache_bits + 8 * sizeof (item_t)), ""); + + hb_cache_t () { clear (); } + + void clear () + { + for (auto &v : values) + v = -1; + } + + bool get (unsigned int key, unsigned int *value) const + { + unsigned int k = key & ((1u<<cache_bits)-1); + unsigned int v = values[k]; + 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<<value_bits)-1); + return true; + } + + bool set (unsigned int key, unsigned int value) + { + if (unlikely ((key >> key_bits) || (value >> value_bits))) + return false; /* Overflows */ + unsigned int k = key & ((1u<<cache_bits)-1); + unsigned int v = ((key>>cache_bits)<<value_bits) | value; + values[k] = v; + return true; + } + + private: + item_t values[1u<<cache_bits]; +}; + + +#endif /* HB_CACHE_HH */ diff --git a/gfx/harfbuzz/src/hb-cairo-utils.cc b/gfx/harfbuzz/src/hb-cairo-utils.cc new file mode 100644 index 0000000000..ec1499e861 --- /dev/null +++ b/gfx/harfbuzz/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 <cairo.h> + +/* 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/gfx/harfbuzz/src/hb-cairo-utils.hh b/gfx/harfbuzz/src/hb-cairo-utils.hh new file mode 100644 index 0000000000..a26bf59d91 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-cairo.cc b/gfx/harfbuzz/src/hb-cairo.cc new file mode 100644 index 0000000000..f4f9f54ab3 --- /dev/null +++ b/gfx/harfbuzz/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<hb_cairo_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<hb_cairo_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<hb_variation_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<hb_utf8_t> ((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<hb_utf8_t> ((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/gfx/harfbuzz/src/hb-cairo.h b/gfx/harfbuzz/src/hb-cairo.h new file mode 100644 index 0000000000..21e284c8f9 --- /dev/null +++ b/gfx/harfbuzz/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 <cairo.h> + +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/gfx/harfbuzz/src/hb-cff-interp-common.hh b/gfx/harfbuzz/src/hb-cff-interp-common.hh new file mode 100644 index 0000000000..1d1f10f2bf --- /dev/null +++ b/gfx/harfbuzz/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 <HBUINT8> +{ + 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 <typename T, typename V> + static bool serialize_int (hb_serialize_context_t *c, op_code_t intOp, V value) + { + TRACE_SERIALIZE (this); + + HBUINT8 *p = c->allocate_size<HBUINT8> (1); + if (unlikely (!p)) return_trace (false); + *p = intOp; + + T *ip = c->allocate_size<T> (T::static_size); + if (unlikely (!ip)) return_trace (false); + return_trace (c->check_assign (*ip, value, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + template <typename V> + static bool serialize_int4 (hb_serialize_context_t *c, V value) + { return serialize_int<HBINT32> (c, OpCode_longintdict, value); } + + template <typename V> + static bool serialize_int2 (hb_serialize_context_t *c, V value) + { return serialize_int<HBINT16> (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 <typename ELEM, int LIMIT> +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<const ELEM> sub_array (unsigned start, unsigned length) const + { return hb_array_t<const ELEM> (elements).sub_array (start, length); } + + private: + bool error = false; + unsigned int count = 0; + ELEM elements[LIMIT]; +}; + +/* argument stack */ +template <typename ARG=number_t> +struct arg_stack_t : cff_stack_t<ARG, 513> +{ + 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<ARG, 513> 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<unsigned char> (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 <typename VAL> +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<VAL> values; +}; + +template <typename ARG=number_t> +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<ARG> + argStack; +}; + +using num_interp_env_t = interp_env_t<>; + +template <typename ARG=number_t> +struct opset_t +{ + static void process_op (op_code_t op, interp_env_t<ARG>& 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 <typename ENV> +struct interpreter_t +{ + interpreter_t (ENV& env_) : env (env_) {} + ENV& env; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-cff-interp-cs-common.hh b/gfx/harfbuzz/src/hb-cff-interp-cs-common.hh new file mode 100644 index 0000000000..28a777eb0d --- /dev/null +++ b/gfx/harfbuzz/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<call_context_t, kMaxCallLimit> {}; + +template <typename SUBRS> +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 <typename ARG, typename SUBRS> +struct cs_interp_env_t : interp_env_t<ARG> +{ + cs_interp_env_t (const hb_ubytes_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_) : + interp_env_t<ARG> (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<SUBRS>& 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<SUBRS>& 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<SUBRS> globalSubrs; + biased_subrs_t<SUBRS> localSubrs; + + private: + point_t pt; + + typedef interp_env_t<ARG> SUPER; +}; + +template <typename ENV, typename PARAM> +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 <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=path_procs_null_t<ENV, PARAM>> +struct cs_opset_t : opset_t<ARG> +{ + 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<ARG> SUPER; +}; + +template <typename PATH, typename ENV, typename PARAM> +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 <typename ENV, typename OPSET, typename PARAM> +struct cs_interpreter_t : interpreter_t<ENV> +{ + cs_interpreter_t (ENV& env_) : interpreter_t<ENV> (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<ENV> SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_CS_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-cff-interp-dict-common.hh b/gfx/harfbuzz/src/hb-cff-interp-dict-common.hh new file mode 100644 index 0000000000..53226b227e --- /dev/null +++ b/gfx/harfbuzz/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 <typename VAL> struct dict_values_t : parsed_values_t<VAL> {}; + +template <typename OPSTR=op_str_t> +struct top_dict_values_t : dict_values_t<OPSTR> +{ + void init () + { + dict_values_t<OPSTR>::init (); + charStringsOffset = 0; + FDArrayOffset = 0; + } + void fini () { dict_values_t<OPSTR>::fini (); } + + unsigned int charStringsOffset; + unsigned int FDArrayOffset; +}; + +struct dict_opset_t : opset_t<number_t> +{ + static void process_op (op_code_t op, interp_env_t<number_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<number_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 <typename VAL=op_str_t> +struct top_dict_opset_t : dict_opset_t +{ + static void process_op (op_code_t op, interp_env_t<number_t>& env, top_dict_values_t<VAL> & 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 <typename OPSET, typename PARAM, typename ENV=num_interp_env_t> +struct dict_interpreter_t : interpreter_t<ENV> +{ + dict_interpreter_t (ENV& env_) : interpreter_t<ENV> (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<ENV> SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_CFF_INTERP_DICT_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-cff1-interp-cs.hh b/gfx/harfbuzz/src/hb-cff1-interp-cs.hh new file mode 100644 index 0000000000..d8868efa53 --- /dev/null +++ b/gfx/harfbuzz/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<CFF1Subrs> cff1_biased_subrs_t; + +struct cff1_cs_interp_env_t : cs_interp_env_t<number_t, CFF1Subrs> +{ + template <typename ACC> + 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<number_t, CFF1Subrs> SUPER; +}; + +template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff1_cs_interp_env_t, PARAM>> +struct cff1_cs_opset_t : cs_opset_t<number_t, OPSET, cff1_cs_interp_env_t, PARAM, PATH> +{ + /* 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<number_t, OPSET, cff1_cs_interp_env_t, PARAM, PATH> SUPER; +}; + +template <typename OPSET, typename PARAM> +using cff1_cs_interpreter_t = cs_interpreter_t<cff1_cs_interp_env_t, OPSET, PARAM>; + +} /* namespace CFF */ + +#endif /* HB_CFF1_INTERP_CS_HH */ diff --git a/gfx/harfbuzz/src/hb-cff2-interp-cs.hh b/gfx/harfbuzz/src/hb-cff2-interp-cs.hh new file mode 100644 index 0000000000..915b10cf39 --- /dev/null +++ b/gfx/harfbuzz/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<const blend_arg_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<number_t> deltas; +}; + +typedef biased_subrs_t<CFF2Subrs> cff2_biased_subrs_t; + +template <typename ELEM> +struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs> +{ + template <typename ACC> + 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<const ELEM> 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<float> scalars; + bool do_blend; + bool seen_vsindex_; + bool seen_blend; + + typedef cs_interp_env_t<ELEM, CFF2Subrs> SUPER; +}; +template <typename OPSET, typename PARAM, typename ELEM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t<ELEM>, PARAM>> +struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH> +{ + static void process_op (op_code_t op, cff2_cs_interp_env_t<ELEM> &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 <typename T = ELEM, + hb_enable_if (hb_is_same (T, blend_arg_t))> + static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env, + ELEM &arg, + const hb_array_t<const ELEM> 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 <typename T = ELEM, + hb_enable_if (!hb_is_same (T, blend_arg_t))> + static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env, + ELEM &arg, + const hb_array_t<const ELEM> blends, + unsigned n, unsigned i) + { + arg.set_real (arg.to_real () + env.blend_deltas (blends)); + } + + static void process_blend (cff2_cs_interp_env_t<ELEM> &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<const ELEM> 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<ELEM> &env, PARAM& param) + { + env.process_vsindex (); + env.clear_args (); + } + + private: + typedef cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH> SUPER; +}; + +template <typename OPSET, typename PARAM, typename ELEM> +using cff2_cs_interpreter_t = cs_interpreter_t<cff2_cs_interp_env_t<ELEM>, OPSET, PARAM>; + +} /* namespace CFF */ + +#endif /* HB_CFF2_INTERP_CS_HH */ diff --git a/gfx/harfbuzz/src/hb-common.cc b/gfx/harfbuzz/src/hb-common.cc new file mode 100644 index 0000000000..0c13c7d171 --- /dev/null +++ b/gfx/harfbuzz/src/hb-common.cc @@ -0,0 +1,1220 @@ +/* + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-machinery.hh" + + +/** + * 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_atomic_int_t _hb_options; + +void +_hb_options_init () +{ + hb_options_union_t u; + u.i = 0; + u.opts.initialized = true; + + 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<size_t>(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.i; +} + + +/* hb_tag_t */ + +/** + * hb_tag_from_string: + * @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: The #hb_tag_t corresponding to @str + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_tag_from_string (const char *str, int len) +{ + char tag[4]; + unsigned int i; + + if (!str || !len || !*str) + return HB_TAG_NONE; + + if (len < 0 || len > 4) + len = 4; + for (i = 0; i < (unsigned) len && str[i]; i++) + tag[i] = str[i]; + for (; i < 4; i++) + tag[i] = ' '; + + return HB_TAG (tag[0], tag[1], tag[2], tag[3]); +} + +/** + * hb_tag_to_string: + * @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 + **/ +void +hb_tag_to_string (hb_tag_t tag, char *buf) +{ + buf[0] = (char) (uint8_t) (tag >> 24); + buf[1] = (char) (uint8_t) (tag >> 16); + buf[2] = (char) (uint8_t) (tag >> 8); + buf[3] = (char) (uint8_t) (tag >> 0); +} + + +/* hb_direction_t */ + +static const char direction_strings[][4] = { + "ltr", + "rtl", + "ttb", + "btt" +}; + +/** + * hb_direction_from_string: + * @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. + * + * 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 + **/ +hb_direction_t +hb_direction_from_string (const char *str, int len) +{ + if (unlikely (!str || !len || !*str)) + return HB_DIRECTION_INVALID; + + /* Lets match loosely: just match the first letter, such that + * all of "ltr", "left-to-right", etc work! + */ + char c = TOLOWER (str[0]); + for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) + if (c == direction_strings[i][0]) + return (hb_direction_t) (HB_DIRECTION_LTR + i); + + return HB_DIRECTION_INVALID; +} + +/** + * hb_direction_to_string: + * @direction: The #hb_direction_t to convert + * + * Converts an #hb_direction_t to a string. + * + * Return value: (transfer none): The string corresponding to @direction + * + * Since: 0.9.2 + **/ +const char * +hb_direction_to_string (hb_direction_t direction) +{ + if (likely ((unsigned int) (direction - HB_DIRECTION_LTR) + < ARRAY_LENGTH (direction_strings))) + return direction_strings[direction - HB_DIRECTION_LTR]; + + return "invalid"; +} + + +/* hb_language_t */ + +struct hb_language_impl_t { + const char s[1]; +}; + +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, + 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, + 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 +}; + +static bool +lang_equal (hb_language_t v1, + const void *v2) +{ + const unsigned char *p1 = (const unsigned char *) v1; + const unsigned char *p2 = (const unsigned char *) v2; + + while (*p1 && *p1 == canon_map[*p2]) { + p1++; + p2++; + } + + return *p1 == canon_map[*p2]; +} + +#if 0 +static unsigned int +lang_hash (const void *key) +{ + const unsigned char *p = key; + unsigned int h = 0; + while (canon_map[*p]) + { + h = (h << 5) - h + canon_map[*p]; + p++; + } + + return h; +} +#endif + + +struct hb_language_item_t { + + struct hb_language_item_t *next; + hb_language_t lang; + + bool operator == (const char *s) const + { return lang_equal (lang, s); } + + 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) hb_malloc(len); + if (likely (lang)) + { + hb_memcpy((unsigned char *) lang, s, len); + for (unsigned char *p = (unsigned char *) lang; *p; p++) + *p = canon_map[*p]; + } + + return *this; + } + + void fini () { hb_free ((void *) lang); } +}; + + +/* Thread-safe lockfree language list */ + +static hb_atomic_ptr_t <hb_language_item_t> langs; + +static inline void +free_langs () +{ +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; + } +} + +static hb_language_item_t * +lang_find_or_insert (const char *key) +{ +retry: + 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 *) hb_calloc (1, sizeof (hb_language_item_t)); + if (unlikely (!lang)) + return nullptr; + lang->next = first_lang; + *lang = key; + if (unlikely (!lang->lang)) + { + hb_free (lang); + return nullptr; + } + + if (unlikely (!langs.cmpexch (first_lang, lang))) + { + lang->fini (); + hb_free (lang); + goto retry; + } + + if (!first_lang) + hb_atexit (free_langs); /* First person registers atexit() callback. */ + + return lang; +} + + +/** + * hb_language_from_string: + * @str: (array length=len) (element-type uint8_t): a string representing + * a BCP 47 language tag + * @len: length of the @str, or -1 if it is `NULL`-terminated. + * + * 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 BCP 47 language tag. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_language_from_string (const char *str, int len) +{ + if (!str || !len || !*str) + return HB_LANGUAGE_INVALID; + + hb_language_item_t *item = nullptr; + if (len >= 0) + { + /* NUL-terminate it. */ + char strbuf[64]; + len = hb_min (len, (int) sizeof (strbuf) - 1); + hb_memcpy (strbuf, str, len); + strbuf[len] = '\0'; + item = lang_find_or_insert (strbuf); + } + else + item = lang_find_or_insert (str); + + return likely (item) ? item->lang : HB_LANGUAGE_INVALID; +} + +/** + * hb_language_to_string: + * @language: The #hb_language_t to convert + * + * Converts an #hb_language_t to a string. + * + * Return value: (transfer none): + * A `NULL`-terminated string representing the @language. Must not be freed by + * the caller. + * + * Since: 0.9.2 + **/ +const char * +hb_language_to_string (hb_language_t language) +{ + if (unlikely (!language)) return nullptr; + + return language->s; +} + +/** + * hb_language_get_default: + * + * Fetch the default language from current locale. + * + * <note>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.</note> + * + * 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 () +{ + static hb_atomic_ptr_t <hb_language_t> default_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 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] == '-'); +} + + +/* hb_script_t */ + +/** + * hb_script_from_iso15924_tag: + * @tag: an #hb_tag_t representing an ISO 15924 tag. + * + * Converts an ISO 15924 script tag to a corresponding #hb_script_t. + * + * Return value: + * An #hb_script_t corresponding to the ISO 15924 tag. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag) +{ + if (unlikely (tag == HB_TAG_NONE)) + return HB_SCRIPT_INVALID; + + /* Be lenient, adjust case (one capital letter followed by three small letters) */ + tag = (tag & 0xDFDFDFDFu) | 0x00202020u; + + switch (tag) { + + /* These graduated from the 'Q' private-area codes, but + * the old code is still aliased by Unicode, and the Qaai + * one in use by ICU. */ + 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 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; + case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; + case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; + } + + /* If it looks right, just use the tag as a script */ + if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u) + return (hb_script_t) tag; + + /* Otherwise, return unknown */ + return HB_SCRIPT_UNKNOWN; +} + +/** + * 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. + * + * 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. + * + * Since: 0.9.2 + **/ +hb_script_t +hb_script_from_string (const char *str, int len) +{ + return hb_script_from_iso15924_tag (hb_tag_from_string (str, len)); +} + +/** + * hb_script_to_iso15924_tag: + * @script: an #hb_script_t to convert. + * + * Converts an #hb_script_t to a corresponding ISO 15924 script tag. + * + * Return value: + * An #hb_tag_t representing an ISO 15924 script tag. + * + * Since: 0.9.2 + **/ +hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script) +{ + return (hb_tag_t) script; +} + +/** + * hb_script_get_horizontal_direction: + * @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: The horizontal #hb_direction_t of @script + * + * Since: 0.9.2 + **/ +hb_direction_t +hb_script_get_horizontal_direction (hb_script_t script) +{ + /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ + switch ((hb_tag_t) script) + { + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_HEBREW: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_SYRIAC: + case HB_SCRIPT_THAANA: + + /* Unicode-4.0 additions */ + case HB_SCRIPT_CYPRIOT: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_KHAROSHTHI: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_PHOENICIAN: + case HB_SCRIPT_NKO: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_LYDIAN: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_AVESTAN: + case HB_SCRIPT_IMPERIAL_ARAMAIC: + case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: + case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: + case HB_SCRIPT_OLD_SOUTH_ARABIAN: + case HB_SCRIPT_OLD_TURKIC: + case HB_SCRIPT_SAMARITAN: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_MEROITIC_CURSIVE: + case HB_SCRIPT_MEROITIC_HIEROGLYPHS: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MENDE_KIKAKUI: + case HB_SCRIPT_NABATAEAN: + case HB_SCRIPT_OLD_NORTH_ARABIAN: + case HB_SCRIPT_PALMYRENE: + case HB_SCRIPT_PSALTER_PAHLAVI: + + /* Unicode-8.0 additions */ + 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_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 + * + * Returns library version as three integer components. + * + * Since: 0.9.2 + **/ +void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro) +{ + *major = HB_VERSION_MAJOR; + *minor = HB_VERSION_MINOR; + *micro = HB_VERSION_MICRO; +} + +/** + * hb_version_string: + * + * Returns library version as a string with three components. + * + * Return value: Library version string + * + * Since: 0.9.2 + **/ +const char * +hb_version_string () +{ + return HB_VERSION_STRING; +} + +/** + * hb_version_atleast: + * @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: `true` if the library is equal to or greater than + * the test value, `false` otherwise + * + * Since: 0.9.30 + **/ +hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro) +{ + return HB_VERSION_ATLEAST (major, minor, micro); +} + + + +/* hb_feature_t and hb_variation_t */ + +static bool +parse_space (const char **pp, const char *end) +{ + while (*pp < end && ISSPACE (**pp)) + (*pp)++; + return true; +} + +static bool +parse_char (const char **pp, const char *end, char c) +{ + parse_space (pp, end); + + if (*pp == end || **pp != c) + return false; + + (*pp)++; + return true; +} + +static bool +parse_uint (const char **pp, const char *end, unsigned int *pv) +{ + /* 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; + return true; +} + +static bool +parse_uint32 (const char **pp, const char *end, uint32_t *pv) +{ + /* 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; + return true; +} + +static bool +parse_bool (const char **pp, const char *end, uint32_t *pv) +{ + parse_space (pp, end); + + const char *p = *pp; + while (*pp < end && ISALPHA(**pp)) + (*pp)++; + + /* CSS allows on/off as aliases 1/0. */ + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') + *pv = 1; + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') + *pv = 0; + else + return false; + + return true; +} + +/* hb_feature_t */ + +static bool +parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) +{ + if (parse_char (pp, end, '-')) + feature->value = 0; + else { + parse_char (pp, end, '+'); + feature->value = 1; + } + + return true; +} + +static bool +parse_tag (const char **pp, const char *end, hb_tag_t *tag) +{ + parse_space (pp, end); + + char quote = 0; + + if (*pp < end && (**pp == '\'' || **pp == '"')) + { + quote = **pp; + (*pp)++; + } + + const char *p = *pp; + while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote)) + (*pp)++; + + if (p == *pp || *pp - p > 4) + return false; + + *tag = hb_tag_from_string (p, *pp - p); + + if (quote) + { + /* CSS expects exactly four bytes. And we only allow quotations for + * CSS compatibility. So, enforce the length. */ + if (*pp - p != 4) + return false; + if (*pp == end || **pp != quote) + return false; + (*pp)++; + } + + return true; +} + +static bool +parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) +{ + parse_space (pp, end); + + bool has_start; + + 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, ':') || parse_char (pp, end, ';')) { + parse_uint (pp, end, &feature->end); + } else { + if (has_start) + feature->end = feature->start + 1; + } + + return parse_char (pp, end, ']'); +} + +static bool +parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) +{ + bool had_equal = parse_char (pp, end, '='); + bool had_value = parse_uint32 (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 equal-sign is ok, but not required. */ + return !had_equal || had_value; +} + +static bool +parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) +{ + return parse_feature_value_prefix (pp, end, feature) && + parse_tag (pp, end, &feature->tag) && + parse_feature_indices (pp, end, feature) && + parse_feature_value_postfix (pp, end, feature) && + parse_space (pp, end) && + *pp == end; +} + +/** + * 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 + * @feature: (out): the #hb_feature_t to initialize with the parsed values + * + * Parses a string into a #hb_feature_t. + * + * 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: + * + * <informaltable pgwide='1' align='left' frame='none'> + * <tgroup cols='5'> + * <thead> + * <row><entry>Syntax</entry> <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row> + * </thead> + * <tbody> + * <row><entry>Setting value:</entry></row> + * <row><entry>kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> + * <row><entry>+kern</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> + * <row><entry>-kern</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row> + * <row><entry>kern=0</entry> <entry>0</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature off</entry></row> + * <row><entry>kern=1</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> + * <row><entry>aalt=2</entry> <entry>2</entry> <entry>0</entry> <entry>∞</entry> <entry>Choose 2nd alternate</entry></row> + * <row><entry>Setting index:</entry></row> + * <row><entry>kern[]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> + * <row><entry>kern[:]</entry> <entry>1</entry> <entry>0</entry> <entry>∞</entry> <entry>Turn feature on</entry></row> + * <row><entry>kern[5:]</entry> <entry>1</entry> <entry>5</entry> <entry>∞</entry> <entry>Turn feature on, partial</entry></row> + * <row><entry>kern[:5]</entry> <entry>1</entry> <entry>0</entry> <entry>5</entry> <entry>Turn feature on, partial</entry></row> + * <row><entry>kern[3:5]</entry> <entry>1</entry> <entry>3</entry> <entry>5</entry> <entry>Turn feature on, range</entry></row> + * <row><entry>kern[3]</entry> <entry>1</entry> <entry>3</entry> <entry>3+1</entry> <entry>Turn feature on, single char</entry></row> + * <row><entry>Mixing it all:</entry></row> + * <row><entry>aalt[3:5]=2</entry> <entry>2</entry> <entry>3</entry> <entry>5</entry> <entry>Turn 2nd alternate on for range</entry></row> + * </tbody> + * </tgroup> + * </informaltable> + * + * Return value: + * `true` if @str is successfully parsed, `false` otherwise + * + * Since: 0.9.5 + **/ +hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature) +{ + hb_feature_t feat; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_feature (&str, str + len, &feat))) + { + if (feature) + *feature = feat; + return true; + } + + if (feature) + hb_memset (feature, 0, sizeof (*feature)); + return false; +} + +/** + * hb_feature_to_string: + * @feature: an #hb_feature_t to convert + * @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 + * understood by hb_feature_from_string(). The client in responsible for + * allocating big enough size for @buf, 128 bytes is more than enough. + * + * Since: 0.9.5 + **/ +void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + if (feature->value == 0) + s[len++] = '-'; + hb_tag_to_string (feature->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END) + { + s[len++] = '['; + if (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 != 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 += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); + } + assert (len < ARRAY_LENGTH (s)); + len = hb_min (len, size - 1); + hb_memcpy (buf, s, len); + buf[len] = '\0'; +} + +/* hb_variation_t */ + +static bool +parse_variation_value (const char **pp, const char *end, hb_variation_t *variation) +{ + parse_char (pp, end, '='); /* Optional. */ + double v; + if (unlikely (!hb_parse_double (pp, end, &v))) return false; + + variation->value = v; + return true; +} + +static bool +parse_one_variation (const char **pp, const char *end, hb_variation_t *variation) +{ + return parse_tag (pp, end, &variation->tag) && + parse_variation_value (pp, end, variation) && + parse_space (pp, end) && + *pp == end; +} + +/** + * 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 + */ +hb_bool_t +hb_variation_from_string (const char *str, int len, + hb_variation_t *variation) +{ + hb_variation_t var; + + if (len < 0) + len = strlen (str); + + if (likely (parse_one_variation (&str, str + len, &var))) + { + if (variation) + *variation = var; + return true; + } + + if (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_remove_pointer<hb_locale_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 + */ +void +hb_variation_to_string (hb_variation_t *variation, + char *buf, unsigned int size) +{ + if (unlikely (!size)) return; + + char s[128]; + unsigned int len = 0; + hb_tag_to_string (variation->tag, s + len); + len += 4; + while (len && s[len - 1] == ' ') + len--; + s[len++] = '='; + + 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 = 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/gfx/harfbuzz/src/hb-common.h b/gfx/harfbuzz/src/hb-common.h new file mode 100644 index 0000000000..a9fe666b39 --- /dev/null +++ b/gfx/harfbuzz/src/hb-common.h @@ -0,0 +1,938 @@ +/* + * Copyright © 2007,2008,2009 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): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> 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" { +# define HB_END_DECLS } +# else /* !__cplusplus */ +# define HB_BEGIN_DECLS +# define HB_END_DECLS +# endif /* !__cplusplus */ +#endif + +#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ + defined (_sgi) || defined (__sun) || defined (sun) || \ + defined (__digital__) || defined (__HP_cc) +# include <inttypes.h> +#elif defined (_AIX) +# include <sys/inttypes.h> +#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 <linux/types.h> +#else +# include <stdint.h> +#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 { + uint32_t u32; + int32_t i32; + uint16_t u16[2]; + int16_t i16[2]; + uint8_t u8[4]; + 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; + +/** + * 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. */ +HB_EXTERN hb_tag_t +hb_tag_from_string (const char *str, int len); + +/* buf should have 4 bytes. */ +HB_EXTERN void +hb_tag_to_string (hb_tag_t tag, char *buf); + + +/** + * hb_direction_t: + * @HB_DIRECTION_INVALID: Initial, unset direction. + * @HB_DIRECTION_LTR: Text is set horizontally from left to right. + * @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, + HB_DIRECTION_LTR = 4, + HB_DIRECTION_RTL, + HB_DIRECTION_TTB, + HB_DIRECTION_BTT +} hb_direction_t; + +/* len=-1 means str is NUL-terminated */ +HB_EXTERN hb_direction_t +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 +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_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. + * + **/ + +/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */ +typedef enum +{ + 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*/ + + 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*/ + + HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'), /*2.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'), /*3.0*/ + + 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*/ + + 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*/ + + 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*/ + + 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*/ + + 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*/ + + 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*/ + + 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*/ + + 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*/ + + 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 + */ + 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*/ + + 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 + */ + 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 + */ + 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, + + /*< private >*/ + + /* Dummy values to ensure any hb_tag_t value can be passed/stored as hb_script_t + * 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: + * + * https://lists.freedesktop.org/archives/harfbuzz/2014-March/004150.html + */ + _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX_SIGNED, /*< skip >*/ + _HB_SCRIPT_MAX_VALUE_SIGNED = HB_TAG_MAX_SIGNED /*< skip >*/ + +} hb_script_t; + + +/* Script functions */ + +HB_EXTERN hb_script_t +hb_script_from_iso15924_tag (hb_tag_t tag); + +HB_EXTERN hb_script_t +hb_script_from_string (const char *str, int len); + +HB_EXTERN hb_tag_t +hb_script_to_iso15924_tag (hb_script_t script); + +HB_EXTERN hb_direction_t +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; + unsigned int start; + unsigned int end; +} hb_feature_t; + +HB_EXTERN hb_bool_t +hb_feature_from_string (const char *str, int len, + hb_feature_t *feature); + +HB_EXTERN void +hb_feature_to_string (hb_feature_t *feature, + char *buf, unsigned int size); + +/** + * 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 { + hb_tag_t tag; + float value; +} hb_variation_t; + +HB_EXTERN hb_bool_t +hb_variation_from_string (const char *str, int len, + hb_variation_t *variation); + +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 + +#endif /* HB_COMMON_H */ diff --git a/gfx/harfbuzz/src/hb-config.hh b/gfx/harfbuzz/src/hb-config.hh new file mode 100644 index 0000000000..816c55c7d3 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-coretext.cc b/gfx/harfbuzz/src/hb-coretext.cc new file mode 100644 index 0000000000..a87cb5cd02 --- /dev/null +++ b/gfx/harfbuzz/src/hb-coretext.cc @@ -0,0 +1,1197 @@ +/* + * Copyright © 2012,2013 Mozilla Foundation. + * 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_CORETEXT + +#include "hb-shaper-impl.hh" + +#include "hb-coretext.h" +#include "hb-aat-layout.hh" + + +/** + * 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 + +static void +release_table_data (void *user_data) +{ + CFDataRef cf_data = reinterpret_cast<CFDataRef> (user_data); + CFRelease(cf_data); +} + +static hb_blob_t * +_hb_cg_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + CGFontRef cg_font = reinterpret_cast<CGFontRef> (user_data); + CFDataRef cf_data = CGFontCopyTableForTag (cg_font, tag); + if (unlikely (!cf_data)) + return nullptr; + + const char *data = reinterpret_cast<const char*> (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<void *> (const_cast<__CFData *> (cf_data)), + release_table_data); +} + +static void +_hb_cg_font_release (void *data) +{ + CGFontRelease ((CGFontRef) data); +} + + +static CTFontDescriptorRef +get_last_resort_font_desc () +{ + // TODO Handle allocation failures? + CTFontDescriptorRef last_resort = CTFontDescriptorCreateWithNameAndSize (CFSTR("LastResort"), 0); + CFArrayRef cascade_list = CFArrayCreate (kCFAllocatorDefault, + (const void **) &last_resort, + 1, + &kCFTypeArrayCallBacks); + CFRelease (last_resort); + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontCascadeListAttribute, + (const void **) &cascade_list, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (cascade_list); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + return font_desc; +} + +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_destroy ((hb_blob_t *) info); +} + +static CGFontRef +create_cg_font (hb_face_t *face) +{ + CGFontRef cg_font = nullptr; + if (face->destroy == _hb_cg_font_release) + { + cg_font = CGFontRetain ((CGFontRef) face->user_data); + } + else + { + hb_blob_t *blob = hb_face_reference_blob (face); + unsigned int blob_length; + const char *blob_data = hb_blob_get_data (blob, &blob_length); + if (unlikely (!blob_length)) + DEBUG_MSG (CORETEXT, face, "Face has empty blob"); + + CGDataProviderRef provider = CGDataProviderCreateWithData (blob, blob_data, blob_length, &release_data); + if (likely (provider)) + { + cg_font = CGFontCreateWithDataProvider (provider); + if (unlikely (!cg_font)) + DEBUG_MSG (CORETEXT, face, "Face CGFontCreateWithDataProvider() failed"); + CGDataProviderRelease (provider); + } + } + return cg_font; +} + +static CTFontRef +create_ct_font (CGFontRef cg_font, CGFloat font_size) +{ + CTFontRef ct_font = nullptr; + + /* CoreText does not enable trak table usage / tracking when creating a CTFont + * using CTFontCreateWithGraphicsFont. The only way of enabling tracking seems + * to be through the CTFontCreateUIFontForLanguage call. */ + CFStringRef cg_postscript_name = CGFontCopyPostScriptName (cg_font); + 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; + + ct_font = CTFontCreateUIFontForLanguage (font_type, font_size, nullptr); + CFStringRef ct_result_name = CTFontCopyPostScriptName(ct_font); + if (CFStringCompare (ct_result_name, cg_postscript_name, 0) != kCFCompareEqualTo) + { + CFRelease(ct_font); + ct_font = nullptr; + } + CFRelease (ct_result_name); + } + CFRelease (cg_postscript_name); + + if (!ct_font) + ct_font = CTFontCreateWithGraphicsFont (cg_font, font_size, nullptr, nullptr); + + if (unlikely (!ct_font)) { + DEBUG_MSG (CORETEXT, cg_font, "Font CTFontCreateWithGraphicsFont() failed"); + return nullptr; + } + + /* crbug.com/576941 and crbug.com/625902 and the investigation in the latter + * bug indicate that the cascade list reconfiguration occasionally causes + * crashes in CoreText on OS X 10.9, thus let's skip this step on older + * operating system versions. Except for the emoji font, where _not_ + * 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); + if (!isEmojiFont) + return ct_font; + } + + 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. */ + { + CTFontDescriptorRef last_resort_font_desc = get_last_resort_font_desc (); + CTFontRef new_ct_font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, last_resort_font_desc); + CFRelease (last_resort_font_desc); + if (new_ct_font) + { + /* The CTFontCreateCopyWithAttributes call fails to stay on the same font + * when reconfiguring the cascade list and may switch to a different font + * when there are fonts that go by the same name, since the descriptor is + * just name and size. + * + * Avoid reconfiguring the cascade lists if the new font is outside the + * 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 = 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; + } else { + CFRelease (new_ct_font); + DEBUG_MSG (CORETEXT, ct_font, "Discarding reconfigured CTFont, location changed."); + } + if (new_url) + CFRelease (new_url); + } + else + DEBUG_MSG (CORETEXT, ct_font, "Font copy with empty cascade list failed"); + } + + if (original_url) + CFRelease (original_url); + return ct_font; +} + +hb_coretext_face_data_t * +_hb_coretext_shaper_face_data_create (hb_face_t *face) +{ + CGFontRef cg_font = create_cg_font (face); + + if (unlikely (!cg_font)) + { + DEBUG_MSG (CORETEXT, face, "CGFont creation failed.."); + return nullptr; + } + + return (hb_coretext_face_data_t *) cg_font; +} + +void +_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 (_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) +{ + return (CGFontRef) (const void *) face->data.coretext; +} + + +hb_coretext_font_data_t * +_hb_coretext_shaper_font_data_create (hb_font_t *font) +{ + hb_face_t *face = font->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 = (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)) + { + DEBUG_MSG (CORETEXT, font, "CGFont creation failed.."); + return nullptr; + } + + 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_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, 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_immutable (font))) + return font; + + hb_font_set_ptem (font, CTFontGetSize (ct_font)); + + /* Let there be dragons here... */ + 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) +{ + CTFontRef ct_font = (CTFontRef) (const void *) font->data.coretext; + return ct_font ? (CTFontRef) ct_font : nullptr; +} + + +/* + * shaper + */ + +struct feature_record_t { + unsigned int feature; + unsigned int setting; +}; + +struct active_feature_t { + feature_record_t rec; + unsigned int order; + + 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 : + a->order < b->order ? -1 : a->order > b->order ? 1 : + a->rec.setting < b->rec.setting ? -1 : a->rec.setting > b->rec.setting ? 1 : + 0; + } + bool operator== (const active_feature_t& f) const { + return cmp (this, &f) == 0; + } +}; + +struct feature_event_t { + unsigned int index; + bool start; + active_feature_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 : + active_feature_t::cmp (&a->feature, &b->feature); + } +}; + +struct range_record_t { + CTFontRef font; + unsigned int index_first; /* == start */ + unsigned int index_last; /* == end - 1 */ +}; + + +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_face_t *face = font->face; + 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 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 + * continue pointing to B2 even though B2 was merged into B1's + * cluster... */ + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) + buffer->merge_clusters (i - 1, i + 1); + } + + hb_vector_t<range_record_t> range_records; + + /* + * Set up features. + * (copied + modified from code from hb-uniscribe.cc) + */ + if (num_features) + { + /* Sort features by start/end events. */ + hb_vector_t<feature_event_t> feature_events; + for (unsigned int i = 0; i < num_features; i++) + { + 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 (); + 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. */ + { + active_feature_t feature; + feature.rec.feature = HB_TAG_NONE; + feature.rec.setting = 0; + feature.order = num_features + 1; + + feature_event_t *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_feature_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. */ + range_record_t *range = range_records.push (); + + 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.length; j++) + { +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + CFStringRef keys[] = { + kCTFontFeatureTypeIdentifierKey, + kCTFontFeatureSelectorIdentifierKey + }; + CFNumberRef values[] = { + 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, + (const void **) values, + ARRAY_LENGTH (keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++) + CFRelease (values[i]); + + CFArrayAppendValue (features_array, dict); + CFRelease (dict); + + } + + CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault, + (const void **) &kCTFontFeatureSettingsAttribute, + (const void **) &features_array, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (features_array); + + CTFontDescriptorRef font_desc = CTFontDescriptorCreateWithAttributes (attributes); + CFRelease (attributes); + + range->font = CTFontCreateCopyWithAttributes (ct_font, 0.0, nullptr, font_desc); + CFRelease (font_desc); + } + else + { + range->font = nullptr; + } + + range->index_first = last_index; + range->index_last = event->index - 1; + + last_index = event->index; + } + + if (event->start) + { + active_features.push (event->feature); + } else { + active_feature_t *feature = active_features.lsearch (event->feature); + if (feature) + active_features.remove_ordered (feature - active_features.arrayZ); + } + } + } + + 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)) \ + { \ + on_no_room; \ + assert (0); \ + } \ + scratch += _consumed; \ + scratch_size -= _consumed; \ + } while (0) + + 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; + if (likely (c <= 0xFFFFu)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len, ((void)nullptr) /*nothing*/); + 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. */ + } + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (CORETEXT, nullptr, __VA_ARGS__); \ + ret = false; \ + goto fail; \ + } HB_STMT_END + + bool ret = true; + CFStringRef string_ref = nullptr; + CTLineRef line = nullptr; + + if (false) + { +resize_and_retry: + DEBUG_MSG (CORETEXT, buffer, "Buffer resize"); + /* string_ref uses the scratch-buffer for backing store, and line references + * string_ref (via attr_string). We must release those before resizing buffer. */ + assert (string_ref); + assert (line); + CFRelease (string_ref); + CFRelease (line); + string_ref = nullptr; + line = nullptr; + + /* Get previous start-of-scratch-area, that we use later for readjusting + * our existing scratch arrays. */ + unsigned int old_scratch_used; + hb_buffer_t::scratch_buffer_t *old_scratch; + old_scratch = buffer->get_scratch_buffer (&old_scratch_used); + old_scratch_used = scratch - old_scratch; + + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + + /* Adjust scratch, pchars, and log_cluster arrays. This is ugly, but really the + * cleanest way to do without completely restructuring the rest of this shaper. */ + scratch = buffer->get_scratch_buffer (&scratch_size); + pchars = reinterpret_cast<UniChar *> (((char *) scratch + ((char *) pchars - (char *) old_scratch))); + log_clusters = reinterpret_cast<unsigned int *> (((char *) scratch + ((char *) log_clusters - (char *) old_scratch))); + scratch += old_scratch_used; + scratch_size -= old_scratch_used; + } + { + string_ref = CFStringCreateWithCharactersNoCopy (nullptr, + pchars, chars_len, + kCFAllocatorNull); + if (unlikely (!string_ref)) + FAIL ("CFStringCreateWithCharactersNoCopy failed"); + + /* Create an attributed string, populate it, and create a line from it, then release attributed string. */ + { + CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (kCFAllocatorDefault, + chars_len); + if (unlikely (!attr_string)) + FAIL ("CFAttributedStringCreateMutable failed"); + CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + { + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTVerticalFormsAttributeName, kCFBooleanTrue); + } + + if (buffer->props.language) + { +/* 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 !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && MAC_OS_X_VERSION_MIN_REQUIRED < 1090 +# define kCTLanguageAttributeName CFSTR ("NSLanguage") +#endif + 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); + } + CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len), + kCTFontAttributeName, ct_font); + + if (num_features && range_records.length) + { + unsigned int start = 0; + range_record_t *last_range = &range_records[0]; + for (unsigned int k = 0; k < chars_len; k++) + { + range_record_t *range = last_range; + while (log_clusters[k] < range->index_first) + range--; + while (log_clusters[k] > range->index_last) + range++; + if (range != last_range) + { + if (last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, k - start), + kCTFontAttributeName, last_range->font); + + start = k; + } + + last_range = range; + } + if (start != chars_len && last_range->font) + CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start), + kCTFontAttributeName, last_range->font); + } + /* Enable/disable kern if requested. + * + * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText. + */ + if (num_features) + { + unsigned int zeroint = 0; + CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint); + for (unsigned int i = 0; i < num_features; i++) + { + const hb_feature_t &feature = features[i]; + if (feature.tag == HB_TAG('k','e','r','n') && + feature.start < chars_len && feature.start < feature.end) + { + CFRange feature_range = CFRangeMake (feature.start, + hb_min (feature.end, chars_len) - feature.start); + if (feature.value) + CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName); + else + CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero); + } + } + CFRelease (zero); + } + + 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, + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (level_number); + if (unlikely (!options)) + { + CFRelease (attr_string); + FAIL ("CFDictionaryCreate failed"); + } + + CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options); + CFRelease (options); + CFRelease (attr_string); + if (unlikely (!typesetter)) + FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed"); + + line = CTTypesetterCreateLine (typesetter, CFRangeMake(0, 0)); + CFRelease (typesetter); + if (unlikely (!line)) + FAIL ("CTTypesetterCreateLine failed"); + } + + CFArrayRef glyph_runs = CTLineGetGlyphRuns (line); + unsigned int num_runs = CFArrayGetCount (glyph_runs); + DEBUG_MSG (CORETEXT, nullptr, "Num runs: %d", num_runs); + + buffer->len = 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://crbug.com/469028 + */ + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + { + advances_so_far -= CTLineGetTrailingWhitespaceWidth (line); + if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction)) + advances_so_far = -advances_so_far; + } + + const CFRange range_all = CFRangeMake (0, 0); + + for (unsigned int i = 0; i < num_runs; i++) + { + CTRunRef run = static_cast<CTRunRef>(CFArrayGetValueAtIndex (glyph_runs, i)); + CTRunStatus run_status = CTRunGetStatus (run); + status_or |= run_status; + DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status); + 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", (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, + * so we must detect if the returned run uses a font other than the requested + * one and fill in the buffer with .notdef glyphs instead of random glyph + * indices from a different font. + */ + CFDictionaryRef attributes = CTRunGetAttributes (run); + CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName)); + if (!CFEqual (run_ct_font, ct_font)) + { + /* The run doesn't use our main font instance. We have to figure out + * whether font fallback happened, or this is just CoreText giving us + * another CTFont using the same underlying CGFont. CoreText seems + * to do that in a variety of situations, one of which being vertical + * text, but also perhaps for caching reasons. + * + * First, see if it uses any of our subfonts created to set font features... + * + * Next, compare the CGFont to the one we used to create our fonts. + * Even this doesn't work all the time. + * + * Finally, we compare PS names, which I don't think are unique... + * + * Looks like if we really want to be sure here we have to modify the + * font to change the name table, similar to what we do in the uniscribe + * backend. + * + * However, even that wouldn't work if we were passed in the CGFont to + * construct a hb_face to begin with. + * + * 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.length; i++) + if (range_records[i].font && CFEqual (run_ct_font, range_records[i].font)) + { + matched = true; + break; + } + if (!matched) + { + CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, nullptr); + if (run_cg_font) + { + matched = CFEqual (run_cg_font, cg_font); + CFRelease (run_cg_font); + } + } + if (!matched) + { + CFStringRef font_ps_name = CTFontCopyName (ct_font, kCTFontPostScriptNameKey); + CFStringRef run_ps_name = CTFontCopyName (run_ct_font, kCTFontPostScriptNameKey); + CFComparisonResult result = CFStringCompare (run_ps_name, font_ps_name, 0); + CFRelease (run_ps_name); + CFRelease (font_ps_name); + if (result == kCFCompareEqualTo) + matched = true; + } + if (!matched) + { + CFRange range = CTRunGetStringRange (run); + 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; + hb_glyph_info_t *info = buffer->info + buffer->len; + + hb_codepoint_t notdef = 0; + hb_direction_t dir = buffer->props.direction; + hb_position_t x_advance, y_advance, x_offset, y_offset; + hb_font_get_glyph_advance_for_direction (font, notdef, dir, &x_advance, &y_advance); + hb_font_get_glyph_origin_for_direction (font, notdef, dir, &x_offset, &y_offset); + hb_position_t advance = x_advance + y_advance; + x_offset = -x_offset; + y_offset = -y_offset; + + unsigned int old_len = buffer->len; + for (CFIndex j = range.location; j < range.location + range.length; j++) + { + UniChar ch = CFStringGetCharacterAtIndex (string_ref, j); + if (hb_in_range<UniChar> (ch, 0xDC00u, 0xDFFFu) && range.location < j) + { + ch = CFStringGetCharacterAtIndex (string_ref, j - 1); + if (hb_in_range<UniChar> (ch, 0xD800u, 0xDBFFu)) + /* This is the second of a surrogate pair. Don't need .notdef + * for this one. */ + continue; + } + if (buffer->unicode->is_default_ignorable (ch)) + continue; + + info->codepoint = notdef; + info->cluster = log_clusters[j]; + + info->mask = advance; + info->var1.i32 = x_offset; + info->var2.i32 = y_offset; + + info++; + buffer->len++; + } + if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction)) + buffer->reverse_range (old_len, buffer->len); + advances_so_far += run_advance; + continue; + } + } + + unsigned int num_glyphs = CTRunGetGlyphCount (run); + if (num_glyphs == 0) + continue; + + if (!buffer->ensure_inplace (buffer->len + num_glyphs)) + goto resize_and_retry; + + hb_glyph_info_t *run_info = buffer->info + buffer->len; + + /* Testing used to indicate that CTRunGetGlyphsPtr, etc (almost?) always + * succeed, and so copying data to our own buffer will be rare. Reports + * have it that this changed in OS X 10.10 Yosemite, and nullptr is returned + * frequently. At any rate, we can test that codepath by setting USE_PTR + * to false. */ + +#define USE_PTR true + +#define SCRATCH_SAVE() \ + unsigned int scratch_size_saved = scratch_size; \ + hb_buffer_t::scratch_buffer_t *scratch_saved = scratch + +#define SCRATCH_RESTORE() \ + scratch_size = scratch_size_saved; \ + scratch = scratch_saved + + { /* Setup glyphs */ + SCRATCH_SAVE(); + const CGGlyph* glyphs = USE_PTR ? CTRunGetGlyphsPtr (run) : nullptr; + if (!glyphs) { + ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs, goto resize_and_retry); + CTRunGetGlyphs (run, range_all, glyph_buf); + glyphs = glyph_buf; + } + const CFIndex* string_indices = USE_PTR ? CTRunGetStringIndicesPtr (run) : nullptr; + if (!string_indices) { + ALLOCATE_ARRAY (CFIndex, index_buf, num_glyphs, goto resize_and_retry); + CTRunGetStringIndices (run, range_all, index_buf); + string_indices = index_buf; + } + hb_glyph_info_t *info = run_info; + for (unsigned int j = 0; j < num_glyphs; j++) + { + info->codepoint = glyphs[j]; + info->cluster = log_clusters[string_indices[j]]; + info++; + } + SCRATCH_RESTORE(); + } + { + /* 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(); + const CGPoint* positions = USE_PTR ? CTRunGetPositionsPtr (run) : nullptr; + if (!positions) { + ALLOCATE_ARRAY (CGPoint, position_buf, num_glyphs, goto resize_and_retry); + CTRunGetPositions (run, range_all, position_buf); + positions = position_buf; + } + hb_glyph_info_t *info = run_info; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + { + hb_position_t x_offset = round ((positions[0].x - advances_so_far) * x_mult); + for (unsigned int j = 0; j < num_glyphs; j++) + { + 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); + /* int cast necessary to pass through negative values. */ + info->mask = (int) round (advance * x_mult); + info->var1.i32 = x_offset; + info->var2.i32 = round (positions[j].y * y_mult); + info++; + } + } + else + { + hb_position_t y_offset = round ((positions[0].y - advances_so_far) * y_mult); + for (unsigned int j = 0; j < num_glyphs; j++) + { + 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); + /* 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++; + } + } + SCRATCH_RESTORE(); + advances_so_far += run_advance; + } +#undef SCRATCH_RESTORE +#undef SCRATCH_SAVE +#undef USE_PTR +#undef ALLOCATE_ARRAY + + buffer->len += num_glyphs; + } + + buffer->clear_positions (); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + for (unsigned int i = 0; i < count; i++) + { + pos->x_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + + info++; pos++; + } + else + for (unsigned int i = 0; i < count; i++) + { + pos->y_advance = info->mask; + pos->x_offset = info->var1.i32; + pos->y_offset = info->var2.i32; + + info++; pos++; + } + + /* Fix up clusters so that we never return out-of-order indices; + * if core text has reordered glyphs, we'll merge them to the + * beginning of the reordered cluster. CoreText is nice enough + * to tell us whenever it has produced nonmonotonic results... + * Note that we assume the input clusters were nonmonotonic to + * begin with. + * + * 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) && + buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_CHARACTERS) + { + hb_glyph_info_t *info = buffer->info; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) + { + unsigned int cluster = info[count - 1].cluster; + for (unsigned int i = count - 1; i > 0; i--) + { + cluster = hb_min (cluster, info[i - 1].cluster); + info[i - 1].cluster = cluster; + } + } + else + { + unsigned int cluster = info[0].cluster; + for (unsigned int i = 1; i < count; i++) + { + cluster = hb_min (cluster, info[i].cluster); + info[i].cluster = cluster; + } + } + } + } + + /* 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 + +fail: + if (string_ref) + CFRelease (string_ref); + if (line) + CFRelease (line); + + for (unsigned int i = 0; i < range_records.length; i++) + if (range_records[i].font) + CFRelease (range_records[i].font); + + return ret; +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-coretext.h b/gfx/harfbuzz/src/hb-coretext.h new file mode 100644 index 0000000000..e53dbaf2c7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-coretext.h @@ -0,0 +1,96 @@ +/* + * Copyright © 2012 Mozilla Foundation. + * + * 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. + * + * Mozilla Author(s): Jonathan Kew + */ + +#ifndef HB_CORETEXT_H +#define HB_CORETEXT_H + +#include "hb.h" + +#include <TargetConditionals.h> +#if TARGET_OS_IPHONE +# include <CoreText/CoreText.h> +# include <CoreGraphics/CoreGraphics.h> +#else +# include <ApplicationServices/ApplicationServices.h> +#endif + +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') + + +HB_EXTERN hb_face_t * +hb_coretext_face_create (CGFontRef cg_font); + +HB_EXTERN hb_font_t * +hb_coretext_font_create (CTFontRef ct_font); + + +HB_EXTERN CGFontRef +hb_coretext_face_get_cg_font (hb_face_t *face); + +HB_EXTERN CTFontRef +hb_coretext_font_get_ct_font (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_CORETEXT_H */ diff --git a/gfx/harfbuzz/src/hb-cplusplus.hh b/gfx/harfbuzz/src/hb-cplusplus.hh new file mode 100644 index 0000000000..531ef1b7c8 --- /dev/null +++ b/gfx/harfbuzz/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 <functional> +#include <utility> + +#if 0 +#if !(__cplusplus >= 201103L) +#error "HarfBuzz C++ helpers require C++11" +#endif +#endif + +namespace hb { + + +template <typename T> +struct vtable; + +template <typename T> +struct shared_ptr +{ + using element_type = T; + + using v = vtable<T>; + + 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<typename T> struct is_shared_ptr : std::false_type {}; +template<typename T> struct is_shared_ptr<shared_ptr<T>> : std::true_type {}; + +template <typename T> +struct unique_ptr +{ + using element_type = T; + + using v = vtable<T>; + + 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<typename T> struct is_unique_ptr : std::false_type {}; +template<typename T> struct is_unique_ptr<unique_ptr<T>> : std::true_type {}; + +template <typename T, + T * (*_get_empty) (void), + T * (*_reference) (T *), + void (*_destroy) (T *), + hb_bool_t (*_set_user_data) (T *, + hb_user_data_key_t *, + void *, + hb_destroy_func_t, + hb_bool_t), + void * (*_get_user_data) (const T *, + hb_user_data_key_t *)> +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<hb_##name##_t> \ + : vtable_t<hb_##name##_t, \ + &hb_##name##_get_empty, \ + &hb_##name##_reference, \ + &hb_##name##_destroy, \ + &hb_##name##_set_user_data, \ + &hb_##name##_get_user_data> {} + +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<hb_##name##_t> \ + : vtable_t<hb_##name##_t, \ + nullptr, \ + &hb_##name##_reference, \ + &hb_##name##_destroy, \ + &hb_##name##_set_user_data, \ + &hb_##name##_get_user_data> {} + + +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<typename T> +struct hash<hb::shared_ptr<T>> +{ + std::size_t operator()(const hb::shared_ptr<T>& v) const noexcept + { + std::size_t h = std::hash<decltype (v.get ())>{}(v.get ()); + return h; + } +}; + +template<typename T> +struct hash<hb::unique_ptr<T>> +{ + std::size_t operator()(const hb::unique_ptr<T>& v) const noexcept + { + std::size_t h = std::hash<decltype (v.get ())>{}(v.get ()); + return h; + } +}; + + +} // namespace std + +#endif /* __cplusplus */ + +#endif /* HB_CPLUSPLUS_HH */ diff --git a/gfx/harfbuzz/src/hb-debug.hh b/gfx/harfbuzz/src/hb-debug.hh new file mode 100644 index 0000000000..559db4067e --- /dev/null +++ b/gfx/harfbuzz/src/hb-debug.hh @@ -0,0 +1,491 @@ +/* + * 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_DEBUG_HH +#define HB_DEBUG_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) +{ + return level < max_level; +} + +#define DEBUG_LEVEL_ENABLED(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT)) +#define DEBUG_ENABLED(WHAT) (DEBUG_LEVEL_ENABLED (WHAT, 0)) + +static inline void +_hb_print_func (const char *func) +{ + if (func) + { + unsigned int func_len = strlen (func); + /* Skip "static" */ + if (0 == strncmp (func, "static ", 7)) + func += 7; + /* Skip "typename" */ + if (0 == strncmp (func, "typename ", 9)) + func += 9; + /* Skip return type */ + const char *space = strchr (func, ' '); + if (space) + func = space + 1; + /* Skip parameter list */ + const char *paren = strchr (func, '('); + if (paren) + func_len = paren - func; + fprintf (stderr, "%.*s", (int) func_len, func); + } +} + +template <int max_level> static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) HB_PRINTF_FUNC(7, 0); +template <int max_level> static inline void +_hb_debug_msg_va (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + va_list ap) +{ + if (!_hb_debug (level, max_level)) + return; + + fprintf (stderr, "%-10s", what ? what : ""); + + if (obj) + fprintf (stderr, "(%*p) ", (int) (2 * sizeof (void *)), obj); + else + fprintf (stderr, " %*s ", (int) (2 * sizeof (void *)), ""); + + if (indented) { +#define VBAR "\342\224\202" /* U+2502 BOX DRAWINGS LIGHT VERTICAL */ +#define VRBAR "\342\224\234" /* U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ +#define DLBAR "\342\225\256" /* U+256E BOX DRAWINGS LIGHT ARC DOWN AND LEFT */ +#define ULBAR "\342\225\257" /* U+256F BOX DRAWINGS LIGHT ARC UP AND LEFT */ +#define LBAR "\342\225\264" /* U+2574 BOX DRAWINGS LIGHT LEFT */ + static const char bars[] = + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR + VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR VBAR; + fprintf (stderr, "%2u %s" VRBAR "%s", + 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); + + _hb_print_func (func); + + if (message) + { + fprintf (stderr, ": "); + vfprintf (stderr, message, ap); + } + + fprintf (stderr, "\n"); +} +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, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + va_list ap HB_UNUSED) {} + +template <int max_level> static inline void +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) HB_PRINTF_FUNC(7, 8); +template <int max_level> static inline void HB_PRINTF_FUNC(7, 8) +_hb_debug_msg (const char *what, + const void *obj, + const char *func, + bool indented, + unsigned int level, + int level_dir, + const char *message, + ...) +{ + va_list ap; + va_start (ap, message); + _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap); + va_end (ap); +} +template <> inline void +_hb_debug_msg<0> (const char *what HB_UNUSED, + const void *obj HB_UNUSED, + const char *func HB_UNUSED, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) HB_PRINTF_FUNC(7, 8); +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, + bool indented HB_UNUSED, + unsigned int level HB_UNUSED, + int level_dir HB_UNUSED, + const char *message HB_UNUSED, + ...) {} + +#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr, true, (LEVEL), (LEVEL_DIR), __VA_ARGS__) +#define DEBUG_MSG(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), nullptr, false, 0, 0, __VA_ARGS__) +#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, false, 0, 0, __VA_ARGS__) + + +/* + * Printer + */ + +template <typename T> +struct hb_printer_t { + const char *print (const T&) { return "something"; } +}; + +template <> +struct hb_printer_t<bool> { + const char *print (bool v) { return v ? "true" : "false"; } +}; + +template <> +struct hb_printer_t<hb_empty_t> { + const char *print (hb_empty_t) { return ""; } +}; + + +/* + * Trace + */ + +template <typename T> +static inline void _hb_warn_no_return (bool returned) +{ + if (unlikely (!returned)) { + fprintf (stderr, "OUCH, returned with no call to return_trace(). This is a bug, please report.\n"); + } +} +template <> +/*static*/ inline void _hb_warn_no_return<hb_empty_t> (bool returned HB_UNUSED) {} +template <> +/*static*/ inline void _hb_warn_no_return<void> (bool returned HB_UNUSED) {} + +template <int max_level, typename ret_t> +struct hb_auto_trace_t +{ + explicit inline hb_auto_trace_t (unsigned int *plevel_, + const char *what_, + const void *obj_, + const char *func, + const char *message, + ...) HB_PRINTF_FUNC(6, 7) + : plevel (plevel_), what (what_), obj (obj_), returned (false) + { + if (plevel) ++*plevel; + + va_list ap; + va_start (ap, message); + _hb_debug_msg_va<max_level> (what, obj, func, true, plevel ? *plevel : 0, +1, message, ap); + va_end (ap); + } + ~hb_auto_trace_t () + { + _hb_warn_no_return<ret_t> (returned); + if (!returned) { + _hb_debug_msg<max_level> (what, obj, nullptr, true, plevel ? *plevel : 1, -1, " "); + } + if (plevel) --*plevel; + } + + template <typename T> + 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 std::forward<T> (v); + } + + _hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1, + "return %s (line %u)", + hb_printer_t<hb_decay<decltype (v)>>().print (v), line); + if (plevel) --*plevel; + plevel = nullptr; + returned = true; + return std::forward<T> (v); + } + + private: + unsigned int *plevel; + const char *what; + const void *obj; + bool returned; +}; +template <typename ret_t> /* Make sure we don't use hb_auto_trace_t when not tracing. */ +struct hb_auto_trace_t<0, ret_t> +{ + explicit inline hb_auto_trace_t (unsigned int *plevel_, + const char *what_, + const void *obj_, + const char *func, + const char *message, + ...) HB_PRINTF_FUNC(6, 7) {} + + template <typename T> + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward<T> (v); } +}; + +/* For disabled tracing; optimize out everything. + * https://github.com/harfbuzz/harfbuzz/pull/605 */ +template <typename ret_t> +struct hb_no_trace_t { + template <typename T> + T ret (T&& v, + const char *func HB_UNUSED = nullptr, + unsigned int line HB_UNUSED = 0) { return std::forward<T> (v); } +}; + +#define return_trace(RET) return trace.ret (RET, HB_FUNC, __LINE__) + + +/* + * Instances. + */ + +#ifndef HB_DEBUG_ARABIC +#define HB_DEBUG_ARABIC (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_BLOB +#define HB_DEBUG_BLOB (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_CORETEXT +#define HB_DEBUG_CORETEXT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_DIRECTWRITE +#define HB_DEBUG_DIRECTWRITE (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_FT +#define HB_DEBUG_FT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_JUSTIFY +#define HB_DEBUG_JUSTIFY (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_OBJECT +#define HB_DEBUG_OBJECT (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_SHAPE_PLAN +#define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_UNISCRIBE +#define HB_DEBUG_UNISCRIBE (HB_DEBUG+0) +#endif + +#ifndef HB_DEBUG_WASM +#define HB_DEBUG_WASM (HB_DEBUG+0) +#endif + +/* + * With tracing. + */ + +#ifndef HB_DEBUG_APPLY +#define HB_DEBUG_APPLY (HB_DEBUG+0) +#endif +#if HB_DEBUG_APPLY +#define TRACE_APPLY(this) \ + hb_auto_trace_t<HB_DEBUG_APPLY, bool> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "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<bool> trace +#endif + +#ifndef HB_DEBUG_SANITIZE +#define HB_DEBUG_SANITIZE (HB_DEBUG+0) +#endif +#if HB_DEBUG_SANITIZE +#define TRACE_SANITIZE(this) \ + hb_auto_trace_t<HB_DEBUG_SANITIZE, bool> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_SANITIZE(this) hb_no_trace_t<bool> trace +#endif + +#ifndef HB_DEBUG_SERIALIZE +#define HB_DEBUG_SERIALIZE (HB_DEBUG+0) +#endif +#if HB_DEBUG_SERIALIZE +#define TRACE_SERIALIZE(this) \ + hb_auto_trace_t<HB_DEBUG_SERIALIZE, bool> trace \ + (&c->debug_depth, "SERIALIZE", c, HB_FUNC, \ + " ") +#else +#define TRACE_SERIALIZE(this) hb_no_trace_t<bool> trace +#endif + +#ifndef HB_DEBUG_SUBSET +#define HB_DEBUG_SUBSET (HB_DEBUG+0) +#endif +#if HB_DEBUG_SUBSET +#define TRACE_SUBSET(this) \ + hb_auto_trace_t<HB_DEBUG_SUBSET, bool> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_SUBSET(this) hb_no_trace_t<bool> 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<HB_DEBUG_PAINT, void> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + " ") +#else +#define TRACE_PAINT(this) HB_UNUSED hb_no_trace_t<void> trace +#endif + + +#ifndef HB_DEBUG_DISPATCH +#define HB_DEBUG_DISPATCH ( \ + HB_DEBUG_APPLY + \ + HB_DEBUG_SANITIZE + \ + HB_DEBUG_SERIALIZE + \ + HB_DEBUG_SUBSET + \ + HB_DEBUG_PAINT + \ + 0) +#endif +#if HB_DEBUG_DISPATCH +#define TRACE_DISPATCH(this, format) \ + hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \ + (&c->debug_depth, c->get_name (), this, HB_FUNC, \ + "format %u", (unsigned) format) +#else +#define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace +#endif + + +#ifndef HB_BUFFER_MESSAGE_MORE +#define HB_BUFFER_MESSAGE_MORE (HB_DEBUG+1) +#endif + + +#endif /* HB_DEBUG_HH */ diff --git a/gfx/harfbuzz/src/hb-deprecated.h b/gfx/harfbuzz/src/hb-deprecated.h new file mode 100644 index 0000000000..ad19f9a3e9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-deprecated.h @@ -0,0 +1,318 @@ +/* + * 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_DEPRECATED_H +#define HB_DEPRECATED_H + +#include "hb-common.h" +#include "hb-unicode.h" +#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_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); + + +/** + * HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION: + * + * Use #HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION instead. + * + * Deprecated: 8.3.0 + */ +#define HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION HB_AAT_LAYOUT_FEATURE_TYPE_CURSIVE_CONNECTION + +#endif + + +HB_END_DECLS + +#endif /* HB_DEPRECATED_H */ diff --git a/gfx/harfbuzz/src/hb-directwrite.cc b/gfx/harfbuzz/src/hb-directwrite.cc new file mode 100644 index 0000000000..42764a244b --- /dev/null +++ b/gfx/harfbuzz/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 <dwrite_1.h> + +#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<size_t> (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<hb_ms_features_t *> range_features; + hb_vector_t<uint32_t> range_char_counts; + if (num_features) + { + hb_vector_t<hb_ms_feature_t> feature_records; + hb_vector_t<hb_ms_range_record_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/gfx/harfbuzz/src/hb-directwrite.h b/gfx/harfbuzz/src/hb-directwrite.h new file mode 100644 index 0000000000..f837627a28 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-dispatch.hh b/gfx/harfbuzz/src/hb-dispatch.hh new file mode 100644 index 0000000000..37ca681465 --- /dev/null +++ b/gfx/harfbuzz/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 <typename Context, typename Return=hb_empty_t, unsigned int MaxDebugDepth=0> +struct hb_dispatch_context_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const Context* thiz () const { return static_cast<const Context *> (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 <typename T, typename F> + bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } + template <typename T, typename ...Ts> + return_t dispatch (const T &obj, Ts&&... ds) + { return obj.dispatch (thiz (), std::forward<Ts> (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/gfx/harfbuzz/src/hb-draw.cc b/gfx/harfbuzz/src/hb-draw.cc new file mode 100644 index 0000000000..f204f56bc7 --- /dev/null +++ b/gfx/harfbuzz/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<hb_draw_funcs_t> ()))) + return const_cast<hb_draw_funcs_t *> (&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<hb_draw_funcs_t *> (&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/gfx/harfbuzz/src/hb-draw.h b/gfx/harfbuzz/src/hb-draw.h new file mode 100644 index 0000000000..9ca0b4006e --- /dev/null +++ b/gfx/harfbuzz/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 <hb.h> 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/gfx/harfbuzz/src/hb-draw.hh b/gfx/harfbuzz/src/hb-draw.hh new file mode 100644 index 0000000000..25dee1261e --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-face-builder.cc b/gfx/harfbuzz/src/hb-face-builder.cc new file mode 100644 index 0000000000..84b14d28d6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-face-builder.cc @@ -0,0 +1,246 @@ +/* + * Copyright © 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 + */ + +#include "hb.hh" + +#include "hb-face.hh" + +#include "hb-map.hh" +#include "hb-open-file.hh" +#include "hb-serialize.hh" + + +/* + * face-builder: A face that has add_table(). + */ + +struct face_table_info_t +{ + hb_blob_t* data; + signed order; +}; + +struct hb_face_builder_data_t +{ + hb_hashmap_t<hb_tag_t, face_table_info_t> tables; +}; + +static int compare_entries (const void* pa, const void* pb) +{ + const auto& a = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pa; + const auto& b = * (const hb_pair_t<hb_tag_t, face_table_info_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<OT::OpenTypeFontFile> (); + + 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<hb_pair_t <hb_tag_t, face_table_info_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<hb_tag_t, face_table_info_t> _) { + return hb_pair_t<hb_tag_t, hb_blob_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/gfx/harfbuzz/src/hb-face.cc b/gfx/harfbuzz/src/hb-face.cc new file mode 100644 index 0000000000..e340710586 --- /dev/null +++ b/gfx/harfbuzz/src/hb-face.cc @@ -0,0 +1,664 @@ +/* + * Copyright © 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 + */ + +#include "hb.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<OT::OpenTypeFontFile> (hb_blob_reference (blob)); + const OT::OpenTypeFontFile& ot = *sanitized->as<OT::OpenTypeFontFile> (); + unsigned int ret = ot.get_face_count (); + hb_blob_destroy (sanitized); + + return ret; +} + +/* + * hb_face_t + */ + +DEFINE_NULL_INSTANCE (hb_face_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, /* reference_table_func */ + nullptr, /* user_data */ + nullptr, /* destroy */ + + 0, /* index */ + 1000, /* upem */ + 0, /* num_glyphs */ + + /* Zero for the rest is fine. */ +}; + + +/** + * hb_face_create_for_tables: + * @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. + * + * 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 + **/ +hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy) +{ + hb_face_t *face; + + if (!reference_table_func || !(face = hb_object_create<hb_face_t> ())) { + if (destroy) + destroy (user_data); + return hb_face_get_empty (); + } + + face->reference_table_func = reference_table_func; + face->user_data = user_data; + face->destroy = destroy; + + face->num_glyphs = -1; + + face->data.init0 (face); + face->table.init0 (face); + + return face; +} + + +typedef struct hb_face_for_data_closure_t { + hb_blob_t *blob; + uint16_t index; +} hb_face_for_data_closure_t; + +static hb_face_for_data_closure_t * +_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 *) hb_calloc (1, sizeof (hb_face_for_data_closure_t)); + if (unlikely (!closure)) + return nullptr; + + closure->blob = blob; + closure->index = (uint16_t) (index & 0xFFFFu); + + return closure; +} + +static void +_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); + hb_free (closure); +} + +static hb_blob_t * +_hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data; + + if (tag == HB_TAG_NONE) + return hb_blob_reference (data->blob); + + const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> (); + 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, base_offset + table.offset, table.length); + + return blob; +} + +/** + * 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. + * + * 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>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> + * + * <note>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.</note> + * + * Return value: (transfer full): The new face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_create (hb_blob_t *blob, + unsigned int index) +{ + hb_face_t *face; + + if (unlikely (!blob)) + blob = hb_blob_get_empty (); + + blob = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (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, + _hb_face_for_data_closure_destroy); + + face->index = index; + + return face; +} + +/** + * hb_face_get_empty: + * + * Fetches the singleton empty face object. + * + * Return value: (transfer full): The empty face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_get_empty () +{ + return const_cast<hb_face_t *> (&Null (hb_face_t)); +} + + +/** + * hb_face_reference: (skip) + * @face: A face object + * + * Increases the reference count on a face object. + * + * Return value: The @face object + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_face_reference (hb_face_t *face) +{ + return hb_object_reference (face); +} + +/** + * hb_face_destroy: (skip) + * @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 + **/ +void +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); + hb_free (node); + node = next; + } +#endif + + face->data.fini (); + face->table.fini (); + + if (face->destroy) + face->destroy (face->user_data); + + hb_free (face); +} + +/** + * hb_face_set_user_data: (skip) + * @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: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (face, key, data, destroy, replace); +} + +/** + * hb_face_get_user_data: (skip) + * @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): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_face_get_user_data (const hb_face_t *face, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (face, key); +} + +/** + * hb_face_make_immutable: + * @face: A face object + * + * Makes the given face object immutable. + * + * Since: 0.9.2 + **/ +void +hb_face_make_immutable (hb_face_t *face) +{ + if (hb_object_is_immutable (face)) + return; + + hb_object_make_immutable (face); +} + +/** + * hb_face_is_immutable: + * @face: A face object + * + * Tests whether the given face object is immutable. + * + * Return value: `true` is @face is immutable, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_face_is_immutable (const hb_face_t *face) +{ + return hb_object_is_immutable (face); +} + + +/** + * hb_face_reference_table: + * @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): A pointer to the @tag table within @face + * + * Since: 0.9.2 + **/ +hb_blob_t * +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 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): A pointer to the blob for @face + * + * Since: 0.9.2 + **/ +hb_blob_t * +hb_face_reference_blob (hb_face_t *face) +{ + return face->reference_table (HB_TAG_NONE); +} + +/** + * hb_face_set_index: + * @face: A face object + * @index: The index to assign + * + * Assigns the specified face-index to @face. Fails if the + * face is immutable. + * + * <note>Note: changing the index has no effect on the face itself + * This only changes the value returned by hb_face_get_index().</note> + * + * Since: 0.9.2 + **/ +void +hb_face_set_index (hb_face_t *face, + unsigned int index) +{ + if (hb_object_is_immutable (face)) + return; + + face->index = index; +} + +/** + * hb_face_get_index: + * @face: A face object + * + * Fetches the face-index corresponding to the given face. + * + * <note>Note: face indices within a collection are zero-based.</note> + * + * Return value: The index of @face. + * + * Since: 0.9.2 + **/ +unsigned int +hb_face_get_index (const hb_face_t *face) +{ + return face->index; +} + +/** + * hb_face_set_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 + **/ +void +hb_face_set_upem (hb_face_t *face, + unsigned int upem) +{ + if (hb_object_is_immutable (face)) + return; + + face->upem = upem; +} + +/** + * hb_face_get_upem: + * @face: A face object + * + * Fetches the units-per-em (UPEM) value of the specified face object. + * + * 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 (const hb_face_t *face) +{ + return face->get_upem (); +} + +/** + * hb_face_set_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 + **/ +void +hb_face_set_glyph_count (hb_face_t *face, + unsigned int glyph_count) +{ + if (hb_object_is_immutable (face)) + return; + + face->num_glyphs = glyph_count; +} + +/** + * hb_face_get_glyph_count: + * @face: A face object + * + * Fetches the glyph-count value of the specified face object. + * + * Return value: The glyph-count value of @face + * + * Since: 0.9.7 + **/ +unsigned int +hb_face_get_glyph_count (const hb_face_t *face) +{ + return face->get_num_glyphs (); +} + +/** + * hb_face_get_table_tags: + * @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 + * + * 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 zero if it is not possible to list + * + * Since: 1.6.0 + **/ +unsigned int +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_destroy_func_t) _hb_face_for_data_closure_destroy) + { + if (table_count) + *table_count = 0; + return 0; + } + + hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) face->user_data; + + const OT::OpenTypeFontFile &ot_file = *data->blob->as<OT::OpenTypeFontFile> (); + 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/gfx/harfbuzz/src/hb-face.h b/gfx/harfbuzz/src/hb-face.h new file mode 100644 index 0000000000..2e54ccf13b --- /dev/null +++ b/gfx/harfbuzz/src/hb-face.h @@ -0,0 +1,187 @@ +/* + * Copyright © 2009 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): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_FACE_H +#define HB_FACE_H + +#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 */ +HB_EXTERN hb_face_t * +hb_face_create_for_tables (hb_reference_table_func_t reference_table_func, + void *user_data, + hb_destroy_func_t destroy); + +HB_EXTERN hb_face_t * +hb_face_get_empty (void); + +HB_EXTERN hb_face_t * +hb_face_reference (hb_face_t *face); + +HB_EXTERN void +hb_face_destroy (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_face_set_user_data (hb_face_t *face, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +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 (const hb_face_t *face); + + +HB_EXTERN hb_blob_t * +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); + +HB_EXTERN void +hb_face_set_index (hb_face_t *face, + unsigned int index); + +HB_EXTERN unsigned int +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 (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 (const hb_face_t *face); + +HB_EXTERN unsigned int +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/gfx/harfbuzz/src/hb-face.hh b/gfx/harfbuzz/src/hb-face.hh new file mode 100644 index 0000000000..aff3ff0d07 --- /dev/null +++ b/gfx/harfbuzz/src/hb-face.hh @@ -0,0 +1,111 @@ +/* + * 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_FACE_HH +#define HB_FACE_HH + +#include "hb.hh" + +#include "hb-shaper.hh" +#include "hb-shape-plan.hh" +#include "hb-ot-face.hh" + + +/* + * hb_face_t + */ + +#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, face); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +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 hb_atomic_int_t upem; /* Units-per-EM. */ + mutable hb_atomic_int_t num_glyphs; /* Number of glyphs. */ + + hb_shaper_object_dataset_t<hb_face_t> data;/* Various shaper data. */ + hb_ot_face_t table; /* All the face's tables. */ + + /* Cache */ + struct plan_node_t + { + hb_shape_plan_t *shape_plan; + plan_node_t *next; + }; +#ifndef HB_NO_SHAPER + hb_atomic_ptr_t<plan_node_t> shape_plans; +#endif + + 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 (/*Oh, well.*/const_cast<hb_face_t *> (this), tag, user_data); + if (unlikely (!blob)) + return hb_blob_get_empty (); + + return blob; + } + + unsigned int get_upem () const + { + unsigned int ret = upem; + if (unlikely (!ret)) + { + return load_upem (); + } + return ret; + } + + unsigned int get_num_glyphs () const + { + unsigned int ret = num_glyphs; + if (unlikely (ret == UINT_MAX)) + return load_num_glyphs (); + return ret; + } + + private: + HB_INTERNAL unsigned int load_upem () const; + HB_INTERNAL unsigned int load_num_glyphs () const; +}; +DECLARE_NULL_INSTANCE (hb_face_t); + + +#endif /* HB_FACE_HH */ diff --git a/gfx/harfbuzz/src/hb-fallback-shape.cc b/gfx/harfbuzz/src/hb-fallback-shape.cc new file mode 100644 index 0000000000..c54ad8764b --- /dev/null +++ b/gfx/harfbuzz/src/hb-fallback-shape.cc @@ -0,0 +1,115 @@ +/* + * 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-shaper-impl.hh" + +#ifndef HB_NO_FALLBACK_SHAPE + +/* + * shaper face data + */ + +struct hb_fallback_face_data_t {}; + +hb_fallback_face_data_t * +_hb_fallback_shaper_face_data_create (hb_face_t *face HB_UNUSED) +{ + return (hb_fallback_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_face_data_destroy (hb_fallback_face_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper font data + */ + +struct hb_fallback_font_data_t {}; + +hb_fallback_font_data_t * +_hb_fallback_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_fallback_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_fallback_shaper_font_data_destroy (hb_fallback_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features HB_UNUSED, + unsigned int num_features HB_UNUSED) +{ + hb_codepoint_t space; + bool has_space = (bool) font->get_nominal_glyph (' ', &space); + + buffer->clear_positions (); + + hb_direction_t direction = buffer->props.direction; + hb_unicode_funcs_t *unicode = buffer->unicode; + 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 (has_space && unicode->is_default_ignorable (info[i].codepoint)) { + info[i].codepoint = space; + pos[i].x_advance = 0; + pos[i].y_advance = 0; + continue; + } + (void) font->get_nominal_glyph (info[i].codepoint, &info[i].codepoint); + font->get_glyph_advance_for_direction (info[i].codepoint, + direction, + &pos[i].x_advance, + &pos[i].y_advance); + font->subtract_glyph_origin_for_direction (info[i].codepoint, + direction, + &pos[i].x_offset, + &pos[i].y_offset); + } + + if (HB_DIRECTION_IS_BACKWARD (direction)) + hb_buffer_reverse (buffer); + + buffer->clear_glyph_flags (); + + return true; +} + +#endif diff --git a/gfx/harfbuzz/src/hb-features.h.in b/gfx/harfbuzz/src/hb-features.h.in new file mode 100644 index 0000000000..4b27bd5e76 --- /dev/null +++ b/gfx/harfbuzz/src/hb-features.h.in @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#ifndef HB_FEATURES_H +#define HB_FEATURES_H + +HB_BEGIN_DECLS + +/** + * SECTION: hb-features + * @title: hb-features + * @short_description: Feature detection + * @include: hb-features.h + * + * Macros for detecting optional HarfBuzz features at build time. + **/ + +/** + * HB_HAS_CAIRO: + * + * Defined if Harfbuzz has been built with cairo support. + */ +#mesondefine HB_HAS_CAIRO + +/** + * HB_HAS_CORETEXT: + * + * Defined if Harfbuzz has been built with CoreText support. + */ +#mesondefine HB_HAS_CORETEXT + +/** + * HB_HAS_DIRECTWRITE: + * + * Defined if Harfbuzz has been built with DirectWrite support. + */ +#mesondefine HB_HAS_DIRECTWRITE + +/** + * HB_HAS_FREETYPE: + * + * Defined if Harfbuzz has been built with Freetype support. + */ +#mesondefine HB_HAS_FREETYPE + +/** + * HB_HAS_GDI: + * + * Defined if Harfbuzz has been built with GDI support. + */ +#mesondefine HB_HAS_GDI + +/** + * HB_HAS_GLIB: + * + * Defined if Harfbuzz has been built with GLib support. + */ +#mesondefine HB_HAS_GLIB + +/** + * HB_HAS_GOBJECT: + * + * Defined if Harfbuzz has been built with GObject support. + */ +#mesondefine HB_HAS_GOBJECT + +/** + * HB_HAS_GRAPHITE: + * + * Defined if Harfbuzz has been built with Graphite support. + */ +#mesondefine HB_HAS_GRAPHITE + +/** + * HB_HAS_ICU: + * + * Defined if Harfbuzz has been built with ICU support. + */ +#mesondefine HB_HAS_ICU + +/** + * HB_HAS_UNISCRIBE: + * + * Defined if Harfbuzz has been built with Uniscribe support. + */ +#mesondefine HB_HAS_UNISCRIBE + +/** + * HB_HAS_WASM: + * + * Defined if Harfbuzz has been built with WebAssembly support. + */ +#mesondefine HB_HAS_WASM + + +HB_END_DECLS + +#endif /* HB_FEATURES_H */ diff --git a/gfx/harfbuzz/src/hb-font.cc b/gfx/harfbuzz/src/hb-font.cc new file mode 100644 index 0000000000..00f1f6d382 --- /dev/null +++ b/gfx/harfbuzz/src/hb-font.cc @@ -0,0 +1,3069 @@ +/* + * Copyright © 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 + */ + +#include "hb.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. + **/ + + +/* + * hb_font_funcs_t + */ + +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 *extents, + void *user_data HB_UNUSED) +{ + hb_memset (extents, 0, sizeof (*extents)); + return false; +} + +static hb_bool_t +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 (extents); + if (ret) { + 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 *extents, + void *user_data HB_UNUSED) +{ + hb_memset (extents, 0, sizeof (*extents)); + return false; +} + +static hb_bool_t +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 (extents); + if (ret) { + 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_UNUSED, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} + +static hb_bool_t +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<hb_codepoint_t> (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (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_UNUSED, + hb_codepoint_t variation_selector HB_UNUSED, + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} + +static hb_bool_t +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, + 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_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, + 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_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<hb_codepoint_t> (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned<hb_position_t> (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<hb_position_t> (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<hb_codepoint_t> (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned<hb_position_t> (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<hb_position_t> (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_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_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) + font->parent_scale_position (x, y); + return ret; +} + +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_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_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) + font->parent_scale_position (x, y); + return ret; +} + +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_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_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_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_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_UNUSED, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_memset (extents, 0, sizeof (*extents)); + return false; +} + +static hb_bool_t +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) { + font->parent_scale_position (&extents->x_bearing, &extents->y_bearing); + font->parent_scale_distance (&extents->width, &extents->height); + } + return ret; +} + +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 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_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) + font->parent_scale_position (x, y); + return ret; +} + +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 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_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 HB_UNUSED, + int len HB_UNUSED, /* -1 means nul-terminated */ + hb_codepoint_t *glyph, + void *user_data HB_UNUSED) +{ + *glyph = 0; + return false; +} + +static hb_bool_t +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 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, + + { +#define HB_DRAW_FUNC_IMPLEMENT(name) hb_draw_##name##_default, + HB_DRAW_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_DRAW_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_t *> (&_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(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_default = { + HB_OBJECT_HEADER_STATIC, + + nullptr, + nullptr, + { + { +#define HB_FONT_FUNC_IMPLEMENT(get_,name) hb_font_##get_##name##_default, + HB_FONT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_FONT_FUNC_IMPLEMENT + } + } +}; + + +/** + * hb_font_funcs_create: + * + * Creates a new #hb_font_funcs_t structure of font functions. + * + * Return value: (transfer full): The font-functions structure + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_create () +{ + hb_font_funcs_t *ffuncs; + + if (!(ffuncs = hb_object_create<hb_font_funcs_t> ())) + return hb_font_funcs_get_empty (); + + ffuncs->get = _hb_font_funcs_default.get; + + return ffuncs; +} + +/** + * hb_font_funcs_get_empty: + * + * Fetches an empty font-functions structure. + * + * Return value: (transfer full): The font-functions structure + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_get_empty () +{ + return const_cast<hb_font_funcs_t *> (&_hb_font_funcs_default); +} + +/** + * hb_font_funcs_reference: (skip) + * @ffuncs: The font-functions structure + * + * Increases the reference count on a font-functions structure. + * + * Return value: The font-functions structure + * + * Since: 0.9.2 + **/ +hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs) +{ + return hb_object_reference (ffuncs); +} + +/** + * hb_font_funcs_destroy: (skip) + * @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 + **/ +void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs) +{ + if (!hb_object_destroy (ffuncs)) return; + + 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 + } + + hb_free (ffuncs->destroy); + hb_free (ffuncs->user_data); + + hb_free (ffuncs); +} + +/** + * hb_font_funcs_set_user_data: (skip) + * @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: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +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 /* May be NULL. */, + hb_bool_t replace) +{ + return hb_object_set_user_data (ffuncs, key, data, destroy, replace); +} + +/** + * hb_font_funcs_get_user_data: (skip) + * @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): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +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); +} + + +/** + * hb_font_funcs_make_immutable: + * @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 (hb_object_is_immutable (ffuncs)) + return; + + hb_object_make_immutable (ffuncs); +} + +/** + * hb_font_funcs_is_immutable: + * @ffuncs: The font-functions structure + * + * Tests whether a font-functions structure is immutable. + * + * 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 hb_object_is_immutable (ffuncs); +} + + +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) \ +{ \ + 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 (!_hb_font_funcs_set_middle (ffuncs, user_data, destroy)) \ + return; \ + \ + if (func) \ + ffuncs->get.f.name = func; \ + 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) +{ + return has_func_set (i) || + (parent && parent != &_hb_Null_hb_font_t && parent->has_func (i)); +} + +/* Public getters */ + +/** + * hb_font_get_h_extents: + * @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: `true` if data found, `false` otherwise + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_h_extents (extents); +} + +/** + * hb_font_get_v_extents: + * @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: `true` if data found, `false` otherwise + * + * Since: 1.1.3 + **/ +hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents) +{ + return font->get_font_v_extents (extents); +} + +/** + * hb_font_get_glyph: + * @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. + * + * 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_codepoint_t *glyph) +{ + if (unlikely (variation_selector)) + return font->get_variation_glyph (unicode, variation_selector, glyph); + return font->get_nominal_glyph (unicode, glyph); +} + +/** + * hb_font_get_nominal_glyph: + * @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. + * + * 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_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: #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: `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_codepoint_t *glyph) +{ + return font->get_variation_glyph (unicode, variation_selector, glyph); +} + +/** + * hb_font_get_glyph_h_advance: + * @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: 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) +{ + return font->get_glyph_h_advance (glyph); +} + +/** + * hb_font_get_glyph_v_advance: + * @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: 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) +{ + 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: #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: `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) +{ + return font->get_glyph_h_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_v_origin: + * @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: `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) +{ + return font->get_glyph_v_origin (glyph, x, y); +} + +/** + * hb_font_get_glyph_h_kerning: + * @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. + * + * <note>It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function).</note> + * + * 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) +{ + return font->get_glyph_h_kerning (left_glyph, right_glyph); +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_font_get_glyph_v_kerning: + * @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. + * + * <note>It handles legacy kerning only (as returned by the corresponding + * #hb_font_funcs_t function).</note> + * + * 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) +{ + return font->get_glyph_v_kerning (top_glyph, bottom_glyph); +} +#endif + +/** + * hb_font_get_glyph_extents: + * @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: `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_glyph_extents_t *extents) +{ + return font->get_glyph_extents (glyph, extents); +} + +/** + * hb_font_get_glyph_contour_point: + * @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: `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) +{ + return font->get_glyph_contour_point (glyph, point_index, x, y); +} + +/** + * hb_font_get_glyph_name: + * @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. + * + * 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) +{ + return font->get_glyph_name (glyph, name, size); +} + +/** + * hb_font_get_glyph_from_name: + * @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. + * + * <note>Note: @len == -1 means the name string is null-terminated.</note> + * + * 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_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: #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_extents_t *extents) +{ + font->get_extents_for_direction (direction, extents); +} +/** + * hb_font_get_glyph_advance_for_direction: + * @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) +{ + 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: #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) +{ + return font->get_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_add_glyph_origin_for_direction: + * @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) +{ + return font->add_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_subtract_glyph_origin_for_direction: + * @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) +{ + return font->subtract_glyph_origin_for_direction (glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_kerning_for_direction: + * @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) +{ + return font->get_glyph_kerning_for_direction (first_glyph, second_glyph, direction, x, y); +} + +/** + * hb_font_get_glyph_extents_for_origin: + * @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. + * + * 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_glyph_extents_t *extents) +{ + return font->get_glyph_extents_for_origin (glyph, direction, extents); +} + +/** + * hb_font_get_glyph_contour_point_for_origin: + * @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. + * + * 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) +{ + return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y); +} + +/** + * hb_font_glyph_to_string: + * @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) +{ + font->glyph_to_string (glyph, s, size); +} + +/** + * hb_font_glyph_from_string: + * @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. + * + * <note>Note: @len == -1 means the string is null-terminated.</note> + * + * 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, + hb_codepoint_t *glyph) +{ + return font->glyph_from_string (s, len, glyph); +} + + +/* + * hb_font_t + */ + +DEFINE_NULL_INSTANCE (hb_font_t) = +{ + HB_OBJECT_HEADER_STATIC, + + 0, /* serial */ + 0, /* serial_coords */ + + nullptr, /* parent */ + const_cast<hb_face_t *> (&_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_font_funcs_t *> (&_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<hb_font_t> ())) + return hb_font_get_empty (); + + hb_face_make_immutable (face); + font->parent = hb_font_get_empty (); + font->face = hb_face_reference (face); + font->klass = hb_font_funcs_get_empty (); + 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>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.</note> + * + * 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: The parent font object + * + * Constructs a sub-font font object from the specified @parent font, + * replicating the parent's properties. + * + * Return value: (transfer full): The new sub-font font object + * + * Since: 0.9.2 + **/ +hb_font_t * +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); + + 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; + + unsigned int num_coords = parent->num_coords; + if (num_coords) + { + 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 + { + 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): The empty font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_empty () +{ + return const_cast<hb_font_t *> (&Null (hb_font_t)); +} + +/** + * hb_font_reference: (skip) + * @font: #hb_font_t to work upon + * + * Increases the reference count on the given font object. + * + * Return value: (transfer full): The @font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_reference (hb_font_t *font) +{ + return hb_object_reference (font); +} + +/** + * hb_font_destroy: (skip) + * @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 + **/ +void +hb_font_destroy (hb_font_t *font) +{ + if (!hb_object_destroy (font)) return; + + font->data.fini (); + + if (font->destroy) + font->destroy (font->user_data); + + hb_font_destroy (font->parent); + hb_face_destroy (font->face); + hb_font_funcs_destroy (font->klass); + + hb_free (font->coords); + hb_free (font->design_coords); + + hb_free (font); +} + +/** + * hb_font_set_user_data: (skip) + * @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: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +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 /* 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: #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): Pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_font_get_user_data (const hb_font_t *font, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (font, key); +} + +/** + * hb_font_make_immutable: + * @font: #hb_font_t to work upon + * + * Makes @font immutable. + * + * Since: 0.9.2 + **/ +void +hb_font_make_immutable (hb_font_t *font) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->parent) + hb_font_make_immutable (font->parent); + + hb_object_make_immutable (font); +} + +/** + * hb_font_is_immutable: + * @font: #hb_font_t to work upon + * + * Tests whether a font object is immutable. + * + * 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 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: #hb_font_t to work upon + * @parent: The parent font object to assign + * + * Sets the parent font of @font. + * + * Since: 1.0.5 + **/ +void +hb_font_set_parent (hb_font_t *font, + hb_font_t *parent) +{ + if (hb_object_is_immutable (font)) + return; + + if (parent == font->parent) + return; + + font->serial++; + + if (!parent) + parent = hb_font_get_empty (); + + hb_font_t *old = font->parent; + + font->parent = hb_font_reference (parent); + + hb_font_destroy (old); +} + +/** + * hb_font_get_parent: + * @font: #hb_font_t to work upon + * + * Fetches the parent font of @font. + * + * Return value: (transfer none): The parent font object + * + * Since: 0.9.2 + **/ +hb_font_t * +hb_font_get_parent (hb_font_t *font) +{ + return font->parent; +} + +/** + * hb_font_set_face: + * @font: #hb_font_t to work upon + * @face: The #hb_face_t to assign + * + * Sets @face as the font-face value of @font. + * + * Since: 1.4.3 + **/ +void +hb_font_set_face (hb_font_t *font, + hb_face_t *face) +{ + 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: #hb_font_t to work upon + * + * Fetches the face associated with the specified font object. + * + * Return value: (transfer none): The #hb_face_t value + * + * Since: 0.9.2 + **/ +hb_face_t * +hb_font_get_face (hb_font_t *font) +{ + return font->face; +} + + +/** + * hb_font_set_funcs: + * @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 + **/ +void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy /* May be NULL. */) +{ + if (hb_object_is_immutable (font)) + { + if (destroy) + destroy (font_data); + return; + } + + font->serial++; + + if (font->destroy) + font->destroy (font->user_data); + + if (!klass) + klass = hb_font_funcs_get_empty (); + + hb_font_funcs_reference (klass); + hb_font_funcs_destroy (font->klass); + font->klass = klass; + font->user_data = font_data; + font->destroy = destroy; +} + +/** + * hb_font_set_funcs_data: + * @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 /* May be NULL. */) +{ + /* Destroy user_data? */ + if (hb_object_is_immutable (font)) + { + if (destroy) + destroy (font_data); + return; + } + + font->serial++; + + if (font->destroy) + font->destroy (font->user_data); + + font->user_data = font_data; + font->destroy = destroy; +} + + +/** + * hb_font_set_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) +{ + 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: #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) +{ + if (x_scale) *x_scale = font->x_scale; + if (y_scale) *y_scale = font->y_scale; +} + +/** + * hb_font_set_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) +{ + 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: #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, + unsigned int *x_ppem, + unsigned int *y_ppem) +{ + if (x_ppem) *x_ppem = font->x_ppem; + if (y_ppem) *y_ppem = font->y_ppem; +} + +/** + * hb_font_set_ptem: + * @font: #hb_font_t to work upon + * @ptem: font size in points. + * + * Sets the "point size" of a font. Set to zero to unset. + * Used in CoreText to implement optical sizing. + * + * <note>Note: There are 72 points in an inch.</note> + * + * Since: 1.6.0 + **/ +void +hb_font_set_ptem (hb_font_t *font, + float ptem) +{ + if (hb_object_is_immutable (font)) + return; + + if (font->ptem == ptem) + return; + + font->serial++; + + font->ptem = ptem; +} + +/** + * hb_font_get_ptem: + * @font: #hb_font_t to work upon + * + * Fetches the "point size" of a font. Used in CoreText to + * implement optical sizing. + * + * Return value: Point size. A value of zero means "not set." + * + * Since: 1.6.0 + **/ +float +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>Note: The glyph shape fetched via the hb_font_draw_glyph() + * function is slanted to reflect this value as well.</note> + * + * <note>Note: The slant value is a ratio. For example, a + * 20% slant would be represented as a 0.2 value.</note> + * + * 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 + */ + +/** + * 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, + const hb_variation_t *variations, + unsigned int variations_length) +{ + if (hb_object_is_immutable (font)) + return; + + 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; + } + + 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. */ + 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; + + 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) +{ + if (hb_object_is_immutable (font)) + return; + + 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 (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>Note: Coordinates should be normalized to 2.14.</note> + * + * 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) +{ + if (hb_object_is_immutable (font)) + return; + + 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) + { + hb_memcpy (copy, coords, coords_length * sizeof (coords[0])); + hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0])); + } + + /* 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, + unsigned int *length) +{ + if (length) + *length = font->num_coords; + + 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(): + */ + +struct hb_trampoline_closure_t +{ + void *user_data; + hb_destroy_func_t destroy; + unsigned int ref_count; +}; + +template <typename FuncType> +struct hb_trampoline_t +{ + hb_trampoline_closure_t closure; /* Must be first. */ + FuncType func; +}; + +template <typename FuncType> +static hb_trampoline_t<FuncType> * +trampoline_create (FuncType func, + void *user_data, + hb_destroy_func_t destroy) +{ + typedef hb_trampoline_t<FuncType> trampoline_t; + + trampoline_t *trampoline = (trampoline_t *) hb_calloc (1, sizeof (trampoline_t)); + + if (unlikely (!trampoline)) + return nullptr; + + trampoline->closure.user_data = user_data; + trampoline->closure.destroy = destroy; + trampoline->closure.ref_count = 1; + trampoline->func = func; + + return trampoline; +} + +static void +trampoline_reference (hb_trampoline_closure_t *closure) +{ + closure->ref_count++; +} + +static void +trampoline_destroy (void *user_data) +{ + hb_trampoline_closure_t *closure = (hb_trampoline_closure_t *) user_data; + + if (--closure->ref_count) + return; + + if (closure->destroy) + closure->destroy (closure->user_data); + hb_free (closure); +} + +typedef hb_trampoline_t<hb_font_get_glyph_func_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_codepoint_t *glyph, + 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_codepoint_t *glyph, + 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); +} + +/** + * hb_font_funcs_set_glyph_func: + * @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. + * + * Since: 0.9.2 + * 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 /* 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); + if (unlikely (!trampoline)) + { + if (destroy) + destroy (user_data); + 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); + + hb_font_funcs_set_variation_glyph_func (ffuncs, + hb_font_get_variation_glyph_trampoline, + trampoline, + trampoline_destroy); +} +#endif + + +#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/gfx/harfbuzz/src/hb-font.h b/gfx/harfbuzz/src/hb-font.h new file mode 100644 index 0000000000..3c2355af2d --- /dev/null +++ b/gfx/harfbuzz/src/hb-font.h @@ -0,0 +1,1153 @@ +/* + * Copyright © 2009 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): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_FONT_H +#define HB_FONT_H + +#include "hb-common.h" +#include "hb-face.h" +#include "hb-draw.h" +#include "hb-paint.h" + +HB_BEGIN_DECLS + +/* + * 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 * +hb_font_funcs_create (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_get_empty (void); + +HB_EXTERN hb_font_funcs_t * +hb_font_funcs_reference (hb_font_funcs_t *ffuncs); + +HB_EXTERN void +hb_font_funcs_destroy (hb_font_funcs_t *ffuncs); + +HB_EXTERN 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_bool_t replace); + + +HB_EXTERN void * +hb_font_funcs_get_user_data (const hb_font_funcs_t *ffuncs, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs); + +HB_EXTERN hb_bool_t +hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs); + + +/* font extents */ + +/** + * 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; + hb_position_t reserved7; + hb_position_t reserved6; + hb_position_t reserved5; + hb_position_t reserved4; + hb_position_t reserved3; + hb_position_t reserved2; + hb_position_t reserved1; +} hb_font_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 *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; + + +/** + * 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: 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 + **/ +HB_EXTERN void +hb_font_funcs_set_font_h_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_h_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_font_v_extents_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_font_v_extents_func_t. + * + * Since: 1.1.2 + **/ +HB_EXTERN void +hb_font_funcs_set_font_v_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_font_v_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_nominal_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_nominal_glyph_func_t. + * + * Since: 1.2.3 + **/ +HB_EXTERN void +hb_font_funcs_set_nominal_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_nominal_glyph_func_t func, + void *user_data, hb_destroy_func_t 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 + **/ +HB_EXTERN void +hb_font_funcs_set_variation_glyph_func (hb_font_funcs_t *ffuncs, + hb_font_get_variation_glyph_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_advance_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_advance_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_advance_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_advance_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_advance_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_advance_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_advance_func_t func, + void *user_data, hb_destroy_func_t 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 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_h_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_h_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_v_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_v_origin_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_v_origin_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_h_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_h_kerning_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +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_extents_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_extents_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_extents_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_extents_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_contour_point_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_contour_point_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_contour_point_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_contour_point_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_name_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_name_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_font_funcs_set_glyph_name_func (hb_font_funcs_t *ffuncs, + hb_font_get_glyph_name_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_font_funcs_set_glyph_from_name_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_from_name_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +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 +hb_font_get_h_extents (hb_font_t *font, + hb_font_extents_t *extents); +HB_EXTERN hb_bool_t +hb_font_get_v_extents (hb_font_t *font, + hb_font_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_font_get_nominal_glyph (hb_font_t *font, + hb_codepoint_t unicode, + hb_codepoint_t *glyph); +HB_EXTERN hb_bool_t +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); +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, + hb_position_t *x, hb_position_t *y); +HB_EXTERN 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_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_bool_t +hb_font_get_glyph_extents (hb_font_t *font, + hb_codepoint_t glyph, + hb_glyph_extents_t *extents); + +HB_EXTERN 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_EXTERN hb_bool_t +hb_font_get_glyph_name (hb_font_t *font, + hb_codepoint_t glyph, + char *name, unsigned int size); +HB_EXTERN hb_bool_t +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 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, + hb_codepoint_t *glyph); + +HB_EXTERN void +hb_font_get_extents_for_direction (hb_font_t *font, + hb_direction_t direction, + hb_font_extents_t *extents); +HB_EXTERN 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_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, + hb_position_t *x, hb_position_t *y); +HB_EXTERN 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_EXTERN 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_EXTERN 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_EXTERN hb_bool_t +hb_font_get_glyph_extents_for_origin (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + hb_glyph_extents_t *extents); + +HB_EXTERN 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); + +/* Generates gidDDD if glyph has no name. */ +HB_EXTERN void +hb_font_glyph_to_string (hb_font_t *font, + hb_codepoint_t glyph, + char *s, unsigned int size); +/* Parses gidDDD and uniUUUU strings automatically. */ +HB_EXTERN hb_bool_t +hb_font_glyph_from_string (hb_font_t *font, + const char *s, int len, /* -1 means nul-terminated */ + hb_codepoint_t *glyph); + + +/* + * hb_font_t + */ + +/* Fonts are very light-weight objects */ + +HB_EXTERN hb_font_t * +hb_font_create (hb_face_t *face); + +HB_EXTERN hb_font_t * +hb_font_create_sub_font (hb_font_t *parent); + +HB_EXTERN hb_font_t * +hb_font_get_empty (void); + +HB_EXTERN hb_font_t * +hb_font_reference (hb_font_t *font); + +HB_EXTERN void +hb_font_destroy (hb_font_t *font); + +HB_EXTERN 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_bool_t replace); + + +HB_EXTERN void * +hb_font_get_user_data (const hb_font_t *font, + hb_user_data_key_t *key); + +HB_EXTERN void +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); + +HB_EXTERN hb_font_t * +hb_font_get_parent (hb_font_t *font); + +HB_EXTERN void +hb_font_set_face (hb_font_t *font, + hb_face_t *face); + +HB_EXTERN hb_face_t * +hb_font_get_face (hb_font_t *font); + + +HB_EXTERN void +hb_font_set_funcs (hb_font_t *font, + hb_font_funcs_t *klass, + void *font_data, + hb_destroy_func_t destroy); + +/* 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); + + +HB_EXTERN void +hb_font_set_scale (hb_font_t *font, + int x_scale, + int y_scale); + +HB_EXTERN void +hb_font_get_scale (hb_font_t *font, + int *x_scale, + int *y_scale); + +/* + * A zero value means "no hinting in that direction" + */ +HB_EXTERN void +hb_font_set_ppem (hb_font_t *font, + unsigned int x_ppem, + unsigned int y_ppem); + +HB_EXTERN void +hb_font_get_ppem (hb_font_t *font, + unsigned int *x_ppem, + unsigned int *y_ppem); + +/* + * Point size per EM. Used for optical-sizing in CoreText. + * A value of zero means "not set". + */ +HB_EXTERN void +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 */ + unsigned int coords_length); + +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/gfx/harfbuzz/src/hb-font.hh b/gfx/harfbuzz/src/hb-font.hh new file mode 100644 index 0000000000..f503575c34 --- /dev/null +++ b/gfx/harfbuzz/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<hb_font_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/gfx/harfbuzz/src/hb-ft-colr.hh b/gfx/harfbuzz/src/hb-ft-colr.hh new file mode 100644 index 0000000000..1afbbbb183 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-ft.cc b/gfx/harfbuzz/src/hb-ft.cc new file mode 100644 index 0000000000..955a9081e0 --- /dev/null +++ b/gfx/harfbuzz/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>Note that FreeType is not thread-safe, therefore these + * functions are not thread-safe either.</note> + **/ + + +/* 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://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.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://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.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<hb_codepoint_t> (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (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<hb_codepoint_t> (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned<hb_position_t> (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<hb_position_t> (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<hb_ft_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>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.</note> + * + * 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>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.</note> + * + * 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_remove_pointer<FT_Library>, + 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>Note: Internally, this function creates an FT_Face. +* </note> + * + * 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/gfx/harfbuzz/src/hb-ft.h b/gfx/harfbuzz/src/hb-ft.h new file mode 100644 index 0000000000..6a8a7abe8c --- /dev/null +++ b/gfx/harfbuzz/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 <ft2build.h> +#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/gfx/harfbuzz/src/hb-gdi.cc b/gfx/harfbuzz/src/hb-gdi.cc new file mode 100644 index 0000000000..8e7589beac --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-gdi.h b/gfx/harfbuzz/src/hb-gdi.h new file mode 100644 index 0000000000..68cc43917e --- /dev/null +++ b/gfx/harfbuzz/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 <windows.h> + +HB_BEGIN_DECLS + +HB_EXTERN hb_face_t * +hb_gdi_face_create (HFONT hfont); + +HB_END_DECLS + +#endif /* HB_GDI_H */ diff --git a/gfx/harfbuzz/src/hb-glib.cc b/gfx/harfbuzz/src/hb-glib.cc new file mode 100644 index 0000000000..1da81696e7 --- /dev/null +++ b/gfx/harfbuzz/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<hb_glib_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/gfx/harfbuzz/src/hb-glib.h b/gfx/harfbuzz/src/hb-glib.h new file mode 100644 index 0000000000..5f04183ba1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-glib.h @@ -0,0 +1,56 @@ +/* + * 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_GLIB_H +#define HB_GLIB_H + +#include "hb.h" + +#include <glib.h> + +HB_BEGIN_DECLS + + +HB_EXTERN hb_script_t +hb_glib_script_to_script (GUnicodeScript script); + +HB_EXTERN GUnicodeScript +hb_glib_script_from_script (hb_script_t script); + + +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_GLIB_H */ diff --git a/gfx/harfbuzz/src/hb-gobject-enums.cc.tmpl b/gfx/harfbuzz/src/hb-gobject-enums.cc.tmpl new file mode 100644 index 0000000000..87a11dd407 --- /dev/null +++ b/gfx/harfbuzz/src/hb-gobject-enums.cc.tmpl @@ -0,0 +1,80 @@ +/*** BEGIN file-header ***/ +/* + * 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 + */ + +#include "hb.hh" + +#ifdef HAVE_GOBJECT + +/* g++ didn't like older gtype.h gcc-only code path. */ +#include <glib.h> +#if !GLIB_CHECK_VERSION(2,29,16) +#undef __GNUC__ +#undef __GNUC_MINOR__ +#define __GNUC__ 2 +#define __GNUC_MINOR__ 6 +#endif + +#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 () +{ + static gsize type_id = 0; + + if (g_once_init_enter (&type_id)) + { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** 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); + } + + return type_id; +} + +/*** END value-tail ***/ diff --git a/gfx/harfbuzz/src/hb-gobject-enums.h.tmpl b/gfx/harfbuzz/src/hb-gobject-enums.h.tmpl new file mode 100644 index 0000000000..6dd98f7fed --- /dev/null +++ b/gfx/harfbuzz/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 <hb-gobject.h> instead." +#endif + +#ifndef HB_GOBJECT_ENUMS_H +#define HB_GOBJECT_ENUMS_H + +#include "hb.h" + +#include <glib-object.h> + +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/gfx/harfbuzz/src/hb-gobject-structs.cc b/gfx/harfbuzz/src/hb-gobject-structs.cc new file mode 100644 index 0000000000..d66de0b237 --- /dev/null +++ b/gfx/harfbuzz/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 <glib.h> +#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/gfx/harfbuzz/src/hb-gobject-structs.h b/gfx/harfbuzz/src/hb-gobject-structs.h new file mode 100644 index 0000000000..b7b5f55ce6 --- /dev/null +++ b/gfx/harfbuzz/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 <hb-gobject.h> instead." +#endif + +#ifndef HB_GOBJECT_STRUCTS_H +#define HB_GOBJECT_STRUCTS_H + +#include "hb.h" + +#include <glib-object.h> + +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/gfx/harfbuzz/src/hb-gobject.h b/gfx/harfbuzz/src/hb-gobject.h new file mode 100644 index 0000000000..8891aa0ee7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-gobject.h @@ -0,0 +1,40 @@ +/* + * 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 + */ + +#ifndef HB_GOBJECT_H +#define HB_GOBJECT_H +#define HB_GOBJECT_H_IN + +#include "hb.h" + +#include "hb-gobject-enums.h" +#include "hb-gobject-structs.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_GOBJECT_H_IN +#endif /* HB_GOBJECT_H */ diff --git a/gfx/harfbuzz/src/hb-graphite2.cc b/gfx/harfbuzz/src/hb-graphite2.cc new file mode 100644 index 0000000000..7ea0386223 --- /dev/null +++ b/gfx/harfbuzz/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 <graphite2/Segment.h> + +#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<hb_graphite2_tablelist_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/gfx/harfbuzz/src/hb-graphite2.h b/gfx/harfbuzz/src/hb-graphite2.h new file mode 100644 index 0000000000..ee9229b8b0 --- /dev/null +++ b/gfx/harfbuzz/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 <graphite2/Font.h> + +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/gfx/harfbuzz/src/hb-icu.cc b/gfx/harfbuzz/src/hb-icu.cc new file mode 100644 index 0000000000..e46401f7a6 --- /dev/null +++ b/gfx/harfbuzz/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 <unicode/uchar.h> +#include <unicode/unorm2.h> +#include <unicode/ustring.h> +#include <unicode/utf16.h> +#include <unicode/uversion.h> + +/* 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<hb_icu_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/gfx/harfbuzz/src/hb-icu.h b/gfx/harfbuzz/src/hb-icu.h new file mode 100644 index 0000000000..2db6a7b679 --- /dev/null +++ b/gfx/harfbuzz/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 <unicode/uscript.h> + +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/gfx/harfbuzz/src/hb-iter.hh b/gfx/harfbuzz/src/hb-iter.hh new file mode 100644 index 0000000000..61e05180be --- /dev/null +++ b/gfx/harfbuzz/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 <typename iter_t, typename Item = typename iter_t::__item_t__> +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<const iter_t *> (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 <typename T = item_t, + hb_enable_if (std::is_reference<T>::value)> + hb_remove_reference<item_t>* 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 <typename T> + iter_t& operator >> (T &v) & { v = **thiz(); ++*thiz(); return *thiz(); } + template <typename T> + iter_t operator >> (T &v) && { v = **thiz(); ++*thiz(); return *thiz(); } + template <typename T> + iter_t& operator << (const T v) & { **thiz() = v; ++*thiz(); return *thiz(); } + template <typename T> + 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 <typename Iterable> +using hb_iter_type = decltype (hb_deref (hb_declval (Iterable)).iter ()); +template <typename Iterable> +using hb_item_type = decltype (*hb_deref (hb_declval (Iterable)).iter ()); + + +template <typename> struct hb_array_t; +template <typename> struct hb_sorted_array_t; + +struct +{ + template <typename T> hb_iter_type<T> + operator () (T&& c) const + { return hb_deref (std::forward<T> (c)).iter (); } + + /* Specialization for C arrays. */ + + template <typename Type> inline hb_array_t<Type> + operator () (Type *array, unsigned int length) const + { return hb_array_t<Type> (array, length); } + + template <typename Type, unsigned int length> hb_array_t<Type> + operator () (Type (&array)[length]) const + { return hb_array_t<Type> (array, length); } + +} +HB_FUNCOBJ (hb_iter); +struct +{ + template <typename T> auto + impl (T&& c, hb_priority<1>) const HB_RETURN (unsigned, c.len ()) + + template <typename T> auto + impl (T&& c, hb_priority<0>) const HB_RETURN (unsigned, c.len) + + public: + + template <typename T> auto + operator () (T&& c) const HB_RETURN (unsigned, impl (std::forward<T> (c), hb_prioritize)) +} +HB_FUNCOBJ (hb_len); + +/* Mixin to fill in what the subclass doesn't provide. */ +template <typename iter_t, typename item_t = typename iter_t::__item_t__> +struct hb_iter_fallback_mixin_t +{ + private: + /* https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ + const iter_t* thiz () const { return static_cast<const iter_t *> (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 <typename iter_t, typename item_t = typename iter_t::__item_t__> +struct hb_iter_with_fallback_t : + hb_iter_t<iter_t, item_t>, + hb_iter_fallback_mixin_t<iter_t, item_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<typename Iter, typename Item> +struct hb_is_iterator_of +{ + template <typename Item2 = Item> + static hb_true_type impl (hb_priority<2>, hb_iter_t<Iter, hb_type_identity<Item2>> *); + 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<Iter, Item>::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<Iter, Item>::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 <typename T> +struct hb_is_iterable +{ + private: + + template <typename U> + static auto impl (hb_priority<1>) -> decltype (hb_declval (U).iter (), hb_true_type ()); + + template <typename> + static hb_false_type impl (hb_priority<0>); + + public: + static constexpr bool value = decltype (impl<T> (hb_prioritize))::value; +}; +#define hb_is_iterable(Iterable) hb_is_iterable<Iterable>::value + +/* hb_is_source_of() / hb_is_sink_of() */ + +template<typename Iter, typename Item> +struct hb_is_source_of +{ + private: + template <typename Iter2 = Iter, + hb_enable_if (hb_is_convertible (typename Iter2::item_t, hb_add_lvalue_reference<const Item>))> + static hb_true_type impl (hb_priority<2>); + template <typename Iter2 = Iter> + 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<Iter, Item>::value + +template<typename Iter, typename Item> +struct hb_is_sink_of +{ + private: + template <typename Iter2 = Iter, + hb_enable_if (hb_is_convertible (typename Iter2::item_t, hb_add_lvalue_reference<Item>))> + static hb_true_type impl (hb_priority<2>); + template <typename Iter2 = Iter> + 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<Iter, Item>::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 <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> +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 <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> +static inline auto begin (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).begin ()) + +template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> +static inline auto end (Iterable&& iterable) HB_AUTO_RETURN (hb_iter (iterable).end ()) + +} + + +/* + * Adaptors, combiners, etc. + */ + +template <typename Lhs, typename Rhs, + hb_requires (hb_is_iterator (Lhs))> +static inline auto +operator | (Lhs&& lhs, Rhs&& rhs) HB_AUTO_RETURN (std::forward<Rhs> (rhs) (std::forward<Lhs> (lhs))) + +/* hb_map(), hb_filter(), hb_reduce() */ + +enum class hb_function_sortedness_t { + NOT_SORTED, + RETAINS_SORTING, + SORTED, +}; + +template <typename Iter, typename Proj, hb_function_sortedness_t Sorted, + hb_requires (hb_is_iterator (Iter))> +struct hb_map_iter_t : + hb_iter_t<hb_map_iter_t<Iter, Proj, Sorted>, + 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<Proj> f; +}; + +template <typename Proj, hb_function_sortedness_t Sorted> +struct hb_map_iter_factory_t +{ + hb_map_iter_factory_t (Proj f) : f (f) {} + + template <typename Iter, + hb_requires (hb_is_iterator (Iter))> + hb_map_iter_t<Iter, Proj, Sorted> + operator () (Iter it) + { return hb_map_iter_t<Iter, Proj, Sorted> (it, f); } + + private: + Proj f; +}; +struct +{ + template <typename Proj> + hb_map_iter_factory_t<Proj, hb_function_sortedness_t::NOT_SORTED> + operator () (Proj&& f) const + { return hb_map_iter_factory_t<Proj, hb_function_sortedness_t::NOT_SORTED> (f); } +} +HB_FUNCOBJ (hb_map); +struct +{ + template <typename Proj> + hb_map_iter_factory_t<Proj, hb_function_sortedness_t::RETAINS_SORTING> + operator () (Proj&& f) const + { return hb_map_iter_factory_t<Proj, hb_function_sortedness_t::RETAINS_SORTING> (f); } +} +HB_FUNCOBJ (hb_map_retains_sorting); +struct +{ + template <typename Proj> + hb_map_iter_factory_t<Proj, hb_function_sortedness_t::SORTED> + operator () (Proj&& f) const + { return hb_map_iter_factory_t<Proj, hb_function_sortedness_t::SORTED> (f); } +} +HB_FUNCOBJ (hb_map_sorted); + +template <typename Iter, typename Pred, typename Proj, + hb_requires (hb_is_iterator (Iter))> +struct hb_filter_iter_t : + hb_iter_with_fallback_t<hb_filter_iter_t<Iter, Pred, Proj>, + 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<Pred> p; + mutable hb_reference_wrapper<Proj> f; +}; +template <typename Pred, typename Proj> +struct hb_filter_iter_factory_t +{ + hb_filter_iter_factory_t (Pred p, Proj f) : p (p), f (f) {} + + template <typename Iter, + hb_requires (hb_is_iterator (Iter))> + hb_filter_iter_t<Iter, Pred, Proj> + operator () (Iter it) + { return hb_filter_iter_t<Iter, Pred, Proj> (it, p, f); } + + private: + Pred p; + Proj f; +}; +struct +{ + template <typename Pred = decltype ((hb_identity)), + typename Proj = decltype ((hb_identity))> + hb_filter_iter_factory_t<Pred, Proj> + operator () (Pred&& p = hb_identity, Proj&& f = hb_identity) const + { return hb_filter_iter_factory_t<Pred, Proj> (p, f); } +} +HB_FUNCOBJ (hb_filter); + +template <typename Redu, typename InitT> +struct hb_reduce_t +{ + hb_reduce_t (Redu r, InitT init_value) : r (r), init_value (init_value) {} + + template <typename Iter, + hb_requires (hb_is_iterator (Iter)), + typename AccuT = hb_decay<decltype (hb_declval (Redu) (hb_declval (InitT), hb_declval (typename Iter::item_t)))>> + 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 <typename Redu, typename InitT> + hb_reduce_t<Redu, InitT> + operator () (Redu&& r, InitT init_value) const + { return hb_reduce_t<Redu, InitT> (r, init_value); } +} +HB_FUNCOBJ (hb_reduce); + + +/* hb_zip() */ + +template <typename A, typename B> +struct hb_zip_iter_t : + hb_iter_t<hb_zip_iter_t<A, B>, + hb_pair_t<typename A::item_t, typename B::item_t>> +{ + hb_zip_iter_t () {} + hb_zip_iter_t (const A& a, const B& b) : a (a), b (b) {} + + typedef hb_pair_t<typename A::item_t, typename B::item_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 <typename A, typename B, + hb_requires (hb_is_iterable (A) && hb_is_iterable (B))> + hb_zip_iter_t<hb_iter_type<A>, hb_iter_type<B>> + operator () (A&& a, B&& b) const + { return hb_zip_iter_t<hb_iter_type<A>, hb_iter_type<B>> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_zip); + +/* hb_concat() */ + +template <typename A, typename B> +struct hb_concat_iter_t : + hb_iter_t<hb_concat_iter_t<A, B>, 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 <typename A, typename B, + hb_requires (hb_is_iterable (A) && hb_is_iterable (B))> + hb_concat_iter_t<hb_iter_type<A>, hb_iter_type<B>> + operator () (A&& a, B&& b) const + { return hb_concat_iter_t<hb_iter_type<A>, hb_iter_type<B>> (hb_iter (a), hb_iter (b)); } +} +HB_FUNCOBJ (hb_concat); + +/* hb_apply() */ + +template <typename Appl> +struct hb_apply_t +{ + hb_apply_t (Appl a) : a (a) {} + + template <typename Iter, + hb_requires (hb_is_iterator (Iter))> + void operator () (Iter it) + { + for (; it; ++it) + (void) hb_invoke (a, *it); + } + + private: + Appl a; +}; +struct +{ + template <typename Appl> hb_apply_t<Appl> + operator () (Appl&& a) const + { return hb_apply_t<Appl> (a); } + + template <typename Appl> hb_apply_t<Appl&> + operator () (Appl *a) const + { return hb_apply_t<Appl&> (*a); } +} +HB_FUNCOBJ (hb_apply); + +/* hb_range()/hb_iota()/hb_repeat() */ + +template <typename T, typename S> +struct hb_range_iter_t : + hb_iter_t<hb_range_iter_t<T, S>, 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 <typename T = unsigned> hb_range_iter_t<T, unsigned> + operator () (T end = (unsigned) -1) const + { return hb_range_iter_t<T, unsigned> (0, end, 1u); } + + template <typename T, typename S = unsigned> hb_range_iter_t<T, S> + operator () (T start, T end, S step = 1u) const + { return hb_range_iter_t<T, S> (start, end, step); } +} +HB_FUNCOBJ (hb_range); + +template <typename T, typename S> +struct hb_iota_iter_t : + hb_iter_with_fallback_t<hb_iota_iter_t<T, S>, T> +{ + hb_iota_iter_t (T start, S step) : v (start), step (step) {} + + private: + + template <typename S2 = S> + auto + inc (hb_type_identity<S2> s, hb_priority<1>) + -> hb_void_t<decltype (hb_invoke (std::forward<S2> (s), hb_declval<T&> ()))> + { v = hb_invoke (std::forward<S2> (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 <typename T = unsigned, typename S = unsigned> hb_iota_iter_t<T, S> + operator () (T start = 0u, S step = 1u) const + { return hb_iota_iter_t<T, S> (start, step); } +} +HB_FUNCOBJ (hb_iota); + +template <typename T> +struct hb_repeat_iter_t : + hb_iter_t<hb_repeat_iter_t<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 <typename T> hb_repeat_iter_t<T> + operator () (T value) const + { return hb_repeat_iter_t<T> (value); } +} +HB_FUNCOBJ (hb_repeat); + +/* hb_enumerate()/hb_take() */ + +struct +{ + template <typename Iterable, + typename Index = unsigned, + hb_requires (hb_is_iterable (Iterable))> + 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 <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + 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 <typename Type> inline hb_array_t<Type> + operator () (hb_array_t<Type> array, unsigned count) const + { return array.sub_array (0, count); } + + template <typename Type> inline hb_sorted_array_t<Type> + operator () (hb_sorted_array_t<Type> array, unsigned count) const + { return array.sub_array (0, count); } +} +HB_FUNCOBJ (hb_take); + +struct +{ HB_PARTIALIZE(2); + template <typename Iter, + hb_requires (hb_is_iterator (Iter))> + 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 <typename Sink> +struct hb_sink_t +{ + hb_sink_t (Sink s) : s (s) {} + + template <typename Iter, + hb_requires (hb_is_iterator (Iter))> + void operator () (Iter it) + { + for (; it; ++it) + s << *it; + } + + private: + Sink s; +}; +struct +{ + template <typename Sink> hb_sink_t<Sink> + operator () (Sink&& s) const + { return hb_sink_t<Sink> (s); } + + template <typename Sink> hb_sink_t<Sink&> + operator () (Sink *s) const + { return hb_sink_t<Sink&> (*s); } +} +HB_FUNCOBJ (hb_sink); + +/* hb-drain: hb_sink to void / blackhole / /dev/null. */ + +struct +{ + template <typename Iter, + hb_requires (hb_is_iterator (Iter))> + void operator () (Iter it) const + { + for (; it; ++it) + (void) *it; + } +} +HB_FUNCOBJ (hb_drain); + +/* hb_unzip(): unzip and sink to two sinks. */ + +template <typename Sink1, typename Sink2> +struct hb_unzip_t +{ + hb_unzip_t (Sink1 s1, Sink2 s2) : s1 (s1), s2 (s2) {} + + template <typename Iter, + hb_requires (hb_is_iterator (Iter))> + void operator () (Iter it) + { + for (; it; ++it) + { + const auto &v = *it; + s1 << v.first; + s2 << v.second; + } + } + + private: + Sink1 s1; + Sink2 s2; +}; +struct +{ + template <typename Sink1, typename Sink2> hb_unzip_t<Sink1, Sink2> + operator () (Sink1&& s1, Sink2&& s2) const + { return hb_unzip_t<Sink1, Sink2> (s1, s2); } + + template <typename Sink1, typename Sink2> hb_unzip_t<Sink1&, Sink2&> + operator () (Sink1 *s1, Sink2 *s2) const + { return hb_unzip_t<Sink1&, Sink2&> (*s1, *s2); } +} +HB_FUNCOBJ (hb_unzip); + + +/* hb-all, hb-any, hb-none. */ + +struct +{ + template <typename Iterable, + typename Pred = decltype ((hb_identity)), + typename Proj = decltype ((hb_identity)), + hb_requires (hb_is_iterable (Iterable))> + 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<Pred> (p), hb_get (std::forward<Proj> (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_all); +struct +{ + template <typename Iterable, + typename Pred = decltype ((hb_identity)), + typename Proj = decltype ((hb_identity)), + hb_requires (hb_is_iterable (Iterable))> + 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<Pred> (p), hb_get (std::forward<Proj> (f), *it))) + return true; + return false; + } +} +HB_FUNCOBJ (hb_any); +struct +{ + template <typename Iterable, + typename Pred = decltype ((hb_identity)), + typename Proj = decltype ((hb_identity)), + hb_requires (hb_is_iterable (Iterable))> + 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<Pred> (p), hb_get (std::forward<Proj> (f), *it))) + return false; + return true; + } +} +HB_FUNCOBJ (hb_none); + +/* + * Algorithms operating on iterators. + */ + +template <typename C, typename V, + hb_requires (hb_is_iterable (C))> +inline void +hb_fill (C&& c, const V &v) +{ + for (auto i = hb_iter (c); i; i++) + *i = v; +} + +template <typename S, typename D> +inline void +hb_copy (S&& is, D&& id) +{ + hb_iter (is) | hb_sink (id); +} + + +#endif /* HB_ITER_HH */ diff --git a/gfx/harfbuzz/src/hb-kern.hh b/gfx/harfbuzz/src/hb-kern.hh new file mode 100644 index 0000000000..0462a0ea8e --- /dev/null +++ b/gfx/harfbuzz/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 <typename Driver> +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/gfx/harfbuzz/src/hb-limits.hh b/gfx/harfbuzz/src/hb-limits.hh new file mode 100644 index 0000000000..25c1e71e13 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-machinery.hh b/gfx/harfbuzz/src/hb-machinery.hh new file mode 100644 index 0000000000..ecff94f1b6 --- /dev/null +++ b/gfx/harfbuzz/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<T>(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template<typename Type> +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast<const Type*> ((const char *) P + offset); } +template<typename Type> +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast<Type*> ((char *) P + offset); } +template<typename Type> +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 Type*> ((const char *) P + offset); +#pragma GCC diagnostic pop +} +template<typename Type> +static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + return * reinterpret_cast<Type*> ((char *) P + offset); +#pragma GCC diagnostic pop +} + +/* StructAfter<T>(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template<typename Type, typename TObject> +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset<Type>(&X, X.get_size()); } +template<typename Type, typename TObject> +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset<Type>(&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 <typename Data, unsigned int WheresData> +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 <typename Stored, typename Subclass> + Stored * call_create () const { return Subclass::create (get_data ()); } +}; +template <> +struct hb_data_wrapper_t<void, 0> +{ + bool is_inert () const { return false; } + + template <typename Stored, typename Funcs> + Stored * call_create () const { return Funcs::create (); } +}; + +template <typename T1, typename T2> struct hb_non_void_t { typedef T1 value; }; +template <typename T2> struct hb_non_void_t<void, T2> { typedef T2 value; }; + +template <typename Returned, + typename Subclass = void, + typename Data = void, + unsigned int WheresData = 0, + typename Stored = Returned> +struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData> +{ + typedef typename hb_non_void_t<Subclass, + hb_lazy_loader_t<Returned,Subclass,Data,WheresData,Stored> + >::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<Stored *> (Funcs::get_null ())) + Funcs::destroy (p); + } + + const Returned * operator -> () const { return get (); } + template <typename U = Returned, hb_enable_if (!hb_is_same (U, void))> + const U & operator * () const { return *get (); } + explicit operator bool () const + { return get_stored () != Funcs::get_null (); } + template <typename C> 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<Stored *> (Funcs::get_null ()); + + p = this->template call_create<Stored, Funcs> (); + if (unlikely (!p)) + p = const_cast<Stored *> (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<Returned *> (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<Stored *> instance; +}; + +/* Specializations. */ + +template <typename T, unsigned int WheresFace> +struct hb_face_lazy_loader_t : hb_lazy_loader_t<T, + hb_face_lazy_loader_t<T, WheresFace>, + 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 <typename T, unsigned int WheresFace, bool core=false> +struct hb_table_lazy_loader_t : hb_lazy_loader_t<T, + hb_table_lazy_loader_t<T, WheresFace, core>, + 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<T> (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<T> (); } + + hb_blob_t* get_blob () const { return this->get_stored (); } +}; + +#define HB_DEFINE_TYPE_FUNCS_LAZY_LOADER_T(Type) \ + template <typename Subclass> \ + struct hb_##Type##_funcs_lazy_loader_t : hb_lazy_loader_t<hb_##Type##_funcs_t, Subclass> \ + { \ + 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/gfx/harfbuzz/src/hb-map.cc b/gfx/harfbuzz/src/hb-map.cc new file mode 100644 index 0000000000..0dc9246f12 --- /dev/null +++ b/gfx/harfbuzz/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<hb_map_t> ())) + 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<hb_map_t *> (&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/gfx/harfbuzz/src/hb-map.h b/gfx/harfbuzz/src/hb-map.h new file mode 100644 index 0000000000..0ae171714e --- /dev/null +++ b/gfx/harfbuzz/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 <hb.h> 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/gfx/harfbuzz/src/hb-map.hh b/gfx/harfbuzz/src/hb-map.hh new file mode 100644 index 0000000000..45a02b830c --- /dev/null +++ b/gfx/harfbuzz/src/hb-map.hh @@ -0,0 +1,571 @@ +/* + * 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 <typename K, typename V, + bool minus_one = false> +struct hb_hashmap_t +{ + static constexpr bool realloc_move = true; + + hb_hashmap_t () { init (); } + ~hb_hashmap_t () { fini (); } + + hb_hashmap_t (const hb_hashmap_t& o) : hb_hashmap_t () + { + if (unlikely (!o.mask)) return; + + if (item_t::is_trivial) + { + items = (item_t *) hb_malloc (sizeof (item_t) * (o.mask + 1)); + if (unlikely (!items)) + { + successful = false; + return; + } + population = o.population; + occupancy = o.occupancy; + mask = o.mask; + prime = o.prime; + max_chain_length = o.max_chain_length; + memcpy (items, o.items, sizeof (item_t) * (mask + 1)); + return; + } + + 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<hb_pair_t<K, V>> lst) : hb_hashmap_t () + { + for (auto&& item : lst) + set (item.first, item.second); + } + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + 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 <bool v = minus_one, + hb_enable_if (v == false)> + static inline const V& default_value () { return Null(V); }; + template <bool v = minus_one, + hb_enable_if (v == true)> + 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<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); } + hb_pair_t<const K &, V &> get_pair_ref() { return hb_pair_t<const K &, V &> (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) + for (unsigned int i = 0; i < old_size; i++) + old_items[i].~item_t (); + + hb_free (old_items); + + return true; + } + + template <typename KK, typename VV> + 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<K>::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<KK> (key); + item.value = std::forward<VV> (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 <typename VV> + bool set (const K &key, VV&& value, bool overwrite = true) { return set_with_hash (key, hb_hash (key), std::forward<VV> (value), overwrite); } + template <typename VV> + 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<VV> (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 <typename VV=V> + 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<K>::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<K, V>& v) + { set (v.first, v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t<K, V&&>& v) + { set (v.first, std::move (v.second)); return *this; } + hb_hashmap_t& operator << (const hb_pair_t<K&&, V>& v) + { set (std::move (v.first), v.second); return *this; } + hb_hashmap_t& operator << (const hb_pair_t<K&&, V&&>& 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<hb_codepoint_t, + hb_codepoint_t, + true> +{ + using hashmap = hb_hashmap_t<hb_codepoint_t, + hb_codepoint_t, + true>; + + ~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<hb_codepoint_pair_t> lst) : hashmap (lst) {} + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + hb_map_t (const Iterable &o) : hashmap (o) {} +}; + + +#endif /* HB_MAP_HH */ diff --git a/gfx/harfbuzz/src/hb-meta.hh b/gfx/harfbuzz/src/hb-meta.hh new file mode 100644 index 0000000000..52ff4a8412 --- /dev/null +++ b/gfx/harfbuzz/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 <memory> +#include <type_traits> +#include <utility> + + +/* + * 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<typename... Ts> struct _hb_void_t { typedef void type; }; +template<typename... Ts> using hb_void_t = typename _hb_void_t<Ts...>::type; + +template<typename Head, typename... Ts> struct _hb_head_t { typedef Head type; }; +template<typename... Ts> using hb_head_t = typename _hb_head_t<Ts...>::type; + +template <typename T, T v> struct hb_integral_constant { static constexpr T value = v; }; +template <bool b> using hb_bool_constant = hb_integral_constant<bool, b>; +using hb_true_type = hb_bool_constant<true>; +using hb_false_type = hb_bool_constant<false>; + +/* Static-assert as expression. */ +template <bool cond> struct static_assert_expr; +template <> struct static_assert_expr<true> : hb_false_type {}; +#define static_assert_expr(C) static_assert_expr<C>::value + +/* Basic type SFINAE. */ + +template <bool B, typename T = void> struct hb_enable_if {}; +template <typename T> struct hb_enable_if<true, T> { 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 <typename T, typename T2> struct hb_is_same : hb_false_type {}; +template <typename T> struct hb_is_same<T, T> : hb_true_type {}; +#define hb_is_same(T, T2) hb_is_same<T, T2>::value + +/* Function overloading SFINAE and priority. */ + +#define HB_RETURN(Ret, E) -> hb_head_t<Ret, decltype ((E))> { return (E); } +#define HB_AUTO_RETURN(E) -> decltype ((E)) { return (E); } +#define HB_VOID_RETURN(E) -> hb_void_t<decltype ((E))> { (E); } + +template <unsigned Pri> struct hb_priority : hb_priority<Pri - 1> {}; +template <> struct hb_priority<0> {}; +#define hb_prioritize hb_priority<16> () + +#define HB_FUNCOBJ(x) static_const x HB_UNUSED + + +template <typename T> struct hb_type_identity_t { typedef T type; }; +template <typename T> using hb_type_identity = typename hb_type_identity_t<T>::type; + +template <typename T> static inline T hb_declval (); +#define hb_declval(T) (hb_declval<T> ()) + +template <typename T> struct hb_match_const : hb_type_identity_t<T>, hb_false_type {}; +template <typename T> struct hb_match_const<const T> : hb_type_identity_t<T>, hb_true_type {}; +template <typename T> using hb_remove_const = typename hb_match_const<T>::type; + +template <typename T> struct hb_match_reference : hb_type_identity_t<T>, hb_false_type {}; +template <typename T> struct hb_match_reference<T &> : hb_type_identity_t<T>, hb_true_type {}; +template <typename T> struct hb_match_reference<T &&> : hb_type_identity_t<T>, hb_true_type {}; +template <typename T> using hb_remove_reference = typename hb_match_reference<T>::type; +template <typename T> auto _hb_try_add_lvalue_reference (hb_priority<1>) -> hb_type_identity<T&>; +template <typename T> auto _hb_try_add_lvalue_reference (hb_priority<0>) -> hb_type_identity<T>; +template <typename T> using hb_add_lvalue_reference = decltype (_hb_try_add_lvalue_reference<T> (hb_prioritize)); +template <typename T> auto _hb_try_add_rvalue_reference (hb_priority<1>) -> hb_type_identity<T&&>; +template <typename T> auto _hb_try_add_rvalue_reference (hb_priority<0>) -> hb_type_identity<T>; +template <typename T> using hb_add_rvalue_reference = decltype (_hb_try_add_rvalue_reference<T> (hb_prioritize)); + +template <typename T> struct hb_match_pointer : hb_type_identity_t<T>, hb_false_type {}; +template <typename T> struct hb_match_pointer<T *> : hb_type_identity_t<T>, hb_true_type {}; +template <typename T> using hb_remove_pointer = typename hb_match_pointer<T>::type; +template <typename T> auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity<hb_remove_reference<T>*>; +template <typename T> auto _hb_try_add_pointer (hb_priority<1>) -> hb_type_identity<T>; +template <typename T> using hb_add_pointer = decltype (_hb_try_add_pointer<T> (hb_prioritize)); + + +template <typename T> using hb_decay = typename std::decay<T>::type; + +#define hb_is_convertible(From,To) std::is_convertible<From, To>::value + +template <typename From, typename To> +using hb_is_cr_convertible = hb_bool_constant< + hb_is_same (hb_decay<From>, hb_decay<To>) && + (!std::is_const<From>::value || std::is_const<To>::value) && + (!std::is_reference<To>::value || std::is_const<To>::value || std::is_reference<To>::value) +>; +#define hb_is_cr_convertible(From,To) hb_is_cr_convertible<From, To>::value + + +struct +{ + template <typename T> constexpr auto + operator () (T&& v) const HB_AUTO_RETURN (std::forward<T> (v)) + + template <typename T> constexpr auto + operator () (T *v) const HB_AUTO_RETURN (*v) + + template <typename T> constexpr auto + operator () (const hb::shared_ptr<T>& v) const HB_AUTO_RETURN (*v) + + template <typename T> constexpr auto + operator () (hb::shared_ptr<T>& v) const HB_AUTO_RETURN (*v) + + template <typename T> constexpr auto + operator () (const hb::unique_ptr<T>& v) const HB_AUTO_RETURN (*v) + + template <typename T> constexpr auto + operator () (hb::unique_ptr<T>& v) const HB_AUTO_RETURN (*v) +} +HB_FUNCOBJ (hb_deref); + +template <typename T> +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 <typename T> +struct hb_reference_wrapper<T&> +{ + 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 <typename T> struct hb_int_min; +template <> struct hb_int_min<char> : hb_integral_constant<char, CHAR_MIN> {}; +template <> struct hb_int_min<signed char> : hb_integral_constant<signed char, SCHAR_MIN> {}; +template <> struct hb_int_min<unsigned char> : hb_integral_constant<unsigned char, 0> {}; +template <> struct hb_int_min<signed short> : hb_integral_constant<signed short, SHRT_MIN> {}; +template <> struct hb_int_min<unsigned short> : hb_integral_constant<unsigned short, 0> {}; +template <> struct hb_int_min<signed int> : hb_integral_constant<signed int, INT_MIN> {}; +template <> struct hb_int_min<unsigned int> : hb_integral_constant<unsigned int, 0> {}; +template <> struct hb_int_min<signed long> : hb_integral_constant<signed long, LONG_MIN> {}; +template <> struct hb_int_min<unsigned long> : hb_integral_constant<unsigned long, 0> {}; +template <> struct hb_int_min<signed long long> : hb_integral_constant<signed long long, LLONG_MIN> {}; +template <> struct hb_int_min<unsigned long long> : hb_integral_constant<unsigned long long, 0> {}; +template <typename T> struct hb_int_min<T *> : hb_integral_constant<T *, nullptr> {}; +#define hb_int_min(T) hb_int_min<T>::value +template <typename T> struct hb_int_max; +template <> struct hb_int_max<char> : hb_integral_constant<char, CHAR_MAX> {}; +template <> struct hb_int_max<signed char> : hb_integral_constant<signed char, SCHAR_MAX> {}; +template <> struct hb_int_max<unsigned char> : hb_integral_constant<unsigned char, UCHAR_MAX> {}; +template <> struct hb_int_max<signed short> : hb_integral_constant<signed short, SHRT_MAX> {}; +template <> struct hb_int_max<unsigned short> : hb_integral_constant<unsigned short, USHRT_MAX> {}; +template <> struct hb_int_max<signed int> : hb_integral_constant<signed int, INT_MAX> {}; +template <> struct hb_int_max<unsigned int> : hb_integral_constant<unsigned int, UINT_MAX> {}; +template <> struct hb_int_max<signed long> : hb_integral_constant<signed long, LONG_MAX> {}; +template <> struct hb_int_max<unsigned long> : hb_integral_constant<unsigned long, ULONG_MAX> {}; +template <> struct hb_int_max<signed long long> : hb_integral_constant<signed long long, LLONG_MAX> {}; +template <> struct hb_int_max<unsigned long long> : hb_integral_constant<unsigned long long, ULLONG_MAX> {}; +#define hb_int_max(T) hb_int_max<T>::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<T>::value +#define hb_is_trivially_copy_assignable(T) std::is_trivially_copy_assignable<T>::value +#define hb_is_trivially_constructible(T) std::is_trivially_constructible<T>::value +#define hb_is_trivially_copy_constructible(T) std::is_trivially_copy_constructible<T>::value +#define hb_is_trivially_destructible(T) std::is_trivially_destructible<T>::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 <typename T, typename> +struct _hb_unwrap_type : hb_type_identity_t<T> {}; +template <typename T> +struct _hb_unwrap_type<T, hb_void_t<typename T::type>> : _hb_unwrap_type<typename T::type, void> {}; +template <typename T> +using hb_unwrap_type = _hb_unwrap_type<T, void>; +#define hb_unwrap_type(T) typename hb_unwrap_type<T>::type + +#endif /* HB_META_HH */ diff --git a/gfx/harfbuzz/src/hb-ms-feature-ranges.hh b/gfx/harfbuzz/src/hb-ms-feature-ranges.hh new file mode 100644 index 0000000000..f7649ab76e --- /dev/null +++ b/gfx/harfbuzz/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<hb_ms_feature_t> &feature_records, /* OUT */ + hb_vector_t<hb_ms_range_record_t> &range_records /* OUT */) +{ + feature_records.shrink(0); + range_records.shrink(0); + + /* Sort features by start/end events. */ + hb_vector_t<hb_ms_feature_event_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<hb_ms_active_feature_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<hb_ms_feature_t *> (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<uintptr_t> (range->features.features); + } + + return !!num_features; +} + +static inline void +hb_ms_make_feature_ranges (hb_vector_t<hb_ms_feature_t> &feature_records, + hb_vector_t<hb_ms_range_record_t> &range_records, + unsigned int chars_offset, + unsigned int chars_len, + uint16_t *log_clusters, + hb_vector_t<hb_ms_features_t*> &range_features, /* OUT */ + hb_vector_t<uint32_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/gfx/harfbuzz/src/hb-multimap.hh b/gfx/harfbuzz/src/hb-multimap.hh new file mode 100644 index 0000000000..0184279c12 --- /dev/null +++ b/gfx/harfbuzz/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<hb_codepoint_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<hb_codepoint_t> {old, v}); + return; + } + + singulars.set (k, v); + } + + hb_array_t<const hb_codepoint_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<hb_codepoint_t> *m; + if (multiples.has (k, &m)) + return m->as_array (); + + return hb_array_t<const hb_codepoint_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<hb_codepoint_t, hb_vector_t<hb_codepoint_t>> multiples; +}; + + + +#endif /* HB_MULTIMAP_HH */ diff --git a/gfx/harfbuzz/src/hb-mutex.hh b/gfx/harfbuzz/src/hb-mutex.hh new file mode 100644 index 0000000000..e329d9864f --- /dev/null +++ b/gfx/harfbuzz/src/hb-mutex.hh @@ -0,0 +1,122 @@ +/* + * 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 <chris@chris-wilson.co.uk> + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MUTEX_HH +#define HB_MUTEX_HH + +#include "hb.hh" + + +/* mutex */ + +/* We need external help for these */ + +#if defined(hb_mutex_impl_init) \ + && defined(hb_mutex_impl_lock) \ + && defined(hb_mutex_impl_unlock) \ + && defined(hb_mutex_impl_finish) + +/* Defined externally, i.e. in config.h; must have typedef'ed hb_mutex_impl_t as well. */ + + +#elif !defined(HB_NO_MT) && !defined(HB_MUTEX_IMPL_STD_MUTEX) && (defined(HAVE_PTHREAD) || defined(__APPLE__)) + +#include <pthread.h> +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) + +typedef CRITICAL_SECTION hb_mutex_impl_t; +#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) +#endif +#define hb_mutex_impl_lock(M) EnterCriticalSection (M) +#define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) +#define hb_mutex_impl_finish(M) DeleteCriticalSection (M) + + +#elif !defined(HB_NO_MT) + +#include <mutex> +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 /* defined(HB_NO_MT) */ + +typedef int hb_mutex_impl_t; +#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 +#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END + + +#endif + + +struct hb_mutex_t +{ + /* Create space for, but do not initialize m. */ + alignas(hb_mutex_impl_t) char m[sizeof (hb_mutex_impl_t)]; + + hb_mutex_t () { init (); } + ~hb_mutex_t () { fini (); } + +#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_HH */ diff --git a/gfx/harfbuzz/src/hb-null.hh b/gfx/harfbuzz/src/hb-null.hh new file mode 100644 index 0000000000..854485d3df --- /dev/null +++ b/gfx/harfbuzz/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 <typename T, typename> +struct _hb_has_min_size : hb_false_type {}; +template <typename T> +struct _hb_has_min_size<T, hb_void_t<decltype (T::min_size)>> + : hb_true_type {}; +template <typename T> +using hb_has_min_size = _hb_has_min_size<T, void>; +#define hb_has_min_size(T) hb_has_min_size<T>::value + +template <typename T, typename> +struct _hb_has_null_size : hb_false_type {}; +template <typename T> +struct _hb_has_null_size<T, hb_void_t<decltype (T::null_size)>> + : hb_true_type {}; +template <typename T> +using hb_has_null_size = _hb_has_null_size<T, void>; +#define hb_has_null_size(T) hb_has_null_size<T>::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 <typename T, typename> +struct _hb_null_size : hb_integral_constant<unsigned, sizeof (T)> {}; +template <typename T> +struct _hb_null_size<T, hb_void_t<decltype (T::min_size)>> + : hb_integral_constant<unsigned, + (sizeof (T) > T::null_size ? sizeof (T) : T::null_size)> {}; +template <typename T> +using hb_null_size = _hb_null_size<T, void>; +#define hb_null_size(T) hb_null_size<T>::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 <typename T, typename> +struct _hb_static_size : hb_integral_constant<unsigned, sizeof (T)> {}; +template <typename T> +struct _hb_static_size<T, hb_void_t<decltype (T::static_size)>> : hb_integral_constant<unsigned, T::static_size> {}; +template <typename T> +using hb_static_size = _hb_static_size<T, void>; +#define hb_static_size(T) hb_static_size<T>::value + +template <typename T, typename> +struct _hb_min_size : hb_integral_constant<unsigned, sizeof (T)> {}; +template <typename T> +struct _hb_min_size<T, hb_void_t<decltype (T::min_size)>> : hb_integral_constant<unsigned, T::min_size> {}; +template <typename T> +using hb_min_size = _hb_min_size<T, void>; +#define hb_min_size(T) hb_min_size<T>::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 <typename Type> +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<Type const *> (_hb_NullPool); + } +}; +template <typename QType> +struct NullHelper +{ + typedef hb_remove_const<hb_remove_reference<QType>> Type; + static const Type & get_null () { return Null<Type>::get_null (); } +}; +#define Null(Type) NullHelper<Type>::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<Namespace::Type> { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast<const Namespace::Type *> (_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 <typename Spec> \ + struct Null<Namespace::Type<Spec>> { \ + static Namespace::Type<Spec> const & get_null () { \ + return *reinterpret_cast<const Namespace::Type<Spec> *> (_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<Type> { \ + 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 <typename Type> +static inline Type& Crap () { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + Type *obj = reinterpret_cast<Type *> (_hb_CrapPool); + memcpy (obj, std::addressof (Null (Type)), sizeof (*obj)); + return *obj; +} +template <typename QType> +struct CrapHelper +{ + typedef hb_remove_const<hb_remove_reference<QType>> Type; + static Type & get_crap () { return Crap<Type> (); } +}; +#define Crap(Type) CrapHelper<Type>::get_crap () + +template <typename Type> +struct CrapOrNullHelper { + static Type & get () { return Crap (Type); } +}; +template <typename Type> +struct CrapOrNullHelper<const Type> { + static const Type & get () { return Null (Type); } +}; +#define CrapOrNull(Type) CrapOrNullHelper<Type>::get () + + +/* + * hb_nonnull_ptr_t + */ + +template <typename P> +struct hb_nonnull_ptr_t +{ + typedef hb_remove_pointer<P> 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 <typename C> operator const C * () const { return get (); } + operator const char * () const { return (const char *) get (); } + T * get () const { return v ? v : const_cast<T *> (std::addressof (Null (T))); } + T * get_raw () const { return v; } + + private: + T *v; +}; + + +#endif /* HB_NULL_HH */ diff --git a/gfx/harfbuzz/src/hb-number-parser.hh b/gfx/harfbuzz/src/hb-number-parser.hh new file mode 100644 index 0000000000..1a9dbba6dd --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-number-parser.rl b/gfx/harfbuzz/src/hb-number-parser.rl new file mode 100644 index 0000000000..c6c4a3bab8 --- /dev/null +++ b/gfx/harfbuzz/src/hb-number-parser.rl @@ -0,0 +1,136 @@ +/* + * 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" + +%%{ + +machine double_parser; +alphtype unsigned char; +write data; + +action see_neg { neg = true; } +action see_exp_neg { exp_neg = true; } + +action add_int { + value = value * 10. + (fc - '0'); +} +action add_frac { + if (likely (frac <= MAX_FRACT / 10)) + { + frac = frac * 10. + (fc - '0'); + ++frac_count; + } +} +action add_exp { + if (likely (exp * 10 + (fc - '0') <= MAX_EXP)) + exp = exp * 10 + (fc - '0'); + else + exp_overflow = true; +} + +num = [0-9]+; + +main := ( + ( + (('+'|'-'@see_neg)? num @add_int) ('.' num @add_frac)? + | + (('+'|'-'@see_neg)? '.' num @add_frac) + ) + (('e'|'E') (('+'|'-'@see_exp_neg)? num @add_exp))? +); + +}%% + +/* 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; + %%{ + write init; + write exec; + }%% + + *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/gfx/harfbuzz/src/hb-number.cc b/gfx/harfbuzz/src/hb-number.cc new file mode 100644 index 0000000000..c52b284e1d --- /dev/null +++ b/gfx/harfbuzz/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<typename T, typename Func> +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<int> (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<unsigned> (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/gfx/harfbuzz/src/hb-number.hh b/gfx/harfbuzz/src/hb-number.hh new file mode 100644 index 0000000000..14d1260aa3 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-object.hh b/gfx/harfbuzz/src/hb-object.hh new file mode 100644 index 0000000000..e2c2c3394c --- /dev/null +++ b/gfx/harfbuzz/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 <chris@chris-wilson.co.uk> + * 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 <typename item_t, typename lock_t> +struct hb_lockable_set_t +{ + hb_vector_t<item_t> items; + + void init () { items.init (); } + + template <typename T> + 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 <typename T> + 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 <typename T> + 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 <typename T> + 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<hb_user_data_item_t, hb_mutex_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<hb_user_data_array_t> user_data; + + bool is_inert () const { return !ref_count.get_relaxed (); } +}; +#define HB_OBJECT_HEADER_STATIC {} + + +/* + * Object + */ + +template <typename Type> +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 <typename Type, typename ...Ts> +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<Ts> (ds)...); + + hb_object_init (obj); + hb_object_trace (obj, HB_FUNC); + + return obj; +} +template <typename Type> +static inline void hb_object_init (Type *obj) +{ + obj->header.ref_count.init (); + obj->header.writable = true; + obj->header.user_data.init (); +} +template <typename Type> +static inline bool hb_object_is_valid (const Type *obj) +{ + return likely (obj->header.ref_count.is_valid ()); +} +template <typename Type> +static inline bool hb_object_is_immutable (const Type *obj) +{ + return !obj->header.writable; +} +template <typename Type> +static inline void hb_object_make_immutable (const Type *obj) +{ + obj->header.writable = false; +} +template <typename Type> +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 <typename Type> +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<Type>::value) + obj->~Type (); + + return true; +} +template <typename Type> +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 <typename Type> +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 <typename Type> +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/gfx/harfbuzz/src/hb-open-file.hh b/gfx/harfbuzz/src/hb-open-file.hh new file mode 100644 index 0000000000..1157ea46d0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-open-file.hh @@ -0,0 +1,543 @@ +/* + * 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 <typename Iterator, + hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::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<hb_tag_t, hb_blob_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<void> (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<TableRecord> + 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<Offset32To<OpenTypeOffsetTable>> + 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); + hb_barrier (); + 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<const OpenTypeFontFace *> ((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) && + hb_barrier () && + 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<Array32Of<HBUINT8>> + 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) && + hb_barrier () && + resourcesZ.sanitize (c, type_base, + get_resource_count (), + data_base)); + } + + protected: + Tag tag; /* Resource type. */ + HBUINT16 resCountM1; /* Number of resources minus 1. */ + NNOffset16To<UnsizedArrayOf<ResourceRecord>> + 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) && + hb_barrier () && + 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<ArrayOfM1<ResourceTypeRecord>> + 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) && + hb_barrier () && + data.sanitize (c, this, dataLen) && + map.sanitize (c, this, &(this+data))); + } + + protected: + NNOffset32To<UnsizedArrayOf<HBUINT8>> + data; /* Offset from beginning of resource fork + * to resource data */ + NNOffset32To<ResourceMap > + 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 <typename Iterator, + hb_requires ((hb_is_source_of<Iterator, hb_pair_t<hb_tag_t, hb_blob_t *>>::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); + hb_barrier (); + 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/gfx/harfbuzz/src/hb-open-type.hh b/gfx/harfbuzz/src/hb-open-type.hh new file mode 100644 index 0000000000..6967bca3d4 --- /dev/null +++ b/gfx/harfbuzz/src/hb-open-type.hh @@ -0,0 +1,1162 @@ +/* + * 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 <typename Type, + unsigned int Size = sizeof (Type)> +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<std::is_signed<Type>::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 <typename Type2, + hb_enable_if (std::is_integral<Type2>::value && + sizeof (Type2) < sizeof (int) && + sizeof (Type) < sizeof (int))> + int cmp (Type2 a) const + { + Type b = v; + return (int) a - (int) b; + } + template <typename Type2, + hb_enable_if (hb_is_convertible (Type2, Type))> + 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<Type, Size> v; + public: + DEFINE_SIZE_STATIC (Size); +}; + +typedef IntType<uint8_t> HBUINT8; /* 8-bit unsigned integer. */ +typedef IntType<int8_t> HBINT8; /* 8-bit signed integer. */ +typedef IntType<uint16_t> HBUINT16; /* 16-bit unsigned integer. */ +typedef IntType<int16_t> HBINT16; /* 16-bit signed integer. */ +typedef IntType<uint32_t> HBUINT32; /* 32-bit unsigned integer. */ +typedef IntType<int32_t> 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<uint32_t, 3> 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 <typename Type, unsigned fraction_bits> +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<HBINT16, 14>; +using F4DOT12 = HBFixed<HBINT16, 12>; +using F6DOT10 = HBFixed<HBINT16, 10>; + +/* 32-bit signed fixed-point number (16.16). */ +using F16DOT16 = HBFixed<HBINT32, 16>; + +/* 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<const char *> (this); } + operator char* () { return reinterpret_cast<char *> (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 <typename Type, bool has_null=true> +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<HBUINT16> Offset16; +typedef Offset<HBUINT24> Offset24; +typedef Offset<HBUINT32> 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 <typename FixedType=HBUINT16> +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 <typename Type, bool has_null> +struct _hb_has_null +{ + static const Type *get_null () { return nullptr; } + static Type *get_crap () { return nullptr; } +}; +template <typename Type> +struct _hb_has_null<Type, true> +{ + static const Type *get_null () { return &Null (Type); } + static Type *get_crap () { return &Crap (Type); } +}; + +template <typename Type, typename OffsetType, typename BaseType=void, bool has_null=true> +struct OffsetTo : Offset<OffsetType, has_null> +{ + 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<Type, has_null>::get_null (); + return StructAtOffset<const Type> (base, *this); + } + Type& operator () (void *base) const + { + if (unlikely (this->is_null ())) return *_hb_has_null<Type, has_null>::get_crap (); + return StructAtOffset<Type> (base, *this); + } + + template <typename Base, + hb_enable_if (hb_is_convertible (const Base, const BaseType *))> + friend const Type& operator + (const Base &base, const OffsetTo &offset) { return offset ((const void *) base); } + template <typename Base, + hb_enable_if (hb_is_convertible (const Base, const BaseType *))> + friend const Type& operator + (const OffsetTo &offset, const Base &base) { return offset ((const void *) base); } + template <typename Base, + hb_enable_if (hb_is_convertible (Base, BaseType *))> + friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); } + template <typename Base, + hb_enable_if (hb_is_convertible (Base, BaseType *))> + friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); } + + + template <typename Base, typename ...Ts> + bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src, + const Base *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<Ts> (ds)...); + + if (ret || !has_null) + s->add_link (*this, s->pop_pack ()); + else + s->pop_discard (); + + return ret; + } + + + template <typename ...Ts> + bool serialize_serialize (hb_serialize_context_t *c, Ts&&... ds) + { + *this = 0; + + Type* obj = c->push<Type> (); + bool ret = obj->serialize (c, std::forward<Ts> (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 <typename ...Ts> + 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<Ts> (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 BaseType *base) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); + //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 <typename ...Ts> +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool sanitize (hb_sanitize_context_t *c, const BaseType *base, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace (sanitize_shallow (c, base) && + hb_barrier () && + (this->is_null () || + c->dispatch (StructAtOffset<Type> (base, *this), std::forward<Ts> (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 <typename Type, typename BaseType=void, bool has_null=true> using Offset16To = OffsetTo<Type, HBUINT16, BaseType, has_null>; +template <typename Type, typename BaseType=void, bool has_null=true> using Offset24To = OffsetTo<Type, HBUINT24, BaseType, has_null>; +template <typename Type, typename BaseType=void, bool has_null=true> using Offset32To = OffsetTo<Type, HBUINT32, BaseType, has_null>; + +template <typename Type, typename OffsetType, typename BaseType=void> using NNOffsetTo = OffsetTo<Type, OffsetType, BaseType, false>; +template <typename Type, typename BaseType=void> using NNOffset16To = Offset16To<Type, BaseType, false>; +template <typename Type, typename BaseType=void> using NNOffset24To = Offset24To<Type, BaseType, false>; +template <typename Type, typename BaseType=void> using NNOffset32To = Offset32To<Type, BaseType, false>; + + +/* + * Array Types + */ + +template <typename Type> +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 <typename T> operator T * () { return arrayZ; } + template <typename T> operator const T * () const { return arrayZ; } + hb_array_t<Type> as_array (unsigned int len) + { return hb_array (arrayZ, len); } + hb_array_t<const Type> as_array (unsigned int len) const + { return hb_array (arrayZ, len); } + + template <typename T> + Type &lsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).lsearch (x, ¬_found); } + template <typename T> + const Type &lsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).lsearch (x, ¬_found); } + template <typename T> + 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 <typename Iterator, + hb_requires (hb_is_source_of (Iterator, Type))> + 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 <typename ...Ts> + 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); + hb_barrier (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (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 <typename Type, typename OffsetType, typename BaseType=void, bool has_null=true> +using UnsizedArray16OfOffsetTo = UnsizedArrayOf<OffsetTo<Type, OffsetType, BaseType, has_null>>; + +/* Unsized array of offsets relative to the beginning of the array itself. */ +template <typename Type, typename OffsetType, typename BaseType=void, bool has_null=true> +struct UnsizedListOfOffset16To : UnsizedArray16OfOffsetTo<Type, OffsetType, BaseType, has_null> +{ + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + const OffsetTo<Type, OffsetType, BaseType, has_null> *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<Type, OffsetType, BaseType, has_null> *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 <typename ...Ts> + bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((UnsizedArray16OfOffsetTo<Type, OffsetType, BaseType, has_null> + ::sanitize (c, count, this, std::forward<Ts> (ds)...))); + } +}; + +/* An array with sorted elements. Supports binary searching. */ +template <typename Type> +struct SortedUnsizedArrayOf : UnsizedArrayOf<Type> +{ + hb_sorted_array_t<Type> as_array (unsigned int len) + { return hb_sorted_array (this->arrayZ, len); } + hb_sorted_array_t<const Type> as_array (unsigned int len) const + { return hb_sorted_array (this->arrayZ, len); } + operator hb_sorted_array_t<Type> () { return as_array (); } + operator hb_sorted_array_t<const Type> () const { return as_array (); } + + template <typename T> + Type &bsearch (unsigned int len, const T &x, Type ¬_found = Crap (Type)) + { return *as_array (len).bsearch (x, ¬_found); } + template <typename T> + const Type &bsearch (unsigned int len, const T &x, const Type ¬_found = Null (Type)) const + { return *as_array (len).bsearch (x, ¬_found); } + template <typename T> + 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 <typename Type, typename LenType> +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<const Type> as_array () const { return hb_array (arrayZ, len); } + + /* Iterator. */ + typedef hb_array_t<const Type> 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 <typename T> + Type &lsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().lsearch (x, ¬_found); } + template <typename T> + const Type &lsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().lsearch (x, ¬_found); } + template <typename T> + 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 <typename Iterator, + hb_requires (hb_is_source_of (Iterator, Type))> + 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 <typename ...Ts> + 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); + hb_barrier (); + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (ds)...))) + return_trace (false); + return_trace (true); + } + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (len.sanitize (c) && + hb_barrier () && + c->check_array_sized (arrayZ, len, sizeof (LenType))); + } + + public: + LenType len; + Type arrayZ[HB_VAR_ARRAY]; + public: + DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); +}; +template <typename Type> using Array16Of = ArrayOf<Type, HBUINT16>; +template <typename Type> using Array24Of = ArrayOf<Type, HBUINT24>; +template <typename Type> using Array32Of = ArrayOf<Type, HBUINT32>; +using PString = ArrayOf<HBUINT8, HBUINT8>; + +/* Array of Offset's */ +template <typename Type> using Array16OfOffset16To = ArrayOf<OffsetTo<Type, HBUINT16>, HBUINT16>; +template <typename Type> using Array16OfOffset32To = ArrayOf<OffsetTo<Type, HBUINT32>, HBUINT16>; +template <typename Type> using Array32OfOffset32To = ArrayOf<OffsetTo<Type, HBUINT32>, HBUINT32>; + +/* Array of offsets relative to the beginning of the array itself. */ +template <typename Type, typename OffsetType> +struct List16OfOffsetTo : ArrayOf<OffsetTo<Type, OffsetType>, 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 <typename ...Ts> + bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const + { + TRACE_SANITIZE (this); + return_trace ((Array16Of<OffsetTo<Type, OffsetType>>::sanitize (c, this, std::forward<Ts> (ds)...))); + } +}; + +template <typename Type> +using List16OfOffset16To = List16OfOffsetTo<Type, HBUINT16>; + +/* An array starting at second element. */ +template <typename Type, typename LenType> +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<const Type> as_array () const { return hb_array (arrayZ, get_length ()); } + + /* Iterator. */ + typedef hb_array_t<const Type> 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 <typename Iterator, + hb_requires (hb_is_source_of (Iterator, Type))> + 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 <typename ...Ts> + 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); + hb_barrier (); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (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) && + hb_barrier () && + (!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 <typename Type> using HeadlessArray16Of = HeadlessArrayOf<Type, HBUINT16>; + +/* An array storing length-1. */ +template <typename Type, typename LenType=HBUINT16> +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 <typename ...Ts> + 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); + hb_barrier (); + unsigned int count = lenM1 + 1; + for (unsigned int i = 0; i < count; i++) + if (unlikely (!c->dispatch (arrayZ[i], std::forward<Ts> (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) && + hb_barrier () && + (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 <typename Type, typename LenType> +struct SortedArrayOf : ArrayOf<Type, LenType> +{ + hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->len); } + hb_sorted_array_t<const Type> as_array () const { return hb_sorted_array (this->arrayZ, this->len); } + + /* Iterator. */ + typedef hb_sorted_array_t<const Type> 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<Type, LenType>::serialize (c, items_len); + return_trace (ret); + } + template <typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, Type))> + bool serialize (hb_serialize_context_t *c, Iterator items) + { + TRACE_SERIALIZE (this); + bool ret = ArrayOf<Type, LenType>::serialize (c, items); + return_trace (ret); + } + + template <typename T> + Type &bsearch (const T &x, Type ¬_found = Crap (Type)) + { return *as_array ().bsearch (x, ¬_found); } + template <typename T> + const Type &bsearch (const T &x, const Type ¬_found = Null (Type)) const + { return *as_array ().bsearch (x, ¬_found); } + template <typename T> + 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 <typename Type> using SortedArray16Of = SortedArrayOf<Type, HBUINT16>; +template <typename Type> using SortedArray24Of = SortedArrayOf<Type, HBUINT24>; +template <typename Type> using SortedArray32Of = SortedArrayOf<Type, HBUINT32>; + +/* + * Binary-search arrays + */ + +template <typename LenType=HBUINT16> +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 <typename Type, typename LenType=HBUINT16> +using BinSearchArrayOf = SortedArrayOf<Type, BinSearchHeader<LenType>>; + + +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 <typename Type> +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<HBUINT16> (&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<Type> (&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<Type> (&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 <typename ...Ts> + 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); + hb_barrier (); + unsigned int count = get_length (); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!(*this)[i].sanitize (c, std::forward<Ts> (ds)...))) + return_trace (false); + return_trace (true); + } + + template <typename T> + 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<T, Type>) + ? (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) && + hb_barrier () && + Type::static_size <= header.unitSize && + c->check_range (bytesZ.arrayZ, + header.nUnits, + header.unitSize)); + } + + protected: + VarSizedBinSearchHeader header; + UnsizedArrayOf<HBUINT8> bytesZ; + public: + DEFINE_SIZE_ARRAY (10, bytesZ); +}; + + +} /* namespace OT */ + + +#endif /* HB_OPEN_TYPE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-cff-common.hh b/gfx/harfbuzz/src/hb-ot-cff-common.hh new file mode 100644 index 0000000000..4fdba197ac --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-cff-common.hh @@ -0,0 +1,604 @@ +/* + * 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<typename Type> +static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) +{ return offset ? StructAtOffset<Type> (P, offset) : Null (Type); } + +struct code_pair_t +{ + unsigned code; + hb_codepoint_t glyph; +}; + + +using str_buff_t = hb_vector_t<unsigned char>; +using str_buff_vec_t = hb_vector_t<str_buff_t>; +using glyph_to_sid_map_t = hb_vector_t<code_pair_t>; + +struct length_f_t +{ + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + unsigned operator () (const Iterable &_) const { return hb_len (hb_iter (_)); } + + unsigned operator () (unsigned _) const { return _; } +} +HB_FUNCOBJ (length_f); + +/* CFF INDEX */ +template <typename COUNT> +struct CFFIndex +{ + unsigned int offset_array_size () const + { return offSize * (count + 1); } + + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + bool serialize (hb_serialize_context_t *c, + const Iterable &iterable, + const unsigned *p_data_size = nullptr, + unsigned min_off_size = 0) + { + 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, min_off_size))) return_trace (false); + unsigned char *ret = c->allocate_size<unsigned char> (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 <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool serialize_header (hb_serialize_context_t *c, + Iterator it, + unsigned data_size, + unsigned min_off_size = 0) + { + TRACE_SERIALIZE (this); + + unsigned off_size = (hb_bit_storage (data_size + 1) + 7) / 8; + off_size = hb_max(min_off_size, off_size); + + /* 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<HBUINT8> (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 <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + static unsigned total_size (const Iterable &iterable, unsigned *data_size = nullptr, unsigned min_off_size = 0) + { + 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; + off_size = hb_max(min_off_size, off_size); + + 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) && + hb_barrier () && + (count == 0 || /* empty INDEX */ + (count < count + 1u && + hb_barrier () && + 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 <typename DICTVAL, typename OP_SERIALIZER, typename ...Ts> + 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<Ts> (ds)...))) + return_trace (false); + + return_trace (true); + } + + template <typename T, typename V> + static bool serialize_int_op (hb_serialize_context_t *c, op_code_t op, V value, op_code_t intOp) + { + if (unlikely ((!serialize_int<T, V> (c, intOp, value)))) + return false; + + TRACE_SERIALIZE (this); + /* serialize the opcode */ + HBUINT8 *p = c->allocate_size<HBUINT8> (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 <typename V> + static bool serialize_int4_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op<HBINT32> (c, op, value, OpCode_longintdict); } + + template <typename V> + static bool serialize_int2_op (hb_serialize_context_t *c, op_code_t op, V value) + { return serialize_int_op<HBINT16> (c, op, value, OpCode_shortint); } + + template <typename T, int int_op> + 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<T> (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<HBINT32, OpCode_longintdict> (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<HBINT16, OpCode_shortint> (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 <typename COUNT> +struct FDArray : CFFIndex<COUNT> +{ + template <typename DICTVAL, typename INFO, typename Iterator, typename OP_SERIALIZER> + bool serialize (hb_serialize_context_t *c, + Iterator it, + OP_SERIALIZER& opszr) + { + TRACE_SERIALIZE (this); + + /* serialize INDEX data */ + hb_vector_t<unsigned> 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<const DICTVAL&, const INFO&> &_) + { + FontDict *dict = c->start_embed<FontDict> (); + 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<COUNT>::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); + hb_barrier (); + 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<unsigned, hb_codepoint_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 <typename GID_TYPE, typename FD_TYPE> +struct FDSelect3_4_Range +{ + bool sanitize (hb_sanitize_context_t *c, const void * /*nullptr*/, unsigned int fdcount) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + 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 <typename GID_TYPE, typename FD_TYPE> +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) && + hb_barrier () && + (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) && + hb_barrier () && + (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<GID_TYPE, FD_TYPE> *range = (FDSelect3_4_Range<GID_TYPE, FD_TYPE> *) _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<unsigned, hb_codepoint_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<GID_TYPE> (ranges[nRanges () - 1]); } + const GID_TYPE &sentinel () const { return StructAfter<GID_TYPE> (ranges[nRanges () - 1]); } + + ArrayOf<FDSelect3_4_Range<GID_TYPE, FD_TYPE>, GID_TYPE> ranges; + /* GID_TYPE sentinel */ + + DEFINE_SIZE_ARRAY (GID_TYPE::static_size, ranges); +}; + +typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3; +typedef FDSelect3_4_Range<HBUINT16, HBUINT8> 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<FDSelect> (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<unsigned, hb_codepoint_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); + hb_barrier (); + + 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 <typename COUNT> +struct Subrs : CFFIndex<COUNT> +{ + typedef COUNT count_type; + typedef CFFIndex<COUNT> SUPER; +}; + +} /* namespace CFF */ + +#endif /* HB_OT_CFF_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-cff1-std-str.hh b/gfx/harfbuzz/src/hb-ot-cff1-std-str.hh new file mode 100644 index 0000000000..65d56ae18b --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-ot-cff1-table.cc b/gfx/harfbuzz/src/hb-ot-cff1-table.cc new file mode 100644 index 0000000000..66df28aae1 --- /dev/null +++ b/gfx/harfbuzz/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<cff1_path_procs_extents_t, cff1_cs_interp_env_t, cff1_extents_param_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<cff1_cs_opset_extents_t, cff1_extents_param_t, cff1_path_procs_extents_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<cff1_cs_opset_extents_t, cff1_extents_param_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<cff1_path_procs_path_t, cff1_cs_interp_env_t, cff1_path_param_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<cff1_cs_opset_path_t, cff1_path_param_t, cff1_path_procs_path_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<cff1_cs_opset_path_t, cff1_path_param_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<cff1_cs_opset_seac_t, get_seac_param_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<cff1_cs_opset_seac_t, get_seac_param_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/gfx/harfbuzz/src/hb-ot-cff1-table.hh b/gfx/harfbuzz/src/hb-ot-cff1-table.hh new file mode 100644 index 0000000000..c869e90554 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-cff1-table.hh @@ -0,0 +1,1561 @@ +/* + * 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<HBUINT16> CFF1Index; + +typedef CFFIndex<HBUINT16> CFF1Index; +typedef CFF1Index CFF1CharStrings; +typedef Subrs<HBUINT16> 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<HBUINT8, HBUINT8> 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<Encoding1_Range, HBUINT8> 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<hb_codepoint_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<SuppEncoding, HBUINT8> 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_pair_t>& code_ranges, + const hb_vector_t<code_pair_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> (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> (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> (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<hb_codepoint_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); + hb_barrier (); + + 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<CFF1SuppEncData> (u.format0.codes[u.format0.nCodes ()-1]); + case 1: return StructAfter<CFF1SuppEncData> (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<HBUINT16>::get_size (num_glyphs - 1); + } + + UnsizedArrayOf<HBUINT16> sids; + + DEFINE_SIZE_ARRAY(0, sids); +}; + +template <typename TYPE> +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 <typename TYPE> +struct Charset1_2 { + bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs, unsigned *num_charset_entries) const + { + TRACE_SANITIZE (this); + num_glyphs--; + unsigned i; + for (i = 0; num_glyphs > 0; i++) + { + if (unlikely (!(ranges[i].sanitize (c) && + hb_barrier () && + (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<Charset_Range<TYPE> >::get_size (num_ranges); + } + + UnsizedArrayOf<Charset_Range<TYPE>> ranges; + + DEFINE_SIZE_ARRAY (0, ranges); +}; + +typedef Charset1_2<HBUINT8> Charset1; +typedef Charset1_2<HBUINT16> Charset2; +typedef Charset_Range<HBUINT8> Charset1_Range; +typedef Charset_Range<HBUINT16> 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<code_pair_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> (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> (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> (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); + hb_barrier (); + + 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<unsigned> &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<hb_ubytes_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<cff1_top_dict_val_t> +{ + void init () + { + top_dict_values_t<cff1_top_dict_val_t>::init (); + + nameSIDs.init (); + ros_supplement = 0; + cidCount = 8720; + EncodingOffset = 0; + CharsetOffset = 0; + FDSelectOffset = 0; + privateDictInfo.init (); + } + void fini () { top_dict_values_t<cff1_top_dict_val_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<cff1_top_dict_val_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<cff1_top_dict_val_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<op_str_t> +{ + void init () + { + dict_values_t<op_str_t>::init (); + privateDictInfo.init (); + fontName = CFF_UNDEF_SID; + } + void fini () { dict_values_t<op_str_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 <typename VAL> +struct cff1_private_dict_values_base_t : dict_values_t<VAL> +{ + void init () + { + dict_values_t<VAL>::init (); + subrsOffset = 0; + localSubrs = &Null (CFF1Subrs); + } + void fini () { dict_values_t<VAL>::fini (); } + + unsigned int subrsOffset; + const CFF1Subrs *localSubrs; +}; + +typedef cff1_private_dict_values_base_t<op_str_t> cff1_private_dict_values_subset_t; +typedef cff1_private_dict_values_base_t<num_dict_val_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_opset_t, cff1_top_dict_values_t, cff1_top_dict_interp_env_t> cff1_top_dict_interpreter_t; +typedef dict_interpreter_t<cff1_font_dict_opset_t, cff1_font_dict_values_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<HBUINT16> +{ + /* FDArray::serialize() requires this partial specialization to compile */ + template <typename ITER, typename OP_SERIALIZER> + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray<HBUINT16>::serialize<cff1_font_dict_values_mod_t, cff1_font_dict_values_mod_t> (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) && + hb_barrier () && + likely (version.major == 1)); + } + + template <typename PRIVOPSET, typename PRIVDICTVAL> + 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<cff1> (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff1 *cff = this->blob->template as<OT::cff1> (); + + if (cff == &Null (OT::cff1)) + goto fail; + + nameIndex = &cff->nameIndex (cff); + if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) + goto fail; + hb_barrier (); + + topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ()); + if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + goto fail; + hb_barrier (); + + { /* parse top dict */ + const hb_ubytes_t topDictStr = (*topDictIndex)[0]; + if (unlikely (!topDictStr.sanitize (&sc))) goto fail; + hb_barrier (); + 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<Charset> (cff, topDict.CharsetOffset); + if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail; + hb_barrier (); + } + + fdCount = 1; + if (is_CID ()) + { + fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset); + if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + goto fail; + hb_barrier (); + + 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<Encoding> (cff, topDict.EncodingOffset); + if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail; + hb_barrier (); + } + } + + stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ()); + if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) + goto fail; + hb_barrier (); + + globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ()); + if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) + goto fail; + hb_barrier (); + + charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset); + + if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc))) + goto fail; + hb_barrier (); + + 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; + hb_barrier (); + 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<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + hb_barrier (); + num_interp_env_t env2 (privDictStr); + dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) goto fail; + + priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + goto fail; + hb_barrier (); + } + } + else /* non-CID */ + { + cff1_top_dict_values_t *font = &topDict; + PRIVDICTVAL *priv = &privateDicts[0]; + + const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + hb_barrier (); + num_interp_env_t env (privDictStr); + dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env); + priv->init (); + if (unlikely (!priv_interp.interpret (*priv))) goto fail; + + priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset); + if (priv->localSubrs != &Null (CFF1Subrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + goto fail; + hb_barrier (); + } + + 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<cff1_font_dict_values_t> + fontDicts; + hb_vector_t<PRIVDICTVAL> privateDicts; + + unsigned int num_glyphs = 0; + unsigned int num_charset_entries = 0; + }; + + struct accelerator_t : accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_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<gname_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<gname_t> *names = glyph_names.get_acquire (); + if (unlikely (!names)) + { + names = (hb_sorted_vector_t<gname_t> *) hb_calloc (sizeof (hb_sorted_vector_t<gname_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<hb_sorted_vector_t<gname_t>> glyph_names; + + typedef accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t> SUPER; + }; + + struct accelerator_subset_t : accelerator_templ_t<cff1_private_dict_opset_subset_t, cff1_private_dict_values_subset_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<cff1_private_dict_opset_subset_t, cff1_private_dict_values_subset_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<HBUINT8> version; /* Version of CFF table. set to 0x0100u */ + NNOffsetTo<CFF1NameIndex, HBUINT8> 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/gfx/harfbuzz/src/hb-ot-cff2-table.cc b/gfx/harfbuzz/src/hb-ot-cff2-table.cc new file mode 100644 index 0000000000..7955565551 --- /dev/null +++ b/gfx/harfbuzz/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_path_procs_extents_t, cff2_cs_interp_env_t<number_t>, cff2_extents_param_t> +{ + static void moveto (cff2_cs_interp_env_t<number_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<number_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<number_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<cff2_cs_opset_extents_t, cff2_extents_param_t, number_t, cff2_path_procs_extents_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<number_t> env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t, number_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_procs_path_t, cff2_cs_interp_env_t<number_t>, cff2_path_param_t> +{ + static void moveto (cff2_cs_interp_env_t<number_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<number_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<number_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<cff2_cs_opset_path_t, cff2_path_param_t, number_t, cff2_path_procs_path_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<number_t> env (str, *this, fd, font->coords, font->num_coords); + cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t, number_t> interp (env); + cff2_path_param_t param (font, draw_session); + if (unlikely (!interp.interpret (param))) return false; + return true; +} + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-cff2-table.hh b/gfx/harfbuzz/src/hb-ot-cff2-table.hh new file mode 100644 index 0000000000..652748b737 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-cff2-table.hh @@ -0,0 +1,573 @@ +/* + * 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<HBUINT32> CFF2Index; + +typedef CFF2Index CFF2CharStrings; +typedef Subrs<HBUINT32> CFF2Subrs; + +typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4; +typedef FDSelect3_4_Range<HBUINT32, HBUINT16> 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<CFF2FDSelect> (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); + hb_barrier (); + + 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 (c->check_struct (this) && + hb_barrier () && + 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<CFF2VariationStore> (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<op_str_t> +{ + void init () + { + dict_values_t<op_str_t>::init (); + privateDictInfo.init (); + } + void fini () { dict_values_t<op_str_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 <typename VAL> +struct cff2_private_dict_values_base_t : dict_values_t<VAL> +{ + void init () + { + dict_values_t<VAL>::init (); + subrsOffset = 0; + localSubrs = &Null (CFF2Subrs); + ivs = 0; + } + void fini () { dict_values_t<VAL>::fini (); } + + unsigned int subrsOffset; + const CFF2Subrs *localSubrs; + unsigned int ivs; +}; + +typedef cff2_private_dict_values_base_t<op_str_t> cff2_private_dict_values_subset_t; +typedef cff2_private_dict_values_base_t<num_dict_val_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_opset_t, cff2_top_dict_values_t> cff2_top_dict_interpreter_t; +typedef dict_interpreter_t<cff2_font_dict_opset_t, cff2_font_dict_values_t> cff2_font_dict_interpreter_t; + +struct CFF2FDArray : FDArray<HBUINT32> +{ + /* FDArray::serialize does not compile without this partial specialization */ + template <typename ITER, typename OP_SERIALIZER> + bool serialize (hb_serialize_context_t *c, ITER it, OP_SERIALIZER& opszr) + { return FDArray<HBUINT32>::serialize<cff2_font_dict_values_t, table_info_t> (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) && + hb_barrier () && + likely (version.major == 2)); + } + + template <typename PRIVOPSET, typename PRIVDICTVAL> + 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<cff2> (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff2 *cff2 = this->blob->template as<OT::cff2> (); + + 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; + hb_barrier (); + 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<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize); + varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset); + charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset); + fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (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)) || + !hb_barrier () || + (((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; + hb_barrier (); + 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<UnsizedByteStr> (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); + if (unlikely (!privDictStr.sanitize (&sc))) goto fail; + hb_barrier (); + cff2_priv_dict_interp_env_t env2 (privDictStr); + dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp (env2); + privateDicts[i].init (); + if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; + + privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset); + if (privateDicts[i].localSubrs != &Null (CFF2Subrs) && + unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) + goto fail; + hb_barrier (); + } + + 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<uint16_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<cff2_font_dict_values_t> fontDicts; + hb_vector_t<PRIVDICTVAL> privateDicts; + + unsigned int num_glyphs = 0; + }; + + struct accelerator_t : accelerator_templ_t<cff2_private_dict_opset_t, cff2_private_dict_values_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<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_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<int> normalized_coords) const; + + mutable CFF::cff_subset_accelerator_t* cff_accelerator = nullptr; + + typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> SUPER; + }; + + public: + FixedVersion<HBUINT8> version; /* Version of CFF2 table. set to 0x0200u */ + NNOffsetTo<TopDict, HBUINT8> 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/gfx/harfbuzz/src/hb-ot-cmap-table.hh b/gfx/harfbuzz/src/hb-ot-cmap-table.hh new file mode 100644 index 0000000000..e2e2581855 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-cmap-table.hh @@ -0,0 +1,2099 @@ +/* + * Copyright © 2014 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_OT_CMAP_TABLE_HH +#define HB_OT_CMAP_TABLE_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 { + + +struct CmapSubtableFormat0 +{ + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; + if (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + 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: + 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); +}; + +struct CmapSubtableFormat4 +{ + + + template<typename Iterator, + typename Writer, + hb_requires (hb_is_iterator (Iterator))> + 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<typename Writer> + 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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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> (HBUINT16::static_size * segcount, false); + (void) c->allocate_size<HBUINT16> (2); // padding + writer.start_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount, false); + writer.id_delta_ = c->allocate_size<HBINT16> (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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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> (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<HBUINT16> () - idRangeOffset - i); + for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++) + { + HBUINT16 gid; + gid = cp_to_gid[cp]; + c->copy<HBUINT16> (gid); + } + } + + return idRangeOffset; + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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<hb_codepoint_pair_t> cp_to_gid { + format4_iter + }; + + //serialize endCode[], startCode[], idDelta[] + HBUINT16* endCode = c->start_embed<HBUINT16> (); + 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 + { + accelerator_t () {} + accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); } + + void init (const CmapSubtableFormat4 *subtable) + { + segCount = subtable->segCountX2 / 2; + endCount = subtable->values.arrayZ; + startCount = endCount + segCount + 1; + idDelta = startCount + segCount; + idRangeOffset = idDelta + segCount; + glyphIdArray = idRangeOffset + segCount; + glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; + } + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + struct CustomRange + { + int cmp (hb_codepoint_t k, + unsigned distance) const + { + if (k > last) return +1; + if (k < (&last)[distance]/*first*/) return -1; + return 0; + } + HBUINT16 last; + }; + + const HBUINT16 *found = hb_bsearch (codepoint, + this->endCount, + this->segCount, + sizeof (CustomRange), + _hb_cmp_method<hb_codepoint_t, CustomRange, unsigned>, + this->segCount + 1); + if (unlikely (!found)) + return false; + unsigned int i = found - endCount; + + hb_codepoint_t gid; + unsigned int rangeOffset = this->idRangeOffset[i]; + if (rangeOffset == 0) + gid = codepoint + this->idDelta[i]; + else + { + /* Somebody has been smoking... */ + unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; + if (unlikely (index >= this->glyphIdArrayLength)) + return false; + gid = this->glyphIdArray[index]; + if (unlikely (!gid)) + return false; + gid += this->idDelta[i]; + } + gid &= 0xFFFFu; + if (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + 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; + }; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + 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); + } + + 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))) + return_trace (false); + hb_barrier (); + + if (unlikely (!c->check_range (this, length))) + { + /* 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) hb_min ((uintptr_t) 65535, + (uintptr_t) (c->end - + (char *) this)); + if (!c->try_set (&length, new_length)) + return_trace (false); + } + + return_trace (16 + 4 * (unsigned int) segCountX2 <= length); + } + + + + 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<HBUINT16> + values; +#if 0 + HBUINT16 endCount[segCount]; /* End characterCode for each segment, + * last=0xFFFFu. */ + 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<HBUINT16> + glyphIdArray; /* Glyph index array (arbitrary length) */ +#endif + + public: + DEFINE_SIZE_ARRAY (14, values); +}; + +struct CmapSubtableLongGroup +{ + friend struct CmapSubtableFormat12; + friend struct CmapSubtableFormat13; + template<typename U> + friend struct CmapSubtableLongSegmented; + friend struct cmap; + + int cmp (hb_codepoint_t codepoint) const + { + if (codepoint < startCharCode) return -1; + if (codepoint > endCharCode) return +1; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + private: + 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 <typename UINT> +struct CmapSubtableTrimmed +{ + 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 (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + 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)); + } + + protected: + UINT formatReserved; /* Subtable format and (maybe) padding. */ + UINT length; /* Byte length of this subtable. */ + UINT language; /* Ignore. */ + UINT startCharCode; /* First character code covered. */ + ArrayOf<HBGlyphID16, UINT> + glyphIdArray; /* Array of glyph index values for character + * codes in the range. */ + public: + DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); +}; + +struct CmapSubtableFormat6 : CmapSubtableTrimmed<HBUINT16> {}; +struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32> {}; + +template <typename T> +struct CmapSubtableLongSegmented +{ + friend struct cmap; + + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { + hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint); + if (unlikely (!gid)) + return false; + *glyph = gid; + return true; + } + + 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; + unsigned count = this->groups.len; + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t start = this->groups.arrayZ[i].startCharCode; + hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups.arrayZ[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.arrayZ[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); + + unicodes->add_range (start, end); + for (unsigned cp = start; cp <= end; 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: + HBUINT16 format; /* Subtable format; set to 12. */ + HBUINT16 reserved; /* Reserved; set to 0. */ + HBUINT32 length; /* Byte length of this subtable. */ + HBUINT32 language; /* Ignore. */ + SortedArray32Of<CmapSubtableLongGroup> + groups; /* Groupings. */ + public: + DEFINE_SIZE_ARRAY (16, groups); +}; + +struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12> +{ + 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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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<CmapSubtableLongGroup> (grouprecord); + + startCharCode = _.first; + endCharCode = _.first; + glyphID = _.second; + } + else + endCharCode = _.first; + } + + CmapSubtableLongGroup record; + record.startCharCode = startCharCode; + record.endCharCode = endCharCode; + record.glyphID = glyphID; + c->copy<CmapSubtableLongGroup> (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<CmapSubtableLongGroup> &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<CmapSubtableFormat13> +{ + 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; } +}; + +typedef enum +{ + GLYPH_VARIANT_NOT_FOUND = 0, + GLYPH_VARIANT_FOUND = 1, + GLYPH_VARIANT_USE_DEFAULT = 2 +} glyph_variant_t; + +struct UnicodeValueRange +{ + int cmp (const hb_codepoint_t &codepoint) const + { + if (codepoint < startUnicodeValue) return -1; + if (codepoint > startUnicodeValue + additionalCount) return +1; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT24 startUnicodeValue; /* First value in this range. */ + HBUINT8 additionalCount; /* Number of additional values in this + * range. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +struct DefaultUVS : SortedArray32Of<UnicodeValueRange> +{ + 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<DefaultUVS> (); + auto snap = c->snapshot (); + + HBUINT32 len; + len = 0; + if (unlikely (!c->copy<HBUINT32> (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<UnicodeValueRange> (rec); + start = u; + } + end = u; + } + if (start != HB_SET_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = start; + rec.additionalCount = end - start; + c->copy<UnicodeValueRange> (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<UnicodeValueRange> (rec); + + lastCode = curEntry; + count = 0; + } + } + } + + if (lastCode != HB_MAP_VALUE_INVALID) + { + UnicodeValueRange rec; + rec.startUnicodeValue = lastCode; + rec.additionalCount = count; + c->copy<UnicodeValueRange> (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 +{ + int cmp (const hb_codepoint_t &codepoint) const + { return unicodeValue.cmp (codepoint); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ + HBGlyphID16 glyphID; /* Glyph ID of the UVS */ + public: + DEFINE_SIZE_STATIC (5); +}; + +struct NonDefaultUVS : SortedArray32Of<UVSMapping> +{ + 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<NonDefaultUVS> (); + 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<HBUINT32> (len))) return nullptr; + + for (const UVSMapping& _ : it) + { + UVSMapping mapping; + mapping.unicodeValue = _.unicodeValue; + mapping.glyphID = glyph_map->get (_.glyphID); + c->copy<UVSMapping> (mapping); + } + + return out; + } + + public: + DEFINE_SIZE_ARRAY (4, *this); +}; + +struct VariationSelectorRecord +{ + glyph_variant_t get_glyph (hb_codepoint_t codepoint, + hb_codepoint_t *glyph, + const void *base) const + { + if ((base+defaultUVS).bfind (codepoint)) + return GLYPH_VARIANT_USE_DEFAULT; + const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint); + if (nonDefault.glyphID) + { + *glyph = nonDefault.glyphID; + return GLYPH_VARIANT_FOUND; + } + return GLYPH_VARIANT_NOT_FOUND; + } + + VariationSelectorRecord(const VariationSelectorRecord& other) + { + *this = other; + } + + 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) && + defaultUVS.sanitize (c, base) && + nonDefaultUVS.sanitize (c, base)); + } + + hb_pair_t<unsigned, unsigned> + 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<VariationSelectorRecord> (*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> + defaultUVS; /* Offset to Default UVS Table. May be 0. */ + Offset32To<NonDefaultUVS> + nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ + public: + DEFINE_SIZE_STATIC (11); +}; + +struct CmapSubtableFormat14 +{ + 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 + { + 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<const CmapSubtableFormat14*> (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<hb_pair_t<unsigned, unsigned>> obj_indices; + for (int i = src_tbl->record.len - 1; i >= 0; i--) + { + hb_pair_t<unsigned, unsigned> 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); + } + + void _reverse_variation_records () + { + record.as_array ().reverse (); + } + + void _add_links_to_variation_records (hb_serialize_context_t *c, + const hb_vector_t<hb_pair_t<unsigned, unsigned>>& 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) && + record.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format number is set to 14. */ + HBUINT32 length; /* Byte length of this subtable. */ + SortedArray32Of<VariationSelectorRecord> + record; /* Variation selector records; sorted + * in increasing order of `varSelector'. */ + public: + DEFINE_SIZE_ARRAY (10, record); +}; + +struct CmapSubtable +{ + /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ + + 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 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; + } + } + + 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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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); + hb_barrier (); + switch (u.format) { + case 0: return_trace (u.format0 .sanitize (c)); + case 4: return_trace (u.format4 .sanitize (c)); + case 6: return_trace (u.format6 .sanitize (c)); + case 10: return_trace (u.format10.sanitize (c)); + case 12: return_trace (u.format12.sanitize (c)); + case 13: return_trace (u.format13.sanitize (c)); + case 14: return_trace (u.format14.sanitize (c)); + default:return_trace (true); + } + } + + public: + union { + HBUINT16 format; /* Format identifier */ + CmapSubtableFormat0 format0; + CmapSubtableFormat4 format4; + CmapSubtableFormat6 format6; + CmapSubtableFormat10 format10; + CmapSubtableFormat12 format12; + CmapSubtableFormat13 format13; + CmapSubtableFormat14 format14; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + + +struct EncodingRecord +{ + int cmp (const EncodingRecord &other) const + { + int ret; + ret = platformID.cmp (other.platformID); + if (ret) return ret; + ret = encodingID.cmp (other.encodingID); + if (ret) return ret; + return 0; + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + subtable.sanitize (c, base)); + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + 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<CmapSubtable> (); + 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<CmapSubtable> + 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<cmap> base_blob; + const char* base; + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> cached_unicodes; + + public: + + static SubtableUnicodesCache* create (hb_blob_ptr_t<cmap> 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<cmap> 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<hb_set_t> {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 constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; + + + static SubtableUnicodesCache* create_filled_cache(hb_blob_ptr_t<cmap> 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<typename Iterator, typename EncodingRecIter, + hb_requires (hb_is_iterator (EncodingRecIter))> + 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<typename Iterator, typename EncodingRecordIterator, + hb_requires (hb_is_iterator (Iterator)), + hb_requires (hb_is_iterator (EncodingRecordIterator))> + 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<cmap> (); + + 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<cmap> (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<CmapSubtable, _hb_symbol_pua_map>; + 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<CmapSubtable, _hb_arabic_pua_simp_map>; + break; + case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: + this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_trad_map>; + break; +#endif + default: + this->get_glyph_funcZ = get_glyph_from<CmapSubtable>; + break; + } + } + else + { + switch (subtable->u.format) { + /* Accelerate format 4 and format 12. */ + default: + this->get_glyph_funcZ = get_glyph_from<CmapSubtable>; + break; + case 12: + this->get_glyph_funcZ = get_glyph_from<CmapSubtableFormat12>; + 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<hb_codepoint_t> (first_unicode, unicode_stride); + first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (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 <typename Type> + 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 <typename Type, hb_pua_remap_func_t remap> + 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<const CmapSubtable> subtable; + hb_nonnull_ptr_t<const CmapSubtableFormat14> 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<cmap> 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) && + hb_barrier () && + likely (version == 0) && + encodingRecord.sanitize (c, this)); + } + + private: + + static bool filter_encoding_records_for_subset(const cmap* cmap, + const EncodingRecord& _) + { + return + (_.platformID == 0 && _.encodingID == 3) || + (_.platformID == 0 && _.encodingID == 4) || + (_.platformID == 3 && _.encodingID == 1) || + (_.platformID == 3 && _.encodingID == 10) || + (cmap + _.subtable).u.format == 14; + } + + protected: + HBUINT16 version; /* Table version number (0). */ + SortedArray16Of<EncodingRecord> + 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 */ + + +#endif /* HB_OT_CMAP_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-color.cc b/gfx/harfbuzz/src/hb-ot-color.cc new file mode 100644 index 0000000000..37d42e08d9 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-ot-color.h b/gfx/harfbuzz/src/hb-ot-color.h new file mode 100644 index 0000000000..22ee497e38 --- /dev/null +++ b/gfx/harfbuzz/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 <hb-ot.h> 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/gfx/harfbuzz/src/hb-ot-deprecated.h b/gfx/harfbuzz/src/hb-ot-deprecated.h new file mode 100644 index 0000000000..60672ab128 --- /dev/null +++ b/gfx/harfbuzz/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 <hb-ot.h> 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. + * + * <note>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.</note> + * + * 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/gfx/harfbuzz/src/hb-ot-face-table-list.hh b/gfx/harfbuzz/src/hb-ot-face-table-list.hh new file mode 100644 index 0000000000..b552dfdd9d --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-ot-face.cc b/gfx/harfbuzz/src/hb-ot-face.cc new file mode 100644 index 0000000000..2243ee0287 --- /dev/null +++ b/gfx/harfbuzz/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/gfx/harfbuzz/src/hb-ot-face.hh b/gfx/harfbuzz/src/hb-ot-face.hh new file mode 100644 index 0000000000..415dae8e20 --- /dev/null +++ b/gfx/harfbuzz/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<Namespace::Type, HB_OT_TABLE_ORDER (Namespace, Type)> Type; +#define HB_OT_CORE_TABLE(Namespace, Type) \ + hb_table_lazy_loader_t<Namespace::Type, HB_OT_TABLE_ORDER (Namespace, Type), true> Type; +#define HB_OT_ACCELERATOR(Namespace, Type) \ + hb_face_lazy_loader_t<Namespace::Type##_accelerator_t, HB_OT_TABLE_ORDER (Namespace, Type)> 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/gfx/harfbuzz/src/hb-ot-font.cc b/gfx/harfbuzz/src/hb-ot-font.cc new file mode 100644 index 0000000000..b3677c6a4c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-font.cc @@ -0,0 +1,654 @@ +/* + * Copyright © 2011,2014 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, Roozbeh Pournader + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_FONT + +#include "hb-ot.h" + +#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-glyf-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-hmtx-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 +{ + 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<hb_ot_font_advance_cache_t> advance_cache; +}; + +static hb_ot_font_t * +_hb_ot_font_create (hb_font_t *font) +{ + 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->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 *font_data) +{ + hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; + + auto *cache = ot_font->advance_cache.get_relaxed (); + hb_free (cache); + + 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; + 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 +hb_ot_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_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_variation_glyph (unicode, + variation_selector, glyph, + cmap_cache); +} + +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; + 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<hb_codepoint_t> (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned<hb_position_t> (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<hb_codepoint_t> (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned<hb_position_t> (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<hb_position_t> (first_advance, advance_stride); + } + } +} + +#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; + const hb_ot_face_t *ot_face = ot_font->ot_face; + const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; + + 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<hb_codepoint_t> (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned<hb_position_t> (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<hb_codepoint_t> (first_glyph, glyph_stride); + first_advance = &StructAtOffsetUnaligned<hb_position_t> (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<hb_position_t> (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; + 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, + 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; + 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_font_v_extents (hb_font_t *font, + void *font_data HB_UNUSED, + hb_font_extents_t *metrics, + void *user_data HB_UNUSED) +{ + 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 + +#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) +{ + bool embolden = font->x_strength || font->y_strength; + hb_outline_t outline; + + { // 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) + { + 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<hb_ot_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_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); + +#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); + + hb_atexit (free_static_ot_funcs); + + return funcs; + } +} static_ot_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); + if (unlikely (!ot_font)) + return; + + hb_font_set_funcs (font, + _hb_ot_get_font_funcs (), + ot_font, + _hb_ot_font_destroy); +} + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-font.h b/gfx/harfbuzz/src/hb-ot-font.h new file mode 100644 index 0000000000..e7959d1ae2 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-font.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2014 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, Roozbeh Pournader + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_FONT_H +#define HB_OT_FONT_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +HB_EXTERN void +hb_ot_font_set_funcs (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_OT_FONT_H */ diff --git a/gfx/harfbuzz/src/hb-ot-gasp-table.hh b/gfx/harfbuzz/src/hb-ot-gasp-table.hh new file mode 100644 index 0000000000..f2a9cad464 --- /dev/null +++ b/gfx/harfbuzz/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<GaspRange> + 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/gfx/harfbuzz/src/hb-ot-glyf-table.hh b/gfx/harfbuzz/src/hb-ot-glyf-table.hh new file mode 100644 index 0000000000..c32ff7636d --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-glyf-table.hh @@ -0,0 +1,35 @@ +/* + * Copyright © 2015 Google, Inc. + * Copyright © 2019 Adobe Inc. + * 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. + * + * 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 "OT/glyf/glyf.hh" + +#endif /* HB_OT_GLYF_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-hdmx-table.hh b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh new file mode 100644 index 0000000000..8582dbe27d --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-hdmx-table.hh @@ -0,0 +1,177 @@ +/* + * 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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool serialize (hb_serialize_context_t *c, + unsigned pixelSize, + Iterator it, + const hb_vector_t<hb_codepoint_pair_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) && + hb_barrier () && + c->check_range (this, sizeDeviceRecord))); + } + + HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */ + HBUINT8 maxWidth; /* Maximum width. */ + UnsizedArrayOf<HBUINT8> 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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool serialize (hb_serialize_context_t *c, + unsigned version, + Iterator it, + const hb_vector_t<hb_codepoint_pair_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<Iterator>& _ : +it) + c->start_embed<DeviceRecord> ()->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 <hdmx> (); + + 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<DeviceRecord> (&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_barrier () && + !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/gfx/harfbuzz/src/hb-ot-head-table.hh b/gfx/harfbuzz/src/hb-ot-head-table.hh new file mode 100644 index 0000000000..4cb6c15c67 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-head-table.hh @@ -0,0 +1,204 @@ +/* + * Copyright © 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_OT_HEAD_TABLE_HH +#define HB_OT_HEAD_TABLE_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 { + + +struct head +{ + friend struct OpenTypeOffsetTable; + + 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; + } + + 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) && + hb_barrier () && + version.major == 1 && + magicNumber == 0x5F0F3CF5u); + } + + protected: + FixedVersion<>version; /* Version of the head table--currently + * 0x00010000u for version 1.0. */ + FixedVersion<>fontRevision; /* Set by font manufacturer. */ + HBUINT32 checkSumAdjustment; /* To compute: set it to 0, sum the + * entire font as HBUINT32, then store + * 0xB1B0AFBAu - sum. */ + 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 + * internal scaler math; may use fractional + * 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. + * Bit 5: This bit should be set in fonts that are + * intended to e laid out vertically, and in + * which the glyphs have been drawn such that an + * x-coordinate of 0 corresponds to the desired + * vertical baseline. + * Bit 6: This bit must be set to zero. + * Bit 7: This bit should be set if the font + * requires layout for correct linguistic + * rendering (e.g. Arabic fonts). + * Bit 8: This bit should be set for a GX font + * which has one or more metamorphosis effects + * designated as happening by default. + * Bit 9: This bit should be set if the font + * 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. + * Bit 12: Font converted (produce compatible metrics) + * Bit 13: Font optimized for ClearType™. + * Note, fonts that rely on embedded bitmaps (EBDT) + * for rendering should not be considered optimized + * for ClearType, and therefore should keep this bit + * cleared. + * Bit 14: Last Resort font. If set, indicates that + * the glyphs encoded in the cmap subtables are simply + * generic symbolic representations of code point + * ranges and don’t truly represent support for those + * code points. If unset, indicates that the glyphs + * encoded in the cmap subtables represent proper + * support for those code points. + * Bit 15: Reserved, set to 0. */ + 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 */ + 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) + * Bit 4: Shadow (if set to 1) + * Bit 5: Condensed (if set to 1) + * Bit 6: Extended (if set to 1) + * Bits 7-15: Reserved (set to 0). */ + 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: + HBUINT16 indexToLocFormat; /* 0 for short offsets, 1 for long. */ + HBUINT16 glyphDataFormat; /* 0 for current format. */ + + DEFINE_SIZE_STATIC (54); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HEAD_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-hhea-table.hh b/gfx/harfbuzz/src/hb-ot-hhea-table.hh new file mode 100644 index 0000000000..27becfda3d --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh @@ -0,0 +1,106 @@ +/* + * 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 HB_OT_HHEA_TABLE_HH +#define HB_OT_HHEA_TABLE_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 { + + +template <typename T> +struct _hea +{ + bool has_data () const { return version.major; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + 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). */ + 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<hhea> { + static constexpr hb_tag_t tableTag = HB_OT_TAG_hhea; +}; +struct vhea : _hea<vhea> { + static constexpr hb_tag_t tableTag = HB_OT_TAG_vhea; +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_HHEA_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh new file mode 100644 index 0000000000..89640b43f1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh @@ -0,0 +1,477 @@ +/* + * 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, Roderick Sheeter + */ + +#ifndef HB_OT_HMTX_TABLE_HH +#define HB_OT_HMTX_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-maxp-table.hh" +#include "hb-ot-hhea-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 { + + +struct LongMetric +{ + UFWORD advance; /* Advance width/height. */ + FWORD sb; /* Leading (left/top) side bearing. */ + public: + DEFINE_SIZE_STATIC (4); +}; + + +template <typename T/*Data table type*/, typename H/*Header table type*/, typename V/*Var table type*/> +struct hmtxvmtx +{ + 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 + * struct do all the hard work... */ + return_trace (true); + } + + const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>>* 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<hb_codepoint_t, hb_pair_t<unsigned, int>> *mtx_map, + const hb_vector_t<unsigned> &bounds_vec) const + { + hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (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<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + Iterator it, + const hb_vector_t<hb_codepoint_pair_t> new_to_old_gid_list, + unsigned num_long_metrics, + unsigned total_num_metrics) + { + LongMetric* long_metrics = c->allocate_size<LongMetric> (num_long_metrics * LongMetric::static_size); + FWORD* short_metrics = c->allocate_size<FWORD> ((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 <T> (); + + accelerator_t _mtx (c->plan->source); + unsigned num_long_metrics; + const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>> *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<unsigned, int> *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 + { + friend struct hmtxvmtx; + + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag); + var_table = hb_sanitize_context_t ().reference_table<V> (face, T::variationsTag); + + default_advance = T::is_horizontal ? hb_face_get_upem (face) / 2 : hb_face_get_upem (face); + + /* Populate count variables and sort them out as we go */ + + unsigned int len = table.get_length (); + if (len & 1) + len--; + + 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; + + num_bearings = face->table.maxp->get_num_glyphs (); + + 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)) + return default_advance; + +#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)]; + } + + unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, + hb_font_t *font, + VariationStore::cache_t *store_cache = nullptr) const + { + 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 + } + + protected: + // 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs + unsigned num_long_metrics; + unsigned num_bearings; + unsigned num_advances; + unsigned num_glyphs; + + unsigned int default_advance; + + public: + hb_blob_ptr_t<hmtxvmtx> table; + hb_blob_ptr_t<V> 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<hb_codepoint_t, hb_pair_t<unsigned, int>> *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: + UnsizedArrayOf<LongMetric> + 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<FWORD> 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. */ +/*UnsizedArrayOf<UFWORD>advancesX;*/ + /* TODO Document. */ + public: + DEFINE_SIZE_ARRAY (0, longMetricZ); +}; + +struct hmtx : hmtxvmtx<hmtx, hhea, HVAR> { + 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<vmtx, vhea, VVAR> { + 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 */ + + +#endif /* HB_OT_HMTX_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-kern-table.hh b/gfx/harfbuzz/src/hb-ot-kern-table.hh new file mode 100644 index 0000000000..39444d803f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-kern-table.hh @@ -0,0 +1,362 @@ +/* + * 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_OT_KERN_TABLE_HH +#define HB_OT_KERN_TABLE_HH + +#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') + + +namespace OT { + + +template <typename KernSubTableHeader> +struct KernSubTableFormat3 +{ + int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const + { + hb_array_t<const FWORD> kernValue = kernValueZ.as_array (kernValueCount); + hb_array_t<const HBUINT8> leftClass = StructAfter<const UnsizedArrayOf<HBUINT8>> (kernValue).as_array (glyphCount); + hb_array_t<const HBUINT8> rightClass = StructAfter<const UnsizedArrayOf<HBUINT8>> (leftClass).as_array (glyphCount); + hb_array_t<const HBUINT8> kernIndex = StructAfter<const UnsizedArrayOf<HBUINT8>> (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]]; + } + + 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<KernSubTableFormat3> 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) && + hb_barrier () && + 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<FWORD> + kernValueZ; /* The kerning values. + * Length kernValueCount. */ +#if 0 + UnsizedArrayOf<HBUINT8> + leftClass; /* The left-hand classes. + * Length glyphCount. */ + UnsizedArrayOf<HBUINT8> + rightClass; /* The right-hand classes. + * Length glyphCount. */ + UnsizedArrayOf<HBUINT8>kernIndex; + /* The indices into the kernValue array. + * Length leftClassCount * rightClassCount */ +#endif + public: + DEFINE_SIZE_ARRAY (KernSubTableHeader::static_size + 6, kernValueZ); +}; + +template <typename KernSubTableHeader> +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, typename ...Ts> + 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<Ts> (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<Ts> (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) && + hb_barrier () && + 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<KernSubTableHeader> format0; + AAT::KerxSubTableFormat1<KernSubTableHeader> format1; + AAT::KerxSubTableFormat2<KernSubTableHeader> format2; + KernSubTableFormat3<KernSubTableHeader> 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)); + } + + 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 KernOT : AAT::KerxTable<KernOT> +{ + friend struct AAT::KerxTable<KernOT>; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0u; + + typedef KernOTSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable<SubTableHeader> SubTable; + + protected: + HBUINT16 version; /* Version--0x0000u */ + HBUINT16 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ + public: + DEFINE_SIZE_MIN (4); +}; + + +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 (c->check_struct (this)); + } + + public: + 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 KernAAT : AAT::KerxTable<KernAAT> +{ + friend struct AAT::KerxTable<KernAAT>; + + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + static constexpr unsigned minVersion = 0x00010000u; + + typedef KernAATSubTableHeader SubTableHeader; + typedef SubTableHeader::Types Types; + typedef KernSubTable<SubTableHeader> SubTable; + + protected: + HBUINT32 version; /* Version--0x00010000u */ + HBUINT32 tableCount; /* Number of subtables in the kerning table. */ + SubTable firstSubTable; /* Subtables. */ + public: + DEFINE_SIZE_MIN (8); +}; + +struct kern +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; + + bool has_data () const { return u.version32; } + unsigned get_type () const { return u.major; } + + bool has_state_machine () const + { + 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; + } + } + + bool apply (AAT::hb_aat_apply_context_t *c) const + { return dispatch (c); } + + template <typename context_t, typename ...Ts> + 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.ot, std::forward<Ts> (ds)...)); +#ifndef HB_NO_AAT_SHAPE + case 1: return_trace (c->dispatch (u.aat, std::forward<Ts> (ds)...)); +#endif + default: return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.version32.sanitize (c)) return_trace (false); + hb_barrier (); + return_trace (dispatch (c)); + } + + protected: + union { + HBUINT32 version32; + HBUINT16 major; + KernOT ot; +#ifndef HB_NO_AAT_SHAPE + KernAAT aat; +#endif + } u; + public: + DEFINE_SIZE_UNION (4, version32); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_KERN_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-base-table.hh b/gfx/harfbuzz/src/hb-ot-layout-base-table.hh new file mode 100644 index 0000000000..a23b6377d1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-base-table.hh @@ -0,0 +1,526 @@ +/* + * Copyright © 2016 Elie Roux <elie.roux@telecom-bretagne.eu> + * 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<Device> + 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); + hb_barrier (); + 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<BaseCoord> + minCoord; /* Offset to BaseCoord table that defines + * the minimum extent value, from beginning + * of MinMax table (may be NULL) */ + Offset16To<BaseCoord> + 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<BaseCoord> + minCoord; /* Offset to BaseCoord table that defines + * minimum extent value, from the beginning + * of MinMax table (may be NULL) */ + Offset16To<BaseCoord> + maxCoord; /* Offset to BaseCoord table that defines + * maximum extent value, from the beginning + * of MinMax table (may be NULL) */ + SortedArray16Of<FeatMinMaxRecord> + 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<BaseCoord> + 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> + 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> + baseValues; /* Offset to BaseValues table, from beginning + * of BaseScript table (may be NULL) */ + Offset16To<MinMax> + defaultMinMax; /* Offset to MinMax table, from beginning of + * BaseScript table (may be NULL) */ + SortedArray16Of<BaseLangSysRecord> + 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> + 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<BaseScriptRecord> + 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<SortedArray16Of<Tag>> + 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> + 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) && + hb_barrier () && + 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 */ + Offset16To<Axis>hAxis; /* Offset to horizontal Axis table, from beginning + * of BASE table (may be NULL) */ + Offset16To<Axis>vAxis; /* Offset to vertical Axis table, from beginning + * of BASE table (may be NULL) */ + Offset32To<VariationStore> + 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/gfx/harfbuzz/src/hb-ot-layout-common.hh b/gfx/harfbuzz/src/hb-ot-layout-common.hh new file mode 100644 index 0000000000..6b359cceb7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-common.hh @@ -0,0 +1,4221 @@ +/* + * 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_HH +#define HB_OT_LAYOUT_COMMON_HH + +#include "hb.hh" +#include "hb-ot-layout.hh" +#include "hb-open-type.hh" +#include "hb-set.hh" +#include "hb-bimap.hh" + +#include "OT/Layout/Common/Coverage.hh" +#include "OT/Layout/types.hh" + +// TODO(garretrieger): cleanup these after migration. +using OT::Layout::Common::Coverage; +using OT::Layout::Common::RangeRecord; +using OT::Layout::SmallTypes; +using OT::Layout::MediumTypes; + + +namespace OT { + +template<typename Iterator> +static inline bool ClassDef_serialize (hb_serialize_context_t *c, + Iterator it); + +static bool ClassDef_remap_and_serialize ( + hb_serialize_context_t *c, + const hb_set_t &klasses, + bool use_class_zero, + hb_sorted_vector_t<hb_codepoint_pair_t> &glyph_and_klass, /* IN/OUT */ + hb_map_t *klass_map /*IN/OUT*/); + +struct hb_collect_feature_substitutes_with_var_context_t +{ + const hb_map_t *axes_index_tag_map; + const hb_hashmap_t<hb_tag_t, Triple> *axes_location; + hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *record_cond_idx_map; + hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map; + hb_set_t& catch_all_record_feature_idxes; + + // not stored in subset_plan + hb_set_t *feature_indices; + bool apply; + bool variation_applied; + bool universal; + unsigned cur_record_idx; + hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> *conditionset_map; +}; + +struct hb_prune_langsys_context_t +{ + hb_prune_langsys_context_t (const void *table_, + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map_, + const hb_map_t *duplicate_feature_map_, + hb_set_t *new_collected_feature_indexes_) + :table (table_), + script_langsys_map (script_langsys_map_), + duplicate_feature_map (duplicate_feature_map_), + new_feature_indexes (new_collected_feature_indexes_), + script_count (0),langsys_feature_count (0) {} + + bool visitScript () + { return script_count++ < HB_MAX_SCRIPTS; } + + bool visitLangsys (unsigned feature_count) + { + langsys_feature_count += feature_count; + return langsys_feature_count < HB_MAX_LANGSYS_FEATURE_COUNT; + } + + public: + const void *table; + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map; + const hb_map_t *duplicate_feature_map; + hb_set_t *new_feature_indexes; + + private: + unsigned script_count; + unsigned langsys_feature_count; +}; + +struct hb_subset_layout_context_t : + hb_dispatch_context_t<hb_subset_layout_context_t, hb_empty_t, HB_DEBUG_SUBSET> +{ + const char *get_name () { return "SUBSET_LAYOUT"; } + static return_t default_return_value () { return hb_empty_t (); } + + bool visitScript () + { + return script_count++ < HB_MAX_SCRIPTS; + } + + bool visitLangSys () + { + return langsys_count++ < HB_MAX_LANGSYS; + } + + bool visitFeatureIndex (int count) + { + feature_index_count += count; + return feature_index_count < HB_MAX_FEATURE_INDICES; + } + + bool visitLookupIndex() + { + lookup_index_count++; + return lookup_index_count < HB_MAX_LOOKUP_VISIT_COUNT; + } + + hb_subset_context_t *subset_context; + const hb_tag_t table_tag; + const hb_map_t *lookup_index_map; + const hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map; + const hb_map_t *feature_index_map; + const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map; + hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map; + const hb_set_t *catch_all_record_feature_idxes; + const hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>> *feature_idx_tag_map; + + unsigned cur_script_index; + unsigned cur_feature_var_record_idx; + + hb_subset_layout_context_t (hb_subset_context_t *c_, + hb_tag_t tag_) : + subset_context (c_), + table_tag (tag_), + cur_script_index (0xFFFFu), + cur_feature_var_record_idx (0u), + script_count (0), + langsys_count (0), + feature_index_count (0), + lookup_index_count (0) + { + if (tag_ == HB_OT_TAG_GSUB) + { + lookup_index_map = &c_->plan->gsub_lookups; + script_langsys_map = &c_->plan->gsub_langsys; + feature_index_map = &c_->plan->gsub_features; + feature_substitutes_map = &c_->plan->gsub_feature_substitutes_map; + feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gsub_feature_record_cond_idx_map; + catch_all_record_feature_idxes = &c_->plan->gsub_old_features; + feature_idx_tag_map = &c_->plan->gsub_old_feature_idx_tag_map; + } + else + { + lookup_index_map = &c_->plan->gpos_lookups; + script_langsys_map = &c_->plan->gpos_langsys; + feature_index_map = &c_->plan->gpos_features; + feature_substitutes_map = &c_->plan->gpos_feature_substitutes_map; + feature_record_cond_idx_map = c_->plan->user_axes_location.is_empty () ? nullptr : &c_->plan->gpos_feature_record_cond_idx_map; + catch_all_record_feature_idxes = &c_->plan->gpos_old_features; + feature_idx_tag_map = &c_->plan->gpos_old_feature_idx_tag_map; + } + } + + private: + unsigned script_count; + unsigned langsys_count; + unsigned feature_index_count; + unsigned lookup_index_count; +}; + +struct VariationStore; +struct hb_collect_variation_indices_context_t : + hb_dispatch_context_t<hb_collect_variation_indices_context_t> +{ + template <typename T> + return_t dispatch (const T &obj) { obj.collect_variation_indices (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + + hb_set_t *layout_variation_indices; + const hb_set_t *glyph_set; + const hb_map_t *gpos_lookups; + + hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_, + const hb_set_t *glyph_set_, + const hb_map_t *gpos_lookups_) : + layout_variation_indices (layout_variation_indices_), + glyph_set (glyph_set_), + gpos_lookups (gpos_lookups_) {} +}; + +template<typename OutputArray> +struct subset_offset_array_t +{ + subset_offset_array_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_) : subset_context (subset_context_), + out (out_), base (base_) {} + + template <typename T> + bool operator () (T&& offset) + { + auto snap = subset_context->serializer->snapshot (); + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (subset_context, offset, base); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; +}; + + +template<typename OutputArray, typename Arg> +struct subset_offset_array_arg_t +{ + subset_offset_array_arg_t (hb_subset_context_t *subset_context_, + OutputArray& out_, + const void *base_, + Arg &&arg_) : subset_context (subset_context_), out (out_), + base (base_), arg (arg_) {} + + template <typename T> + bool operator () (T&& offset) + { + auto snap = subset_context->serializer->snapshot (); + auto *o = out.serialize_append (subset_context->serializer); + if (unlikely (!o)) return false; + bool ret = o->serialize_subset (subset_context, offset, base, arg); + if (!ret) + { + out.pop (); + subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *subset_context; + OutputArray &out; + const void *base; + Arg &&arg; +}; + +/* + * Helper to subset an array of offsets. Subsets the thing pointed to by each offset + * and discards the offset in the array if the subset operation results in an empty + * thing. + */ +struct +{ + template<typename OutputArray> + subset_offset_array_t<OutputArray> + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base) const + { return subset_offset_array_t<OutputArray> (subset_context, out, base); } + + /* Variant with one extra argument passed to serialize_subset */ + template<typename OutputArray, typename Arg> + subset_offset_array_arg_t<OutputArray, Arg> + operator () (hb_subset_context_t *subset_context, OutputArray& out, + const void *base, Arg &&arg) const + { return subset_offset_array_arg_t<OutputArray, Arg> (subset_context, out, base, arg); } +} +HB_FUNCOBJ (subset_offset_array); + +template<typename OutputArray> +struct subset_record_array_t +{ + subset_record_array_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_) : subset_layout_context (c_), + out (out_), base (base_) {} + + template <typename T> + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; +}; + +template<typename OutputArray, typename Arg> +struct subset_record_array_arg_t +{ + subset_record_array_arg_t (hb_subset_layout_context_t *c_, OutputArray* out_, + const void *base_, + Arg &&arg_) : subset_layout_context (c_), + out (out_), base (base_), arg (arg_) {} + + template <typename T> + void + operator () (T&& record) + { + auto snap = subset_layout_context->subset_context->serializer->snapshot (); + bool ret = record.subset (subset_layout_context, base, arg); + if (!ret) subset_layout_context->subset_context->serializer->revert (snap); + else out->len++; + } + + private: + hb_subset_layout_context_t *subset_layout_context; + OutputArray *out; + const void *base; + Arg &&arg; +}; + +/* + * Helper to subset a RecordList/record array. Subsets each Record in the array and + * discards the record if the subset operation returns false. + */ +struct +{ + template<typename OutputArray> + subset_record_array_t<OutputArray> + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base) const + { return subset_record_array_t<OutputArray> (c, out, base); } + + /* Variant with one extra argument passed to subset */ + template<typename OutputArray, typename Arg> + subset_record_array_arg_t<OutputArray, Arg> + operator () (hb_subset_layout_context_t *c, OutputArray* out, + const void *base, Arg &&arg) const + { return subset_record_array_arg_t<OutputArray, Arg> (c, out, base, arg); } +} +HB_FUNCOBJ (subset_record_array); + + +template<typename OutputArray> +struct serialize_math_record_array_t +{ + serialize_math_record_array_t (hb_serialize_context_t *serialize_context_, + OutputArray& out_, + const void *base_) : serialize_context (serialize_context_), + out (out_), base (base_) {} + + template <typename T> + bool operator () (T&& record) + { + if (!serialize_context->copy (record, base)) return false; + out.len++; + return true; + } + + private: + hb_serialize_context_t *serialize_context; + OutputArray &out; + const void *base; +}; + +/* + * Helper to serialize an array of MATH records. + */ +struct +{ + template<typename OutputArray> + serialize_math_record_array_t<OutputArray> + operator () (hb_serialize_context_t *serialize_context, OutputArray& out, + const void *base) const + { return serialize_math_record_array_t<OutputArray> (serialize_context, out, base); } + +} +HB_FUNCOBJ (serialize_math_record_array); + +/* + * + * OpenType Layout Common Table Formats + * + */ + + +/* + * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList + */ + +struct IndexArray : Array16Of<Index> +{ + bool intersects (const hb_map_t *indexes) const + { return hb_any (*this, indexes); } + + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + hb_subset_layout_context_t *l, + Iterator it) + { + if (!it) return; + if (unlikely (!c->extend_min ((*this)))) return; + + for (const auto _ : it) + { + if (!l->visitLookupIndex()) break; + + Index i; + i = _; + c->copy (i); + this->len++; + } + } + + unsigned int get_indexes (unsigned int start_offset, + unsigned int *_count /* IN/OUT */, + unsigned int *_indexes /* OUT */) const + { + if (_count) + { + + this->as_array ().sub_array (start_offset, _count) + | hb_sink (hb_array (_indexes, *_count)) + ; + } + return this->len; + } + + void add_indexes_to (hb_set_t* output /* OUT */) const + { + output->add_array (as_array ()); + } +}; + + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */ +struct FeatureParamsSize +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); + + /* This subtable has some "history", if you will. Some earlier versions of + * Adobe tools calculated the offset of the FeatureParams subtable from the + * beginning of the FeatureList table! Now, that is dealt with in the + * Feature implementation. But we still need to be able to tell junk from + * real data. Note: We don't check that the nameID actually exists. + * + * Read Roberts wrote on 9/15/06 on opentype-list@indx.co.uk : + * + * Yes, it is correct that a new version of the AFDKO (version 2.0) will be + * coming out soon, and that the makeotf program will build a font with a + * 'size' feature that is correct by the specification. + * + * The specification for this feature tag is in the "OpenType Layout Tag + * Registry". You can see a copy of this at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size + * + * Here is one set of rules to determine if the 'size' feature is built + * correctly, or as by the older versions of MakeOTF. You may be able to do + * better. + * + * Assume that the offset to the size feature is according to specification, + * and make the following value checks. If it fails, assume the size + * feature is calculated as versions of MakeOTF before the AFDKO 2.0 built it. + * If this fails, reject the 'size' feature. The older makeOTF's calculated the + * offset from the beginning of the FeatureList table, rather than from the + * beginning of the 'size' Feature table. + * + * If "design size" == 0: + * fails check + * + * Else if ("subfamily identifier" == 0 and + * "range start" == 0 and + * "range end" == 0 and + * "range start" == 0 and + * "menu name ID" == 0) + * passes check: this is the format used when there is a design size + * specified, but there is no recommended size range. + * + * Else if ("design size" < "range start" or + * "design size" > "range end" or + * "range end" <= "range start" or + * "menu name ID" < 256 or + * "menu name ID" > 32767 or + * menu name ID is not a name ID which is actually in the name table) + * fails test + * Else + * passes test. + */ + + if (!designSize) + return_trace (false); + else if (subfamilyID == 0 && + subfamilyNameID == 0 && + rangeStart == 0 && + rangeEnd == 0) + return_trace (true); + else if (designSize < rangeStart || + designSize > rangeEnd || + subfamilyNameID < 256 || + subfamilyNameID > 32767) + return_trace (false); + else + return_trace (true); + } + + void collect_name_ids (hb_set_t *nameids_to_retain /* OUT */) const + { nameids_to_retain->add (subfamilyNameID); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + + HBUINT16 designSize; /* Represents the design size in 720/inch + * units (decipoints). The design size entry + * must be non-zero. When there is a design + * size but no recommended size range, the + * rest of the array will consist of zeros. */ + HBUINT16 subfamilyID; /* Has no independent meaning, but serves + * as an identifier that associates fonts + * in a subfamily. All fonts which share a + * Preferred or Font Family name and which + * differ only by size range shall have the + * same subfamily value, and no fonts which + * differ in weight or style shall have the + * same subfamily value. If this value is + * zero, the remaining fields in the array + * will be ignored. */ + NameID subfamilyNameID;/* If the preceding value is non-zero, this + * value must be set in the range 256 - 32767 + * (inclusive). It records the value of a + * field in the name table, which must + * contain English-language strings encoded + * in Windows Unicode and Macintosh Roman, + * and may contain additional strings + * localized to other scripts and languages. + * Each of these strings is the name an + * application should use, in combination + * with the family name, to represent the + * subfamily in a menu. Applications will + * choose the appropriate version based on + * their selection criteria. */ + HBUINT16 rangeStart; /* Large end of the recommended usage range + * (inclusive), stored in 720/inch units + * (decipoints). */ + HBUINT16 rangeEnd; /* Small end of the recommended usage range + (exclusive), stored in 720/inch units + * (decipoints). */ + public: + DEFINE_SIZE_STATIC (10); +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#ssxx */ +struct FeatureParamsStylisticSet +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* Right now minorVersion is at zero. Which means, any table supports + * the uiNameID field. */ + return_trace (c->check_struct (this)); + } + + void collect_name_ids (hb_set_t *nameids_to_retain /* OUT */) const + { nameids_to_retain->add (uiNameID); } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + + HBUINT16 version; /* (set to 0): This corresponds to a “minor” + * version number. Additional data may be + * added to the end of this Feature Parameters + * table in the future. */ + + NameID uiNameID; /* The 'name' table name ID that specifies a + * string (or strings, for multiple languages) + * for a user-interface label for this + * feature. The values of uiLabelNameId and + * sampleTextNameId are expected to be in the + * font-specific name ID range (256-32767), + * though that is not a requirement in this + * Feature Parameters specification. The + * user-interface label for the feature can + * be provided in multiple languages. An + * English string should be included as a + * fallback. The string should be kept to a + * minimal length to fit comfortably with + * different application interfaces. */ + public: + DEFINE_SIZE_STATIC (4); +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99 */ +struct FeatureParamsCharacterVariants +{ + unsigned + get_characters (unsigned start_offset, unsigned *char_count, hb_codepoint_t *chars) const + { + if (char_count) + { + + characters.as_array ().sub_array (start_offset, char_count) + | hb_sink (hb_array (chars, *char_count)) + ; + } + return characters.len; + } + + unsigned get_size () const + { return min_size + characters.len * HBUINT24::static_size; } + + void collect_name_ids (hb_set_t *nameids_to_retain /* OUT */) const + { + if (featUILableNameID) nameids_to_retain->add (featUILableNameID); + if (featUITooltipTextNameID) nameids_to_retain->add (featUITooltipTextNameID); + if (sampleTextNameID) nameids_to_retain->add (sampleTextNameID); + + if (!firstParamUILabelNameID || !numNamedParameters || numNamedParameters >= 0x7FFF) + return; + + unsigned last_name_id = (unsigned) firstParamUILabelNameID + (unsigned) numNamedParameters - 1; + if (last_name_id >= 256 && last_name_id <= 32767) + nameids_to_retain->add_range (firstParamUILabelNameID, last_name_id); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + return_trace ((bool) c->serializer->embed (*this)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + characters.sanitize (c)); + } + + HBUINT16 format; /* Format number is set to 0. */ + NameID featUILableNameID; /* The ‘name’ table name ID that + * specifies a string (or strings, + * for multiple languages) for a + * user-interface label for this + * feature. (May be NULL.) */ + NameID featUITooltipTextNameID;/* The ‘name’ table name ID that + * specifies a string (or strings, + * for multiple languages) that an + * application can use for tooltip + * text for this feature. (May be + * nullptr.) */ + NameID sampleTextNameID; /* The ‘name’ table name ID that + * specifies sample text that + * illustrates the effect of this + * feature. (May be NULL.) */ + HBUINT16 numNamedParameters; /* Number of named parameters. (May + * be zero.) */ + NameID firstParamUILabelNameID;/* The first ‘name’ table name ID + * used to specify strings for + * user-interface labels for the + * feature parameters. (Must be zero + * if numParameters is zero.) */ + Array16Of<HBUINT24> + characters; /* Array of the Unicode Scalar Value + * of the characters for which this + * feature provides glyph variants. + * (May be zero.) */ + public: + DEFINE_SIZE_ARRAY (14, characters); +}; + +struct FeatureParams +{ + bool sanitize (hb_sanitize_context_t *c, hb_tag_t tag) const + { +#ifdef HB_NO_LAYOUT_FEATURE_PARAMS + return true; +#endif + TRACE_SANITIZE (this); + if (tag == HB_TAG ('s','i','z','e')) + return_trace (u.size.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return_trace (u.stylisticSet.sanitize (c)); + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return_trace (u.characterVariants.sanitize (c)); + return_trace (true); + } + + void collect_name_ids (hb_tag_t tag, hb_set_t *nameids_to_retain /* OUT */) const + { +#ifdef HB_NO_LAYOUT_FEATURE_PARAMS + return; +#endif + if (tag == HB_TAG ('s','i','z','e')) + return (u.size.collect_name_ids (nameids_to_retain)); + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return (u.stylisticSet.collect_name_ids (nameids_to_retain)); + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return (u.characterVariants.collect_name_ids (nameids_to_retain)); + } + + bool subset (hb_subset_context_t *c, const Tag* tag) const + { + TRACE_SUBSET (this); + if (!tag) return_trace (false); + if (*tag == HB_TAG ('s','i','z','e')) + return_trace (u.size.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return_trace (u.stylisticSet.subset (c)); + if ((*tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return_trace (u.characterVariants.subset (c)); + return_trace (false); + } + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS + const FeatureParamsSize& get_size_params (hb_tag_t tag) const + { + if (tag == HB_TAG ('s','i','z','e')) + return u.size; + return Null (FeatureParamsSize); + } + const FeatureParamsStylisticSet& get_stylistic_set_params (hb_tag_t tag) const + { + if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) /* ssXX */ + return u.stylisticSet; + return Null (FeatureParamsStylisticSet); + } + const FeatureParamsCharacterVariants& get_character_variants_params (hb_tag_t tag) const + { + if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) /* cvXX */ + return u.characterVariants; + return Null (FeatureParamsCharacterVariants); + } +#endif + + private: + union { + FeatureParamsSize size; + FeatureParamsStylisticSet stylisticSet; + FeatureParamsCharacterVariants characterVariants; + } u; + public: + DEFINE_SIZE_MIN (0); +}; + +struct Record_sanitize_closure_t { + hb_tag_t tag; + const void *list_base; +}; + +struct Feature +{ + unsigned int get_lookup_count () const + { return lookupIndex.len; } + hb_tag_t get_lookup_index (unsigned int i) const + { return lookupIndex[i]; } + unsigned int get_lookup_indexes (unsigned int start_index, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_tags /* OUT */) const + { return lookupIndex.get_indexes (start_index, lookup_count, lookup_tags); } + void add_lookup_indexes_to (hb_set_t *lookup_indexes) const + { lookupIndex.add_indexes_to (lookup_indexes); } + + const FeatureParams &get_feature_params () const + { return this+featureParams; } + + bool intersects_lookup_indexes (const hb_map_t *lookup_indexes) const + { return lookupIndex.intersects (lookup_indexes); } + + void collect_name_ids (hb_tag_t tag, hb_set_t *nameids_to_retain /* OUT */) const + { + if (featureParams) + get_feature_params ().collect_name_ids (tag, nameids_to_retain); + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + out->featureParams.serialize_subset (c, featureParams, this, tag); + + auto it = + + hb_iter (lookupIndex) + | hb_filter (l->lookup_index_map) + | hb_map (l->lookup_index_map) + ; + + out->lookupIndex.serialize (c->serializer, l, it); + // The decision to keep or drop this feature is already made before we get here + // so always retain it. + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t *closure = nullptr) const + { + TRACE_SANITIZE (this); + if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) + return_trace (false); + hb_barrier (); + + /* Some earlier versions of Adobe tools calculated the offset of the + * FeatureParams subtable from the beginning of the FeatureList table! + * + * If sanitizing "failed" for the FeatureParams subtable, try it with the + * alternative location. We would know sanitize "failed" if old value + * of the offset was non-zero, but it's zeroed now. + * + * Only do this for the 'size' feature, since at the time of the faulty + * Adobe tools, only the 'size' feature had FeatureParams defined. + */ + + if (likely (featureParams.is_null ())) + return_trace (true); + + unsigned int orig_offset = featureParams; + if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) + return_trace (false); + hb_barrier (); + + if (featureParams == 0 && closure && + closure->tag == HB_TAG ('s','i','z','e') && + closure->list_base && closure->list_base < this) + { + unsigned int new_offset_int = orig_offset - + (((char *) this) - ((char *) closure->list_base)); + + Offset16To<FeatureParams> new_offset; + /* Check that it would not overflow. */ + new_offset = new_offset_int; + if (new_offset == new_offset_int && + c->try_set (&featureParams, new_offset_int) && + !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) + return_trace (false); + } + + return_trace (true); + } + + Offset16To<FeatureParams> + featureParams; /* Offset to Feature Parameters table (if one + * has been defined for the feature), relative + * to the beginning of the Feature Table; = Null + * if not required */ + IndexArray lookupIndex; /* Array of LookupList indices */ + public: + DEFINE_SIZE_ARRAY_SIZED (4, lookupIndex); +}; + +template <typename Type> +struct Record +{ + int cmp (hb_tag_t a) const { return tag.cmp (a); } + + bool subset (hb_subset_layout_context_t *c, const void *base, const void *f_sub = nullptr) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (!f_sub) + return_trace (out->offset.serialize_subset (c->subset_context, offset, base, c, &tag)); + + const Feature& f = *reinterpret_cast<const Feature *> (f_sub); + auto *s = c->subset_context->serializer; + s->push (); + + out->offset = 0; + bool ret = f.subset (c->subset_context, c, &tag); + if (ret) + s->add_link (out->offset, s->pop_pack ()); + else + s->pop_discard (); + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + const Record_sanitize_closure_t closure = {tag, base}; + return_trace (c->check_struct (this) && + offset.sanitize (c, base, &closure)); + } + + Tag tag; /* 4-byte Tag identifier */ + Offset16To<Type> + offset; /* Offset from beginning of object holding + * the Record */ + public: + DEFINE_SIZE_STATIC (6); +}; + +template <typename Type> +struct RecordArrayOf : SortedArray16Of<Record<Type>> +{ + const Offset16To<Type>& get_offset (unsigned int i) const + { return (*this)[i].offset; } + Offset16To<Type>& get_offset (unsigned int i) + { return (*this)[i].offset; } + const Tag& get_tag (unsigned int i) const + { return (*this)[i].tag; } + unsigned int get_tags (unsigned int start_offset, + unsigned int *record_count /* IN/OUT */, + hb_tag_t *record_tags /* OUT */) const + { + if (record_count) + { + + this->as_array ().sub_array (start_offset, record_count) + | hb_map (&Record<Type>::tag) + | hb_sink (hb_array (record_tags, *record_count)) + ; + } + return this->len; + } + bool find_index (hb_tag_t tag, unsigned int *index) const + { + return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX); + } +}; + +template <typename Type> +struct RecordListOf : RecordArrayOf<Type> +{ + const Type& operator [] (unsigned int i) const + { return this+this->get_offset (i); } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + this->iter () + | hb_apply (subset_record_array (l, out, this)) + ; + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (RecordArrayOf<Type>::sanitize (c, this)); + } +}; + +struct RecordListOfFeature : RecordListOf<Feature> +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_enumerate (*this) + | hb_filter (l->feature_index_map, hb_first) + | hb_apply ([l, out, this] (const hb_pair_t<unsigned, const Record<Feature>&>& _) + { + const Feature *f_sub = nullptr; + const Feature **f = nullptr; + if (l->feature_substitutes_map->has (_.first, &f)) + f_sub = *f; + + subset_record_array (l, out, this, f_sub) (_.second); + }) + ; + + return_trace (true); + } +}; + +typedef RecordListOf<Feature> FeatureList; + + +struct LangSys +{ + unsigned int get_feature_count () const + { return featureIndex.len; } + hb_tag_t get_feature_index (unsigned int i) const + { return featureIndex[i]; } + 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); } + void add_feature_indexes_to (hb_set_t *feature_indexes) const + { featureIndex.add_indexes_to (feature_indexes); } + + bool has_required_feature () const { return reqFeatureIndex != 0xFFFFu; } + unsigned int get_required_feature_index () const + { + if (reqFeatureIndex == 0xFFFFu) + return Index::NOT_FOUND_INDEX; + return reqFeatureIndex; + } + + LangSys* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + bool compare (const LangSys& o, const hb_map_t *feature_index_map) const + { + if (reqFeatureIndex != o.reqFeatureIndex) + return false; + + auto iter = + + hb_iter (featureIndex) + | hb_filter (feature_index_map) + | hb_map (feature_index_map) + ; + + auto o_iter = + + hb_iter (o.featureIndex) + | hb_filter (feature_index_map) + | hb_map (feature_index_map) + ; + + for (; iter && o_iter; iter++, o_iter++) + { + unsigned a = *iter; + unsigned b = *o_iter; + if (a != b) return false; + } + + if (iter || o_iter) return false; + + return true; + } + + void collect_features (hb_prune_langsys_context_t *c) const + { + if (!has_required_feature () && !get_feature_count ()) return; + if (has_required_feature () && + c->duplicate_feature_map->has (reqFeatureIndex)) + c->new_feature_indexes->add (get_required_feature_index ()); + + + hb_iter (featureIndex) + | hb_filter (c->duplicate_feature_map) + | hb_sink (c->new_feature_indexes) + ; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag = nullptr) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + const uint32_t *v; + out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex, &v) ? *v : 0xFFFFu; + + if (!l->visitFeatureIndex (featureIndex.len)) + return_trace (false); + + auto it = + + hb_iter (featureIndex) + | hb_filter (l->feature_index_map) + | hb_map (l->feature_index_map) + ; + + bool ret = bool (it); + out->featureIndex.serialize (c->serializer, l, it); + return_trace (ret); + } + + 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) */ + HBUINT16 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_SIZED (6, featureIndex); +}; +DECLARE_NULL_NAMESPACE_BYTES (OT, LangSys); + +struct Script +{ + unsigned int get_lang_sys_count () const + { return langSys.len; } + const Tag& get_lang_sys_tag (unsigned int i) const + { return langSys.get_tag (i); } + 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); } + 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; + } + bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const + { return langSys.find_index (tag, index); } + + bool has_default_lang_sys () const { return defaultLangSys != 0; } + const LangSys& get_default_lang_sys () const { return this+defaultLangSys; } + + void prune_langsys (hb_prune_langsys_context_t *c, + unsigned script_index) const + { + if (!has_default_lang_sys () && !get_lang_sys_count ()) return; + if (!c->visitScript ()) return; + + if (!c->script_langsys_map->has (script_index)) + { + if (unlikely (!c->script_langsys_map->set (script_index, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) + return; + } + + if (has_default_lang_sys ()) + { + //only collect features from non-redundant langsys + const LangSys& d = get_default_lang_sys (); + if (c->visitLangsys (d.get_feature_count ())) { + d.collect_features (c); + } + + for (auto _ : + hb_enumerate (langSys)) + { + const LangSys& l = this+_.second.offset; + if (!c->visitLangsys (l.get_feature_count ())) continue; + if (l.compare (d, c->duplicate_feature_map)) continue; + + l.collect_features (c); + c->script_langsys_map->get (script_index)->add (_.first); + } + } + else + { + for (auto _ : + hb_enumerate (langSys)) + { + const LangSys& l = this+_.second.offset; + if (!c->visitLangsys (l.get_feature_count ())) continue; + l.collect_features (c); + c->script_langsys_map->get (script_index)->add (_.first); + } + } + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + const Tag *tag) const + { + TRACE_SUBSET (this); + if (!l->visitScript ()) return_trace (false); + if (tag && !c->plan->layout_scripts.has (*tag)) + return false; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + bool defaultLang = false; + if (has_default_lang_sys ()) + { + c->serializer->push (); + const LangSys& ls = this+defaultLangSys; + bool ret = ls.subset (c, l); + if (!ret && tag && *tag != HB_TAG ('D', 'F', 'L', 'T')) + { + c->serializer->pop_discard (); + out->defaultLangSys = 0; + } + else + { + c->serializer->add_link (out->defaultLangSys, c->serializer->pop_pack ()); + defaultLang = true; + } + } + + const hb_set_t *active_langsys = l->script_langsys_map->get (l->cur_script_index); + if (active_langsys) + { + + hb_enumerate (langSys) + | hb_filter (active_langsys, hb_first) + | hb_map (hb_second) + | hb_filter ([=] (const Record<LangSys>& record) {return l->visitLangSys (); }) + | hb_apply (subset_record_array (l, &(out->langSys), this)) + ; + } + + return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB); + } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t * = nullptr) const + { + TRACE_SANITIZE (this); + return_trace (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this)); + } + + protected: + Offset16To<LangSys> + defaultLangSys; /* Offset to DefaultLangSys table--from + * beginning of Script table--may be Null */ + RecordArrayOf<LangSys> + langSys; /* Array of LangSysRecords--listed + * alphabetically by LangSysTag */ + public: + DEFINE_SIZE_ARRAY_SIZED (4, langSys); +}; + +struct RecordListOfScript : RecordListOf<Script> +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (auto _ : + hb_enumerate (*this)) + { + auto snap = c->serializer->snapshot (); + l->cur_script_index = _.first; + bool ret = _.second.subset (l, this); + if (!ret) c->serializer->revert (snap); + else out->len++; + } + + return_trace (true); + } +}; + +typedef RecordListOfScript ScriptList; + + + +struct LookupFlag : HBUINT16 +{ + enum Flags { + RightToLeft = 0x0001u, + IgnoreBaseGlyphs = 0x0002u, + IgnoreLigatures = 0x0004u, + IgnoreMarks = 0x0008u, + IgnoreFlags = 0x000Eu, + UseMarkFilteringSet = 0x0010u, + Reserved = 0x00E0u, + MarkAttachmentType = 0xFF00u + }; + public: + DEFINE_SIZE_STATIC (2); +}; + +} /* namespace OT */ +/* This has to be outside the namespace. */ +HB_MARK_AS_FLAG_T (OT::LookupFlag::Flags); +namespace OT { + +struct Lookup +{ + unsigned int get_subtable_count () const { return subTable.len; } + + template <typename TSubTable> + const Array16OfOffset16To<TSubTable>& get_subtables () const + { return reinterpret_cast<const Array16OfOffset16To<TSubTable> &> (subTable); } + template <typename TSubTable> + Array16OfOffset16To<TSubTable>& get_subtables () + { return reinterpret_cast<Array16OfOffset16To<TSubTable> &> (subTable); } + + template <typename TSubTable> + const TSubTable& get_subtable (unsigned int i) const + { return this+get_subtables<TSubTable> ()[i]; } + template <typename TSubTable> + TSubTable& get_subtable (unsigned int i) + { return this+get_subtables<TSubTable> ()[i]; } + + unsigned int get_size () const + { + const HBUINT16 &markFilteringSet = StructAfter<const HBUINT16> (subTable); + if (lookupFlag & LookupFlag::UseMarkFilteringSet) + return (const char *) &StructAfter<const char> (markFilteringSet) - (const char *) this; + return (const char *) &markFilteringSet - (const char *) this; + } + + unsigned int get_type () const { return lookupType; } + + /* lookup_props is a 32-bit integer where the lower 16-bit is LookupFlag and + * higher 16-bit is mark-filtering-set if the lookup uses one. + * Not to be confused with glyph_props which is very similar. */ + uint32_t get_props () const + { + unsigned int flag = lookupFlag; + if (unlikely (flag & LookupFlag::UseMarkFilteringSet)) + { + const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); + flag += (markFilteringSet << 16); + } + return flag; + } + + template <typename TSubTable, typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + unsigned int lookup_type = get_type (); + TRACE_DISPATCH (this, lookup_type); + unsigned int count = get_subtable_count (); + for (unsigned int i = 0; i < count; i++) { + typename context_t::return_t r = get_subtable<TSubTable> (i).dispatch (c, lookup_type, std::forward<Ts> (ds)...); + if (c->stop_sublookup_iteration (r)) + return_trace (r); + } + return_trace (c->default_return_value ()); + } + + bool serialize (hb_serialize_context_t *c, + unsigned int lookup_type, + uint32_t lookup_props, + unsigned int num_subtables) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + lookupType = lookup_type; + lookupFlag = lookup_props & 0xFFFFu; + if (unlikely (!subTable.serialize (c, num_subtables))) return_trace (false); + if (lookupFlag & LookupFlag::UseMarkFilteringSet) + { + if (unlikely (!c->extend (this))) return_trace (false); + HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); + markFilteringSet = lookup_props >> 16; + } + return_trace (true); + } + + template <typename TSubTable> + 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->lookupType = lookupType; + out->lookupFlag = lookupFlag; + + const hb_set_t *glyphset = c->plan->glyphset_gsub (); + unsigned int lookup_type = get_type (); + + hb_iter (get_subtables <TSubTable> ()) + | hb_filter ([this, glyphset, lookup_type] (const Offset16To<TSubTable> &_) { return (this+_).intersects (glyphset, lookup_type); }) + | hb_apply (subset_offset_array (c, out->get_subtables<TSubTable> (), this, lookup_type)) + ; + + if (lookupFlag & LookupFlag::UseMarkFilteringSet) + { + const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); + hb_codepoint_t *idx; + if (!c->plan->used_mark_sets_map.has (markFilteringSet, &idx)) + { + unsigned new_flag = lookupFlag; + new_flag &= ~LookupFlag::UseMarkFilteringSet; + out->lookupFlag = new_flag; + } + else + { + if (unlikely (!c->serializer->extend (out))) return_trace (false); + HBUINT16 &outMarkFilteringSet = StructAfter<HBUINT16> (out->subTable); + outMarkFilteringSet = *idx; + } + } + + // Always keep the lookup even if it's empty. The rest of layout subsetting depends on lookup + // indices being consistent with those computed during planning. So if an empty lookup is + // discarded during the subset phase it will invalidate all subsequent lookup indices. + // Generally we shouldn't end up with an empty lookup as we pre-prune them during the planning + // phase, but it can happen in rare cases such as when during closure subtable is considered + // degenerate (see: https://github.com/harfbuzz/harfbuzz/issues/3853) + return_trace (true); + } + + template <typename TSubTable> + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(c->check_struct (this) && subTable.sanitize (c))) return_trace (false); + hb_barrier (); + + unsigned subtables = get_subtable_count (); + if (unlikely (!c->visit_subtables (subtables))) return_trace (false); + + if (lookupFlag & LookupFlag::UseMarkFilteringSet) + { + const HBUINT16 &markFilteringSet = StructAfter<HBUINT16> (subTable); + if (!markFilteringSet.sanitize (c)) return_trace (false); + } + + if (unlikely (!get_subtables<TSubTable> ().sanitize (c, this, get_type ()))) + return_trace (false); + + if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) + { + hb_barrier (); + + /* The spec says all subtables of an Extension lookup should + * have the same type, which shall not be the Extension type + * itself (but we already checked for that). + * This is specially important if one has a reverse type! + * + * We only do this if sanitizer edit_count is zero. Otherwise, + * some of the subtables might have become insane after they + * were sanity-checked by the edits of subsequent subtables. + * https://bugs.chromium.org/p/chromium/issues/detail?id=960331 + */ + unsigned int type = get_subtable<TSubTable> (0).u.extension.get_type (); + for (unsigned int i = 1; i < subtables; i++) + if (get_subtable<TSubTable> (i).u.extension.get_type () != type) + return_trace (false); + } + return_trace (true); + } + + protected: + HBUINT16 lookupType; /* Different enumerations for GSUB and GPOS */ + HBUINT16 lookupFlag; /* Lookup qualifiers */ + Array16Of<Offset16> + subTable; /* Array of SubTables */ +/*HBUINT16 markFilteringSetX[HB_VAR_ARRAY];*//* Index (base 0) into GDEF mark glyph sets + * structure. This field is only present if bit + * UseMarkFilteringSet of lookup flags is set. */ + public: + DEFINE_SIZE_ARRAY (6, subTable); +}; + +template <typename Types> +using LookupList = List16OfOffsetTo<Lookup, typename Types::HBUINT>; + +template <typename TLookup, typename OffsetType> +struct LookupOffsetList : List16OfOffsetTo<TLookup, OffsetType> +{ + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + + hb_enumerate (*this) + | hb_filter (l->lookup_index_map, hb_first) + | hb_map (hb_second) + | hb_apply (subset_offset_array (c, *out, this)) + ; + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (List16OfOffset16To<TLookup>::sanitize (c, this)); + } +}; + + +/* + * Coverage Table + */ + + +static bool ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &klasses, + bool use_class_zero, + hb_sorted_vector_t<hb_codepoint_pair_t> &glyph_and_klass, /* IN/OUT */ + hb_map_t *klass_map /*IN/OUT*/) +{ + if (!klass_map) + return ClassDef_serialize (c, glyph_and_klass.iter ()); + + /* any glyph not assigned a class value falls into Class zero (0), + * if any glyph assigned to class 0, remapping must start with 0->0*/ + if (!use_class_zero) + klass_map->set (0, 0); + + unsigned idx = klass_map->has (0) ? 1 : 0; + for (const unsigned k: klasses) + { + if (klass_map->has (k)) continue; + klass_map->set (k, idx); + idx++; + } + + + for (unsigned i = 0; i < glyph_and_klass.length; i++) + { + hb_codepoint_t klass = glyph_and_klass[i].second; + glyph_and_klass[i].second = klass_map->get (klass); + } + + c->propagate_error (glyph_and_klass, klasses); + return ClassDef_serialize (c, glyph_and_klass.iter ()); +} + +/* + * Class Definition Table + */ + +template <typename Types> +struct ClassDefFormat1_3 +{ + friend struct ClassDef; + + private: + unsigned int get_class (hb_codepoint_t glyph_id) const + { + return classValue[(unsigned int) (glyph_id - startGlyph)]; + } + + unsigned get_population () const + { + return classValue.len; + } + + template<typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + if (unlikely (!it)) + { + classFormat = 1; + startGlyph = 0; + classValue.len = 0; + return_trace (true); + } + + hb_codepoint_t glyph_min = (*it).first; + hb_codepoint_t glyph_max = + it + | hb_map (hb_first) + | hb_reduce (hb_max, 0u); + unsigned glyph_count = glyph_max - glyph_min + 1; + + startGlyph = glyph_min; + if (unlikely (!classValue.serialize (c, glyph_count))) return_trace (false); + for (const hb_pair_t<hb_codepoint_t, uint32_t> gid_klass_pair : + it) + { + unsigned idx = gid_klass_pair.first - glyph_min; + classValue[idx] = gid_klass_pair.second; + } + return_trace (true); + } + + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/, + bool keep_empty_table = true, + bool use_class_zero = true, + const Coverage* glyph_filter = nullptr) const + { + TRACE_SUBSET (this); + const hb_map_t &glyph_map = c->plan->glyph_map_gsub; + + hb_sorted_vector_t<hb_codepoint_pair_t> glyph_and_klass; + hb_set_t orig_klasses; + + hb_codepoint_t start = startGlyph; + hb_codepoint_t end = start + classValue.len; + + for (const hb_codepoint_t gid : + hb_range (start, end)) + { + hb_codepoint_t new_gid = glyph_map[gid]; + if (new_gid == HB_MAP_VALUE_INVALID) continue; + if (glyph_filter && !glyph_filter->has(gid)) continue; + + unsigned klass = classValue[gid - start]; + if (!klass) continue; + + glyph_and_klass.push (hb_pair (new_gid, klass)); + orig_klasses.add (klass); + } + + if (use_class_zero) + { + unsigned glyph_count = glyph_filter + ? hb_len (hb_iter (glyph_map.keys()) | hb_filter (glyph_filter)) + : glyph_map.get_population (); + use_class_zero = glyph_count <= glyph_and_klass.length; + } + if (!ClassDef_remap_and_serialize (c->serializer, + orig_klasses, + use_class_zero, + glyph_and_klass, + klass_map)) + return_trace (false); + return_trace (keep_empty_table || (bool) glyph_and_klass); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && classValue.sanitize (c)); + } + + unsigned cost () const { return 1; } + + template <typename set_t> + bool collect_coverage (set_t *glyphs) const + { + unsigned int start = 0; + unsigned int count = classValue.len; + for (unsigned int i = 0; i < count; i++) + { + if (classValue[i]) + continue; + + if (start != i) + if (unlikely (!glyphs->add_range (startGlyph + start, startGlyph + i))) + return false; + + start = i + 1; + } + if (start != count) + if (unlikely (!glyphs->add_range (startGlyph + start, startGlyph + count))) + return false; + + return true; + } + + template <typename set_t> + bool collect_class (set_t *glyphs, unsigned klass) const + { + unsigned int count = classValue.len; + for (unsigned int i = 0; i < count; i++) + if (classValue[i] == klass) glyphs->add (startGlyph + i); + return true; + } + + bool intersects (const hb_set_t *glyphs) const + { + hb_codepoint_t start = startGlyph; + hb_codepoint_t end = startGlyph + classValue.len; + for (hb_codepoint_t iter = startGlyph - 1; + glyphs->next (&iter) && iter < end;) + if (classValue[iter - start]) return true; + return false; + } + bool intersects_class (const hb_set_t *glyphs, uint16_t klass) const + { + unsigned int count = classValue.len; + if (klass == 0) + { + /* Match if there's any glyph that is not listed! */ + hb_codepoint_t g = HB_SET_VALUE_INVALID; + if (!glyphs->next (&g)) return false; + if (g < startGlyph) return true; + g = startGlyph + count - 1; + if (glyphs->next (&g)) return true; + /* Fall through. */ + } + /* TODO Speed up, using set overlap first? */ + /* TODO(iter) Rewrite as dagger. */ + const HBUINT16 *arr = classValue.arrayZ; + for (unsigned int i = 0; i < count; i++) + if (arr[i] == klass && glyphs->has (startGlyph + i)) + return true; + return false; + } + + void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const + { + unsigned count = classValue.len; + if (klass == 0) + { + unsigned start_glyph = startGlyph; + for (uint32_t g = HB_SET_VALUE_INVALID; + glyphs->next (&g) && g < start_glyph;) + intersect_glyphs->add (g); + + for (uint32_t g = startGlyph + count - 1; + glyphs-> next (&g);) + intersect_glyphs->add (g); + + return; + } + + for (unsigned i = 0; i < count; i++) + if (classValue[i] == klass && glyphs->has (startGlyph + i)) + intersect_glyphs->add (startGlyph + i); + +#if 0 + /* The following implementation is faster asymptotically, but slower + * in practice. */ + unsigned start_glyph = startGlyph; + unsigned end_glyph = start_glyph + count; + for (unsigned g = startGlyph - 1; + glyphs->next (&g) && g < end_glyph;) + if (classValue.arrayZ[g - start_glyph] == klass) + intersect_glyphs->add (g); +#endif + } + + void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const + { + if (glyphs->is_empty ()) return; + hb_codepoint_t end_glyph = startGlyph + classValue.len - 1; + if (glyphs->get_min () < startGlyph || + glyphs->get_max () > end_glyph) + intersect_classes->add (0); + + for (const auto& _ : + hb_enumerate (classValue)) + { + hb_codepoint_t g = startGlyph + _.first; + if (glyphs->has (g)) + intersect_classes->add (_.second); + } + } + + protected: + HBUINT16 classFormat; /* Format identifier--format = 1 */ + typename Types::HBGlyphID + startGlyph; /* First GlyphID of the classValueArray */ + typename Types::template ArrayOf<HBUINT16> + classValue; /* Array of Class Values--one per GlyphID */ + public: + DEFINE_SIZE_ARRAY (2 + 2 * Types::size, classValue); +}; + +template <typename Types> +struct ClassDefFormat2_4 +{ + friend struct ClassDef; + + private: + unsigned int get_class (hb_codepoint_t glyph_id) const + { + return rangeRecord.bsearch (glyph_id).value; + } + + 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<typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> + bool serialize (hb_serialize_context_t *c, + Iterator it) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + if (unlikely (!it)) + { + classFormat = 2; + rangeRecord.len = 0; + return_trace (true); + } + + unsigned unsorted = false; + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = (*it).first; + unsigned prev_klass = (*it).second; + + RangeRecord<Types> range_rec; + range_rec.first = prev_gid; + range_rec.last = prev_gid; + range_rec.value = prev_klass; + + auto *record = c->copy (range_rec); + if (unlikely (!record)) return_trace (false); + + for (const auto gid_klass_pair : + (++it)) + { + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) + { + + if (unlikely (cur_gid < prev_gid)) + unsorted = true; + + if (unlikely (!record)) break; + record->last = prev_gid; + num_ranges++; + + range_rec.first = cur_gid; + range_rec.last = cur_gid; + range_rec.value = cur_klass; + + record = c->copy (range_rec); + } + + prev_klass = cur_klass; + prev_gid = cur_gid; + } + + if (unlikely (c->in_error ())) return_trace (false); + + if (likely (record)) record->last = prev_gid; + rangeRecord.len = num_ranges; + + if (unlikely (unsorted)) + rangeRecord.as_array ().qsort (RangeRecord<Types>::cmp_range); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/, + bool keep_empty_table = true, + bool use_class_zero = true, + const Coverage* glyph_filter = nullptr) const + { + TRACE_SUBSET (this); + const hb_map_t &glyph_map = c->plan->glyph_map_gsub; + const hb_set_t &glyph_set = *c->plan->glyphset_gsub (); + + hb_sorted_vector_t<hb_codepoint_pair_t> glyph_and_klass; + hb_set_t orig_klasses; + + if (glyph_set.get_population () * hb_bit_storage ((unsigned) rangeRecord.len) / 2 + < get_population ()) + { + for (hb_codepoint_t g : glyph_set) + { + unsigned klass = get_class (g); + if (!klass) continue; + hb_codepoint_t new_gid = glyph_map[g]; + if (new_gid == HB_MAP_VALUE_INVALID) continue; + if (glyph_filter && !glyph_filter->has (g)) continue; + glyph_and_klass.push (hb_pair (new_gid, klass)); + orig_klasses.add (klass); + } + } + else + { + unsigned num_source_glyphs = c->plan->source->get_num_glyphs (); + for (auto &range : rangeRecord) + { + unsigned klass = range.value; + if (!klass) continue; + hb_codepoint_t start = range.first; + hb_codepoint_t end = hb_min (range.last + 1, num_source_glyphs); + for (hb_codepoint_t g = start; g < end; g++) + { + hb_codepoint_t new_gid = glyph_map[g]; + if (new_gid == HB_MAP_VALUE_INVALID) continue; + if (glyph_filter && !glyph_filter->has (g)) continue; + + glyph_and_klass.push (hb_pair (new_gid, klass)); + orig_klasses.add (klass); + } + } + } + + const hb_set_t& glyphset = *c->plan->glyphset_gsub (); + unsigned glyph_count = glyph_filter + ? hb_len (hb_iter (glyphset) | hb_filter (glyph_filter)) + : glyph_map.get_population (); + use_class_zero = use_class_zero && glyph_count <= glyph_and_klass.length; + if (!ClassDef_remap_and_serialize (c->serializer, + orig_klasses, + use_class_zero, + glyph_and_klass, + klass_map)) + return_trace (false); + return_trace (keep_empty_table || (bool) glyph_and_klass); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rangeRecord.sanitize (c)); + } + + unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ } + + template <typename set_t> + bool collect_coverage (set_t *glyphs) const + { + for (auto &range : rangeRecord) + if (range.value) + if (unlikely (!range.collect_coverage (glyphs))) + return false; + return true; + } + + template <typename set_t> + bool collect_class (set_t *glyphs, unsigned int klass) const + { + for (auto &range : rangeRecord) + { + if (range.value == klass) + if (unlikely (!range.collect_coverage (glyphs))) + return false; + } + return 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_class (g)) + return true; + return false; + } + + return hb_any (+ hb_iter (rangeRecord) + | hb_map ([glyphs] (const RangeRecord<Types> &range) { return range.intersects (*glyphs) && range.value; })); + } + bool intersects_class (const hb_set_t *glyphs, uint16_t klass) const + { + if (klass == 0) + { + /* Match if there's any glyph that is not listed! */ + hb_codepoint_t g = HB_SET_VALUE_INVALID; + hb_codepoint_t last = HB_SET_VALUE_INVALID; + auto it = hb_iter (rangeRecord); + for (auto &range : it) + { + if (it->first == last + 1) + { + it++; + continue; + } + + if (!glyphs->next (&g)) + break; + if (g < range.first) + return true; + g = range.last; + last = g; + } + if (g != HB_SET_VALUE_INVALID && glyphs->next (&g)) + return true; + /* Fall through. */ + } + for (const auto &range : rangeRecord) + if (range.value == klass && range.intersects (*glyphs)) + return true; + return false; + } + + void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const + { + if (klass == 0) + { + hb_codepoint_t g = HB_SET_VALUE_INVALID; + for (auto &range : rangeRecord) + { + if (!glyphs->next (&g)) + goto done; + while (g < range.first) + { + intersect_glyphs->add (g); + if (!glyphs->next (&g)) + goto done; + } + g = range.last; + } + while (glyphs->next (&g)) + intersect_glyphs->add (g); + done: + + return; + } + + unsigned count = rangeRecord.len; + if (count > glyphs->get_population () * hb_bit_storage (count) * 8) + { + for (auto g : *glyphs) + { + unsigned i; + if (rangeRecord.as_array ().bfind (g, &i) && + rangeRecord.arrayZ[i].value == klass) + intersect_glyphs->add (g); + } + return; + } + + for (auto &range : rangeRecord) + { + if (range.value != klass) continue; + + unsigned end = range.last + 1; + for (hb_codepoint_t g = range.first - 1; + glyphs->next (&g) && g < end;) + intersect_glyphs->add (g); + } + } + + void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const + { + if (glyphs->is_empty ()) return; + + hb_codepoint_t g = HB_SET_VALUE_INVALID; + for (auto &range : rangeRecord) + { + if (!glyphs->next (&g)) + break; + if (g < range.first) + { + intersect_classes->add (0); + break; + } + g = range.last; + } + if (g != HB_SET_VALUE_INVALID && glyphs->next (&g)) + intersect_classes->add (0); + + for (const auto& range : rangeRecord) + if (range.intersects (*glyphs)) + intersect_classes->add (range.value); + } + + protected: + HBUINT16 classFormat; /* Format identifier--format = 2 */ + typename Types::template SortedArrayOf<RangeRecord<Types>> + rangeRecord; /* Array of glyph ranges--ordered by + * Start GlyphID */ + public: + DEFINE_SIZE_ARRAY (2 + Types::size, rangeRecord); +}; + +struct ClassDef +{ + /* Has interface. */ + unsigned operator [] (hb_codepoint_t k) const { return get (k); } + bool has (hb_codepoint_t k) const { return (*this)[k]; } + /* Projection. */ + hb_codepoint_t operator () (hb_codepoint_t k) const { return get (k); } + + unsigned int get (hb_codepoint_t k) const { return get_class (k); } + unsigned int get_class (hb_codepoint_t glyph_id) const + { + switch (u.format) { + case 1: return u.format1.get_class (glyph_id); + case 2: return u.format2.get_class (glyph_id); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.get_class (glyph_id); + case 4: return u.format4.get_class (glyph_id); +#endif + default:return 0; + } + } + + 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<typename Iterator, + hb_requires (hb_is_sorted_source_of (Iterator, hb_codepoint_t))> + bool serialize (hb_serialize_context_t *c, Iterator it_with_class_zero) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + + auto it = + it_with_class_zero | hb_filter (hb_second); + + unsigned format = 2; + hb_codepoint_t glyph_max = 0; + if (likely (it)) + { + hb_codepoint_t glyph_min = (*it).first; + glyph_max = glyph_min; + + unsigned num_glyphs = 0; + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = glyph_min; + unsigned prev_klass = (*it).second; + + for (const auto gid_klass_pair : it) + { + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + num_glyphs++; + if (cur_gid == glyph_min) continue; + if (cur_gid > glyph_max) glyph_max = cur_gid; + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) + num_ranges++; + + prev_gid = cur_gid; + prev_klass = cur_klass; + } + + if (num_glyphs && 1 + (glyph_max - glyph_min + 1) <= num_ranges * 3) + format = 1; + } + +#ifndef HB_NO_BEYOND_64K + if (glyph_max > 0xFFFFu) + u.format += 2; + if (unlikely (glyph_max > 0xFFFFFFu)) +#else + if (unlikely (glyph_max > 0xFFFFu)) +#endif + { + c->check_success (false, HB_SERIALIZE_ERROR_INT_OVERFLOW); + return_trace (false); + } + + u.format = format; + + switch (u.format) + { + case 1: return_trace (u.format1.serialize (c, it)); + case 2: return_trace (u.format2.serialize (c, it)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.serialize (c, it)); + case 4: return_trace (u.format4.serialize (c, it)); +#endif + default:return_trace (false); + } + } + + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/, + bool keep_empty_table = true, + bool use_class_zero = true, + const Coverage* glyph_filter = nullptr) const + { + TRACE_SUBSET (this); + switch (u.format) { + case 1: return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); + case 2: return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); +#ifndef HB_NO_BEYOND_64K + case 3: return_trace (u.format3.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); + case 4: return_trace (u.format4.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); +#endif + default:return_trace (false); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + hb_barrier (); + 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); + } + } + + unsigned cost () const + { + switch (u.format) { + case 1: return u.format1.cost (); + case 2: return u.format2.cost (); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.cost (); + case 4: return u.format4.cost (); +#endif + default:return 0u; + } + } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template <typename set_t> + 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; + } + } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template <typename set_t> + bool collect_class (set_t *glyphs, unsigned int klass) const + { + switch (u.format) { + case 1: return u.format1.collect_class (glyphs, klass); + case 2: return u.format2.collect_class (glyphs, klass); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.collect_class (glyphs, klass); + case 4: return u.format4.collect_class (glyphs, klass); +#endif + default:return false; + } + } + + 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_class (const hb_set_t *glyphs, unsigned int klass) const + { + switch (u.format) { + case 1: return u.format1.intersects_class (glyphs, klass); + case 2: return u.format2.intersects_class (glyphs, klass); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersects_class (glyphs, klass); + case 4: return u.format4.intersects_class (glyphs, klass); +#endif + default:return false; + } + } + + void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const + { + switch (u.format) { + case 1: return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs); + case 2: return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersected_class_glyphs (glyphs, klass, intersect_glyphs); + case 4: return u.format4.intersected_class_glyphs (glyphs, klass, intersect_glyphs); +#endif + default:return; + } + } + + void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const + { + switch (u.format) { + case 1: return u.format1.intersected_classes (glyphs, intersect_classes); + case 2: return u.format2.intersected_classes (glyphs, intersect_classes); +#ifndef HB_NO_BEYOND_64K + case 3: return u.format3.intersected_classes (glyphs, intersect_classes); + case 4: return u.format4.intersected_classes (glyphs, intersect_classes); +#endif + default:return; + } + } + + + protected: + union { + HBUINT16 format; /* Format identifier */ + ClassDefFormat1_3<SmallTypes> format1; + ClassDefFormat2_4<SmallTypes> format2; +#ifndef HB_NO_BEYOND_64K + ClassDefFormat1_3<MediumTypes>format3; + ClassDefFormat2_4<MediumTypes>format4; +#endif + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +template<typename Iterator> +static inline bool ClassDef_serialize (hb_serialize_context_t *c, + Iterator it) +{ return (c->start_embed<ClassDef> ()->serialize (c, it)); } + + +/* + * Item Variation Store + */ + +/* ported from fonttools (class _Encoding) */ +struct delta_row_encoding_t +{ + /* each byte represents a region, value is one of 0/1/2/4, which means bytes + * needed for this region */ + hb_vector_t<uint8_t> chars; + unsigned width = 0; + hb_vector_t<uint8_t> columns; + unsigned overhead = 0; + hb_vector_t<const hb_vector_t<int>*> items; + + delta_row_encoding_t () = default; + delta_row_encoding_t (hb_vector_t<uint8_t>&& chars_, + const hb_vector_t<int>* row = nullptr) : + delta_row_encoding_t () + + { + chars = std::move (chars_); + width = get_width (); + columns = get_columns (); + overhead = get_chars_overhead (columns); + if (row) items.push (row); + } + + bool is_empty () const + { return !items; } + + static hb_vector_t<uint8_t> get_row_chars (const hb_vector_t<int>& row) + { + hb_vector_t<uint8_t> ret; + if (!ret.alloc (row.length)) return ret; + + bool long_words = false; + + /* 0/1/2 byte encoding */ + for (int i = row.length - 1; i >= 0; i--) + { + int v = row.arrayZ[i]; + if (v == 0) + ret.push (0); + else if (v > 32767 || v < -32768) + { + long_words = true; + break; + } + else if (v > 127 || v < -128) + ret.push (2); + else + ret.push (1); + } + + if (!long_words) + return ret; + + /* redo, 0/2/4 bytes encoding */ + ret.reset (); + for (int i = row.length - 1; i >= 0; i--) + { + int v = row.arrayZ[i]; + if (v == 0) + ret.push (0); + else if (v > 32767 || v < -32768) + ret.push (4); + else + ret.push (2); + } + return ret; + } + + inline unsigned get_width () + { + unsigned ret = + hb_iter (chars) + | hb_reduce (hb_add, 0u) + ; + return ret; + } + + hb_vector_t<uint8_t> get_columns () + { + hb_vector_t<uint8_t> cols; + cols.alloc (chars.length); + for (auto v : chars) + { + uint8_t flag = v ? 1 : 0; + cols.push (flag); + } + return cols; + } + + static inline unsigned get_chars_overhead (const hb_vector_t<uint8_t>& cols) + { + unsigned c = 4 + 6; // 4 bytes for LOffset, 6 bytes for VarData header + unsigned cols_bit_count = 0; + for (auto v : cols) + if (v) cols_bit_count++; + return c + cols_bit_count * 2; + } + + unsigned get_gain () const + { + int count = items.length; + return hb_max (0, (int) overhead - count); + } + + int gain_from_merging (const delta_row_encoding_t& other_encoding) const + { + int combined_width = 0; + for (unsigned i = 0; i < chars.length; i++) + combined_width += hb_max (chars.arrayZ[i], other_encoding.chars.arrayZ[i]); + + hb_vector_t<uint8_t> combined_columns; + combined_columns.alloc (columns.length); + for (unsigned i = 0; i < columns.length; i++) + combined_columns.push (columns.arrayZ[i] | other_encoding.columns.arrayZ[i]); + + int combined_overhead = get_chars_overhead (combined_columns); + int combined_gain = (int) overhead + (int) other_encoding.overhead - combined_overhead + - (combined_width - (int) width) * items.length + - (combined_width - (int) other_encoding.width) * other_encoding.items.length; + + return combined_gain; + } + + static int cmp (const void *pa, const void *pb) + { + const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; + const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; + + int gain_a = a->get_gain (); + int gain_b = b->get_gain (); + + if (gain_a != gain_b) + return gain_a - gain_b; + + return (b->chars).as_array ().cmp ((a->chars).as_array ()); + } + + static int cmp_width (const void *pa, const void *pb) + { + const delta_row_encoding_t *a = (const delta_row_encoding_t *)pa; + const delta_row_encoding_t *b = (const delta_row_encoding_t *)pb; + + if (a->width != b->width) + return (int) a->width - (int) b->width; + + return (b->chars).as_array ().cmp ((a->chars).as_array ()); + } + + bool add_row (const hb_vector_t<int>* row) + { return items.push (row); } +}; + +struct VarRegionAxis +{ + float evaluate (int coord) const + { + int peak = peakCoord.to_int (); + if (peak == 0 || coord == peak) + return 1.f; + + int start = startCoord.to_int (), end = endCoord.to_int (); + + /* TODO Move these to sanitize(). */ + if (unlikely (start > peak || peak > end)) + return 1.f; + if (unlikely (start < 0 && end > 0 && peak != 0)) + return 1.f; + + if (coord <= start || end <= coord) + return 0.f; + + /* Interpolate */ + if (coord < peak) + return float (coord - start) / (peak - start); + else + return float (end - coord) / (end - peak); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + /* TODO Handle invalid start/peak/end configs, so we don't + * have to do that at runtime. */ + } + + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + public: + F2DOT14 startCoord; + F2DOT14 peakCoord; + F2DOT14 endCoord; + public: + DEFINE_SIZE_STATIC (6); +}; + +#define REGION_CACHE_ITEM_CACHE_INVALID 2.f + +struct VarRegionList +{ + using cache_t = float; + + float evaluate (unsigned int region_index, + const int *coords, unsigned int coord_len, + cache_t *cache = nullptr) const + { + if (unlikely (region_index >= regionCount)) + return 0.; + + float *cached_value = nullptr; + if (cache) + { + cached_value = &(cache[region_index]); + if (likely (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)) + return *cached_value; + } + + const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount); + + float v = 1.; + unsigned int count = axisCount; + for (unsigned int i = 0; i < count; i++) + { + int coord = i < coord_len ? coords[i] : 0; + float factor = axes[i].evaluate (coord); + if (factor == 0.f) + { + if (cache) + *cached_value = 0.; + return 0.; + } + v *= factor; + } + + if (cache) + *cached_value = v; + return v; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + axesZ.sanitize (c, axisCount * regionCount)); + } + + bool serialize (hb_serialize_context_t *c, + const hb_vector_t<hb_tag_t>& axis_tags, + const hb_vector_t<const hb_hashmap_t<hb_tag_t, Triple>*>& regions) + { + TRACE_SERIALIZE (this); + unsigned axis_count = axis_tags.length; + unsigned region_count = regions.length; + if (!axis_count || !region_count) return_trace (false); + if (unlikely (hb_unsigned_mul_overflows (axis_count * region_count, + VarRegionAxis::static_size))) return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); + axisCount = axis_count; + regionCount = region_count; + + for (unsigned r = 0; r < region_count; r++) + { + const auto& region = regions[r]; + for (unsigned i = 0; i < axis_count; i++) + { + hb_tag_t tag = axis_tags.arrayZ[i]; + VarRegionAxis var_region_rec; + Triple *coords; + if (region->has (tag, &coords)) + { + var_region_rec.startCoord.set_float (coords->minimum); + var_region_rec.peakCoord.set_float (coords->middle); + var_region_rec.endCoord.set_float (coords->maximum); + } + else + { + var_region_rec.startCoord.set_int (0); + var_region_rec.peakCoord.set_int (0); + var_region_rec.endCoord.set_int (0); + } + if (!var_region_rec.serialize (c)) + return_trace (false); + } + } + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_inc_bimap_t ®ion_map) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + axisCount = src->axisCount; + regionCount = region_map.get_population (); + if (unlikely (hb_unsigned_mul_overflows (axisCount * regionCount, + VarRegionAxis::static_size))) return_trace (false); + if (unlikely (!c->extend (this))) return_trace (false); + unsigned int region_count = src->regionCount; + for (unsigned int r = 0; r < regionCount; r++) + { + unsigned int backward = region_map.backward (r); + if (backward >= region_count) return_trace (false); + hb_memcpy (&axesZ[axisCount * r], &src->axesZ[axisCount * backward], VarRegionAxis::static_size * axisCount); + } + + return_trace (true); + } + + bool get_var_region (unsigned region_index, + const hb_map_t& axes_old_index_tag_map, + hb_hashmap_t<hb_tag_t, Triple>& axis_tuples /* OUT */) const + { + if (region_index >= regionCount) return false; + const VarRegionAxis* axis_region = axesZ.arrayZ + (region_index * axisCount); + for (unsigned i = 0; i < axisCount; i++) + { + hb_tag_t *axis_tag; + if (!axes_old_index_tag_map.has (i, &axis_tag)) + return false; + + float min_val = axis_region->startCoord.to_float (); + float def_val = axis_region->peakCoord.to_float (); + float max_val = axis_region->endCoord.to_float (); + + if (def_val != 0.f) + axis_tuples.set (*axis_tag, Triple (min_val, def_val, max_val)); + axis_region++; + } + return !axis_tuples.in_error (); + } + + bool get_var_regions (const hb_map_t& axes_old_index_tag_map, + hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>>& regions /* OUT */) const + { + if (!regions.alloc (regionCount)) + return false; + + for (unsigned i = 0; i < regionCount; i++) + { + hb_hashmap_t<hb_tag_t, Triple> axis_tuples; + if (!get_var_region (i, axes_old_index_tag_map, axis_tuples)) + return false; + regions.push (std::move (axis_tuples)); + } + return !regions.in_error (); + } + + unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } + + public: + HBUINT16 axisCount; + HBUINT15 regionCount; + protected: + UnsizedArrayOf<VarRegionAxis> + axesZ; + public: + DEFINE_SIZE_ARRAY (4, axesZ); +}; + +struct VarData +{ + unsigned int get_item_count () const + { return itemCount; } + + unsigned int get_region_index_count () const + { return regionIndices.len; } + + unsigned get_region_index (unsigned i) const + { return i >= regionIndices.len ? -1 : regionIndices[i]; } + + unsigned int get_row_size () const + { return (wordCount () + regionIndices.len) * (longWords () ? 2 : 1); } + + unsigned int get_size () const + { return min_size + - regionIndices.min_size + regionIndices.get_size () + + itemCount * get_row_size (); + } + + float get_delta (unsigned int inner, + const int *coords, unsigned int coord_count, + const VarRegionList ®ions, + VarRegionList::cache_t *cache = nullptr) const + { + if (unlikely (inner >= itemCount)) + return 0.; + + unsigned int count = regionIndices.len; + bool is_long = longWords (); + unsigned word_count = wordCount (); + unsigned int scount = is_long ? count : word_count; + unsigned int lcount = is_long ? word_count : 0; + + const HBUINT8 *bytes = get_delta_bytes (); + const HBUINT8 *row = bytes + inner * get_row_size (); + + float delta = 0.; + unsigned int i = 0; + + const HBINT32 *lcursor = reinterpret_cast<const HBINT32 *> (row); + for (; i < lcount; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + delta += scalar * *lcursor++; + } + const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (lcursor); + for (; i < scount; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + delta += scalar * *scursor++; + } + const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor); + for (; i < count; i++) + { + float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache); + delta += scalar * *bcursor++; + } + + return delta; + } + + void get_region_scalars (const int *coords, unsigned int coord_count, + const VarRegionList ®ions, + float *scalars /*OUT */, + unsigned int num_scalars) const + { + unsigned count = hb_min (num_scalars, regionIndices.len); + for (unsigned int i = 0; i < count; i++) + scalars[i] = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count); + for (unsigned int i = count; i < num_scalars; i++) + scalars[i] = 0.f; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + regionIndices.sanitize (c) && + hb_barrier () && + wordCount () <= regionIndices.len && + c->check_range (get_delta_bytes (), + itemCount, + get_row_size ())); + } + + bool serialize (hb_serialize_context_t *c, + bool has_long, + const hb_vector_t<const hb_vector_t<int>*>& rows) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + unsigned row_count = rows.length; + itemCount = row_count; + + int min_threshold = has_long ? -65536 : -128; + int max_threshold = has_long ? +65535 : +127; + enum delta_size_t { kZero=0, kNonWord, kWord }; + hb_vector_t<delta_size_t> delta_sz; + unsigned num_regions = rows[0]->length; + if (!delta_sz.resize (num_regions)) + return_trace (false); + + unsigned word_count = 0; + for (unsigned r = 0; r < num_regions; r++) + { + for (unsigned i = 0; i < row_count; i++) + { + int delta = rows[i]->arrayZ[r]; + if (delta < min_threshold || delta > max_threshold) + { + delta_sz[r] = kWord; + word_count++; + break; + } + else if (delta != 0) + { + delta_sz[r] = kNonWord; + } + } + } + + /* reorder regions: words and then non-words*/ + unsigned word_index = 0; + unsigned non_word_index = word_count; + hb_map_t ri_map; + for (unsigned r = 0; r < num_regions; r++) + { + if (!delta_sz[r]) continue; + unsigned new_r = (delta_sz[r] == kWord)? word_index++ : non_word_index++; + if (!ri_map.set (new_r, r)) + return_trace (false); + } + + wordSizeCount = word_count | (has_long ? 0x8000u /* LONG_WORDS */ : 0); + + unsigned ri_count = ri_map.get_population (); + regionIndices.len = ri_count; + if (unlikely (!c->extend (this))) return_trace (false); + + for (unsigned r = 0; r < ri_count; r++) + { + hb_codepoint_t *idx; + if (!ri_map.has (r, &idx)) + return_trace (false); + regionIndices[r] = *idx; + } + + HBUINT8 *delta_bytes = get_delta_bytes (); + unsigned row_size = get_row_size (); + for (unsigned int i = 0; i < row_count; i++) + { + for (unsigned int r = 0; r < ri_count; r++) + { + int delta = rows[i]->arrayZ[ri_map[r]]; + set_item_delta_fast (i, r, delta, delta_bytes, row_size); + } + } + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + const VarData *src, + const hb_inc_bimap_t &inner_map, + const hb_inc_bimap_t ®ion_map) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->extend_min (this))) return_trace (false); + itemCount = inner_map.get_next_value (); + + /* Optimize word count */ + unsigned ri_count = src->regionIndices.len; + enum delta_size_t { kZero=0, kNonWord, kWord }; + hb_vector_t<delta_size_t> delta_sz; + hb_vector_t<unsigned int> ri_map; /* maps new index to old index */ + delta_sz.resize (ri_count); + ri_map.resize (ri_count); + unsigned int new_word_count = 0; + unsigned int r; + + const HBUINT8 *src_delta_bytes = src->get_delta_bytes (); + unsigned src_row_size = src->get_row_size (); + unsigned src_word_count = src->wordCount (); + bool src_long_words = src->longWords (); + + bool has_long = false; + if (src_long_words) + { + for (r = 0; r < src_word_count; r++) + { + for (unsigned old_gid : inner_map.keys()) + { + int32_t delta = src->get_item_delta_fast (old_gid, r, src_delta_bytes, src_row_size); + if (delta < -65536 || 65535 < delta) + { + has_long = true; + break; + } + } + } + } + + signed min_threshold = has_long ? -65536 : -128; + signed max_threshold = has_long ? +65535 : +127; + for (r = 0; r < ri_count; r++) + { + bool short_circuit = src_long_words == has_long && src_word_count <= r; + + delta_sz[r] = kZero; + for (unsigned old_gid : inner_map.keys()) + { + int32_t delta = src->get_item_delta_fast (old_gid, r, src_delta_bytes, src_row_size); + if (delta < min_threshold || max_threshold < delta) + { + delta_sz[r] = kWord; + new_word_count++; + break; + } + else if (delta != 0) + { + delta_sz[r] = kNonWord; + if (short_circuit) + break; + } + } + } + + unsigned int word_index = 0; + unsigned int non_word_index = new_word_count; + unsigned int new_ri_count = 0; + for (r = 0; r < ri_count; r++) + if (delta_sz[r]) + { + unsigned new_r = (delta_sz[r] == kWord)? word_index++ : non_word_index++; + ri_map[new_r] = r; + new_ri_count++; + } + + wordSizeCount = new_word_count | (has_long ? 0x8000u /* LONG_WORDS */ : 0); + + regionIndices.len = new_ri_count; + + if (unlikely (!c->extend (this))) return_trace (false); + + for (r = 0; r < new_ri_count; r++) + regionIndices[r] = region_map[src->regionIndices[ri_map[r]]]; + + HBUINT8 *delta_bytes = get_delta_bytes (); + unsigned row_size = get_row_size (); + unsigned count = itemCount; + for (unsigned int i = 0; i < count; i++) + { + unsigned int old = inner_map.backward (i); + for (unsigned int r = 0; r < new_ri_count; r++) + set_item_delta_fast (i, r, + src->get_item_delta_fast (old, ri_map[r], + src_delta_bytes, src_row_size), + delta_bytes, row_size); + } + + return_trace (true); + } + + void collect_region_refs (hb_set_t ®ion_indices, const hb_inc_bimap_t &inner_map) const + { + const HBUINT8 *delta_bytes = get_delta_bytes (); + unsigned row_size = get_row_size (); + + for (unsigned int r = 0; r < regionIndices.len; r++) + { + unsigned int region = regionIndices.arrayZ[r]; + if (region_indices.has (region)) continue; + for (hb_codepoint_t old_gid : inner_map.keys()) + if (get_item_delta_fast (old_gid, r, delta_bytes, row_size) != 0) + { + region_indices.add (region); + break; + } + } + } + + public: + const HBUINT8 *get_delta_bytes () const + { return &StructAfter<HBUINT8> (regionIndices); } + + protected: + HBUINT8 *get_delta_bytes () + { return &StructAfter<HBUINT8> (regionIndices); } + + public: + int32_t get_item_delta_fast (unsigned int item, unsigned int region, + const HBUINT8 *delta_bytes, unsigned row_size) const + { + if (unlikely (item >= itemCount || region >= regionIndices.len)) return 0; + + const HBINT8 *p = (const HBINT8 *) delta_bytes + item * row_size; + unsigned word_count = wordCount (); + bool is_long = longWords (); + if (is_long) + { + if (region < word_count) + return ((const HBINT32 *) p)[region]; + else + return ((const HBINT16 *)(p + HBINT32::static_size * word_count))[region - word_count]; + } + else + { + if (region < word_count) + return ((const HBINT16 *) p)[region]; + else + return (p + HBINT16::static_size * word_count)[region - word_count]; + } + } + int32_t get_item_delta (unsigned int item, unsigned int region) const + { + return get_item_delta_fast (item, region, + get_delta_bytes (), + get_row_size ()); + } + + protected: + void set_item_delta_fast (unsigned int item, unsigned int region, int32_t delta, + HBUINT8 *delta_bytes, unsigned row_size) + { + HBINT8 *p = (HBINT8 *) delta_bytes + item * row_size; + unsigned word_count = wordCount (); + bool is_long = longWords (); + if (is_long) + { + if (region < word_count) + ((HBINT32 *) p)[region] = delta; + else + ((HBINT16 *)(p + HBINT32::static_size * word_count))[region - word_count] = delta; + } + else + { + if (region < word_count) + ((HBINT16 *) p)[region] = delta; + else + (p + HBINT16::static_size * word_count)[region - word_count] = delta; + } + } + void set_item_delta (unsigned int item, unsigned int region, int32_t delta) + { + set_item_delta_fast (item, region, delta, + get_delta_bytes (), + get_row_size ()); + } + + bool longWords () const { return wordSizeCount & 0x8000u /* LONG_WORDS */; } + unsigned wordCount () const { return wordSizeCount & 0x7FFFu /* WORD_DELTA_COUNT_MASK */; } + + protected: + HBUINT16 itemCount; + HBUINT16 wordSizeCount; + Array16Of<HBUINT16> regionIndices; +/*UnsizedArrayOf<HBUINT8>bytesX;*/ + public: + DEFINE_SIZE_ARRAY (6, regionIndices); +}; + +struct VariationStore +{ + friend struct item_variations_t; + using cache_t = VarRegionList::cache_t; + + cache_t *create_cache () const + { +#ifdef HB_NO_VAR + return nullptr; +#endif + auto &r = this+regions; + unsigned count = r.regionCount; + + float *cache = (float *) hb_malloc (sizeof (float) * count); + if (unlikely (!cache)) return nullptr; + + for (unsigned i = 0; i < count; i++) + cache[i] = REGION_CACHE_ITEM_CACHE_INVALID; + + return cache; + } + + static void destroy_cache (cache_t *cache) { hb_free (cache); } + + private: + float get_delta (unsigned int outer, unsigned int inner, + const int *coords, unsigned int coord_count, + VarRegionList::cache_t *cache = nullptr) const + { +#ifdef HB_NO_VAR + return 0.f; +#endif + + if (unlikely (outer >= dataSets.len)) + return 0.f; + + return (this+dataSets[outer]).get_delta (inner, + coords, coord_count, + this+regions, + cache); + } + + public: + float get_delta (unsigned int index, + const int *coords, unsigned int coord_count, + VarRegionList::cache_t *cache = nullptr) const + { + unsigned int outer = index >> 16; + unsigned int inner = index & 0xFFFF; + return get_delta (outer, inner, coords, coord_count, cache); + } + float get_delta (unsigned int index, + hb_array_t<int> coords, + VarRegionList::cache_t *cache = nullptr) const + { + return get_delta (index, + coords.arrayZ, coords.length, + cache); + } + + bool sanitize (hb_sanitize_context_t *c) const + { +#ifdef HB_NO_VAR + return true; +#endif + + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + format == 1 && + regions.sanitize (c, this) && + dataSets.sanitize (c, this)); + } + + bool serialize (hb_serialize_context_t *c, + bool has_long, + const hb_vector_t<hb_tag_t>& axis_tags, + const hb_vector_t<const hb_hashmap_t<hb_tag_t, Triple>*>& region_list, + const hb_vector_t<delta_row_encoding_t>& vardata_encodings) + { + TRACE_SERIALIZE (this); +#ifdef HB_NO_VAR + return_trace (false); +#endif + if (unlikely (!c->extend_min (this))) return_trace (false); + + format = 1; + if (!regions.serialize_serialize (c, axis_tags, region_list)) + return_trace (false); + + unsigned num_var_data = vardata_encodings.length; + if (!num_var_data) return_trace (false); + if (unlikely (!c->check_assign (dataSets.len, num_var_data, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); + + if (unlikely (!c->extend (dataSets))) return_trace (false); + for (unsigned i = 0; i < num_var_data; i++) + if (!dataSets[i].serialize_serialize (c, has_long, vardata_encodings[i].items)) + return_trace (false); + + return_trace (true); + } + + bool serialize (hb_serialize_context_t *c, + const VariationStore *src, + const hb_array_t <const hb_inc_bimap_t> &inner_maps) + { + TRACE_SERIALIZE (this); +#ifdef HB_NO_VAR + return_trace (false); +#endif + + if (unlikely (!c->extend_min (this))) return_trace (false); + + unsigned int set_count = 0; + for (unsigned int i = 0; i < inner_maps.length; i++) + if (inner_maps[i].get_population ()) + set_count++; + + format = 1; + + const auto &src_regions = src+src->regions; + + hb_set_t region_indices; + for (unsigned int i = 0; i < inner_maps.length; i++) + (src+src->dataSets[i]).collect_region_refs (region_indices, inner_maps[i]); + + if (region_indices.in_error ()) + return_trace (false); + + region_indices.del_range ((src_regions).regionCount, hb_set_t::INVALID); + + /* TODO use constructor when our data-structures support that. */ + hb_inc_bimap_t region_map; + + hb_iter (region_indices) + | hb_apply ([®ion_map] (unsigned _) { region_map.add(_); }) + ; + if (region_map.in_error()) + return_trace (false); + + if (unlikely (!regions.serialize_serialize (c, &src_regions, region_map))) + return_trace (false); + + dataSets.len = set_count; + if (unlikely (!c->extend (dataSets))) return_trace (false); + + /* TODO: The following code could be simplified when + * List16OfOffset16To::subset () can take a custom param to be passed to VarData::serialize () */ + unsigned int set_index = 0; + for (unsigned int i = 0; i < inner_maps.length; i++) + { + if (!inner_maps[i].get_population ()) continue; + if (unlikely (!dataSets[set_index++] + .serialize_serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map))) + return_trace (false); + } + + return_trace (true); + } + + VariationStore *copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!out)) return_trace (nullptr); + + hb_vector_t <hb_inc_bimap_t> inner_maps; + unsigned count = dataSets.len; + for (unsigned i = 0; i < count; i++) + { + hb_inc_bimap_t *map = inner_maps.push (); + auto &data = this+dataSets[i]; + + unsigned itemCount = data.get_item_count (); + for (unsigned j = 0; j < itemCount; j++) + map->add (j); + } + + if (unlikely (!out->serialize (c, this, inner_maps))) return_trace (nullptr); + + return_trace (out); + } + + bool subset (hb_subset_context_t *c, const hb_array_t<const hb_inc_bimap_t> &inner_maps) const + { + TRACE_SUBSET (this); +#ifdef HB_NO_VAR + return_trace (false); +#endif + + VariationStore *varstore_prime = c->serializer->start_embed<VariationStore> (); + if (unlikely (!varstore_prime)) return_trace (false); + + varstore_prime->serialize (c->serializer, this, inner_maps); + + return_trace ( + !c->serializer->in_error() + && varstore_prime->dataSets); + } + + unsigned int get_region_index_count (unsigned int major) const + { +#ifdef HB_NO_VAR + return 0; +#endif + return (this+dataSets[major]).get_region_index_count (); + } + + void get_region_scalars (unsigned int major, + const int *coords, unsigned int coord_count, + float *scalars /*OUT*/, + unsigned int num_scalars) const + { +#ifdef HB_NO_VAR + for (unsigned i = 0; i < num_scalars; i++) + scalars[i] = 0.f; + return; +#endif + + (this+dataSets[major]).get_region_scalars (coords, coord_count, + this+regions, + &scalars[0], num_scalars); + } + + unsigned int get_sub_table_count () const + { +#ifdef HB_NO_VAR + return 0; +#endif + return dataSets.len; + } + + const VarData& get_sub_table (unsigned i) const + { +#ifdef HB_NO_VAR + return Null (VarData); +#endif + return this+dataSets[i]; + } + + const VarRegionList& get_region_list () const + { +#ifdef HB_NO_VAR + return Null (VarRegionList); +#endif + return this+regions; + } + + protected: + HBUINT16 format; + Offset32To<VarRegionList> regions; + Array16OfOffset32To<VarData> dataSets; + public: + DEFINE_SIZE_ARRAY_SIZED (8, dataSets); +}; + +#undef REGION_CACHE_ITEM_CACHE_INVALID + +/* + * Feature Variations + */ +enum Cond_with_Var_flag_t +{ + KEEP_COND_WITH_VAR = 0, + KEEP_RECORD_WITH_VAR = 1, + DROP_COND_WITH_VAR = 2, + DROP_RECORD_WITH_VAR = 3, +}; + +struct ConditionFormat1 +{ + friend struct Condition; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + const hb_map_t *index_map = &c->plan->axes_index_map; + if (index_map->is_empty ()) return_trace (true); + + const hb_map_t& axes_old_index_tag_map = c->plan->axes_old_index_tag_map; + hb_codepoint_t *axis_tag; + if (!axes_old_index_tag_map.has (axisIndex, &axis_tag) || + !index_map->has (axisIndex)) + return_trace (false); + + const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location = c->plan->axes_location; + Triple axis_limit{-1.f, 0.f, 1.f}; + Triple *normalized_limit; + if (normalized_axes_location.has (*axis_tag, &normalized_limit)) + axis_limit = *normalized_limit; + + const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances = c->plan->axes_triple_distances; + TripleDistances axis_triple_distances{1.f, 1.f}; + TripleDistances *triple_dists; + if (axes_triple_distances.has (*axis_tag, &triple_dists)) + axis_triple_distances = *triple_dists; + + float normalized_min = renormalizeValue (filterRangeMinValue.to_float (), axis_limit, axis_triple_distances, false); + float normalized_max = renormalizeValue (filterRangeMaxValue.to_float (), axis_limit, axis_triple_distances, false); + out->filterRangeMinValue.set_float (normalized_min); + out->filterRangeMaxValue.set_float (normalized_max); + + return_trace (c->serializer->check_assign (out->axisIndex, index_map->get (axisIndex), + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + private: + Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, + hb_map_t *condition_map /* OUT */) const + { + //invalid axis index, drop the entire record + if (!c->axes_index_tag_map->has (axisIndex)) + return DROP_RECORD_WITH_VAR; + + hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex); + + Triple axis_range (-1.f, 0.f, 1.f); + Triple *axis_limit; + bool axis_set_by_user = false; + if (c->axes_location->has (axis_tag, &axis_limit)) + { + axis_range = *axis_limit; + axis_set_by_user = true; + } + + float axis_min_val = axis_range.minimum; + float axis_default_val = axis_range.middle; + float axis_max_val = axis_range.maximum; + + float filter_min_val = filterRangeMinValue.to_float (); + float filter_max_val = filterRangeMaxValue.to_float (); + + if (axis_default_val < filter_min_val || + axis_default_val > filter_max_val) + c->apply = false; + + //condition not met, drop the entire record + if (axis_min_val > filter_max_val || axis_max_val < filter_min_val || + filter_min_val > filter_max_val) + return DROP_RECORD_WITH_VAR; + + //condition met and axis pinned, drop the condition + if (axis_set_by_user && axis_range.is_point ()) + return DROP_COND_WITH_VAR; + + if (filter_max_val != axis_max_val || filter_min_val != axis_min_val) + { + // add axisIndex->value into the hashmap so we can check if the record is + // unique with variations + int16_t int_filter_max_val = filterRangeMaxValue.to_int (); + int16_t int_filter_min_val = filterRangeMinValue.to_int (); + hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val; + + condition_map->set (axisIndex, val); + return KEEP_COND_WITH_VAR; + } + return KEEP_RECORD_WITH_VAR; + } + + bool evaluate (const int *coords, unsigned int coord_len) const + { + int coord = axisIndex < coord_len ? coords[axisIndex] : 0; + return filterRangeMinValue.to_int () <= coord && coord <= filterRangeMaxValue.to_int (); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + HBUINT16 axisIndex; + F2DOT14 filterRangeMinValue; + F2DOT14 filterRangeMaxValue; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct Condition +{ + bool evaluate (const int *coords, unsigned int coord_len) const + { + switch (u.format) { + case 1: return u.format1.evaluate (coords, coord_len); + default:return false; + } + } + + Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, + hb_map_t *condition_map /* OUT */) const + { + switch (u.format) { + case 1: return u.format1.keep_with_variations (c, condition_map); + default: c->apply = false; return KEEP_COND_WITH_VAR; + } + } + + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + hb_barrier (); + switch (u.format) { + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + ConditionFormat1 format1; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct ConditionSet +{ + bool evaluate (const int *coords, unsigned int coord_len) const + { + unsigned int count = conditions.len; + for (unsigned int i = 0; i < count; i++) + if (!(this+conditions.arrayZ[i]).evaluate (coords, coord_len)) + return false; + return true; + } + + void keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const + { + hb_map_t *condition_map = hb_map_create (); + if (unlikely (!condition_map)) return; + hb::shared_ptr<hb_map_t> p {condition_map}; + + hb_set_t *cond_set = hb_set_create (); + if (unlikely (!cond_set)) return; + hb::shared_ptr<hb_set_t> s {cond_set}; + + c->apply = true; + bool should_keep = false; + unsigned num_kept_cond = 0, cond_idx = 0; + for (const auto& offset : conditions) + { + Cond_with_Var_flag_t ret = (this+offset).keep_with_variations (c, condition_map); + // condition is not met or condition out of range, drop the entire record + if (ret == DROP_RECORD_WITH_VAR) + return; + + if (ret == KEEP_COND_WITH_VAR) + { + should_keep = true; + cond_set->add (cond_idx); + num_kept_cond++; + } + + if (ret == KEEP_RECORD_WITH_VAR) + should_keep = true; + + cond_idx++; + } + + if (!should_keep) return; + + //check if condition_set is unique with variations + if (c->conditionset_map->has (p)) + //duplicate found, drop the entire record + return; + + c->conditionset_map->set (p, 1); + c->record_cond_idx_map->set (c->cur_record_idx, s); + if (should_keep && num_kept_cond == 0) + c->universal = true; + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + bool insert_catch_all) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + if (insert_catch_all) return_trace (true); + + hb_set_t *retained_cond_set = nullptr; + if (l->feature_record_cond_idx_map != nullptr) + retained_cond_set = l->feature_record_cond_idx_map->get (l->cur_feature_var_record_idx); + + unsigned int count = conditions.len; + for (unsigned int i = 0; i < count; i++) + { + if (retained_cond_set != nullptr && !retained_cond_set->has (i)) + continue; + subset_offset_array (c, out->conditions, this) (conditions[i]); + } + + return_trace (bool (out->conditions)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (conditions.sanitize (c, this)); + } + + protected: + Array16OfOffset32To<Condition> conditions; + public: + DEFINE_SIZE_ARRAY (2, conditions); +}; + +struct FeatureTableSubstitutionRecord +{ + friend struct FeatureTableSubstitution; + + void collect_lookups (const void *base, hb_set_t *lookup_indexes /* OUT */) const + { + return (base+feature).add_lookup_indexes_to (lookup_indexes); + } + + void closure_features (const void *base, + const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + if ((base+feature).intersects_lookup_indexes (lookup_indexes)) + feature_indexes->add (featureIndex); + } + + void collect_feature_substitutes_with_variations (hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, + hb_set_t& catch_all_record_feature_idxes, + const hb_set_t *feature_indices, + const void *base) const + { + if (feature_indices->has (featureIndex)) + { + feature_substitutes_map->set (featureIndex, &(base+feature)); + catch_all_record_feature_idxes.add (featureIndex); + } + } + + bool serialize (hb_subset_layout_context_t *c, + unsigned feature_index, + const Feature *f, const Tag *tag) + { + TRACE_SERIALIZE (this); + hb_serialize_context_t *s = c->subset_context->serializer; + if (unlikely (!s->extend_min (this))) return_trace (false); + + uint32_t *new_feature_idx; + if (!c->feature_index_map->has (feature_index, &new_feature_idx)) + return_trace (false); + + if (!s->check_assign (featureIndex, *new_feature_idx, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + s->push (); + bool ret = f->subset (c->subset_context, c, tag); + if (ret) s->add_link (feature, s->pop_pack ()); + else s->pop_discard (); + + return_trace (ret); + } + + bool subset (hb_subset_layout_context_t *c, const void *base) const + { + TRACE_SUBSET (this); + uint32_t *new_feature_index; + if (!c->feature_index_map->has (featureIndex, &new_feature_index)) + return_trace (false); + + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + out->featureIndex = *new_feature_index; + return_trace (out->feature.serialize_subset (c->subset_context, feature, base, c)); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && feature.sanitize (c, base)); + } + + protected: + HBUINT16 featureIndex; + Offset32To<Feature> feature; + public: + DEFINE_SIZE_STATIC (6); +}; + +struct FeatureTableSubstitution +{ + const Feature *find_substitute (unsigned int feature_index) const + { + unsigned int count = substitutions.len; + for (unsigned int i = 0; i < count; i++) + { + const FeatureTableSubstitutionRecord &record = substitutions.arrayZ[i]; + if (record.featureIndex == feature_index) + return &(this+record.feature); + } + return nullptr; + } + + void collect_lookups (const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + + hb_iter (substitutions) + | hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex) + | hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r) + { r.collect_lookups (this, lookup_indexes); }) + ; + } + + void closure_features (const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + for (const FeatureTableSubstitutionRecord& record : substitutions) + record.closure_features (this, lookup_indexes, feature_indexes); + } + + bool intersects_features (const hb_map_t *feature_index_map) const + { + for (const FeatureTableSubstitutionRecord& record : substitutions) + { + if (feature_index_map->has (record.featureIndex)) return true; + } + return false; + } + + void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const + { + for (const FeatureTableSubstitutionRecord& record : substitutions) + record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, + c->catch_all_record_feature_idxes, + c->feature_indices, this); + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l, + bool insert_catch_all) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version.major = version.major; + out->version.minor = version.minor; + + if (insert_catch_all) + { + for (unsigned feature_index : *(l->catch_all_record_feature_idxes)) + { + hb_pair_t<const void*, const void*> *p; + if (!l->feature_idx_tag_map->has (feature_index, &p)) + return_trace (false); + auto *o = out->substitutions.serialize_append (c->serializer); + if (!o->serialize (l, feature_index, + reinterpret_cast<const Feature*> (p->first), + reinterpret_cast<const Tag*> (p->second))) + return_trace (false); + } + return_trace (true); + } + + + substitutions.iter () + | hb_apply (subset_record_array (l, &(out->substitutions), this)) + ; + + return_trace (bool (out->substitutions)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + hb_barrier () && + likely (version.major == 1) && + substitutions.sanitize (c, this)); + } + + protected: + FixedVersion<> version; /* Version--0x00010000u */ + Array16Of<FeatureTableSubstitutionRecord> + substitutions; + public: + DEFINE_SIZE_ARRAY (6, substitutions); +}; + +struct FeatureVariationRecord +{ + friend struct FeatureVariations; + + void collect_lookups (const void *base, + const hb_set_t *feature_indexes, + hb_set_t *lookup_indexes /* OUT */) const + { + return (base+substitutions).collect_lookups (feature_indexes, lookup_indexes); + } + + void closure_features (const void *base, + const hb_map_t *lookup_indexes, + hb_set_t *feature_indexes /* OUT */) const + { + (base+substitutions).closure_features (lookup_indexes, feature_indexes); + } + + bool intersects_features (const void *base, const hb_map_t *feature_index_map) const + { + return (base+substitutions).intersects_features (feature_index_map); + } + + void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, + const void *base) const + { + (base+conditions).keep_with_variations (c); + if (c->apply && !c->variation_applied) + { + (base+substitutions).collect_feature_substitutes_with_variations (c); + c->variation_applied = true; // set variations only once + } + } + + bool subset (hb_subset_layout_context_t *c, const void *base, + bool insert_catch_all = false) const + { + TRACE_SUBSET (this); + auto *out = c->subset_context->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + out->conditions.serialize_subset (c->subset_context, conditions, base, c, insert_catch_all); + out->substitutions.serialize_subset (c->subset_context, substitutions, base, c, insert_catch_all); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (conditions.sanitize (c, base) && + substitutions.sanitize (c, base)); + } + + protected: + Offset32To<ConditionSet> + conditions; + Offset32To<FeatureTableSubstitution> + substitutions; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct FeatureVariations +{ + static constexpr unsigned NOT_FOUND_INDEX = 0xFFFFFFFFu; + + bool find_index (const int *coords, unsigned int coord_len, + unsigned int *index) const + { + unsigned int count = varRecords.len; + for (unsigned int i = 0; i < count; i++) + { + const FeatureVariationRecord &record = varRecords.arrayZ[i]; + if ((this+record.conditions).evaluate (coords, coord_len)) + { + *index = i; + return true; + } + } + *index = NOT_FOUND_INDEX; + return false; + } + + const Feature *find_substitute (unsigned int variations_index, + unsigned int feature_index) const + { + const FeatureVariationRecord &record = varRecords[variations_index]; + return (this+record.substitutions).find_substitute (feature_index); + } + + void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const + { + unsigned int count = varRecords.len; + for (unsigned int i = 0; i < count; i++) + { + c->cur_record_idx = i; + varRecords[i].collect_feature_substitutes_with_variations (c, this); + if (c->universal) + break; + } + if (c->universal || c->record_cond_idx_map->is_empty ()) + c->catch_all_record_feature_idxes.reset (); + } + + FeatureVariations* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (*this)); + } + + void collect_lookups (const hb_set_t *feature_indexes, + const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, + hb_set_t *lookup_indexes /* OUT */) const + { + unsigned count = varRecords.len; + for (unsigned int i = 0; i < count; i++) + { + if (feature_record_cond_idx_map && + !feature_record_cond_idx_map->has (i)) + continue; + varRecords[i].collect_lookups (this, feature_indexes, lookup_indexes); + } + } + + void closure_features (const hb_map_t *lookup_indexes, + const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, + hb_set_t *feature_indexes /* OUT */) const + { + unsigned int count = varRecords.len; + for (unsigned int i = 0; i < count; i++) + { + if (feature_record_cond_idx_map != nullptr && + !feature_record_cond_idx_map->has (i)) + continue; + varRecords[i].closure_features (this, lookup_indexes, feature_indexes); + } + } + + bool subset (hb_subset_context_t *c, + hb_subset_layout_context_t *l) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + + out->version.major = version.major; + out->version.minor = version.minor; + + int keep_up_to = -1; + for (int i = varRecords.len - 1; i >= 0; i--) { + if (varRecords[i].intersects_features (this, l->feature_index_map)) { + keep_up_to = i; + break; + } + } + + unsigned count = (unsigned) (keep_up_to + 1); + for (unsigned i = 0; i < count; i++) + { + if (l->feature_record_cond_idx_map != nullptr && + !l->feature_record_cond_idx_map->has (i)) + continue; + + l->cur_feature_var_record_idx = i; + subset_record_array (l, &(out->varRecords), this) (varRecords[i]); + } + + if (out->varRecords.len && !l->catch_all_record_feature_idxes->is_empty ()) + { + bool insert_catch_all_record = true; + subset_record_array (l, &(out->varRecords), this, insert_catch_all_record) (varRecords[0]); + } + + return_trace (bool (out->varRecords)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + hb_barrier () && + likely (version.major == 1) && + varRecords.sanitize (c, this)); + } + + protected: + FixedVersion<> version; /* Version--0x00010000u */ + Array32Of<FeatureVariationRecord> + varRecords; + public: + DEFINE_SIZE_ARRAY_SIZED (8, varRecords); +}; + + +/* + * Device Tables + */ + +struct HintingDevice +{ + friend struct Device; + + private: + + hb_position_t get_x_delta (hb_font_t *font) const + { return get_delta (font->x_ppem, font->x_scale); } + + hb_position_t get_y_delta (hb_font_t *font) const + { return get_delta (font->y_ppem, font->y_scale); } + + public: + + unsigned int get_size () const + { + unsigned int f = deltaFormat; + if (unlikely (f < 1 || f > 3 || startSize > endSize)) return 3 * HBUINT16::static_size; + return HBUINT16::static_size * (4 + ((endSize - startSize) >> (4 - f))); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && c->check_range (this, this->get_size ())); + } + + HintingDevice* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed<HintingDevice> (this)); + } + + private: + + int get_delta (unsigned int ppem, int scale) const + { + if (!ppem) return 0; + + int pixels = get_delta_pixels (ppem); + + if (!pixels) return 0; + + return (int) (pixels * (int64_t) scale / ppem); + } + int get_delta_pixels (unsigned int ppem_size) const + { + unsigned int f = deltaFormat; + if (unlikely (f < 1 || f > 3)) + return 0; + + if (ppem_size < startSize || ppem_size > endSize) + return 0; + + unsigned int s = ppem_size - startSize; + + unsigned int byte = deltaValueZ[s >> (4 - f)]; + unsigned int bits = (byte >> (16 - (((s & ((1 << (4 - f)) - 1)) + 1) << f))); + unsigned int mask = (0xFFFFu >> (16 - (1 << f))); + + int delta = bits & mask; + + if ((unsigned int) delta >= ((mask + 1) >> 1)) + delta -= mask + 1; + + return delta; + } + + protected: + HBUINT16 startSize; /* Smallest size to correct--in ppem */ + HBUINT16 endSize; /* Largest size to correct--in ppem */ + HBUINT16 deltaFormat; /* Format of DeltaValue array data: 1, 2, or 3 + * 1 Signed 2-bit value, 8 values per uint16 + * 2 Signed 4-bit value, 4 values per uint16 + * 3 Signed 8-bit value, 2 values per uint16 + */ + UnsizedArrayOf<HBUINT16> + deltaValueZ; /* Array of compressed data */ + public: + DEFINE_SIZE_ARRAY (6, deltaValueZ); +}; + +struct VariationDevice +{ + friend struct Device; + + private: + + hb_position_t get_x_delta (hb_font_t *font, + const VariationStore &store, + VariationStore::cache_t *store_cache = nullptr) const + { return font->em_scalef_x (get_delta (font, store, store_cache)); } + + hb_position_t get_y_delta (hb_font_t *font, + const VariationStore &store, + VariationStore::cache_t *store_cache = nullptr) const + { return font->em_scalef_y (get_delta (font, store, store_cache)); } + + VariationDevice* copy (hb_serialize_context_t *c, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const + { + TRACE_SERIALIZE (this); + if (!layout_variation_idx_delta_map) return_trace (nullptr); + + hb_pair_t<unsigned, int> *v; + if (!layout_variation_idx_delta_map->has (varIdx, &v)) + return_trace (nullptr); + + c->start_zerocopy (this->static_size); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + if (!c->check_assign (out->varIdx, hb_first (*v), HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (nullptr); + return_trace (out); + } + + void collect_variation_index (hb_collect_variation_indices_context_t *c) const + { c->layout_variation_indices->add (varIdx); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + private: + + float get_delta (hb_font_t *font, + const VariationStore &store, + VariationStore::cache_t *store_cache = nullptr) const + { + return store.get_delta (varIdx, font->coords, font->num_coords, (VariationStore::cache_t *) store_cache); + } + + protected: + VarIdx varIdx; + HBUINT16 deltaFormat; /* Format identifier for this table: 0x0x8000 */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct DeviceHeader +{ + protected: + HBUINT16 reserved1; + HBUINT16 reserved2; + public: + HBUINT16 format; /* Format identifier */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct Device +{ + hb_position_t get_x_delta (hb_font_t *font, + const VariationStore &store=Null (VariationStore), + VariationStore::cache_t *store_cache = nullptr) const + { + switch (u.b.format) + { +#ifndef HB_NO_HINTING + case 1: case 2: case 3: + return u.hinting.get_x_delta (font); +#endif +#ifndef HB_NO_VAR + case 0x8000: + return u.variation.get_x_delta (font, store, store_cache); +#endif + default: + return 0; + } + } + hb_position_t get_y_delta (hb_font_t *font, + const VariationStore &store=Null (VariationStore), + VariationStore::cache_t *store_cache = nullptr) const + { + switch (u.b.format) + { + case 1: case 2: case 3: +#ifndef HB_NO_HINTING + return u.hinting.get_y_delta (font); +#endif +#ifndef HB_NO_VAR + case 0x8000: + return u.variation.get_y_delta (font, store, store_cache); +#endif + default: + return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.b.format.sanitize (c)) return_trace (false); + switch (u.b.format) { +#ifndef HB_NO_HINTING + case 1: case 2: case 3: + return_trace (u.hinting.sanitize (c)); +#endif +#ifndef HB_NO_VAR + case 0x8000: + return_trace (u.variation.sanitize (c)); +#endif + default: + return_trace (true); + } + } + + Device* copy (hb_serialize_context_t *c, + const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map=nullptr) const + { + TRACE_SERIALIZE (this); + switch (u.b.format) { +#ifndef HB_NO_HINTING + case 1: + case 2: + case 3: + return_trace (reinterpret_cast<Device *> (u.hinting.copy (c))); +#endif +#ifndef HB_NO_VAR + case 0x8000: + return_trace (reinterpret_cast<Device *> (u.variation.copy (c, layout_variation_idx_delta_map))); +#endif + default: + return_trace (nullptr); + } + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { + switch (u.b.format) { +#ifndef HB_NO_HINTING + case 1: + case 2: + case 3: + return; +#endif +#ifndef HB_NO_VAR + case 0x8000: + u.variation.collect_variation_index (c); + return; +#endif + default: + return; + } + } + + unsigned get_variation_index () const + { + switch (u.b.format) { +#ifndef HB_NO_VAR + case 0x8000: + return u.variation.varIdx; +#endif + default: + return HB_OT_LAYOUT_NO_VARIATIONS_INDEX; + } + } + + protected: + union { + DeviceHeader b; + HintingDevice hinting; +#ifndef HB_NO_VAR + VariationDevice variation; +#endif + } u; + public: + DEFINE_SIZE_UNION (6, b); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh new file mode 100644 index 0000000000..c8a2cf817a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh @@ -0,0 +1,34 @@ +/* + * 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 HB_OT_LAYOUT_GDEF_TABLE_HH +#define HB_OT_LAYOUT_GDEF_TABLE_HH + +#include "OT/Layout/GDEF/GDEF.hh" + +#endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh new file mode 100644 index 0000000000..0cfa139a26 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh @@ -0,0 +1,81 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2010,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_LAYOUT_GPOS_TABLE_HH +#define HB_OT_LAYOUT_GPOS_TABLE_HH + +#include "OT/Layout/GPOS/GPOS.hh" + +namespace OT { +namespace Layout { +namespace GPOS_impl { + +// TODO(garretrieger): Move into new layout directory. +/* Out-of-class implementation for methods recursing */ +#ifndef HB_NO_OT_LAYOUT +template <typename context_t> +/*static*/ typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +{ + const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (lookup_index); + return l.dispatch (c); +} + +template <> +inline hb_closure_lookups_context_t::return_t +PosLookup::dispatch_recurse_func<hb_closure_lookups_context_t> (hb_closure_lookups_context_t *c, unsigned this_index) +{ + const PosLookup &l = c->face->table.GPOS.get_relaxed ()->table->get_lookup (this_index); + return l.closure_lookups (c, this_index); +} + +template <> +inline bool PosLookup::dispatch_recurse_func<hb_ot_apply_context_t> (hb_ot_apply_context_t *c, unsigned int lookup_index) +{ + auto *gpos = c->face->table.GPOS.get_relaxed (); + const PosLookup &l = gpos->table->get_lookup (lookup_index); + unsigned int saved_lookup_props = c->lookup_props; + unsigned int saved_lookup_index = c->lookup_index; + c->set_lookup_index (lookup_index); + c->set_lookup_props (l.get_props ()); + + bool ret = false; + auto *accel = gpos->get_accel (lookup_index); + ret = accel && accel->apply (c, l.get_subtable_count (), false); + + c->set_lookup_index (saved_lookup_index); + c->set_lookup_props (saved_lookup_props); + return ret; +} +#endif + +} /* namespace GPOS_impl */ +} /* namespace Layout */ +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh new file mode 100644 index 0000000000..fd8a68be02 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh @@ -0,0 +1,94 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2010,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_LAYOUT_GSUB_TABLE_HH +#define HB_OT_LAYOUT_GSUB_TABLE_HH + +#include "OT/Layout/GSUB/GSUB.hh" + +namespace OT { +namespace Layout { +namespace GSUB_impl { + +// TODO(garretrieger): Move into the new layout directory. +/* Out-of-class implementation for methods recursing */ + +#ifndef HB_NO_OT_LAYOUT +/*static*/ inline bool ExtensionSubst::is_reverse () const +{ + return SubstLookup::lookup_type_is_reverse (get_type ()); +} +template <typename context_t> +/*static*/ typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index) +{ + const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index); + return l.dispatch (c); +} + +/*static*/ typename hb_closure_context_t::return_t SubstLookup::closure_glyphs_recurse_func (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indices, unsigned seq_index, unsigned end_index) +{ + const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (lookup_index); + if (l.may_have_non_1to1 ()) + hb_set_add_range (covered_seq_indices, seq_index, end_index); + return l.dispatch (c); +} + +template <> +inline hb_closure_lookups_context_t::return_t +SubstLookup::dispatch_recurse_func<hb_closure_lookups_context_t> (hb_closure_lookups_context_t *c, unsigned this_index) +{ + const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->table->get_lookup (this_index); + return l.closure_lookups (c, this_index); +} + +template <> +inline bool SubstLookup::dispatch_recurse_func<hb_ot_apply_context_t> (hb_ot_apply_context_t *c, unsigned int lookup_index) +{ + auto *gsub = c->face->table.GSUB.get_relaxed (); + const SubstLookup &l = gsub->table->get_lookup (lookup_index); + unsigned int saved_lookup_props = c->lookup_props; + unsigned int saved_lookup_index = c->lookup_index; + c->set_lookup_index (lookup_index); + c->set_lookup_props (l.get_props ()); + + bool ret = false; + auto *accel = gsub->get_accel (lookup_index); + ret = accel && accel->apply (c, l.get_subtable_count (), false); + + c->set_lookup_index (saved_lookup_index); + c->set_lookup_props (saved_lookup_props); + return ret; +} +#endif + +} /* namespace GSUB_impl */ +} /* namespace Layout */ +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh new file mode 100644 index 0000000000..499ad673e4 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh @@ -0,0 +1,4828 @@ +/* + * Copyright © 2007,2008,2009,2010 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_GSUBGPOS_HH +#define HB_OT_LAYOUT_GSUBGPOS_HH + +#include "hb.hh" +#include "hb-buffer.hh" +#include "hb-map.hh" +#include "hb-set.hh" +#include "hb-ot-map.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-layout-gdef-table.hh" + + +namespace OT { + + +struct hb_intersects_context_t : + hb_dispatch_context_t<hb_intersects_context_t, bool> +{ + template <typename T> + return_t dispatch (const T &obj) { return obj.intersects (this->glyphs); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + const hb_set_t *glyphs; + + hb_intersects_context_t (const hb_set_t *glyphs_) : + glyphs (glyphs_) {} +}; + +struct hb_have_non_1to1_context_t : + hb_dispatch_context_t<hb_have_non_1to1_context_t, bool> +{ + template <typename T> + return_t dispatch (const T &obj) { return obj.may_have_non_1to1 (); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } +}; + +struct hb_closure_context_t : + hb_dispatch_context_t<hb_closure_context_t> +{ + typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned lookup_index, hb_set_t *covered_seq_indicies, unsigned seq_index, unsigned end_index); + template <typename T> + return_t dispatch (const T &obj) { obj.closure (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + void recurse (unsigned lookup_index, hb_set_t *covered_seq_indicies, unsigned seq_index, unsigned end_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return; + + nesting_level_left--; + recurse_func (this, lookup_index, covered_seq_indicies, seq_index, end_index); + nesting_level_left++; + } + + void reset_lookup_visit_count () + { lookup_count = 0; } + + bool lookup_limit_exceeded () + { return lookup_count > HB_MAX_LOOKUP_VISIT_COUNT; } + + bool should_visit_lookup (unsigned int lookup_index) + { + if (lookup_count++ > HB_MAX_LOOKUP_VISIT_COUNT) + return false; + + if (is_lookup_done (lookup_index)) + return false; + + return true; + } + + bool is_lookup_done (unsigned int lookup_index) + { + if (unlikely (done_lookups_glyph_count->in_error () || + done_lookups_glyph_set->in_error ())) + return true; + + /* Have we visited this lookup with the current set of glyphs? */ + if (done_lookups_glyph_count->get (lookup_index) != glyphs->get_population ()) + { + done_lookups_glyph_count->set (lookup_index, glyphs->get_population ()); + + if (!done_lookups_glyph_set->has (lookup_index)) + { + if (unlikely (!done_lookups_glyph_set->set (lookup_index, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) + return true; + } + + done_lookups_glyph_set->get (lookup_index)->clear (); + } + + hb_set_t *covered_glyph_set = done_lookups_glyph_set->get (lookup_index); + if (unlikely (covered_glyph_set->in_error ())) + return true; + if (parent_active_glyphs ().is_subset (*covered_glyph_set)) + return true; + + covered_glyph_set->union_ (parent_active_glyphs ()); + return false; + } + + const hb_set_t& previous_parent_active_glyphs () { + if (active_glyphs_stack.length <= 1) + return *glyphs; + + return active_glyphs_stack[active_glyphs_stack.length - 2]; + } + + const hb_set_t& parent_active_glyphs () + { + if (!active_glyphs_stack) + return *glyphs; + + return active_glyphs_stack.tail (); + } + + hb_set_t* push_cur_active_glyphs () + { + hb_set_t *s = active_glyphs_stack.push (); + if (unlikely (active_glyphs_stack.in_error ())) + return nullptr; + return s; + } + + bool pop_cur_done_glyphs () + { + if (!active_glyphs_stack) + return false; + + active_glyphs_stack.pop (); + return true; + } + + hb_face_t *face; + hb_set_t *glyphs; + hb_set_t output[1]; + hb_vector_t<hb_set_t> active_glyphs_stack; + recurse_func_t recurse_func = nullptr; + unsigned int nesting_level_left; + + hb_closure_context_t (hb_face_t *face_, + hb_set_t *glyphs_, + hb_map_t *done_lookups_glyph_count_, + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *done_lookups_glyph_set_, + unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + face (face_), + glyphs (glyphs_), + nesting_level_left (nesting_level_left_), + done_lookups_glyph_count (done_lookups_glyph_count_), + done_lookups_glyph_set (done_lookups_glyph_set_) + {} + + ~hb_closure_context_t () { flush (); } + + void set_recurse_func (recurse_func_t func) { recurse_func = func; } + + void flush () + { + output->del_range (face->get_num_glyphs (), HB_SET_VALUE_INVALID); /* Remove invalid glyphs. */ + glyphs->union_ (*output); + output->clear (); + active_glyphs_stack.pop (); + active_glyphs_stack.reset (); + } + + private: + hb_map_t *done_lookups_glyph_count; + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *done_lookups_glyph_set; + unsigned int lookup_count = 0; +}; + + + +struct hb_closure_lookups_context_t : + hb_dispatch_context_t<hb_closure_lookups_context_t> +{ + typedef return_t (*recurse_func_t) (hb_closure_lookups_context_t *c, unsigned lookup_index); + template <typename T> + return_t dispatch (const T &obj) { obj.closure_lookups (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + void recurse (unsigned lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return; + + /* Return if new lookup was recursed to before. */ + if (lookup_limit_exceeded () + || visited_lookups->in_error () + || visited_lookups->has (lookup_index)) + // Don't increment lookup count here, that will be done in the call to closure_lookups() + // made by recurse_func. + return; + + nesting_level_left--; + recurse_func (this, lookup_index); + nesting_level_left++; + } + + void set_lookup_visited (unsigned lookup_index) + { visited_lookups->add (lookup_index); } + + void set_lookup_inactive (unsigned lookup_index) + { inactive_lookups->add (lookup_index); } + + bool lookup_limit_exceeded () + { + bool ret = lookup_count > HB_MAX_LOOKUP_VISIT_COUNT; + if (ret) + DEBUG_MSG (SUBSET, nullptr, "lookup visit count limit exceeded in lookup closure!"); + return ret; } + + bool is_lookup_visited (unsigned lookup_index) + { + if (unlikely (lookup_count++ > HB_MAX_LOOKUP_VISIT_COUNT)) + { + DEBUG_MSG (SUBSET, nullptr, "total visited lookup count %u exceeds max limit, lookup %u is dropped.", + lookup_count, lookup_index); + return true; + } + + if (unlikely (visited_lookups->in_error ())) + return true; + + return visited_lookups->has (lookup_index); + } + + hb_face_t *face; + const hb_set_t *glyphs; + recurse_func_t recurse_func; + unsigned int nesting_level_left; + + hb_closure_lookups_context_t (hb_face_t *face_, + const hb_set_t *glyphs_, + hb_set_t *visited_lookups_, + hb_set_t *inactive_lookups_, + unsigned nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + face (face_), + glyphs (glyphs_), + recurse_func (nullptr), + nesting_level_left (nesting_level_left_), + visited_lookups (visited_lookups_), + inactive_lookups (inactive_lookups_), + lookup_count (0) {} + + void set_recurse_func (recurse_func_t func) { recurse_func = func; } + + private: + hb_set_t *visited_lookups; + hb_set_t *inactive_lookups; + unsigned int lookup_count; +}; + +struct hb_would_apply_context_t : + hb_dispatch_context_t<hb_would_apply_context_t, bool> +{ + template <typename T> + return_t dispatch (const T &obj) { return obj.would_apply (this); } + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + hb_face_t *face; + const hb_codepoint_t *glyphs; + unsigned int len; + bool zero_context; + + hb_would_apply_context_t (hb_face_t *face_, + const hb_codepoint_t *glyphs_, + unsigned int len_, + bool zero_context_) : + face (face_), + glyphs (glyphs_), + len (len_), + zero_context (zero_context_) {} +}; + +struct hb_collect_glyphs_context_t : + hb_dispatch_context_t<hb_collect_glyphs_context_t> +{ + typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index); + template <typename T> + return_t dispatch (const T &obj) { obj.collect_glyphs (this); return hb_empty_t (); } + static return_t default_return_value () { return hb_empty_t (); } + void recurse (unsigned int lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func)) + return; + + /* Note that GPOS sets recurse_func to nullptr already, so it doesn't get + * past the previous check. For GSUB, we only want to collect the output + * glyphs in the recursion. If output is not requested, we can go home now. + * + * Note further, that the above is not exactly correct. A recursed lookup + * is allowed to match input that is not matched in the context, but that's + * not how most fonts are built. It's possible to relax that and recurse + * with all sets here if it proves to be an issue. + */ + + if (output == hb_set_get_empty ()) + return; + + /* Return if new lookup was recursed to before. */ + if (recursed_lookups->has (lookup_index)) + return; + + hb_set_t *old_before = before; + hb_set_t *old_input = input; + hb_set_t *old_after = after; + before = input = after = hb_set_get_empty (); + + nesting_level_left--; + recurse_func (this, lookup_index); + nesting_level_left++; + + before = old_before; + input = old_input; + after = old_after; + + recursed_lookups->add (lookup_index); + } + + hb_face_t *face; + hb_set_t *before; + hb_set_t *input; + hb_set_t *after; + hb_set_t *output; + recurse_func_t recurse_func; + hb_set_t *recursed_lookups; + unsigned int nesting_level_left; + + hb_collect_glyphs_context_t (hb_face_t *face_, + hb_set_t *glyphs_before, /* OUT. May be NULL */ + hb_set_t *glyphs_input, /* OUT. May be NULL */ + hb_set_t *glyphs_after, /* OUT. May be NULL */ + hb_set_t *glyphs_output, /* OUT. May be NULL */ + unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) : + face (face_), + before (glyphs_before ? glyphs_before : hb_set_get_empty ()), + input (glyphs_input ? glyphs_input : hb_set_get_empty ()), + after (glyphs_after ? glyphs_after : hb_set_get_empty ()), + output (glyphs_output ? glyphs_output : hb_set_get_empty ()), + recurse_func (nullptr), + recursed_lookups (hb_set_create ()), + nesting_level_left (nesting_level_left_) {} + ~hb_collect_glyphs_context_t () { hb_set_destroy (recursed_lookups); } + + void set_recurse_func (recurse_func_t func) { recurse_func = func; } +}; + + + +template <typename set_t> +struct hb_collect_coverage_context_t : + hb_dispatch_context_t<hb_collect_coverage_context_t<set_t>, const Coverage &> +{ + typedef const Coverage &return_t; // Stoopid that we have to dupe this here. + template <typename T> + return_t dispatch (const T &obj) { return obj.get_coverage (); } + static return_t default_return_value () { return Null (Coverage); } + bool stop_sublookup_iteration (return_t r) const + { + r.collect_coverage (set); + return false; + } + + hb_collect_coverage_context_t (set_t *set_) : + set (set_) {} + + set_t *set; +}; + +struct hb_ot_apply_context_t : + hb_dispatch_context_t<hb_ot_apply_context_t, bool, HB_DEBUG_APPLY> +{ + struct matcher_t + { + typedef bool (*match_func_t) (hb_glyph_info_t &info, unsigned value, const void *data); + + void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; } + void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; } + void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; } + void set_mask (hb_mask_t mask_) { mask = mask_; } + void set_per_syllable (bool per_syllable_) { per_syllable = per_syllable_; } + void set_syllable (uint8_t syllable_) { syllable = per_syllable ? syllable_ : 0; } + void set_match_func (match_func_t match_func_, + const void *match_data_) + { match_func = match_func_; match_data = match_data_; } + + enum may_match_t { + MATCH_NO, + MATCH_YES, + MATCH_MAYBE + }; + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + may_match_t may_match (hb_glyph_info_t &info, + hb_codepoint_t glyph_data) const + { + if (!(info.mask & mask) || + (syllable && syllable != info.syllable ())) + return MATCH_NO; + + if (match_func) + return match_func (info, glyph_data, match_data) ? MATCH_YES : MATCH_NO; + + return MATCH_MAYBE; + } + + enum may_skip_t { + SKIP_NO, + SKIP_YES, + SKIP_MAYBE + }; + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + may_skip_t may_skip (const hb_ot_apply_context_t *c, + const hb_glyph_info_t &info) const + { + if (!c->check_glyph_property (&info, lookup_props)) + return SKIP_YES; + + if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_hidden (&info) && + (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) && + (ignore_zwj || !_hb_glyph_info_is_zwj (&info)))) + return SKIP_MAYBE; + + return SKIP_NO; + } + + protected: + unsigned int lookup_props = 0; + hb_mask_t mask = -1; + bool ignore_zwnj = false; + bool ignore_zwj = false; + bool per_syllable = false; + uint8_t syllable = 0; + match_func_t match_func = nullptr; + const void *match_data = nullptr; + }; + + struct skipping_iterator_t + { + void init (hb_ot_apply_context_t *c_, bool context_match = false) + { + c = c_; + end = c->buffer->len; + match_glyph_data16 = nullptr; +#ifndef HB_NO_BEYOND_64K + match_glyph_data24 = nullptr; +#endif + matcher.set_match_func (nullptr, nullptr); + matcher.set_lookup_props (c->lookup_props); + /* Ignore ZWNJ if we are matching GPOS, or matching GSUB context and asked to. */ + matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj)); + /* Ignore ZWJ if we are matching context, or asked to. */ + matcher.set_ignore_zwj (context_match || c->auto_zwj); + matcher.set_mask (context_match ? -1 : c->lookup_mask); + /* Per syllable matching is only for GSUB. */ + matcher.set_per_syllable (c->table_index == 0 && c->per_syllable); + matcher.set_syllable (0); + } + void set_lookup_props (unsigned int lookup_props) + { + matcher.set_lookup_props (lookup_props); + } + void set_match_func (matcher_t::match_func_t match_func_, + const void *match_data_) + { + matcher.set_match_func (match_func_, match_data_); + } + void set_glyph_data (const HBUINT16 glyph_data[]) + { + match_glyph_data16 = glyph_data; +#ifndef HB_NO_BEYOND_64K + match_glyph_data24 = nullptr; +#endif + } +#ifndef HB_NO_BEYOND_64K + void set_glyph_data (const HBUINT24 glyph_data[]) + { + match_glyph_data16 = nullptr; + match_glyph_data24 = glyph_data; + } +#endif + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + void reset (unsigned int start_index_) + { + idx = start_index_; + end = c->buffer->len; + matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0); + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + void reset_fast (unsigned int start_index_) + { + // Doesn't set end or syllable. Used by GPOS which doesn't care / change. + idx = start_index_; + } + + void reject () + { + backup_glyph_data (); + } + + matcher_t::may_skip_t +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + may_skip (const hb_glyph_info_t &info) const + { return matcher.may_skip (c, info); } + + enum match_t { + MATCH, + NOT_MATCH, + SKIP + }; + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + match_t match (hb_glyph_info_t &info) + { + matcher_t::may_skip_t skip = matcher.may_skip (c, info); + if (unlikely (skip == matcher_t::SKIP_YES)) + return SKIP; + + matcher_t::may_match_t match = matcher.may_match (info, get_glyph_data ()); + if (match == matcher_t::MATCH_YES || + (match == matcher_t::MATCH_MAYBE && + skip == matcher_t::SKIP_NO)) + return MATCH; + + if (skip == matcher_t::SKIP_NO) + return NOT_MATCH; + + return SKIP; + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool next (unsigned *unsafe_to = nullptr) + { + const signed stop = (signed) end - 1; + while ((signed) idx < stop) + { + idx++; + switch (match (c->buffer->info[idx])) + { + case MATCH: + { + advance_glyph_data (); + return true; + } + case NOT_MATCH: + { + if (unsafe_to) + *unsafe_to = idx + 1; + return false; + } + case SKIP: + continue; + } + } + if (unsafe_to) + *unsafe_to = end; + return false; + } +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool prev (unsigned *unsafe_from = nullptr) + { + const unsigned stop = 0; + while (idx > stop) + { + idx--; + switch (match (c->buffer->out_info[idx])) + { + case MATCH: + { + advance_glyph_data (); + return true; + } + case NOT_MATCH: + { + if (unsafe_from) + *unsafe_from = hb_max (1u, idx) - 1u; + return false; + } + case SKIP: + continue; + } + } + if (unsafe_from) + *unsafe_from = 0; + return false; + } + + HB_ALWAYS_INLINE + hb_codepoint_t + get_glyph_data () + { + if (match_glyph_data16) return *match_glyph_data16; +#ifndef HB_NO_BEYOND_64K + else + if (match_glyph_data24) return *match_glyph_data24; +#endif + return 0; + } + HB_ALWAYS_INLINE + void + advance_glyph_data () + { + if (match_glyph_data16) match_glyph_data16++; +#ifndef HB_NO_BEYOND_64K + else + if (match_glyph_data24) match_glyph_data24++; +#endif + } + void + backup_glyph_data () + { + if (match_glyph_data16) match_glyph_data16--; +#ifndef HB_NO_BEYOND_64K + else + if (match_glyph_data24) match_glyph_data24--; +#endif + } + + unsigned int idx; + protected: + hb_ot_apply_context_t *c; + matcher_t matcher; + const HBUINT16 *match_glyph_data16; +#ifndef HB_NO_BEYOND_64K + const HBUINT24 *match_glyph_data24; +#endif + + unsigned int end; + }; + + + const char *get_name () { return "APPLY"; } + typedef return_t (*recurse_func_t) (hb_ot_apply_context_t *c, unsigned int lookup_index); + template <typename T> + 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; } + return_t recurse (unsigned int sub_lookup_index) + { + if (unlikely (nesting_level_left == 0 || !recurse_func || buffer->max_ops-- <= 0)) + { + buffer->shaping_failed = true; + return default_return_value (); + } + + nesting_level_left--; + bool ret = recurse_func (this, sub_lookup_index); + nesting_level_left++; + return ret; + } + + skipping_iterator_t iter_input, iter_context; + + unsigned int table_index; /* GSUB/GPOS */ + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + hb_sanitize_context_t sanitizer; + recurse_func_t recurse_func = nullptr; + const GDEF &gdef; + const GDEF::accelerator_t &gdef_accel; + const VariationStore &var_store; + VariationStore::cache_t *var_store_cache; + hb_set_digest_t digest; + + hb_direction_t direction; + hb_mask_t lookup_mask = 1; + unsigned int lookup_index = (unsigned) -1; + unsigned int lookup_props = 0; + unsigned int nesting_level_left = HB_MAX_NESTING_LEVEL; + + bool has_glyph_classes; + bool auto_zwnj = true; + bool auto_zwj = true; + bool per_syllable = false; + bool random = false; + uint32_t random_state = 1; + unsigned new_syllables = (unsigned) -1; + + signed last_base = -1; // GPOS uses + unsigned last_base_until = 0; // GPOS uses + + hb_ot_apply_context_t (unsigned int table_index_, + hb_font_t *font_, + hb_buffer_t *buffer_, + hb_blob_t *table_blob_) : + table_index (table_index_), + font (font_), face (font->face), buffer (buffer_), + sanitizer (table_blob_), + gdef ( +#ifndef HB_NO_OT_LAYOUT + *face->table.GDEF->table +#else + Null (GDEF) +#endif + ), + gdef_accel ( +#ifndef HB_NO_OT_LAYOUT + *face->table.GDEF +#else + Null (GDEF::accelerator_t) +#endif + ), + var_store (gdef.get_var_store ()), + var_store_cache ( +#ifndef HB_NO_VAR + table_index == 1 && font->num_coords ? var_store.create_cache () : nullptr +#else + nullptr +#endif + ), + digest (buffer_->digest ()), + direction (buffer_->props.direction), + has_glyph_classes (gdef.has_glyph_classes ()) + { init_iters (); } + + ~hb_ot_apply_context_t () + { +#ifndef HB_NO_VAR + VariationStore::destroy_cache (var_store_cache); +#endif + } + + void init_iters () + { + iter_input.init (this, false); + iter_context.init (this, true); + } + + void set_lookup_mask (hb_mask_t mask, bool init = true) { lookup_mask = mask; last_base = -1; last_base_until = 0; if (init) init_iters (); } + void set_auto_zwj (bool auto_zwj_, bool init = true) { auto_zwj = auto_zwj_; if (init) init_iters (); } + void set_auto_zwnj (bool auto_zwnj_, bool init = true) { auto_zwnj = auto_zwnj_; if (init) init_iters (); } + void set_per_syllable (bool per_syllable_, bool init = true) { per_syllable = per_syllable_; if (init) init_iters (); } + void set_random (bool random_) { random = random_; } + void set_recurse_func (recurse_func_t func) { recurse_func = func; } + void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; } + void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; init_iters (); } + + uint32_t random_number () + { + /* http://www.cplusplus.com/reference/random/minstd_rand/ */ + random_state = random_state * 48271 % 2147483647; + return random_state; + } + + bool match_properties_mark (hb_codepoint_t glyph, + unsigned int glyph_props, + unsigned int match_props) const + { + /* If using mark filtering sets, the high short of + * match_props has the set index. + */ + if (match_props & LookupFlag::UseMarkFilteringSet) + return gdef_accel.mark_set_covers (match_props >> 16, glyph); + + /* The second byte of match_props has the meaning + * "ignore marks of attachment type different than + * the attachment type specified." + */ + if (match_props & LookupFlag::MarkAttachmentType) + return (match_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType); + + return true; + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool check_glyph_property (const hb_glyph_info_t *info, + unsigned int match_props) const + { + unsigned int glyph_props = _hb_glyph_info_get_glyph_props (info); + + /* Not covered, if, for example, glyph class is ligature and + * match_props includes LookupFlags::IgnoreLigatures + */ + if (glyph_props & match_props & LookupFlag::IgnoreFlags) + return false; + + if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) + return match_properties_mark (info->codepoint, glyph_props, match_props); + + return true; + } + + void _set_glyph_class (hb_codepoint_t glyph_index, + unsigned int class_guess = 0, + bool ligature = false, + bool component = false) + { + digest.add (glyph_index); + + if (new_syllables != (unsigned) -1) + buffer->cur().syllable() = new_syllables; + + unsigned int props = _hb_glyph_info_get_glyph_props (&buffer->cur()); + props |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED; + if (ligature) + { + props |= HB_OT_LAYOUT_GLYPH_PROPS_LIGATED; + /* In the only place that the MULTIPLIED bit is used, Uniscribe + * seems to only care about the "last" transformation between + * Ligature and Multiple substitutions. Ie. if you ligate, expand, + * and ligate again, it forgives the multiplication and acts as + * if only ligation happened. As such, clear MULTIPLIED bit. + */ + props &= ~HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED; + } + if (component) + props |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED; + if (likely (has_glyph_classes)) + { + props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE; + _hb_glyph_info_set_glyph_props (&buffer->cur(), props | gdef_accel.get_glyph_props (glyph_index)); + } + else if (class_guess) + { + props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE; + _hb_glyph_info_set_glyph_props (&buffer->cur(), props | class_guess); + } + else + _hb_glyph_info_set_glyph_props (&buffer->cur(), props); + } + + void replace_glyph (hb_codepoint_t glyph_index) + { + _set_glyph_class (glyph_index); + (void) buffer->replace_glyph (glyph_index); + } + void replace_glyph_inplace (hb_codepoint_t glyph_index) + { + _set_glyph_class (glyph_index); + buffer->cur().codepoint = glyph_index; + } + void replace_glyph_with_ligature (hb_codepoint_t glyph_index, + unsigned int class_guess) + { + _set_glyph_class (glyph_index, class_guess, true); + (void) buffer->replace_glyph (glyph_index); + } + void output_glyph_for_component (hb_codepoint_t glyph_index, + unsigned int class_guess) + { + _set_glyph_class (glyph_index, class_guess, false, true); + (void) buffer->output_glyph (glyph_index); + } +}; + + +struct hb_accelerate_subtables_context_t : + hb_dispatch_context_t<hb_accelerate_subtables_context_t> +{ + template <typename Type> + static inline bool apply_to (const void *obj, hb_ot_apply_context_t *c) + { + const Type *typed_obj = (const Type *) obj; + return typed_obj->apply (c); + } + +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + template <typename T> + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<1>) HB_RETURN (bool, obj->apply_cached (c) ) + template <typename T> + static inline auto apply_cached_ (const T *obj, hb_ot_apply_context_t *c, hb_priority<0>) HB_RETURN (bool, obj->apply (c) ) + template <typename Type> + static inline bool apply_cached_to (const void *obj, hb_ot_apply_context_t *c) + { + const Type *typed_obj = (const Type *) obj; + return apply_cached_ (typed_obj, c, hb_prioritize); + } + + template <typename T> + static inline auto cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) ) + template <typename T> + static inline bool cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; } + template <typename Type> + static inline bool cache_func_to (const void *obj, hb_ot_apply_context_t *c, bool enter) + { + const Type *typed_obj = (const Type *) obj; + return cache_func_ (typed_obj, c, enter, hb_prioritize); + } +#endif + + typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c); + typedef bool (*hb_cache_func_t) (const void *obj, hb_ot_apply_context_t *c, bool enter); + + struct hb_applicable_t + { + friend struct hb_accelerate_subtables_context_t; + friend struct hb_ot_layout_lookup_accelerator_t; + + template <typename T> + void init (const T &obj_, + hb_apply_func_t apply_func_ +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + , hb_apply_func_t apply_cached_func_ + , hb_cache_func_t cache_func_ +#endif + ) + { + obj = &obj_; + apply_func = apply_func_; +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + apply_cached_func = apply_cached_func_; + cache_func = cache_func_; +#endif + digest.init (); + obj_.get_coverage ().collect_coverage (&digest); + } + + bool apply (hb_ot_apply_context_t *c) const + { + return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c); + } +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + bool apply_cached (hb_ot_apply_context_t *c) const + { + return digest.may_have (c->buffer->cur().codepoint) && apply_cached_func (obj, c); + } + bool cache_enter (hb_ot_apply_context_t *c) const + { + return cache_func (obj, c, true); + } + void cache_leave (hb_ot_apply_context_t *c) const + { + cache_func (obj, c, false); + } +#endif + + private: + const void *obj; + hb_apply_func_t apply_func; +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + hb_apply_func_t apply_cached_func; + hb_cache_func_t cache_func; +#endif + hb_set_digest_t digest; + }; + +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + template <typename T> + auto cache_cost (const T &obj, hb_priority<1>) HB_AUTO_RETURN ( obj.cache_cost () ) + template <typename T> + auto cache_cost (const T &obj, hb_priority<0>) HB_AUTO_RETURN ( 0u ) +#endif + + /* Dispatch interface. */ + template <typename T> + return_t dispatch (const T &obj) + { + hb_applicable_t *entry = &array[i++]; + + entry->init (obj, + apply_to<T> +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + , apply_cached_to<T> + , cache_func_to<T> +#endif + ); + +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + /* Cache handling + * + * We allow one subtable from each lookup to use a cache. The assumption + * being that multiple subtables of the same lookup cannot use a cache + * because the resources they would use will collide. As such, we ask + * each subtable to tell us how much it costs (which a cache would avoid), + * and we allocate the cache opportunity to the costliest subtable. + */ + unsigned cost = cache_cost (obj, hb_prioritize); + if (cost > cache_user_cost) + { + cache_user_idx = i - 1; + cache_user_cost = cost; + } +#endif + + return hb_empty_t (); + } + static return_t default_return_value () { return hb_empty_t (); } + + hb_accelerate_subtables_context_t (hb_applicable_t *array_) : + array (array_) {} + + hb_applicable_t *array; + unsigned i = 0; + +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + unsigned cache_user_idx = (unsigned) -1; + unsigned cache_user_cost = 0; +#endif +}; + + +typedef bool (*intersects_func_t) (const hb_set_t *glyphs, unsigned value, const void *data, void *cache); +typedef void (*intersected_glyphs_func_t) (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs, void *cache); +typedef void (*collect_glyphs_func_t) (hb_set_t *glyphs, unsigned value, const void *data); +typedef bool (*match_func_t) (hb_glyph_info_t &info, unsigned value, const void *data); + +struct ContextClosureFuncs +{ + intersects_func_t intersects; + intersected_glyphs_func_t intersected_glyphs; +}; +struct ContextCollectGlyphsFuncs +{ + collect_glyphs_func_t collect; +}; +struct ContextApplyFuncs +{ + match_func_t match; +}; +struct ChainContextApplyFuncs +{ + match_func_t match[3]; +}; + + +static inline bool intersects_glyph (const hb_set_t *glyphs, unsigned value, const void *data HB_UNUSED, void *cache HB_UNUSED) +{ + return glyphs->has (value); +} +static inline bool intersects_class (const hb_set_t *glyphs, unsigned value, const void *data, void *cache) +{ + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + hb_map_t *map = (hb_map_t *) cache; + + hb_codepoint_t *cached_v; + if (map->has (value, &cached_v)) + return *cached_v; + + bool v = class_def.intersects_class (glyphs, value); + map->set (value, v); + + return v; +} +static inline bool intersects_coverage (const hb_set_t *glyphs, unsigned value, const void *data, void *cache HB_UNUSED) +{ + Offset16To<Coverage> coverage; + coverage = value; + return (data+coverage).intersects (glyphs); +} + + +static inline void intersected_glyph (const hb_set_t *glyphs HB_UNUSED, const void *data, unsigned value, hb_set_t *intersected_glyphs, HB_UNUSED void *cache) +{ + unsigned g = reinterpret_cast<const HBUINT16 *>(data)[value]; + intersected_glyphs->add (g); +} + +using intersected_class_cache_t = hb_hashmap_t<unsigned, hb_set_t>; + +static inline void intersected_class_glyphs (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs, void *cache) +{ + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + + intersected_class_cache_t *map = (intersected_class_cache_t *) cache; + + hb_set_t *cached_v; + if (map->has (value, &cached_v)) + { + intersected_glyphs->union_ (*cached_v); + return; + } + + hb_set_t v; + class_def.intersected_class_glyphs (glyphs, value, &v); + + intersected_glyphs->union_ (v); + + map->set (value, std::move (v)); +} + +static inline void intersected_coverage_glyphs (const hb_set_t *glyphs, const void *data, unsigned value, hb_set_t *intersected_glyphs, HB_UNUSED void *cache) +{ + Offset16To<Coverage> coverage; + coverage = value; + (data+coverage).intersect_set (*glyphs, *intersected_glyphs); +} + + +template <typename HBUINT> +static inline bool array_is_subset_of (const hb_set_t *glyphs, + unsigned int count, + const HBUINT values[], + intersects_func_t intersects_func, + const void *intersects_data, + void *cache) +{ + for (const auto &_ : + hb_iter (values, count)) + if (!intersects_func (glyphs, _, intersects_data, cache)) return false; + return true; +} + + +static inline void collect_glyph (hb_set_t *glyphs, unsigned value, const void *data HB_UNUSED) +{ + glyphs->add (value); +} +static inline void collect_class (hb_set_t *glyphs, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + class_def.collect_class (glyphs, value); +} +static inline void collect_coverage (hb_set_t *glyphs, unsigned value, const void *data) +{ + Offset16To<Coverage> coverage; + coverage = value; + (data+coverage).collect_coverage (glyphs); +} +template <typename HBUINT> +static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED, + hb_set_t *glyphs, + unsigned int count, + const HBUINT values[], + collect_glyphs_func_t collect_func, + const void *collect_data) +{ + return + + hb_iter (values, count) + | hb_apply ([&] (const HBUINT &_) { collect_func (glyphs, _, collect_data); }) + ; +} + + +static inline bool match_always (hb_glyph_info_t &info HB_UNUSED, unsigned value HB_UNUSED, const void *data HB_UNUSED) +{ + return true; +} +static inline bool match_glyph (hb_glyph_info_t &info, unsigned value, const void *data HB_UNUSED) +{ + return info.codepoint == value; +} +static inline bool match_class (hb_glyph_info_t &info, unsigned value, const void *data) +{ + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + return class_def.get_class (info.codepoint) == value; +} +static inline bool match_class_cached (hb_glyph_info_t &info, unsigned value, const void *data) +{ + unsigned klass = info.syllable(); + if (klass < 255) + return klass == value; + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + klass = class_def.get_class (info.codepoint); + if (likely (klass < 255)) + info.syllable() = klass; + return klass == value; +} +static inline bool match_class_cached1 (hb_glyph_info_t &info, unsigned value, const void *data) +{ + unsigned klass = info.syllable() & 0x0F; + if (klass < 15) + return klass == value; + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + klass = class_def.get_class (info.codepoint); + if (likely (klass < 15)) + info.syllable() = (info.syllable() & 0xF0) | klass; + return klass == value; +} +static inline bool match_class_cached2 (hb_glyph_info_t &info, unsigned value, const void *data) +{ + unsigned klass = (info.syllable() & 0xF0) >> 4; + if (klass < 15) + return klass == value; + const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data); + klass = class_def.get_class (info.codepoint); + if (likely (klass < 15)) + info.syllable() = (info.syllable() & 0x0F) | (klass << 4); + return klass == value; +} +static inline bool match_coverage (hb_glyph_info_t &info, unsigned value, const void *data) +{ + Offset16To<Coverage> coverage; + coverage = value; + return (data+coverage).get_coverage (info.codepoint) != NOT_COVERED; +} + +template <typename HBUINT> +static inline bool would_match_input (hb_would_apply_context_t *c, + unsigned int count, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + match_func_t match_func, + const void *match_data) +{ + if (count != c->len) + return false; + + for (unsigned int i = 1; i < count; i++) + { + hb_glyph_info_t info; + info.codepoint = c->glyphs[i]; + if (likely (!match_func (info, input[i - 1], match_data))) + return false; + } + + return true; +} +template <typename HBUINT> +#ifndef HB_OPTIMIZE_SIZE +HB_ALWAYS_INLINE +#endif +static bool match_input (hb_ot_apply_context_t *c, + unsigned int count, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + match_func_t match_func, + const void *match_data, + unsigned int *end_position, + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], + unsigned int *p_total_component_count = nullptr) +{ + TRACE_APPLY (nullptr); + + if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); + + hb_buffer_t *buffer = c->buffer; + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; + skippy_iter.reset (buffer->idx); + skippy_iter.set_match_func (match_func, match_data); + skippy_iter.set_glyph_data (input); + + /* + * This is perhaps the trickiest part of OpenType... Remarks: + * + * - If all components of the ligature were marks, we call this a mark ligature. + * + * - If there is no GDEF, and the ligature is NOT a mark ligature, we categorize + * it as a ligature glyph. + * + * - Ligatures cannot be formed across glyphs attached to different components + * of previous ligatures. Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and + * LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother. + * However, it would be wrong to ligate that SHADDA,FATHA sequence. + * There are a couple of exceptions to this: + * + * o If a ligature tries ligating with marks that belong to it itself, go ahead, + * assuming that the font designer knows what they are doing (otherwise it can + * break Indic stuff when a matra wants to ligate with a conjunct, + * + * o If two marks want to ligate and they belong to different components of the + * same ligature glyph, and said ligature glyph is to be ignored according to + * mark-filtering rules, then allow. + * https://github.com/harfbuzz/harfbuzz/issues/545 + */ + + unsigned int total_component_count = 0; + + unsigned int first_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int first_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + + enum { + LIGBASE_NOT_CHECKED, + LIGBASE_MAY_NOT_SKIP, + LIGBASE_MAY_SKIP + } ligbase = LIGBASE_NOT_CHECKED; + + for (unsigned int i = 1; i < count; i++) + { + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + *end_position = unsafe_to; + return_trace (false); + } + + match_positions[i] = skippy_iter.idx; + + unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]); + unsigned int this_lig_comp = _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]); + + if (first_lig_id && first_lig_comp) + { + /* If first component was attached to a previous ligature component, + * all subsequent components should be attached to the same ligature + * component, otherwise we shouldn't ligate them... */ + if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp) + { + /* ...unless, we are attached to a base ligature and that base + * ligature is ignorable. */ + if (ligbase == LIGBASE_NOT_CHECKED) + { + bool found = false; + const auto *out = buffer->out_info; + unsigned int j = buffer->out_len; + while (j && _hb_glyph_info_get_lig_id (&out[j - 1]) == first_lig_id) + { + if (_hb_glyph_info_get_lig_comp (&out[j - 1]) == 0) + { + j--; + found = true; + break; + } + j--; + } + + if (found && skippy_iter.may_skip (out[j]) == hb_ot_apply_context_t::matcher_t::SKIP_YES) + ligbase = LIGBASE_MAY_SKIP; + else + ligbase = LIGBASE_MAY_NOT_SKIP; + } + + if (ligbase == LIGBASE_MAY_NOT_SKIP) + return_trace (false); + } + } + else + { + /* If first component was NOT attached to a previous ligature component, + * all subsequent components should also NOT be attached to any ligature + * component, unless they are attached to the first component itself! */ + if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id)) + return_trace (false); + } + + total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->info[skippy_iter.idx]); + } + + *end_position = skippy_iter.idx + 1; + + if (p_total_component_count) + { + total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + *p_total_component_count = total_component_count; + } + + match_positions[0] = buffer->idx; + + return_trace (true); +} +static inline bool ligate_input (hb_ot_apply_context_t *c, + unsigned int count, /* Including the first glyph */ + const unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */ + unsigned int match_end, + hb_codepoint_t lig_glyph, + unsigned int total_component_count) +{ + TRACE_APPLY (nullptr); + + hb_buffer_t *buffer = c->buffer; + + buffer->merge_clusters (buffer->idx, match_end); + + /* - If a base and one or more marks ligate, consider that as a base, NOT + * ligature, such that all following marks can still attach to it. + * https://github.com/harfbuzz/harfbuzz/issues/1109 + * + * - If all components of the ligature were marks, we call this a mark ligature. + * If it *is* a mark ligature, we don't allocate a new ligature id, and leave + * the ligature to keep its old ligature id. This will allow it to attach to + * a base ligature in GPOS. Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH, + * and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA with a + * ligature id and component value of 2. Then if SHADDA,FATHA form a ligature + * later, we don't want them to lose their ligature id/component, otherwise + * GPOS will fail to correctly position the mark ligature on top of the + * LAM,LAM,HEH ligature. See: + * https://bugzilla.gnome.org/show_bug.cgi?id=676343 + * + * - If a ligature is formed of components that some of which are also ligatures + * themselves, and those ligature components had marks attached to *their* + * components, we have to attach the marks to the new ligature component + * positions! Now *that*'s tricky! And these marks may be following the + * last component of the whole sequence, so we should loop forward looking + * for them and update them. + * + * Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a + * 'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature + * id and component == 1. Now, during 'liga', the LAM and the LAM-HEH ligature + * form a LAM-LAM-HEH ligature. We need to reassign the SHADDA and FATHA to + * the new ligature with a component value of 2. + * + * This in fact happened to a font... See: + * https://bugzilla.gnome.org/show_bug.cgi?id=437633 + */ + + bool is_base_ligature = _hb_glyph_info_is_base_glyph (&buffer->info[match_positions[0]]); + bool is_mark_ligature = _hb_glyph_info_is_mark (&buffer->info[match_positions[0]]); + for (unsigned int i = 1; i < count; i++) + if (!_hb_glyph_info_is_mark (&buffer->info[match_positions[i]])) + { + is_base_ligature = false; + is_mark_ligature = false; + break; + } + bool is_ligature = !is_base_ligature && !is_mark_ligature; + + unsigned int klass = is_ligature ? HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE : 0; + unsigned int lig_id = is_ligature ? _hb_allocate_lig_id (buffer) : 0; + unsigned int last_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + unsigned int last_num_components = _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + unsigned int components_so_far = last_num_components; + + if (is_ligature) + { + _hb_glyph_info_set_lig_props_for_ligature (&buffer->cur(), lig_id, total_component_count); + if (_hb_glyph_info_get_general_category (&buffer->cur()) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + { + _hb_glyph_info_set_general_category (&buffer->cur(), HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER); + } + } + c->replace_glyph_with_ligature (lig_glyph, klass); + + for (unsigned int i = 1; i < count; i++) + { + while (buffer->idx < match_positions[i] && buffer->successful) + { + if (is_ligature) + { + unsigned int this_comp = _hb_glyph_info_get_lig_comp (&buffer->cur()); + if (this_comp == 0) + this_comp = last_num_components; + unsigned int new_lig_comp = components_so_far - last_num_components + + hb_min (this_comp, last_num_components); + _hb_glyph_info_set_lig_props_for_mark (&buffer->cur(), lig_id, new_lig_comp); + } + (void) buffer->next_glyph (); + } + + last_lig_id = _hb_glyph_info_get_lig_id (&buffer->cur()); + last_num_components = _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + components_so_far += last_num_components; + + /* Skip the base glyph */ + buffer->idx++; + } + + if (!is_mark_ligature && last_lig_id) + { + /* Re-adjust components for any marks following. */ + for (unsigned i = buffer->idx; i < buffer->len; ++i) + { + if (last_lig_id != _hb_glyph_info_get_lig_id (&buffer->info[i])) break; + + unsigned this_comp = _hb_glyph_info_get_lig_comp (&buffer->info[i]); + if (!this_comp) break; + + unsigned new_lig_comp = components_so_far - last_num_components + + hb_min (this_comp, last_num_components); + _hb_glyph_info_set_lig_props_for_mark (&buffer->info[i], lig_id, new_lig_comp); + } + } + return_trace (true); +} + +template <typename HBUINT> +#ifndef HB_OPTIMIZE_SIZE +HB_ALWAYS_INLINE +#endif +static bool match_backtrack (hb_ot_apply_context_t *c, + unsigned int count, + const HBUINT backtrack[], + match_func_t match_func, + const void *match_data, + unsigned int *match_start) +{ + TRACE_APPLY (nullptr); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; + skippy_iter.reset (c->buffer->backtrack_len ()); + skippy_iter.set_match_func (match_func, match_data); + skippy_iter.set_glyph_data (backtrack); + + for (unsigned int i = 0; i < count; i++) + { + unsigned unsafe_from; + if (!skippy_iter.prev (&unsafe_from)) + { + *match_start = unsafe_from; + return_trace (false); + } + } + + *match_start = skippy_iter.idx; + return_trace (true); +} + +template <typename HBUINT> +#ifndef HB_OPTIMIZE_SIZE +HB_ALWAYS_INLINE +#endif +static bool match_lookahead (hb_ot_apply_context_t *c, + unsigned int count, + const HBUINT lookahead[], + match_func_t match_func, + const void *match_data, + unsigned int start_index, + unsigned int *end_index) +{ + TRACE_APPLY (nullptr); + + hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context; + skippy_iter.reset (start_index - 1); + skippy_iter.set_match_func (match_func, match_data); + skippy_iter.set_glyph_data (lookahead); + + for (unsigned int i = 0; i < count; i++) + { + unsigned unsafe_to; + if (!skippy_iter.next (&unsafe_to)) + { + *end_index = unsafe_to; + return_trace (false); + } + } + + *end_index = skippy_iter.idx + 1; + return_trace (true); +} + + + +struct LookupRecord +{ + bool serialize (hb_serialize_context_t *c, + const hb_map_t *lookup_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (*this); + if (unlikely (!out)) return_trace (false); + + return_trace (c->check_assign (out->lookupListIndex, lookup_map->get (lookupListIndex), HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 sequenceIndex; /* Index into current glyph + * sequence--first glyph = 0 */ + HBUINT16 lookupListIndex; /* Lookup to apply to that + * position--zero--based */ + public: + DEFINE_SIZE_STATIC (4); +}; + +static unsigned serialize_lookuprecord_array (hb_serialize_context_t *c, + const hb_array_t<const LookupRecord> lookupRecords, + const hb_map_t *lookup_map) +{ + unsigned count = 0; + for (const LookupRecord& r : lookupRecords) + { + if (!lookup_map->has (r.lookupListIndex)) + continue; + + if (!r.serialize (c, lookup_map)) + return 0; + + count++; + } + return count; +} + +enum ContextFormat { SimpleContext = 1, ClassBasedContext = 2, CoverageBasedContext = 3 }; + +template <typename HBUINT> +static void context_closure_recurse_lookups (hb_closure_context_t *c, + unsigned inputCount, const HBUINT input[], + unsigned lookupCount, + const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */, + unsigned value, + ContextFormat context_format, + const void *data, + intersected_glyphs_func_t intersected_glyphs_func, + void *cache) +{ + hb_set_t covered_seq_indicies; + hb_set_t pos_glyphs; + for (unsigned int i = 0; i < lookupCount; i++) + { + unsigned seqIndex = lookupRecord[i].sequenceIndex; + if (seqIndex >= inputCount) continue; + + bool has_pos_glyphs = false; + + if (!covered_seq_indicies.has (seqIndex)) + { + has_pos_glyphs = true; + pos_glyphs.clear (); + if (seqIndex == 0) + { + switch (context_format) { + case ContextFormat::SimpleContext: + pos_glyphs.add (value); + break; + case ContextFormat::ClassBasedContext: + intersected_glyphs_func (&c->parent_active_glyphs (), data, value, &pos_glyphs, cache); + break; + case ContextFormat::CoverageBasedContext: + pos_glyphs.set (c->parent_active_glyphs ()); + break; + } + } + else + { + const void *input_data = input; + unsigned input_value = seqIndex - 1; + if (context_format != ContextFormat::SimpleContext) + { + input_data = data; + input_value = input[seqIndex - 1]; + } + + intersected_glyphs_func (c->glyphs, input_data, input_value, &pos_glyphs, cache); + } + } + + covered_seq_indicies.add (seqIndex); + hb_set_t *cur_active_glyphs = c->push_cur_active_glyphs (); + if (unlikely (!cur_active_glyphs)) + return; + if (has_pos_glyphs) { + *cur_active_glyphs = std::move (pos_glyphs); + } else { + *cur_active_glyphs = *c->glyphs; + } + + unsigned endIndex = inputCount; + if (context_format == ContextFormat::CoverageBasedContext) + endIndex += 1; + + c->recurse (lookupRecord[i].lookupListIndex, &covered_seq_indicies, seqIndex, endIndex); + + c->pop_cur_done_glyphs (); + } +} + +template <typename context_t> +static inline void recurse_lookups (context_t *c, + unsigned int lookupCount, + const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */) +{ + for (unsigned int i = 0; i < lookupCount; i++) + c->recurse (lookupRecord[i].lookupListIndex); +} + +static inline void apply_lookup (hb_ot_apply_context_t *c, + unsigned int count, /* Including the first glyph */ + unsigned int match_positions[HB_MAX_CONTEXT_LENGTH], /* Including the first glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */ + unsigned int match_end) +{ + hb_buffer_t *buffer = c->buffer; + int end; + + /* All positions are distance from beginning of *output* buffer. + * Adjust. */ + { + unsigned int bl = buffer->backtrack_len (); + end = bl + match_end - buffer->idx; + + int delta = bl - buffer->idx; + /* Convert positions to new indexing. */ + for (unsigned int j = 0; j < count; j++) + match_positions[j] += delta; + } + + for (unsigned int i = 0; i < lookupCount && buffer->successful; i++) + { + unsigned int idx = lookupRecord[i].sequenceIndex; + if (idx >= count) + continue; + + unsigned int orig_len = buffer->backtrack_len () + buffer->lookahead_len (); + + /* This can happen if earlier recursed lookups deleted many entries. */ + if (unlikely (match_positions[idx] >= orig_len)) + continue; + + if (unlikely (!buffer->move_to (match_positions[idx]))) + break; + + if (unlikely (buffer->max_ops <= 0)) + break; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + if (buffer->have_output) + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "recursing to lookup %u at %u", + (unsigned) lookupRecord[i].lookupListIndex, + buffer->idx); + } + + if (!c->recurse (lookupRecord[i].lookupListIndex)) + continue; + + if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) + { + if (buffer->have_output) + c->buffer->sync_so_far (); + c->buffer->message (c->font, + "recursed to lookup %u", + (unsigned) lookupRecord[i].lookupListIndex); + } + + unsigned int new_len = buffer->backtrack_len () + buffer->lookahead_len (); + int delta = new_len - orig_len; + + if (!delta) + continue; + + /* Recursed lookup changed buffer len. Adjust. + * + * TODO: + * + * Right now, if buffer length increased by n, we assume n new glyphs + * were added right after the current position, and if buffer length + * was decreased by n, we assume n match positions after the current + * one where removed. The former (buffer length increased) case is + * fine, but the decrease case can be improved in at least two ways, + * both of which are significant: + * + * - If recursed-to lookup is MultipleSubst and buffer length + * decreased, then it's current match position that was deleted, + * NOT the one after it. + * + * - If buffer length was decreased by n, it does not necessarily + * mean that n match positions where removed, as there recursed-to + * lookup might had a different LookupFlag. Here's a constructed + * case of that: + * https://github.com/harfbuzz/harfbuzz/discussions/3538 + * + * It should be possible to construct tests for both of these cases. + */ + + end += delta; + if (end < int (match_positions[idx])) + { + /* End might end up being smaller than match_positions[idx] if the recursed + * lookup ended up removing many items. + * Just never rewind end beyond start of current position, since that is + * not possible in the recursed lookup. Also adjust delta as such. + * + * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 + * https://github.com/harfbuzz/harfbuzz/issues/1611 + */ + delta += match_positions[idx] - end; + end = match_positions[idx]; + } + + unsigned int next = idx + 1; /* next now is the position after the recursed lookup. */ + + if (delta > 0) + { + if (unlikely (delta + count > HB_MAX_CONTEXT_LENGTH)) + break; + } + else + { + /* NOTE: delta is non-positive. */ + delta = hb_max (delta, (int) next - (int) count); + next -= delta; + } + + /* Shift! */ + memmove (match_positions + next + delta, match_positions + next, + (count - next) * sizeof (match_positions[0])); + next += delta; + count += delta; + + /* Fill in new entries. */ + for (unsigned int j = idx + 1; j < next; j++) + match_positions[j] = match_positions[j - 1] + 1; + + /* And fixup the rest. */ + for (; next < count; next++) + match_positions[next] += delta; + } + + (void) buffer->move_to (end); +} + + + +/* Contextual lookups */ + +struct ContextClosureLookupContext +{ + ContextClosureFuncs funcs; + ContextFormat context_format; + const void *intersects_data; + void *intersects_cache; + void *intersected_glyphs_cache; +}; + +struct ContextCollectGlyphsLookupContext +{ + ContextCollectGlyphsFuncs funcs; + const void *collect_data; +}; + +struct ContextApplyLookupContext +{ + ContextApplyFuncs funcs; + const void *match_data; +}; + +template <typename HBUINT> +static inline bool context_intersects (const hb_set_t *glyphs, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + ContextClosureLookupContext &lookup_context) +{ + return array_is_subset_of (glyphs, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, + lookup_context.intersects_data, + lookup_context.intersects_cache); +} + +template <typename HBUINT> +static inline void context_closure_lookup (hb_closure_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], + unsigned value, /* Index of first glyph in Coverage or Class value in ClassDef table */ + ContextClosureLookupContext &lookup_context) +{ + if (context_intersects (c->glyphs, + inputCount, input, + lookup_context)) + context_closure_recurse_lookups (c, + inputCount, input, + lookupCount, lookupRecord, + value, + lookup_context.context_format, + lookup_context.intersects_data, + lookup_context.funcs.intersected_glyphs, + lookup_context.intersected_glyphs_cache); +} + +template <typename HBUINT> +static inline void context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], + ContextCollectGlyphsLookupContext &lookup_context) +{ + collect_array (c, c->input, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.collect, lookup_context.collect_data); + recurse_lookups (c, + lookupCount, lookupRecord); +} + +template <typename HBUINT> +static inline bool context_would_apply_lookup (hb_would_apply_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount HB_UNUSED, + const LookupRecord lookupRecord[] HB_UNUSED, + const ContextApplyLookupContext &lookup_context) +{ + return would_match_input (c, + inputCount, input, + lookup_context.funcs.match, lookup_context.match_data); +} + +template <typename HBUINT> +HB_ALWAYS_INLINE +static bool context_apply_lookup (hb_ot_apply_context_t *c, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookupCount, + const LookupRecord lookupRecord[], + const ContextApplyLookupContext &lookup_context) +{ + unsigned match_end = 0; + unsigned match_positions[HB_MAX_CONTEXT_LENGTH]; + if (match_input (c, + inputCount, input, + lookup_context.funcs.match, lookup_context.match_data, + &match_end, match_positions)) + { + c->buffer->unsafe_to_break (c->buffer->idx, match_end); + apply_lookup (c, + inputCount, match_positions, + lookupCount, lookupRecord, + match_end); + return true; + } + else + { + c->buffer->unsafe_to_concat (c->buffer->idx, match_end); + return false; + } +} + +template <typename Types> +struct Rule +{ + template <typename T> + friend struct RuleSet; + + bool intersects (const hb_set_t *glyphs, ContextClosureLookupContext &lookup_context) const + { + return context_intersects (glyphs, + inputCount, inputZ.arrayZ, + lookup_context); + } + + void closure (hb_closure_context_t *c, unsigned value, ContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> + (inputZ.as_array ((inputCount ? inputCount - 1 : 0))); + context_closure_lookup (c, + inputCount, inputZ.arrayZ, + lookupCount, lookupRecord.arrayZ, + value, lookup_context); + } + + void closure_lookups (hb_closure_lookups_context_t *c, + ContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + if (!intersects (c->glyphs, lookup_context)) return; + + const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> + (inputZ.as_array (inputCount ? inputCount - 1 : 0)); + recurse_lookups (c, lookupCount, lookupRecord.arrayZ); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + ContextCollectGlyphsLookupContext &lookup_context) const + { + const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> + (inputZ.as_array (inputCount ? inputCount - 1 : 0)); + context_collect_glyphs_lookup (c, + inputCount, inputZ.arrayZ, + lookupCount, lookupRecord.arrayZ, + lookup_context); + } + + bool would_apply (hb_would_apply_context_t *c, + const ContextApplyLookupContext &lookup_context) const + { + const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> + (inputZ.as_array (inputCount ? inputCount - 1 : 0)); + return context_would_apply_lookup (c, + inputCount, inputZ.arrayZ, + lookupCount, lookupRecord.arrayZ, + lookup_context); + } + + bool apply (hb_ot_apply_context_t *c, + const ContextApplyLookupContext &lookup_context) const + { + TRACE_APPLY (this); + const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> + (inputZ.as_array (inputCount ? inputCount - 1 : 0)); + return_trace (context_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context)); + } + + bool serialize (hb_serialize_context_t *c, + const hb_map_t *input_mapping, /* old->new glyphid or class mapping */ + const hb_map_t *lookup_map) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (false); + + out->inputCount = inputCount; + const auto input = inputZ.as_array (inputCount - 1); + for (const auto org : input) + { + HBUINT16 d; + d = input_mapping->get (org); + c->copy (d); + } + + const auto &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> + (inputZ.as_array ((inputCount ? inputCount - 1 : 0))); + + unsigned count = serialize_lookuprecord_array (c, lookupRecord.as_array (lookupCount), lookup_map); + return_trace (c->check_assign (out->lookupCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *klass_map = nullptr) const + { + TRACE_SUBSET (this); + if (unlikely (!inputCount)) return_trace (false); + const auto input = inputZ.as_array (inputCount - 1); + + const hb_map_t *mapping = klass_map == nullptr ? c->plan->glyph_map : klass_map; + if (!hb_all (input, mapping)) return_trace (false); + return_trace (serialize (c->serializer, mapping, lookup_map)); + } + + public: + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + c->check_range (inputZ.arrayZ, + inputZ.item_size * (inputCount ? inputCount - 1 : 0) + + LookupRecord::static_size * lookupCount)); + } + + protected: + HBUINT16 inputCount; /* Total number of glyphs in input + * glyph sequence--includes the first + * glyph */ + HBUINT16 lookupCount; /* Number of LookupRecords */ + UnsizedArrayOf<typename Types::HBUINT> + inputZ; /* Array of match inputs--start with + * second glyph */ +/*UnsizedArrayOf<LookupRecord> + lookupRecordX;*/ /* Array of LookupRecords--in + * design order */ + public: + DEFINE_SIZE_ARRAY (4, inputZ); +}; + +template <typename Types> +struct RuleSet +{ + using Rule = OT::Rule<Types>; + + bool intersects (const hb_set_t *glyphs, + ContextClosureLookupContext &lookup_context) const + { + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; + } + + void closure (hb_closure_context_t *c, unsigned value, + ContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.closure (c, value, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c, + ContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.closure_lookups (c, lookup_context); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + ContextCollectGlyphsLookupContext &lookup_context) const + { + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const Rule &_) { _.collect_glyphs (c, lookup_context); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c, + const ContextApplyLookupContext &lookup_context) const + { + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.would_apply (c, lookup_context); }) + | hb_any + ; + } + + bool apply (hb_ot_apply_context_t *c, + const ContextApplyLookupContext &lookup_context) const + { + TRACE_APPLY (this); + + unsigned num_rules = rule.len; + +#ifndef HB_NO_OT_RULESETS_FAST_PATH + if (HB_OPTIMIZE_SIZE_VAL || num_rules <= 4) +#endif + { + slow: + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const Rule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + + /* This version is optimized for speed by matching the first & second + * components of the rule here, instead of calling into the matching code. + * + * Replicated from LigatureSet::apply(). */ + + 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 = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0; + hb_glyph_info_t *first = nullptr, *second = nullptr; + bool matched = skippy_iter.next (); + if (likely (matched)) + { + first = &c->buffer->info[skippy_iter.idx]; + 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 + { + /* Failed to match a next glyph. Only try applying rules that have + * no further input. */ + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_filter ([&] (const Rule &_) { return _.inputCount <= 1; }) + | hb_map ([&] (const Rule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + matched = skippy_iter.next (); + if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + { + second = &c->buffer->info[skippy_iter.idx]; + unsafe_to2 = skippy_iter.idx + 1; + } + + auto match_input = lookup_context.funcs.match; + auto *input_data = lookup_context.match_data; + for (unsigned int i = 0; i < num_rules; i++) + { + const auto &r = this+rule.arrayZ[i]; + + const auto &input = r.inputZ; + + if (r.inputCount <= 1 || + (!match_input || + match_input (*first, input.arrayZ[0], input_data))) + { + if (!second || + (r.inputCount <= 2 || + (!match_input || + match_input (*second, input.arrayZ[1], input_data))) + ) + { + if (r.apply (c, lookup_context)) + { + if (unsafe_to != (unsigned) -1) + c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to); + return_trace (true); + } + } + else + unsafe_to = unsafe_to2; + } + else + { + if (unsafe_to == (unsigned) -1) + unsafe_to = unsafe_to1; + } + } + if (likely (unsafe_to != (unsigned) -1)) + c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to); + + return_trace (false); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *klass_map = nullptr) 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); + + for (const Offset16To<Rule>& _ : rule) + { + if (!_) continue; + auto o_snap = c->serializer->snapshot (); + auto *o = out->rule.serialize_append (c->serializer); + if (unlikely (!o)) continue; + + if (!o->serialize_subset (c, _, this, lookup_map, klass_map)) + { + out->rule.pop (); + c->serializer->revert (o_snap); + } + } + + bool ret = bool (out->rule); + if (!ret) c->serializer->revert (snap); + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rule.sanitize (c, this)); + } + + protected: + Array16OfOffset16To<Rule> + rule; /* Array of Rule tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, rule); +}; + + +template <typename Types> +struct ContextFormat1_4 +{ + using RuleSet = OT::RuleSet<Types>; + + bool intersects (const hb_set_t *glyphs) const + { + struct ContextClosureLookupContext lookup_context = { + {intersects_glyph, intersected_glyph}, + ContextFormat::SimpleContext, + nullptr + }; + + return + + hb_zip (this+coverage, ruleSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_map ([&] (const RuleSet &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs (); + if (unlikely (!cur_active_glyphs)) return; + get_coverage ().intersect_set (c->previous_parent_active_glyphs (), *cur_active_glyphs); + + struct ContextClosureLookupContext lookup_context = { + {intersects_glyph, intersected_glyph}, + ContextFormat::SimpleContext, + nullptr + }; + + + hb_zip (this+coverage, hb_range ((unsigned) ruleSet.len)) + | hb_filter ([&] (hb_codepoint_t _) { + return c->previous_parent_active_glyphs ().has (_); + }, hb_first) + | hb_map ([&](const hb_pair_t<hb_codepoint_t, unsigned> _) { return hb_pair_t<unsigned, const RuleSet&> (_.first, this+ruleSet[_.second]); }) + | hb_apply ([&] (const hb_pair_t<unsigned, const RuleSet&>& _) { _.second.closure (c, _.first, lookup_context); }) + ; + + c->pop_cur_done_glyphs (); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + struct ContextClosureLookupContext lookup_context = { + {intersects_glyph, nullptr}, + ContextFormat::SimpleContext, + nullptr + }; + + + hb_zip (this+coverage, ruleSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c, lookup_context); }) + ; + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + (this+coverage).collect_coverage (c->input); + + struct ContextCollectGlyphsLookupContext lookup_context = { + {collect_glyph}, + nullptr + }; + + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c) const + { + const RuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; + struct ContextApplyLookupContext lookup_context = { + {match_glyph}, + nullptr + }; + return rule_set.would_apply (c, lookup_context); + } + + const Coverage &get_coverage () const { return this+coverage; } + + 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 RuleSet &rule_set = this+ruleSet[index]; + struct ContextApplyLookupContext lookup_context = { + {match_glyph}, + nullptr + }; + return_trace (rule_set.apply (c, lookup_context)); + } + + 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; + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? &c->plan->gsub_lookups : &c->plan->gpos_lookups; + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + hb_zip (this+coverage, ruleSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ruleSet, this, lookup_map), 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) && ruleSet.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + Array16Of<typename Types::template OffsetTo<RuleSet>> + ruleSet; /* Array of RuleSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (2 + 2 * Types::size, ruleSet); +}; + + +template <typename Types> +struct ContextFormat2_5 +{ + using RuleSet = OT::RuleSet<SmallTypes>; + + bool intersects (const hb_set_t *glyphs) const + { + if (!(this+coverage).intersects (glyphs)) + return false; + + const ClassDef &class_def = this+classDef; + + hb_map_t cache; + struct ContextClosureLookupContext lookup_context = { + {intersects_class, nullptr}, + ContextFormat::ClassBasedContext, + &class_def, + &cache + }; + + hb_set_t retained_coverage_glyphs; + (this+coverage).intersect_set (*glyphs, retained_coverage_glyphs); + + hb_set_t coverage_glyph_classes; + class_def.intersected_classes (&retained_coverage_glyphs, &coverage_glyph_classes); + + + return + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_map ([&] (const hb_pair_t<unsigned, const RuleSet &> p) + { return class_def.intersects_class (glyphs, p.first) && + coverage_glyph_classes.has (p.first) && + p.second.intersects (glyphs, lookup_context); }) + | hb_any + ; + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + if (!(this+coverage).intersects (c->glyphs)) + return; + + hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs (); + if (unlikely (!cur_active_glyphs)) return; + get_coverage ().intersect_set (c->previous_parent_active_glyphs (), + *cur_active_glyphs); + + const ClassDef &class_def = this+classDef; + + hb_map_t cache; + intersected_class_cache_t intersected_cache; + struct ContextClosureLookupContext lookup_context = { + {intersects_class, intersected_class_glyphs}, + ContextFormat::ClassBasedContext, + &class_def, + &cache, + &intersected_cache + }; + + + hb_enumerate (ruleSet) + | hb_filter ([&] (unsigned _) + { return class_def.intersects_class (&c->parent_active_glyphs (), _); }, + hb_first) + | hb_apply ([&] (const hb_pair_t<unsigned, const typename Types::template OffsetTo<RuleSet>&> _) + { + const RuleSet& rule_set = this+_.second; + rule_set.closure (c, _.first, lookup_context); + }) + ; + + c->pop_cur_done_glyphs (); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (!(this+coverage).intersects (c->glyphs)) + return; + + const ClassDef &class_def = this+classDef; + + hb_map_t cache; + struct ContextClosureLookupContext lookup_context = { + {intersects_class, nullptr}, + ContextFormat::ClassBasedContext, + &class_def, + &cache + }; + + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_filter ([&] (const hb_pair_t<unsigned, const RuleSet &> p) + { return class_def.intersects_class (c->glyphs, p.first); }) + | hb_map (hb_second) + | hb_apply ([&] (const RuleSet & _) + { _.closure_lookups (c, lookup_context); }); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + (this+coverage).collect_coverage (c->input); + + const ClassDef &class_def = this+classDef; + struct ContextCollectGlyphsLookupContext lookup_context = { + {collect_class}, + &class_def + }; + + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const RuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c) const + { + const ClassDef &class_def = this+classDef; + unsigned int index = class_def.get_class (c->glyphs[0]); + const RuleSet &rule_set = this+ruleSet[index]; + struct ContextApplyLookupContext lookup_context = { + {match_class}, + &class_def + }; + return rule_set.would_apply (c, lookup_context); + } + + const Coverage &get_coverage () const { return this+coverage; } + + unsigned cache_cost () const + { + unsigned c = (this+classDef).cost () * ruleSet.len; + return c >= 4 ? c : 0; + } + bool cache_func (hb_ot_apply_context_t *c, bool enter) const + { + if (enter) + { + if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) + return false; + auto &info = c->buffer->info; + unsigned count = c->buffer->len; + for (unsigned i = 0; i < count; i++) + info[i].syllable() = 255; + c->new_syllables = 255; + return true; + } + else + { + c->new_syllables = (unsigned) -1; + HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); + return true; + } + } + + bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } + bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } + bool _apply (hb_ot_apply_context_t *c, bool cached) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const ClassDef &class_def = this+classDef; + + struct ContextApplyLookupContext lookup_context = { + {cached ? match_class_cached : match_class}, + &class_def + }; + + if (cached && c->buffer->cur().syllable() < 255) + index = c->buffer->cur().syllable (); + else + index = class_def.get_class (c->buffer->cur().codepoint); + const RuleSet &rule_set = this+ruleSet[index]; + return_trace (rule_set.apply (c, lookup_context)); + } + + 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; + if (unlikely (!out->coverage.serialize_subset (c, coverage, this))) + return_trace (false); + + hb_map_t klass_map; + out->classDef.serialize_subset (c, classDef, this, &klass_map); + + const hb_set_t* glyphset = c->plan->glyphset_gsub (); + hb_set_t retained_coverage_glyphs; + (this+coverage).intersect_set (*glyphset, retained_coverage_glyphs); + + hb_set_t coverage_glyph_classes; + (this+classDef).intersected_classes (&retained_coverage_glyphs, &coverage_glyph_classes); + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? &c->plan->gsub_lookups : &c->plan->gpos_lookups; + bool ret = true; + int non_zero_index = -1, index = 0; + auto snapshot = c->serializer->snapshot(); + for (const auto& _ : + hb_enumerate (ruleSet) + | hb_filter (klass_map, hb_first)) + { + auto *o = out->ruleSet.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + + if (coverage_glyph_classes.has (_.first) && + o->serialize_subset (c, _.second, this, lookup_map, &klass_map)) { + non_zero_index = index; + snapshot = c->serializer->snapshot(); + } + + index++; + } + + if (!ret || non_zero_index == -1) return_trace (false); + + //prune empty trailing ruleSets + --index; + while (index > non_zero_index) + { + out->ruleSet.pop (); + index--; + } + c->serializer->revert (snapshot); + + return_trace (bool (out->ruleSet)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + typename Types::template OffsetTo<ClassDef> + classDef; /* Offset to glyph ClassDef table--from + * beginning of table */ + Array16Of<typename Types::template OffsetTo<RuleSet>> + ruleSet; /* Array of RuleSet tables + * ordered by class */ + public: + DEFINE_SIZE_ARRAY (4 + 2 * Types::size, ruleSet); +}; + + +struct ContextFormat3 +{ + using RuleSet = OT::RuleSet<SmallTypes>; + + bool intersects (const hb_set_t *glyphs) const + { + if (!(this+coverageZ[0]).intersects (glyphs)) + return false; + + struct ContextClosureLookupContext lookup_context = { + {intersects_coverage, nullptr}, + ContextFormat::CoverageBasedContext, + this + }; + return context_intersects (glyphs, + glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), + lookup_context); + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + if (!(this+coverageZ[0]).intersects (c->glyphs)) + return; + + hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs (); + if (unlikely (!cur_active_glyphs)) return; + get_coverage ().intersect_set (c->previous_parent_active_glyphs (), + *cur_active_glyphs); + + const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount)); + struct ContextClosureLookupContext lookup_context = { + {intersects_coverage, intersected_coverage_glyphs}, + ContextFormat::CoverageBasedContext, + this + }; + context_closure_lookup (c, + glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), + lookupCount, lookupRecord, + 0, lookup_context); + + c->pop_cur_done_glyphs (); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (!intersects (c->glyphs)) + return; + const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount)); + recurse_lookups (c, lookupCount, lookupRecord); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + (this+coverageZ[0]).collect_coverage (c->input); + + const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount)); + struct ContextCollectGlyphsLookupContext lookup_context = { + {collect_coverage}, + this + }; + + context_collect_glyphs_lookup (c, + glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), + lookupCount, lookupRecord, + lookup_context); + } + + bool would_apply (hb_would_apply_context_t *c) const + { + const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount)); + struct ContextApplyLookupContext lookup_context = { + {match_coverage}, + this + }; + return context_would_apply_lookup (c, + glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), + lookupCount, lookupRecord, + lookup_context); + } + + const Coverage &get_coverage () const { return this+coverageZ[0]; } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount)); + struct ContextApplyLookupContext lookup_context = { + {match_coverage}, + this + }; + return_trace (context_apply_lookup (c, glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1), lookupCount, lookupRecord, lookup_context)); + } + + 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; + out->glyphCount = glyphCount; + + auto coverages = coverageZ.as_array (glyphCount); + + for (const Offset16To<Coverage>& offset : coverages) + { + /* TODO(subset) This looks like should not be necessary to write this way. */ + auto *o = c->serializer->allocate_size<Offset16To<Coverage>> (Offset16To<Coverage>::static_size); + if (unlikely (!o)) return_trace (false); + if (!o->serialize_subset (c, offset, this)) return_trace (false); + } + + const auto& lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>> (coverageZ.as_array (glyphCount)); + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? &c->plan->gsub_lookups : &c->plan->gpos_lookups; + + + unsigned count = serialize_lookuprecord_array (c->serializer, lookupRecord.as_array (lookupCount), lookup_map); + return_trace (c->serializer->check_assign (out->lookupCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); + unsigned int count = glyphCount; + if (unlikely (!count)) return_trace (false); /* We want to access coverageZ[0] freely. */ + if (unlikely (!c->check_array (coverageZ.arrayZ, count))) return_trace (false); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!coverageZ[i].sanitize (c, this))) return_trace (false); + const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount)); + return_trace (likely (c->check_array (lookupRecord, lookupCount))); + } + + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + HBUINT16 glyphCount; /* Number of glyphs in the input glyph + * sequence */ + HBUINT16 lookupCount; /* Number of LookupRecords */ + UnsizedArrayOf<Offset16To<Coverage>> + coverageZ; /* Array of offsets to Coverage + * table in glyph sequence order */ +/*UnsizedArrayOf<LookupRecord> + lookupRecordX;*/ /* Array of LookupRecords--in + * design order */ + public: + DEFINE_SIZE_ARRAY (6, coverageZ); +}; + +struct Context +{ + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); + case 5: return_trace (c->dispatch (u.format5, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + ContextFormat1_4<SmallTypes> format1; + ContextFormat2_5<SmallTypes> format2; + ContextFormat3 format3; +#ifndef HB_NO_BEYOND_64K + ContextFormat1_4<MediumTypes> format4; + ContextFormat2_5<MediumTypes> format5; +#endif + } u; +}; + + +/* Chaining Contextual lookups */ + +struct ChainContextClosureLookupContext +{ + ContextClosureFuncs funcs; + ContextFormat context_format; + const void *intersects_data[3]; + void *intersects_cache[3]; + void *intersected_glyphs_cache; +}; + +struct ChainContextCollectGlyphsLookupContext +{ + ContextCollectGlyphsFuncs funcs; + const void *collect_data[3]; +}; + +struct ChainContextApplyLookupContext +{ + ChainContextApplyFuncs funcs; + const void *match_data[3]; +}; + +template <typename HBUINT> +static inline bool chain_context_intersects (const hb_set_t *glyphs, + unsigned int backtrackCount, + const HBUINT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const HBUINT lookahead[], + ChainContextClosureLookupContext &lookup_context) +{ + return array_is_subset_of (glyphs, + backtrackCount, backtrack, + lookup_context.funcs.intersects, + lookup_context.intersects_data[0], + lookup_context.intersects_cache[0]) + && array_is_subset_of (glyphs, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.intersects, + lookup_context.intersects_data[1], + lookup_context.intersects_cache[1]) + && array_is_subset_of (glyphs, + lookaheadCount, lookahead, + lookup_context.funcs.intersects, + lookup_context.intersects_data[2], + lookup_context.intersects_cache[2]); +} + +template <typename HBUINT> +static inline void chain_context_closure_lookup (hb_closure_context_t *c, + unsigned int backtrackCount, + const HBUINT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const HBUINT lookahead[], + unsigned int lookupCount, + const LookupRecord lookupRecord[], + unsigned value, + ChainContextClosureLookupContext &lookup_context) +{ + if (chain_context_intersects (c->glyphs, + backtrackCount, backtrack, + inputCount, input, + lookaheadCount, lookahead, + lookup_context)) + context_closure_recurse_lookups (c, + inputCount, input, + lookupCount, lookupRecord, + value, + lookup_context.context_format, + lookup_context.intersects_data[1], + lookup_context.funcs.intersected_glyphs, + lookup_context.intersected_glyphs_cache); +} + +template <typename HBUINT> +static inline void chain_context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c, + unsigned int backtrackCount, + const HBUINT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const HBUINT lookahead[], + unsigned int lookupCount, + const LookupRecord lookupRecord[], + ChainContextCollectGlyphsLookupContext &lookup_context) +{ + collect_array (c, c->before, + backtrackCount, backtrack, + lookup_context.funcs.collect, lookup_context.collect_data[0]); + collect_array (c, c->input, + inputCount ? inputCount - 1 : 0, input, + lookup_context.funcs.collect, lookup_context.collect_data[1]); + collect_array (c, c->after, + lookaheadCount, lookahead, + lookup_context.funcs.collect, lookup_context.collect_data[2]); + recurse_lookups (c, + lookupCount, lookupRecord); +} + +template <typename HBUINT> +static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c, + unsigned int backtrackCount, + const HBUINT backtrack[] HB_UNUSED, + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const HBUINT lookahead[] HB_UNUSED, + unsigned int lookupCount HB_UNUSED, + const LookupRecord lookupRecord[] HB_UNUSED, + const ChainContextApplyLookupContext &lookup_context) +{ + return (c->zero_context ? !backtrackCount && !lookaheadCount : true) + && would_match_input (c, + inputCount, input, + lookup_context.funcs.match[1], lookup_context.match_data[1]); +} + +template <typename HBUINT> +HB_ALWAYS_INLINE +static bool chain_context_apply_lookup (hb_ot_apply_context_t *c, + unsigned int backtrackCount, + const HBUINT backtrack[], + unsigned int inputCount, /* Including the first glyph (not matched) */ + const HBUINT input[], /* Array of input values--start with second glyph */ + unsigned int lookaheadCount, + const HBUINT lookahead[], + unsigned int lookupCount, + const LookupRecord lookupRecord[], + const ChainContextApplyLookupContext &lookup_context) +{ + unsigned end_index = c->buffer->idx; + unsigned match_end = 0; + unsigned match_positions[HB_MAX_CONTEXT_LENGTH]; + if (!(match_input (c, + inputCount, input, + lookup_context.funcs.match[1], lookup_context.match_data[1], + &match_end, match_positions) && (end_index = match_end) + && match_lookahead (c, + lookaheadCount, lookahead, + lookup_context.funcs.match[2], lookup_context.match_data[2], + match_end, &end_index))) + { + c->buffer->unsafe_to_concat (c->buffer->idx, end_index); + return false; + } + + unsigned start_index = c->buffer->out_len; + if (!match_backtrack (c, + backtrackCount, backtrack, + lookup_context.funcs.match[0], lookup_context.match_data[0], + &start_index)) + { + c->buffer->unsafe_to_concat_from_outbuffer (start_index, end_index); + return false; + } + + c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index); + apply_lookup (c, + inputCount, match_positions, + lookupCount, lookupRecord, + match_end); + return true; +} + +template <typename Types> +struct ChainRule +{ + template <typename T> + friend struct ChainRuleSet; + + bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + return chain_context_intersects (glyphs, + backtrack.len, backtrack.arrayZ, + input.lenP1, input.arrayZ, + lookahead.len, lookahead.arrayZ, + lookup_context); + } + + void closure (hb_closure_context_t *c, unsigned value, + ChainContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + chain_context_closure_lookup (c, + backtrack.len, backtrack.arrayZ, + input.lenP1, input.arrayZ, + lookahead.len, lookahead.arrayZ, + lookup.len, lookup.arrayZ, + value, + lookup_context); + } + + void closure_lookups (hb_closure_lookups_context_t *c, + ChainContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + if (!intersects (c->glyphs, lookup_context)) return; + + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + recurse_lookups (c, lookup.len, lookup.arrayZ); + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, + ChainContextCollectGlyphsLookupContext &lookup_context) const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + chain_context_collect_glyphs_lookup (c, + backtrack.len, backtrack.arrayZ, + input.lenP1, input.arrayZ, + lookahead.len, lookahead.arrayZ, + lookup.len, lookup.arrayZ, + lookup_context); + } + + bool would_apply (hb_would_apply_context_t *c, + const ChainContextApplyLookupContext &lookup_context) const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + return chain_context_would_apply_lookup (c, + backtrack.len, backtrack.arrayZ, + input.lenP1, input.arrayZ, + lookahead.len, lookahead.arrayZ, lookup.len, + lookup.arrayZ, lookup_context); + } + + bool apply (hb_ot_apply_context_t *c, + const ChainContextApplyLookupContext &lookup_context) const + { + TRACE_APPLY (this); + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + return_trace (chain_context_apply_lookup (c, + backtrack.len, backtrack.arrayZ, + input.lenP1, input.arrayZ, + lookahead.len, lookahead.arrayZ, lookup.len, + lookup.arrayZ, lookup_context)); + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void serialize_array (hb_serialize_context_t *c, + HBUINT16 len, + Iterator it) const + { + c->copy (len); + for (const auto g : it) + c->copy ((HBUINT16) g); + } + + bool serialize (hb_serialize_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_map, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SERIALIZE (this); + + const hb_map_t *mapping = backtrack_map; + serialize_array (c, backtrack.len, + backtrack.iter () + | hb_map (mapping)); + + const auto &input = StructAfter<decltype (inputX)> (backtrack); + if (input_map) mapping = input_map; + serialize_array (c, input.lenP1, + input.iter () + | hb_map (mapping)); + + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + if (lookahead_map) mapping = lookahead_map; + serialize_array (c, lookahead.len, + lookahead.iter () + | hb_map (mapping)); + + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + + HBUINT16* lookupCount = c->embed (&(lookup.len)); + if (!lookupCount) return_trace (false); + + unsigned count = serialize_lookuprecord_array (c, lookup.as_array (), lookup_map); + return_trace (c->check_assign (*lookupCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_map = nullptr, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SUBSET (this); + + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + + if (!backtrack_map) + { + const hb_set_t &glyphset = *c->plan->glyphset_gsub (); + if (!hb_all (backtrack, glyphset) || + !hb_all (input, glyphset) || + !hb_all (lookahead, glyphset)) + return_trace (false); + + serialize (c->serializer, lookup_map, c->plan->glyph_map); + } + else + { + if (!hb_all (backtrack, backtrack_map) || + !hb_all (input, input_map) || + !hb_all (lookahead, lookahead_map)) + return_trace (false); + + serialize (c->serializer, lookup_map, backtrack_map, input_map, lookahead_map); + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + /* Hyper-optimized sanitized because this is really hot. */ + if (unlikely (!backtrack.len.sanitize (c))) return_trace (false); + hb_barrier (); + const auto &input = StructAfter<decltype (inputX)> (backtrack); + if (unlikely (!input.lenP1.sanitize (c))) return_trace (false); + hb_barrier (); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + if (unlikely (!lookahead.len.sanitize (c))) return_trace (false); + hb_barrier (); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + return_trace (likely (lookup.sanitize (c))); + } + + protected: + Array16Of<typename Types::HBUINT> + backtrack; /* Array of backtracking values + * (to be matched before the input + * sequence) */ + HeadlessArray16Of<typename Types::HBUINT> + inputX; /* Array of input values (start with + * second glyph) */ + Array16Of<typename Types::HBUINT> + lookaheadX; /* Array of lookahead values's (to be + * matched after the input sequence) */ + Array16Of<LookupRecord> + lookupX; /* Array of LookupRecords--in + * design order) */ + public: + DEFINE_SIZE_MIN (8); +}; + +template <typename Types> +struct ChainRuleSet +{ + using ChainRule = OT::ChainRule<Types>; + + bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const + { + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; + } + void closure (hb_closure_context_t *c, unsigned value, ChainContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.closure (c, value, lookup_context); }) + ; + } + + void closure_lookups (hb_closure_lookups_context_t *c, + ChainContextClosureLookupContext &lookup_context) const + { + if (unlikely (c->lookup_limit_exceeded ())) return; + + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.closure_lookups (c, lookup_context); }) + ; + } + + void collect_glyphs (hb_collect_glyphs_context_t *c, ChainContextCollectGlyphsLookupContext &lookup_context) const + { + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRule &_) { _.collect_glyphs (c, lookup_context); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c, + const ChainContextApplyLookupContext &lookup_context) const + { + return + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.would_apply (c, lookup_context); }) + | hb_any + ; + } + + bool apply (hb_ot_apply_context_t *c, + const ChainContextApplyLookupContext &lookup_context) const + { + TRACE_APPLY (this); + + unsigned num_rules = rule.len; + +#ifndef HB_NO_OT_RULESETS_FAST_PATH + if (HB_OPTIMIZE_SIZE_VAL || num_rules <= 4) +#endif + { + slow: + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + + /* This version is optimized for speed by matching the first & second + * components of the rule here, instead of calling into the matching code. + * + * Replicated from LigatureSet::apply(). */ + + 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 = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0; + hb_glyph_info_t *first = nullptr, *second = nullptr; + bool matched = skippy_iter.next (); + if (likely (matched)) + { + first = &c->buffer->info[skippy_iter.idx]; + unsafe_to1 = 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 + { + /* Failed to match a next glyph. Only try applying rules that have + * no further input and lookahead. */ + return_trace ( + + hb_iter (rule) + | hb_map (hb_add (this)) + | hb_filter ([&] (const ChainRule &_) + { + const auto &input = StructAfter<decltype (_.inputX)> (_.backtrack); + const auto &lookahead = StructAfter<decltype (_.lookaheadX)> (input); + return input.lenP1 <= 1 && lookahead.len == 0; + }) + | hb_map ([&] (const ChainRule &_) { return _.apply (c, lookup_context); }) + | hb_any + ) + ; + } + matched = skippy_iter.next (); + if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + { + second = &c->buffer->info[skippy_iter.idx]; + unsafe_to2 = skippy_iter.idx + 1; + } + + auto match_input = lookup_context.funcs.match[1]; + auto match_lookahead = lookup_context.funcs.match[2]; + auto *input_data = lookup_context.match_data[1]; + auto *lookahead_data = lookup_context.match_data[2]; + for (unsigned int i = 0; i < num_rules; i++) + { + const auto &r = this+rule.arrayZ[i]; + + const auto &input = StructAfter<decltype (r.inputX)> (r.backtrack); + const auto &lookahead = StructAfter<decltype (r.lookaheadX)> (input); + + unsigned lenP1 = hb_max ((unsigned) input.lenP1, 1u); + if (lenP1 > 1 ? + (!match_input || + match_input (*first, input.arrayZ[0], input_data)) + : + (!lookahead.len || !match_lookahead || + match_lookahead (*first, lookahead.arrayZ[0], lookahead_data))) + { + if (!second || + (lenP1 > 2 ? + (!match_input || + match_input (*second, input.arrayZ[1], input_data)) + : + (lookahead.len <= 2 - lenP1 || !match_lookahead || + match_lookahead (*second, lookahead.arrayZ[2 - lenP1], lookahead_data)))) + { + if (r.apply (c, lookup_context)) + { + if (unsafe_to != (unsigned) -1) + c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to); + return_trace (true); + } + } + else + unsafe_to = unsafe_to2; + } + else + { + if (unsafe_to == (unsigned) -1) + unsafe_to = unsafe_to1; + } + } + if (likely (unsafe_to != (unsigned) -1)) + c->buffer->unsafe_to_concat (c->buffer->idx, unsafe_to); + + return_trace (false); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *lookup_map, + const hb_map_t *backtrack_klass_map = nullptr, + const hb_map_t *input_klass_map = nullptr, + const hb_map_t *lookahead_klass_map = nullptr) 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); + + for (const Offset16To<ChainRule>& _ : rule) + { + if (!_) continue; + auto o_snap = c->serializer->snapshot (); + auto *o = out->rule.serialize_append (c->serializer); + if (unlikely (!o)) continue; + + if (!o->serialize_subset (c, _, this, + lookup_map, + backtrack_klass_map, + input_klass_map, + lookahead_klass_map)) + { + out->rule.pop (); + c->serializer->revert (o_snap); + } + } + + bool ret = bool (out->rule); + if (!ret) c->serializer->revert (snap); + + return_trace (ret); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (rule.sanitize (c, this)); + } + + protected: + Array16OfOffset16To<ChainRule> + rule; /* Array of ChainRule tables + * ordered by preference */ + public: + DEFINE_SIZE_ARRAY (2, rule); +}; + +template <typename Types> +struct ChainContextFormat1_4 +{ + using ChainRuleSet = OT::ChainRuleSet<Types>; + + bool intersects (const hb_set_t *glyphs) const + { + struct ChainContextClosureLookupContext lookup_context = { + {intersects_glyph, intersected_glyph}, + ContextFormat::SimpleContext, + {nullptr, nullptr, nullptr} + }; + + return + + hb_zip (this+coverage, ruleSet) + | hb_filter (*glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_map ([&] (const ChainRuleSet &_) { return _.intersects (glyphs, lookup_context); }) + | hb_any + ; + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs (); + if (unlikely (!cur_active_glyphs)) return; + get_coverage ().intersect_set (c->previous_parent_active_glyphs (), + *cur_active_glyphs); + + struct ChainContextClosureLookupContext lookup_context = { + {intersects_glyph, intersected_glyph}, + ContextFormat::SimpleContext, + {nullptr, nullptr, nullptr} + }; + + + hb_zip (this+coverage, hb_range ((unsigned) ruleSet.len)) + | hb_filter ([&] (hb_codepoint_t _) { + return c->previous_parent_active_glyphs ().has (_); + }, hb_first) + | hb_map ([&](const hb_pair_t<hb_codepoint_t, unsigned> _) { return hb_pair_t<unsigned, const ChainRuleSet&> (_.first, this+ruleSet[_.second]); }) + | hb_apply ([&] (const hb_pair_t<unsigned, const ChainRuleSet&>& _) { _.second.closure (c, _.first, lookup_context); }) + ; + + c->pop_cur_done_glyphs (); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + struct ChainContextClosureLookupContext lookup_context = { + {intersects_glyph, nullptr}, + ContextFormat::SimpleContext, + {nullptr, nullptr, nullptr} + }; + + + hb_zip (this+coverage, ruleSet) + | hb_filter (*c->glyphs, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c, lookup_context); }) + ; + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + (this+coverage).collect_coverage (c->input); + + struct ChainContextCollectGlyphsLookupContext lookup_context = { + {collect_glyph}, + {nullptr, nullptr, nullptr} + }; + + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c) const + { + const ChainRuleSet &rule_set = this+ruleSet[(this+coverage).get_coverage (c->glyphs[0])]; + struct ChainContextApplyLookupContext lookup_context = { + {{match_glyph, match_glyph, match_glyph}}, + {nullptr, nullptr, nullptr} + }; + return rule_set.would_apply (c, lookup_context); + } + + const Coverage &get_coverage () const { return this+coverage; } + + 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 ChainRuleSet &rule_set = this+ruleSet[index]; + struct ChainContextApplyLookupContext lookup_context = { + {{match_glyph, match_glyph, match_glyph}}, + {nullptr, nullptr, nullptr} + }; + return_trace (rule_set.apply (c, lookup_context)); + } + + 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; + + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? &c->plan->gsub_lookups : &c->plan->gpos_lookups; + hb_sorted_vector_t<hb_codepoint_t> new_coverage; + + hb_zip (this+coverage, ruleSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ruleSet, this, lookup_map), 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) && ruleSet.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 1 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + Array16Of<typename Types::template OffsetTo<ChainRuleSet>> + ruleSet; /* Array of ChainRuleSet tables + * ordered by Coverage Index */ + public: + DEFINE_SIZE_ARRAY (2 + 2 * Types::size, ruleSet); +}; + +template <typename Types> +struct ChainContextFormat2_5 +{ + using ChainRuleSet = OT::ChainRuleSet<SmallTypes>; + + bool intersects (const hb_set_t *glyphs) const + { + if (!(this+coverage).intersects (glyphs)) + return false; + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + hb_map_t caches[3] = {}; + struct ChainContextClosureLookupContext lookup_context = { + {intersects_class, nullptr}, + ContextFormat::ClassBasedContext, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def}, + {&caches[0], &caches[1], &caches[2]} + }; + + hb_set_t retained_coverage_glyphs; + (this+coverage).intersect_set (*glyphs, retained_coverage_glyphs); + + hb_set_t coverage_glyph_classes; + input_class_def.intersected_classes (&retained_coverage_glyphs, &coverage_glyph_classes); + + return + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_map ([&] (const hb_pair_t<unsigned, const ChainRuleSet &> p) + { return input_class_def.intersects_class (glyphs, p.first) && + coverage_glyph_classes.has (p.first) && + p.second.intersects (glyphs, lookup_context); }) + | hb_any + ; + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + if (!(this+coverage).intersects (c->glyphs)) + return; + + hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs (); + if (unlikely (!cur_active_glyphs)) return; + get_coverage ().intersect_set (c->previous_parent_active_glyphs (), + *cur_active_glyphs); + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + hb_map_t caches[3] = {}; + intersected_class_cache_t intersected_cache; + struct ChainContextClosureLookupContext lookup_context = { + {intersects_class, intersected_class_glyphs}, + ContextFormat::ClassBasedContext, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def}, + {&caches[0], &caches[1], &caches[2]}, + &intersected_cache + }; + + + hb_enumerate (ruleSet) + | hb_filter ([&] (unsigned _) + { return input_class_def.intersects_class (&c->parent_active_glyphs (), _); }, + hb_first) + | hb_apply ([&] (const hb_pair_t<unsigned, const typename Types::template OffsetTo<ChainRuleSet>&> _) + { + const ChainRuleSet& chainrule_set = this+_.second; + chainrule_set.closure (c, _.first, lookup_context); + }) + ; + + c->pop_cur_done_glyphs (); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (!(this+coverage).intersects (c->glyphs)) + return; + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + hb_map_t caches[3] = {}; + struct ChainContextClosureLookupContext lookup_context = { + {intersects_class, nullptr}, + ContextFormat::ClassBasedContext, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def}, + {&caches[0], &caches[1], &caches[2]} + }; + + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_enumerate + | hb_filter([&] (unsigned klass) + { return input_class_def.intersects_class (c->glyphs, klass); }, hb_first) + | hb_map (hb_second) + | hb_apply ([&] (const ChainRuleSet &_) + { _.closure_lookups (c, lookup_context); }) + ; + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + (this+coverage).collect_coverage (c->input); + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + struct ChainContextCollectGlyphsLookupContext lookup_context = { + {collect_class}, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def} + }; + + + hb_iter (ruleSet) + | hb_map (hb_add (this)) + | hb_apply ([&] (const ChainRuleSet &_) { _.collect_glyphs (c, lookup_context); }) + ; + } + + bool would_apply (hb_would_apply_context_t *c) const + { + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + unsigned int index = input_class_def.get_class (c->glyphs[0]); + const ChainRuleSet &rule_set = this+ruleSet[index]; + struct ChainContextApplyLookupContext lookup_context = { + {{match_class, match_class, match_class}}, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def} + }; + return rule_set.would_apply (c, lookup_context); + } + + const Coverage &get_coverage () const { return this+coverage; } + + unsigned cache_cost () const + { + unsigned c = (this+lookaheadClassDef).cost () * ruleSet.len; + return c >= 4 ? c : 0; + } + bool cache_func (hb_ot_apply_context_t *c, bool enter) const + { + if (enter) + { + if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) + return false; + auto &info = c->buffer->info; + unsigned count = c->buffer->len; + for (unsigned i = 0; i < count; i++) + info[i].syllable() = 255; + c->new_syllables = 255; + return true; + } + else + { + c->new_syllables = (unsigned) -1; + HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); + return true; + } + } + + bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } + bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } + bool _apply (hb_ot_apply_context_t *c, bool cached) const + { + TRACE_APPLY (this); + unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const ClassDef &backtrack_class_def = this+backtrackClassDef; + const ClassDef &input_class_def = this+inputClassDef; + const ClassDef &lookahead_class_def = this+lookaheadClassDef; + + /* match_class_caches1 is slightly faster. Use it for lookahead, + * which is typically longer. */ + struct ChainContextApplyLookupContext lookup_context = { + {{cached && &backtrack_class_def == &lookahead_class_def ? match_class_cached1 : match_class, + cached ? match_class_cached2 : match_class, + cached ? match_class_cached1 : match_class}}, + {&backtrack_class_def, + &input_class_def, + &lookahead_class_def} + }; + + // Note: Corresponds to match_class_cached2 + if (cached && ((c->buffer->cur().syllable() & 0xF0) >> 4) < 15) + index = (c->buffer->cur().syllable () & 0xF0) >> 4; + else + index = input_class_def.get_class (c->buffer->cur().codepoint); + const ChainRuleSet &rule_set = this+ruleSet[index]; + return_trace (rule_set.apply (c, lookup_context)); + } + + 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; + out->coverage.serialize_subset (c, coverage, this); + + hb_map_t backtrack_klass_map; + hb_map_t input_klass_map; + hb_map_t lookahead_klass_map; + + out->backtrackClassDef.serialize_subset (c, backtrackClassDef, this, &backtrack_klass_map); + // TODO: subset inputClassDef based on glyphs survived in Coverage subsetting + out->inputClassDef.serialize_subset (c, inputClassDef, this, &input_klass_map); + out->lookaheadClassDef.serialize_subset (c, lookaheadClassDef, this, &lookahead_klass_map); + + if (unlikely (!c->serializer->propagate_error (backtrack_klass_map, + input_klass_map, + lookahead_klass_map))) + return_trace (false); + + const hb_set_t* glyphset = c->plan->glyphset_gsub (); + hb_set_t retained_coverage_glyphs; + (this+coverage).intersect_set (*glyphset, retained_coverage_glyphs); + + hb_set_t coverage_glyph_classes; + (this+inputClassDef).intersected_classes (&retained_coverage_glyphs, &coverage_glyph_classes); + + int non_zero_index = -1, index = 0; + bool ret = true; + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? &c->plan->gsub_lookups : &c->plan->gpos_lookups; + auto last_non_zero = c->serializer->snapshot (); + for (const auto& _ : + hb_enumerate (ruleSet) + | hb_filter (input_klass_map, hb_first)) + { + auto *o = out->ruleSet.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + if (coverage_glyph_classes.has (_.first) && + o->serialize_subset (c, _.second, this, + lookup_map, + &backtrack_klass_map, + &input_klass_map, + &lookahead_klass_map)) + { + last_non_zero = c->serializer->snapshot (); + non_zero_index = index; + } + + index++; + } + + if (!ret || non_zero_index == -1) return_trace (false); + + // prune empty trailing ruleSets + if (index > non_zero_index) { + c->serializer->revert (last_non_zero); + out->ruleSet.len = non_zero_index + 1; + } + + return_trace (bool (out->ruleSet)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (coverage.sanitize (c, this) && + backtrackClassDef.sanitize (c, this) && + inputClassDef.sanitize (c, this) && + lookaheadClassDef.sanitize (c, this) && + ruleSet.sanitize (c, this)); + } + + protected: + HBUINT16 format; /* Format identifier--format = 2 */ + typename Types::template OffsetTo<Coverage> + coverage; /* Offset to Coverage table--from + * beginning of table */ + typename Types::template OffsetTo<ClassDef> + backtrackClassDef; /* Offset to glyph ClassDef table + * containing backtrack sequence + * data--from beginning of table */ + typename Types::template OffsetTo<ClassDef> + inputClassDef; /* Offset to glyph ClassDef + * table containing input sequence + * data--from beginning of table */ + typename Types::template OffsetTo<ClassDef> + lookaheadClassDef; /* Offset to glyph ClassDef table + * containing lookahead sequence + * data--from beginning of table */ + Array16Of<typename Types::template OffsetTo<ChainRuleSet>> + ruleSet; /* Array of ChainRuleSet tables + * ordered by class */ + public: + DEFINE_SIZE_ARRAY (4 + 4 * Types::size, ruleSet); +}; + +struct ChainContextFormat3 +{ + using RuleSet = OT::RuleSet<SmallTypes>; + + bool intersects (const hb_set_t *glyphs) const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + + if (!(this+input[0]).intersects (glyphs)) + return false; + + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + struct ChainContextClosureLookupContext lookup_context = { + {intersects_coverage, nullptr}, + ContextFormat::CoverageBasedContext, + {this, this, this} + }; + return chain_context_intersects (glyphs, + backtrack.len, (const HBUINT16 *) backtrack.arrayZ, + input.len, (const HBUINT16 *) input.arrayZ + 1, + lookahead.len, (const HBUINT16 *) lookahead.arrayZ, + lookup_context); + } + + bool may_have_non_1to1 () const + { return true; } + + void closure (hb_closure_context_t *c) const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + + if (!(this+input[0]).intersects (c->glyphs)) + return; + + hb_set_t* cur_active_glyphs = c->push_cur_active_glyphs (); + if (unlikely (!cur_active_glyphs)) + return; + get_coverage ().intersect_set (c->previous_parent_active_glyphs (), + *cur_active_glyphs); + + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + struct ChainContextClosureLookupContext lookup_context = { + {intersects_coverage, intersected_coverage_glyphs}, + ContextFormat::CoverageBasedContext, + {this, this, this} + }; + chain_context_closure_lookup (c, + backtrack.len, (const HBUINT16 *) backtrack.arrayZ, + input.len, (const HBUINT16 *) input.arrayZ + 1, + lookahead.len, (const HBUINT16 *) lookahead.arrayZ, + lookup.len, lookup.arrayZ, + 0, lookup_context); + + c->pop_cur_done_glyphs (); + } + + void closure_lookups (hb_closure_lookups_context_t *c) const + { + if (!intersects (c->glyphs)) + return; + + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + recurse_lookups (c, lookup.len, lookup.arrayZ); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const {} + + void collect_glyphs (hb_collect_glyphs_context_t *c) const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + + (this+input[0]).collect_coverage (c->input); + + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + + struct ChainContextCollectGlyphsLookupContext lookup_context = { + {collect_coverage}, + {this, this, this} + }; + chain_context_collect_glyphs_lookup (c, + backtrack.len, (const HBUINT16 *) backtrack.arrayZ, + input.len, (const HBUINT16 *) input.arrayZ + 1, + lookahead.len, (const HBUINT16 *) lookahead.arrayZ, + lookup.len, lookup.arrayZ, + lookup_context); + } + + bool would_apply (hb_would_apply_context_t *c) const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + struct ChainContextApplyLookupContext lookup_context = { + {{match_coverage, match_coverage, match_coverage}}, + {this, this, this} + }; + return chain_context_would_apply_lookup (c, + backtrack.len, (const HBUINT16 *) backtrack.arrayZ, + input.len, (const HBUINT16 *) input.arrayZ + 1, + lookahead.len, (const HBUINT16 *) lookahead.arrayZ, + lookup.len, lookup.arrayZ, lookup_context); + } + + const Coverage &get_coverage () const + { + const auto &input = StructAfter<decltype (inputX)> (backtrack); + return this+input[0]; + } + + bool apply (hb_ot_apply_context_t *c) const + { + TRACE_APPLY (this); + const auto &input = StructAfter<decltype (inputX)> (backtrack); + + unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint); + if (likely (index == NOT_COVERED)) return_trace (false); + + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + struct ChainContextApplyLookupContext lookup_context = { + {{match_coverage, match_coverage, match_coverage}}, + {this, this, this} + }; + return_trace (chain_context_apply_lookup (c, + backtrack.len, (const HBUINT16 *) backtrack.arrayZ, + input.len, (const HBUINT16 *) input.arrayZ + 1, + lookahead.len, (const HBUINT16 *) lookahead.arrayZ, + lookup.len, lookup.arrayZ, lookup_context)); + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool serialize_coverage_offsets (hb_subset_context_t *c, Iterator it, const void* base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed<Array16OfOffset16To<Coverage>> (); + + if (unlikely (!c->serializer->allocate_size<HBUINT16> (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, base)) + return_trace (false); + } + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + if (unlikely (!c->serializer->embed (this->format))) return_trace (false); + + if (!serialize_coverage_offsets (c, backtrack.iter (), this)) + return_trace (false); + + const auto &input = StructAfter<decltype (inputX)> (backtrack); + if (!serialize_coverage_offsets (c, input.iter (), this)) + return_trace (false); + + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + if (!serialize_coverage_offsets (c, lookahead.iter (), this)) + return_trace (false); + + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? &c->plan->gsub_lookups : &c->plan->gpos_lookups; + + HBUINT16 *lookupCount = c->serializer->copy<HBUINT16> (lookup.len); + if (!lookupCount) return_trace (false); + + unsigned count = serialize_lookuprecord_array (c->serializer, lookup.as_array (), lookup_map); + return_trace (c->serializer->check_assign (*lookupCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!backtrack.sanitize (c, this))) return_trace (false); + hb_barrier (); + const auto &input = StructAfter<decltype (inputX)> (backtrack); + if (unlikely (!input.sanitize (c, this))) return_trace (false); + hb_barrier (); + if (unlikely (!input.len)) return_trace (false); /* To be consistent with Context. */ + const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); + if (unlikely (!lookahead.sanitize (c, this))) return_trace (false); + hb_barrier (); + const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); + return_trace (likely (lookup.sanitize (c))); + } + + protected: + HBUINT16 format; /* Format identifier--format = 3 */ + Array16OfOffset16To<Coverage> + backtrack; /* Array of coverage tables + * in backtracking sequence, in glyph + * sequence order */ + Array16OfOffset16To<Coverage> + inputX ; /* Array of coverage + * tables in input sequence, in glyph + * sequence order */ + Array16OfOffset16To<Coverage> + lookaheadX; /* Array of coverage tables + * in lookahead sequence, in glyph + * sequence order */ + Array16Of<LookupRecord> + lookupX; /* Array of LookupRecords--in + * design order) */ + public: + DEFINE_SIZE_MIN (10); +}; + +struct ChainContext +{ + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); +#ifndef HB_NO_BEYOND_64K + case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); + case 5: return_trace (c->dispatch (u.format5, std::forward<Ts> (ds)...)); +#endif + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + ChainContextFormat1_4<SmallTypes> format1; + ChainContextFormat2_5<SmallTypes> format2; + ChainContextFormat3 format3; +#ifndef HB_NO_BEYOND_64K + ChainContextFormat1_4<MediumTypes> format4; + ChainContextFormat2_5<MediumTypes> format5; +#endif + } u; +}; + + +template <typename T> +struct ExtensionFormat1 +{ + unsigned int get_type () const { return extensionLookupType; } + + template <typename X> + const X& get_subtable () const + { return this + reinterpret_cast<const Offset32To<typename T::SubTable> &> (extensionOffset); } + + template <typename context_t, typename ...Ts> + typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const + { + if (unlikely (!c->may_dispatch (this, this))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, format); + return_trace (get_subtable<typename T::SubTable> ().dispatch (c, get_type (), std::forward<Ts> (ds)...)); + } + + void collect_variation_indices (hb_collect_variation_indices_context_t *c) const + { dispatch (c); } + + /* This is called from may_dispatch() above with hb_sanitize_context_t. */ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + extensionLookupType != T::SubTable::Extension); + } + + 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; + out->extensionLookupType = extensionLookupType; + + const auto& src_offset = + reinterpret_cast<const Offset32To<typename T::SubTable> &> (extensionOffset); + auto& dest_offset = + reinterpret_cast<Offset32To<typename T::SubTable> &> (out->extensionOffset); + + return_trace (dest_offset.serialize_subset (c, src_offset, this, get_type ())); + } + + protected: + HBUINT16 format; /* Format identifier. Set to 1. */ + HBUINT16 extensionLookupType; /* Lookup type of subtable referenced + * by ExtensionOffset (i.e. the + * extension subtable). */ + Offset32 extensionOffset; /* Offset to the extension subtable, + * of lookup type subtable. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +template <typename T> +struct Extension +{ + unsigned int get_type () const + { + switch (u.format) { + case 1: return u.format1.get_type (); + default:return 0; + } + } + template <typename X> + const X& get_subtable () const + { + switch (u.format) { + case 1: return u.format1.template get_subtable<typename T::SubTable> (); + default:return Null (typename T::SubTable); + } + } + + // Specialization of dispatch for subset. dispatch() normally just + // dispatches to the sub table this points too, but for subset + // we need to run subset on this subtable too. + template <typename ...Ts> + typename hb_subset_context_t::return_t dispatch (hb_subset_context_t *c, Ts&&... ds) const + { + switch (u.format) { + case 1: return u.format1.subset (c); + default: return c->default_return_value (); + } + } + + template <typename context_t, typename ...Ts> + 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 (u.format1.dispatch (c, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + protected: + union { + HBUINT16 format; /* Format identifier */ + ExtensionFormat1<T> format1; + } u; +}; + + +/* + * GSUB/GPOS Common + */ + +struct hb_ot_layout_lookup_accelerator_t +{ + template <typename TLookup> + static hb_ot_layout_lookup_accelerator_t *create (const TLookup &lookup) + { + unsigned count = lookup.get_subtable_count (); + + unsigned size = sizeof (hb_ot_layout_lookup_accelerator_t) - + HB_VAR_ARRAY * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t) + + count * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t); + + /* The following is a calloc because when we are collecting subtables, + * some of them might be invalid and hence not collect; as a result, + * we might not fill in all the count entries of the subtables array. + * Zeroing it allows the set digest to gatekeep it without having to + * initialize it further. */ + auto *thiz = (hb_ot_layout_lookup_accelerator_t *) hb_calloc (1, size); + if (unlikely (!thiz)) + return nullptr; + + hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables); + lookup.dispatch (&c_accelerate_subtables); + + thiz->digest.init (); + for (auto& subtable : hb_iter (thiz->subtables, count)) + thiz->digest.add (subtable.digest); + +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx; + for (unsigned i = 0; i < count; i++) + if (i != thiz->cache_user_idx) + thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func; +#endif + + return thiz; + } + + bool may_have (hb_codepoint_t g) const + { return digest.may_have (g); } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const + { +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + if (use_cache) + { + return + + hb_iter (hb_iter (subtables, subtables_count)) + | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply_cached (c); }) + | hb_any + ; + } + else +#endif + { + return + + hb_iter (hb_iter (subtables, subtables_count)) + | hb_map ([&c] (const hb_accelerate_subtables_context_t::hb_applicable_t &_) { return _.apply (c); }) + | hb_any + ; + } + return false; + } + + bool cache_enter (hb_ot_apply_context_t *c) const + { +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + return cache_user_idx != (unsigned) -1 && + subtables[cache_user_idx].cache_enter (c); +#else + return false; +#endif + } + void cache_leave (hb_ot_apply_context_t *c) const + { +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + subtables[cache_user_idx].cache_leave (c); +#endif + } + + + hb_set_digest_t digest; + private: +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + unsigned cache_user_idx = (unsigned) -1; +#endif + hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY]; +}; + +template <typename Types> +struct GSUBGPOSVersion1_2 +{ + friend struct GSUBGPOS; + + protected: + FixedVersion<>version; /* Version of the GSUB/GPOS table--initially set + * to 0x00010000u */ + typename Types:: template OffsetTo<ScriptList> + scriptList; /* ScriptList table */ + typename Types::template OffsetTo<FeatureList> + featureList; /* FeatureList table */ + typename Types::template OffsetTo<LookupList<Types>> + lookupList; /* LookupList table */ + Offset32To<FeatureVariations> + featureVars; /* Offset to Feature Variations + table--from beginning of table + * (may be NULL). Introduced + * in version 0x00010001. */ + public: + DEFINE_SIZE_MIN (4 + 3 * Types::size); + + unsigned int get_size () const + { + return min_size + + (version.to_int () >= 0x00010001u ? featureVars.static_size : 0); + } + + const typename Types::template OffsetTo<LookupList<Types>>* get_lookup_list_offset () const + { + return &lookupList; + } + + template <typename TLookup> + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + typedef List16OfOffsetTo<TLookup, typename Types::HBUINT> TLookupList; + if (unlikely (!(scriptList.sanitize (c, this) && + featureList.sanitize (c, this) && + reinterpret_cast<const typename Types::template OffsetTo<TLookupList> &> (lookupList).sanitize (c, this)))) + return_trace (false); + +#ifndef HB_NO_VAR + if (unlikely (!(version.to_int () < 0x00010001u || featureVars.sanitize (c, this)))) + return_trace (false); +#endif + + return_trace (true); + } + + template <typename TLookup> + bool subset (hb_subset_layout_context_t *c) const + { + TRACE_SUBSET (this); + + auto *out = c->subset_context->serializer->start_embed (this); + if (unlikely (!c->subset_context->serializer->extend_min (out))) return_trace (false); + + out->version = version; + + typedef LookupOffsetList<TLookup, typename Types::HBUINT> TLookupList; + reinterpret_cast<typename Types::template OffsetTo<TLookupList> &> (out->lookupList) + .serialize_subset (c->subset_context, + reinterpret_cast<const typename Types::template OffsetTo<TLookupList> &> (lookupList), + this, + c); + + reinterpret_cast<typename Types::template OffsetTo<RecordListOfFeature> &> (out->featureList) + .serialize_subset (c->subset_context, + reinterpret_cast<const typename Types::template OffsetTo<RecordListOfFeature> &> (featureList), + this, + c); + + out->scriptList.serialize_subset (c->subset_context, + scriptList, + this, + c); + +#ifndef HB_NO_VAR + if (version.to_int () >= 0x00010001u) + { + auto snapshot = c->subset_context->serializer->snapshot (); + if (!c->subset_context->serializer->extend_min (&out->featureVars)) + return_trace (false); + + // if all axes are pinned all feature vars are dropped. + bool ret = !c->subset_context->plan->all_axes_pinned + && out->featureVars.serialize_subset (c->subset_context, featureVars, this, c); + if (!ret && version.major == 1) + { + c->subset_context->serializer->revert (snapshot); + out->version.major = 1; + out->version.minor = 0; + } + } +#endif + + return_trace (true); + } +}; + +struct GSUBGPOS +{ + 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; + } + } + + template <typename TLookup> + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!u.version.sanitize (c))) return_trace (false); + hb_barrier (); + switch (u.version.major) { + case 1: return_trace (u.version1.sanitize<TLookup> (c)); +#ifndef HB_NO_BEYOND_64K + case 2: return_trace (u.version2.sanitize<TLookup> (c)); +#endif + default: return_trace (true); + } + } + + template <typename TLookup> + bool subset (hb_subset_layout_context_t *c) const + { + switch (u.version.major) { + case 1: return u.version1.subset<TLookup> (c); +#ifndef HB_NO_BEYOND_64K + case 2: return u.version2.subset<TLookup> (c); +#endif + default: return false; + } + } + + const ScriptList &get_script_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.scriptList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.scriptList; +#endif + default: return Null (ScriptList); + } + } + const FeatureList &get_feature_list () const + { + switch (u.version.major) { + case 1: return this+u.version1.featureList; +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.featureList; +#endif + default: return Null (FeatureList); + } + } + unsigned int get_lookup_count () const + { + switch (u.version.major) { + case 1: return (this+u.version1.lookupList).len; +#ifndef HB_NO_BEYOND_64K + case 2: return (this+u.version2.lookupList).len; +#endif + default: return 0; + } + } + const Lookup& get_lookup (unsigned int i) const + { + switch (u.version.major) { + case 1: return (this+u.version1.lookupList)[i]; +#ifndef HB_NO_BEYOND_64K + case 2: return (this+u.version2.lookupList)[i]; +#endif + default: return Null (Lookup); + } + } + const FeatureVariations &get_feature_variations () const + { + switch (u.version.major) { + case 1: return (u.version.to_int () >= 0x00010001u ? this+u.version1.featureVars : Null (FeatureVariations)); +#ifndef HB_NO_BEYOND_64K + case 2: return this+u.version2.featureVars; +#endif + default: return Null (FeatureVariations); + } + } + + bool has_data () const { return u.version.to_int (); } + unsigned int get_script_count () const + { return get_script_list ().len; } + const Tag& get_script_tag (unsigned int i) const + { return get_script_list ().get_tag (i); } + unsigned int get_script_tags (unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */) const + { return get_script_list ().get_tags (start_offset, script_count, script_tags); } + const Script& get_script (unsigned int i) const + { return get_script_list ()[i]; } + bool find_script_index (hb_tag_t tag, unsigned int *index) const + { return get_script_list ().find_index (tag, index); } + + unsigned int get_feature_count () const + { return get_feature_list ().len; } + hb_tag_t get_feature_tag (unsigned int i) const + { return i == Index::NOT_FOUND_INDEX ? HB_TAG_NONE : get_feature_list ().get_tag (i); } + unsigned int get_feature_tags (unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */) const + { return get_feature_list ().get_tags (start_offset, feature_count, feature_tags); } + const Feature& get_feature (unsigned int i) const + { return get_feature_list ()[i]; } + bool find_feature_index (hb_tag_t tag, unsigned int *index) const + { return get_feature_list ().find_index (tag, index); } + + bool find_variations_index (const int *coords, unsigned int num_coords, + unsigned int *index) const + { +#ifdef HB_NO_VAR + *index = FeatureVariations::NOT_FOUND_INDEX; + return false; +#endif + return get_feature_variations ().find_index (coords, num_coords, index); + } + const Feature& get_feature_variation (unsigned int feature_index, + unsigned int variations_index) const + { +#ifndef HB_NO_VAR + if (FeatureVariations::NOT_FOUND_INDEX != variations_index && + u.version.to_int () >= 0x00010001u) + { + const Feature *feature = get_feature_variations ().find_substitute (variations_index, + feature_index); + if (feature) + return *feature; + } +#endif + return get_feature (feature_index); + } + + void feature_variation_collect_lookups (const hb_set_t *feature_indexes, + const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, + hb_set_t *lookup_indexes /* OUT */) const + { +#ifndef HB_NO_VAR + get_feature_variations ().collect_lookups (feature_indexes, feature_record_cond_idx_map, lookup_indexes); +#endif + } + +#ifndef HB_NO_VAR + void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const + { get_feature_variations ().collect_feature_substitutes_with_variations (c); } +#endif + + template <typename TLookup> + void closure_lookups (hb_face_t *face, + const hb_set_t *glyphs, + hb_set_t *lookup_indexes /* IN/OUT */) const + { + hb_set_t visited_lookups, inactive_lookups; + hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups); + + c.set_recurse_func (TLookup::template dispatch_recurse_func<hb_closure_lookups_context_t>); + + for (unsigned lookup_index : *lookup_indexes) + reinterpret_cast<const TLookup &> (get_lookup (lookup_index)).closure_lookups (&c, lookup_index); + + hb_set_union (lookup_indexes, &visited_lookups); + hb_set_subtract (lookup_indexes, &inactive_lookups); + } + + void prune_langsys (const hb_map_t *duplicate_feature_map, + const hb_set_t *layout_scripts, + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map, + hb_set_t *new_feature_indexes /* OUT */) const + { + hb_prune_langsys_context_t c (this, script_langsys_map, duplicate_feature_map, new_feature_indexes); + + unsigned count = get_script_count (); + for (unsigned script_index = 0; script_index < count; script_index++) + { + const Tag& tag = get_script_tag (script_index); + if (!layout_scripts->has (tag)) continue; + const Script& s = get_script (script_index); + s.prune_langsys (&c, script_index); + } + } + + void prune_features (const hb_map_t *lookup_indices, /* IN */ + const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* IN */ + const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, /* IN */ + hb_set_t *feature_indices /* IN/OUT */) const + { +#ifndef HB_NO_VAR + // This is the set of feature indices which have alternate versions defined + // if the FeatureVariation's table and the alternate version(s) intersect the + // set of lookup indices. + hb_set_t alternate_feature_indices; + get_feature_variations ().closure_features (lookup_indices, feature_record_cond_idx_map, &alternate_feature_indices); + if (unlikely (alternate_feature_indices.in_error())) + { + feature_indices->err (); + return; + } +#endif + + for (unsigned i : hb_iter (feature_indices)) + { + hb_tag_t tag = get_feature_tag (i); + if (tag == HB_TAG ('p', 'r', 'e', 'f')) + // Note: Never ever drop feature 'pref', even if it's empty. + // HarfBuzz chooses shaper for Khmer based on presence of this + // feature. See thread at: + // http://lists.freedesktop.org/archives/harfbuzz/2012-November/002660.html + continue; + + + const Feature *f = &(get_feature (i)); + const Feature** p = nullptr; + if (feature_substitutes_map->has (i, &p)) + f = *p; + + if (!f->featureParams.is_null () && + tag == HB_TAG ('s', 'i', 'z', 'e')) + continue; + + if (!f->intersects_lookup_indexes (lookup_indices) +#ifndef HB_NO_VAR + && !alternate_feature_indices.has (i) +#endif + ) + feature_indices->del (i); + } + } + + void collect_name_ids (const hb_map_t *feature_index_map, + hb_set_t *nameids_to_retain /* OUT */) const + { + unsigned count = get_feature_count (); + for (unsigned i = 0 ; i < count; i++) + { + if (!feature_index_map->has (i)) continue; + hb_tag_t tag = get_feature_tag (i); + get_feature (i).collect_name_ids (tag, nameids_to_retain); + } + } + + template <typename T> + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + hb_sanitize_context_t sc; + sc.lazy_some_gpos = true; + this->table = sc.reference_table<T> (face); + + if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face))) + { + hb_blob_destroy (this->table.get_blob ()); + this->table = hb_blob_get_empty (); + } + + this->lookup_count = table->get_lookup_count (); + + this->accels = (hb_atomic_ptr_t<hb_ot_layout_lookup_accelerator_t> *) hb_calloc (this->lookup_count, sizeof (*accels)); + if (unlikely (!this->accels)) + { + this->lookup_count = 0; + this->table.destroy (); + this->table = hb_blob_get_empty (); + } + } + ~accelerator_t () + { + for (unsigned int i = 0; i < this->lookup_count; i++) + hb_free (this->accels[i]); + hb_free (this->accels); + this->table.destroy (); + } + + hb_blob_t *get_blob () const { return table.get_blob (); } + + hb_ot_layout_lookup_accelerator_t *get_accel (unsigned lookup_index) const + { + if (unlikely (lookup_index >= lookup_count)) return nullptr; + + retry: + auto *accel = accels[lookup_index].get_acquire (); + if (unlikely (!accel)) + { + accel = hb_ot_layout_lookup_accelerator_t::create (table->get_lookup (lookup_index)); + if (unlikely (!accel)) + return nullptr; + + if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel))) + { + hb_free (accel); + goto retry; + } + } + + return accel; + } + + hb_blob_ptr_t<T> table; + unsigned int lookup_count; + hb_atomic_ptr_t<hb_ot_layout_lookup_accelerator_t> *accels; + }; + + protected: + union { + FixedVersion<> version; /* Version identifier */ + GSUBGPOSVersion1_2<SmallTypes> version1; +#ifndef HB_NO_BEYOND_64K + GSUBGPOSVersion1_2<MediumTypes> version2; +#endif + } u; + public: + DEFINE_SIZE_MIN (4); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_GSUBGPOS_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh b/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh new file mode 100644 index 0000000000..0ba7eaa2c5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout-jstf-table.hh @@ -0,0 +1,236 @@ +/* + * 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_OT_LAYOUT_JSTF_TABLE_HH +#define HB_OT_LAYOUT_JSTF_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-gpos-table.hh" + + +namespace OT { + + +/* + * JstfModList -- Justification Modification List Tables + */ + +typedef IndexArray JstfModList; + + +/* + * JstfMax -- Justification Maximum Table + */ + +typedef List16OfOffset16To<PosLookup> JstfMax; + + +/* + * JstfPriority -- Justification Priority Table + */ + +struct JstfPriority +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + shrinkageEnableGSUB.sanitize (c, this) && + shrinkageDisableGSUB.sanitize (c, this) && + shrinkageEnableGPOS.sanitize (c, this) && + shrinkageDisableGPOS.sanitize (c, this) && + shrinkageJstfMax.sanitize (c, this) && + extensionEnableGSUB.sanitize (c, this) && + extensionDisableGSUB.sanitize (c, this) && + extensionEnableGPOS.sanitize (c, this) && + extensionDisableGPOS.sanitize (c, this) && + extensionJstfMax.sanitize (c, this)); + } + + protected: + Offset16To<JstfModList> + shrinkageEnableGSUB; /* Offset to Shrinkage Enable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfModList> + shrinkageDisableGSUB; /* Offset to Shrinkage Disable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfModList> + shrinkageEnableGPOS; /* Offset to Shrinkage Enable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfModList> + shrinkageDisableGPOS; /* Offset to Shrinkage Disable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfMax> + shrinkageJstfMax; /* Offset to Shrinkage JstfMax table-- + * from beginning of JstfPriority table + * --may be NULL */ + Offset16To<JstfModList> + extensionEnableGSUB; /* Offset to Extension Enable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfModList> + extensionDisableGSUB; /* Offset to Extension Disable GSUB + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfModList> + extensionEnableGPOS; /* Offset to Extension Enable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfModList> + extensionDisableGPOS; /* Offset to Extension Disable GPOS + * JstfModList table--from beginning of + * JstfPriority table--may be NULL */ + Offset16To<JstfMax> + extensionJstfMax; /* Offset to Extension JstfMax table-- + * from beginning of JstfPriority table + * --may be NULL */ + + public: + DEFINE_SIZE_STATIC (20); +}; + + +/* + * JstfLangSys -- Justification Language System Table + */ + +struct JstfLangSys : List16OfOffset16To<JstfPriority> +{ + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t * = nullptr) const + { + TRACE_SANITIZE (this); + return_trace (List16OfOffset16To<JstfPriority>::sanitize (c)); + } +}; + + +/* + * ExtenderGlyphs -- Extender Glyph Table + */ + +typedef SortedArray16Of<HBGlyphID16> ExtenderGlyphs; + + +/* + * JstfScript -- The Justification Table + */ + +struct JstfScript +{ + unsigned int get_lang_sys_count () const + { return langSys.len; } + const Tag& get_lang_sys_tag (unsigned int i) const + { return langSys.get_tag (i); } + 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); } + const JstfLangSys& get_lang_sys (unsigned int i) const + { + if (i == Index::NOT_FOUND_INDEX) return get_default_lang_sys (); + return this+langSys[i].offset; + } + bool find_lang_sys_index (hb_tag_t tag, unsigned int *index) const + { return langSys.find_index (tag, index); } + + bool has_default_lang_sys () const { return defaultLangSys != 0; } + const JstfLangSys& get_default_lang_sys () const { return this+defaultLangSys; } + + bool sanitize (hb_sanitize_context_t *c, + const Record_sanitize_closure_t * = nullptr) const + { + TRACE_SANITIZE (this); + return_trace (extenderGlyphs.sanitize (c, this) && + defaultLangSys.sanitize (c, this) && + langSys.sanitize (c, this)); + } + + protected: + Offset16To<ExtenderGlyphs> + extenderGlyphs; /* Offset to ExtenderGlyph table--from beginning + * of JstfScript table-may be NULL */ + Offset16To<JstfLangSys> + defaultLangSys; /* Offset to DefaultJstfLangSys table--from + * beginning of JstfScript table--may be Null */ + RecordArrayOf<JstfLangSys> + langSys; /* Array of JstfLangSysRecords--listed + * alphabetically by LangSysTag */ + public: + DEFINE_SIZE_ARRAY (6, langSys); +}; + + +/* + * JSTF -- Justification + * https://docs.microsoft.com/en-us/typography/opentype/spec/jstf + */ + +struct JSTF +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_JSTF; + + unsigned int get_script_count () const + { return scriptList.len; } + const Tag& get_script_tag (unsigned int i) const + { return scriptList.get_tag (i); } + unsigned int get_script_tags (unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */) const + { return scriptList.get_tags (start_offset, script_count, script_tags); } + const JstfScript& get_script (unsigned int i) const + { return this+scriptList[i].offset; } + bool find_script_index (hb_tag_t tag, unsigned int *index) const + { return scriptList.find_index (tag, index); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + hb_barrier () && + likely (version.major == 1) && + scriptList.sanitize (c, this)); + } + + protected: + FixedVersion<>version; /* Version of the JSTF table--initially set + * to 0x00010000u */ + RecordArrayOf<JstfScript> + scriptList; /* Array of JstfScripts--listed + * alphabetically by ScriptTag */ + public: + DEFINE_SIZE_ARRAY (6, scriptList); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_LAYOUT_JSTF_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-layout.cc b/gfx/harfbuzz/src/hb-ot-layout.cc new file mode 100644 index 0000000000..2eb8535db5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout.cc @@ -0,0 +1,2701 @@ +/* + * Copyright © 1998-2004 David Turner and Werner Lemberg + * Copyright © 2006 Behdad Esfahbod + * 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_LAYOUT + +#ifdef HB_NO_OT_TAG +#error "Cannot compile hb-ot-layout.cc with HB_NO_OT_TAG." +#endif + +#include "hb-open-type.hh" +#include "hb-ot-layout.hh" +#include "hb-ot-face.hh" +#include "hb-ot-map.hh" +#include "hb-map.hh" + +#include "hb-ot-kern-table.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-layout-base-table.hh" +#include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-name-table.hh" +#include "hb-ot-os2-table.hh" + +#include "hb-aat-layout-morx-table.hh" +#include "hb-aat-layout-opbd-table.hh" // Just so we compile it; unused otherwise. + +using OT::Layout::GSUB; +using OT::Layout::GPOS; + +/** + * SECTION:hb-ot-layout + * @title: hb-ot-layout + * @short_description: OpenType Layout + * @include: hb-ot.h + * + * Functions for querying OpenType Layout features in the font face. + * See the [OpenType specification](http://www.microsoft.com/typography/otspec/) + * for details. + **/ + + +/* + * kern + */ + +#ifndef HB_NO_OT_KERN +/** + * hb_ot_layout_has_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any kerning data in the 'kern' table. + * Does NOT test for kerning lookups in the GPOS table. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +bool +hb_ot_layout_has_kerning (hb_face_t *face) +{ + return face->table.kern->has_data (); +} + +/** + * hb_ot_layout_has_machine_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any state-machine kerning in the 'kern' table. + * Does NOT examine the GPOS table. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +bool +hb_ot_layout_has_machine_kerning (hb_face_t *face) +{ + return face->table.kern->has_state_machine (); +} + +/** + * hb_ot_layout_has_cross_kerning: + * @face: The #hb_face_t to work on + * + * Tests whether a face has any cross-stream kerning (i.e., kerns + * that make adjustments perpendicular to the direction of the text + * flow: Y adjustments in horizontal text or X adjustments in + * vertical text) in the 'kern' table. + * + * Does NOT examine the GPOS table. + * + * Return value: `true` is data found, `false` otherwise + * + **/ +bool +hb_ot_layout_has_cross_kerning (hb_face_t *face) +{ + return face->table.kern->has_cross_stream (); +} + +void +hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_blob_t *blob = font->face->table.kern.get_blob (); + const AAT::kern& kern = *blob->as<AAT::kern> (); + + AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); + + if (!buffer->message (font, "start table kern")) return; + kern.apply (&c); + (void) buffer->message (font, "end table kern"); +} +#endif + + +/* + * GDEF + */ + +bool +OT::GDEF::is_blocklisted (hb_blob_t *blob, + hb_face_t *face) const +{ +#ifdef HB_NO_OT_LAYOUT_BLOCKLIST + return false; +#endif + /* The ugly business of blocklisting individual fonts' tables happen here! + * See this thread for why we finally had to bend in and do this: + * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html + * + * In certain versions of Times New Roman Italic and Bold Italic, + * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark) + * in GDEF. Many versions of Tahoma have bad GDEF tables that + * incorrectly classify some spacing marks such as certain IPA + * symbols as glyph class 3. So do older versions of Microsoft + * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04. + * + * Nuke the GDEF tables of to avoid unwanted width-zeroing. + * + * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 + * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 + * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 + */ + switch HB_CODEPOINT_ENCODE3(blob->length, + face->table.GSUB->table.get_length (), + face->table.GPOS->table.get_length ()) + { + /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ + case HB_CODEPOINT_ENCODE3 (442, 2874, 42038): + /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */ + case HB_CODEPOINT_ENCODE3 (430, 2874, 40662): + /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */ + case HB_CODEPOINT_ENCODE3 (442, 2874, 39116): + /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */ + case HB_CODEPOINT_ENCODE3 (430, 2874, 39374): + /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */ + case HB_CODEPOINT_ENCODE3 (490, 3046, 41638): + /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */ + case HB_CODEPOINT_ENCODE3 (478, 3046, 41902): + /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */ + case HB_CODEPOINT_ENCODE3 (898, 12554, 46470): + /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */ + case HB_CODEPOINT_ENCODE3 (910, 12566, 47732): + /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */ + case HB_CODEPOINT_ENCODE3 (928, 23298, 59332): + /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */ + case HB_CODEPOINT_ENCODE3 (940, 23310, 60732): + /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ + case HB_CODEPOINT_ENCODE3 (964, 23836, 60072): + /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ + case HB_CODEPOINT_ENCODE3 (976, 23832, 61456): + /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */ + case HB_CODEPOINT_ENCODE3 (994, 24474, 60336): + /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */ + case HB_CODEPOINT_ENCODE3 (1006, 24470, 61740): + /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ + case HB_CODEPOINT_ENCODE3 (1006, 24576, 61346): + /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ + case HB_CODEPOINT_ENCODE3 (1018, 24572, 62828): + /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5 tahoma.ttf from Windows 10 AU */ + case HB_CODEPOINT_ENCODE3 (1006, 24576, 61352): + /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2 tahomabd.ttf from Windows 10 AU */ + case HB_CODEPOINT_ENCODE3 (1018, 24572, 62834): + /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */ + case HB_CODEPOINT_ENCODE3 (832, 7324, 47162): + /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */ + case HB_CODEPOINT_ENCODE3 (844, 7302, 45474): + /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc himalaya.ttf from Windows 7 */ + case HB_CODEPOINT_ENCODE3 (180, 13054, 7254): + /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */ + case HB_CODEPOINT_ENCODE3 (192, 12638, 7254): + /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */ + case HB_CODEPOINT_ENCODE3 (192, 12690, 7254): + /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */ + /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */ + case HB_CODEPOINT_ENCODE3 (188, 248, 3852): + /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */ + /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */ + case HB_CODEPOINT_ENCODE3 (188, 264, 3426): + /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */ + case HB_CODEPOINT_ENCODE3 (1058, 47032, 11818): + /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/ + case HB_CODEPOINT_ENCODE3 (1046, 47030, 12600): + /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */ + case HB_CODEPOINT_ENCODE3 (1058, 71796, 16770): + /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */ + case HB_CODEPOINT_ENCODE3 (1046, 71790, 17862): + /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */ + case HB_CODEPOINT_ENCODE3 (1046, 71788, 17112): + /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */ + case HB_CODEPOINT_ENCODE3 (1058, 71794, 17514): + /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */ + case HB_CODEPOINT_ENCODE3 (1330, 109904, 57938): + /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */ + case HB_CODEPOINT_ENCODE3 (1330, 109904, 58972): + /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85 Padauk.ttf + * "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */ + case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836): + return true; + } + return false; +} + +static void +_hb_ot_layout_set_glyph_props (hb_font_t *font, + hb_buffer_t *buffer) +{ + _hb_buffer_assert_gsubgpos_vars (buffer); + + const auto &gdef = *font->face->table.GDEF; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + _hb_glyph_info_set_glyph_props (&info[i], gdef.get_glyph_props (info[i].codepoint)); + _hb_glyph_info_clear_lig_props (&info[i]); + } +} + +/* Public API */ + +/** + * hb_ot_layout_has_glyph_classes: + * @face: #hb_face_t to work upon + * + * Tests whether a face has any glyph classes defined in its GDEF table. + * + * Return value: `true` if data found, `false` otherwise + * + **/ +hb_bool_t +hb_ot_layout_has_glyph_classes (hb_face_t *face) +{ + return face->table.GDEF->table->has_glyph_classes (); +} + +/** + * hb_ot_layout_get_glyph_class: + * @face: The #hb_face_t to work on + * @glyph: The #hb_codepoint_t code point to query + * + * Fetches the GDEF class of the requested glyph in the specified face. + * + * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code + * point in the GDEF table of the face. + * + * Since: 0.9.7 + **/ +hb_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class (hb_face_t *face, + hb_codepoint_t glyph) +{ + return (hb_ot_layout_glyph_class_t) face->table.GDEF->table->get_glyph_class (glyph); +} + +/** + * hb_ot_layout_get_glyphs_in_class: + * @face: The #hb_face_t to work on + * @klass: The #hb_ot_layout_glyph_class_t GDEF class to retrieve + * @glyphs: (out): The #hb_set_t set of all glyphs belonging to the requested + * class. + * + * Retrieves the set of all glyphs from the face that belong to the requested + * glyph class in the face's GDEF table. + * + * Since: 0.9.7 + **/ +void +hb_ot_layout_get_glyphs_in_class (hb_face_t *face, + hb_ot_layout_glyph_class_t klass, + hb_set_t *glyphs /* OUT */) +{ + return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs); +} + +#ifndef HB_NO_LAYOUT_UNUSED +/** + * hb_ot_layout_get_attach_points: + * @face: The #hb_face_t to work on + * @glyph: The #hb_codepoint_t code point to query + * @start_offset: offset of the first attachment point to retrieve + * @point_count: (inout) (optional): Input = the maximum number of attachment points to return; + * Output = the actual number of attachment points returned (may be zero) + * @point_array: (out) (array length=point_count): The array of attachment points found for the query + * + * Fetches a list of all attachment points for the specified glyph in the GDEF + * table of the face. The list returned will begin at the offset provided. + * + * Useful if the client program wishes to cache the list. + * + * Return value: Total number of attachment points for @glyph. + * + **/ +unsigned int +hb_ot_layout_get_attach_points (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */) +{ + return face->table.GDEF->table->get_attach_points (glyph, + start_offset, + point_count, + point_array); +} +/** + * hb_ot_layout_get_ligature_carets: + * @font: The #hb_font_t to work on + * @direction: The #hb_direction_t text direction to use + * @glyph: The #hb_codepoint_t code point to query + * @start_offset: offset of the first caret position to retrieve + * @caret_count: (inout) (optional): Input = the maximum number of caret positions to return; + * Output = the actual number of caret positions returned (may be zero) + * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query + * + * Fetches a list of the caret positions defined for a ligature glyph in the GDEF + * table of the font. The list returned will begin at the offset provided. + * + * Note that a ligature that is formed from n characters will have n-1 + * caret positions. The first character is not represented in the array, + * since its caret position is the glyph position. + * + * The positions returned by this function are 'unshaped', and will have to + * be fixed up for kerning that may be applied to the ligature glyph. + * + * Return value: Total number of ligature caret positions for @glyph. + * + **/ +unsigned int +hb_ot_layout_get_ligature_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */) +{ + return font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); +} +#endif + + +/* + * GSUB/GPOS + */ + +bool +GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED, + hb_face_t *face) const +{ +#ifdef HB_NO_OT_LAYOUT_BLOCKLIST + return false; +#endif + return false; +} + +bool +GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED, + hb_face_t *face HB_UNUSED) const +{ +#ifdef HB_NO_OT_LAYOUT_BLOCKLIST + return false; +#endif + return false; +} + +static const OT::GSUBGPOS& +get_gsubgpos_table (hb_face_t *face, + hb_tag_t table_tag) +{ + switch (table_tag) { + case HB_OT_TAG_GSUB: return *face->table.GSUB->table; + case HB_OT_TAG_GPOS: return *face->table.GPOS->table; + default: return Null (OT::GSUBGPOS); + } +} + + +/** + * hb_ot_layout_table_get_script_tags: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @start_offset: offset of the first script tag to retrieve + * @script_count: (inout) (optional): Input = the maximum number of script tags to return; + * Output = the actual number of script tags returned (may be zero) + * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query + * + * Fetches a list of all scripts enumerated in the specified face's GSUB table + * or GPOS table. The list returned will begin at the offset provided. + * + * Return value: Total number of script tags. + * + **/ +unsigned int +hb_ot_layout_table_get_script_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + return g.get_script_tags (start_offset, script_count, script_tags); +} + +#define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') + +/** + * hb_ot_layout_table_find_script: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_tag: #hb_tag_t of the script tag requested + * @script_index: (out): The index of the requested script tag + * + * Fetches the index if a given script tag in the specified face's GSUB table + * or GPOS table. + * + * Return value: `true` if the script is found, `false` otherwise + * + **/ +hb_bool_t +hb_ot_layout_table_find_script (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t script_tag, + unsigned int *script_index /* OUT */) +{ + static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), ""); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + if (g.find_script_index (script_tag, script_index)) + return true; + + /* try finding 'DFLT' */ + if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) + return false; + + /* try with 'dflt'; MS site has had typos and many fonts use it now :(. + * including many versions of DejaVu Sans Mono! */ + if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) + return false; + + /* try with 'latn'; some old fonts put their features there even though + they're really trying to support Thai, for example :( */ + if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) + return false; + + if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; + return false; +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_layout_table_choose_script: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_tags: Array of #hb_tag_t script tags + * @script_index: (out): The index of the chosen script + * @chosen_script: (out): #hb_tag_t of the chosen script + * + * Deprecated since 2.0.0 + **/ +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 /* OUT */, + hb_tag_t *chosen_script /* OUT */) +{ + const hb_tag_t *t; + for (t = script_tags; *t; t++); + return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script); +} +#endif + +/** + * hb_ot_layout_table_select_script: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_count: Number of script tags in the array + * @script_tags: Array of #hb_tag_t script tags + * @script_index: (out) (optional): The index of the requested script + * @chosen_script: (out) (optional): #hb_tag_t of the requested script + * + * Selects an OpenType script for @table_tag from the @script_tags array. + * + * If the table does not have any of the requested scripts, then `DFLT`, + * `dflt`, and `latn` tags are tried in that order. If the table still does not + * have any of these scripts, @script_index is set to + * #HB_OT_LAYOUT_NO_SCRIPT_INDEX and @chosen_script is set to #HB_TAG_NONE. + * + * Return value: + * `true` if one of the requested scripts is selected, `false` if a fallback + * script is selected or if no scripts are selected. + * + * Since: 2.0.0 + **/ +hb_bool_t +hb_ot_layout_table_select_script (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_count, + const hb_tag_t *script_tags, + unsigned int *script_index /* OUT */, + hb_tag_t *chosen_script /* OUT */) +{ + static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), ""); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + unsigned int i; + + for (i = 0; i < script_count; i++) + { + if (g.find_script_index (script_tags[i], script_index)) + { + if (chosen_script) + *chosen_script = script_tags[i]; + return true; + } + } + + /* try finding 'DFLT' */ + if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { + if (chosen_script) + *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; + return false; + } + + /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ + if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { + if (chosen_script) + *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; + return false; + } + + /* try with 'latn'; some old fonts put their features there even though + they're really trying to support Thai, for example :( */ + if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { + if (chosen_script) + *chosen_script = HB_OT_TAG_LATIN_SCRIPT; + return false; + } + + if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; + if (chosen_script) + *chosen_script = HB_TAG_NONE; + return false; +} + + +/** + * hb_ot_layout_table_get_feature_tags: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table + * + * Fetches a list of all feature tags in the given face's GSUB or GPOS table. + * Note that there might be duplicate feature tags, belonging to different + * script/language-system pairs of the table. + * + * Return value: Total number of feature tags. + * + * Since: 0.6.0 + * + **/ +unsigned int +hb_ot_layout_table_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + return g.get_feature_tags (start_offset, feature_count, feature_tags); +} + + +/** + * hb_ot_layout_table_find_feature: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @feature_tag: The #hb_tag_t of the requested feature tag + * @feature_index: (out): The index of the requested feature + * + * Fetches the index for a given feature tag in the specified face's GSUB table + * or GPOS table. + * + * Return value: `true` if the feature is found, `false` otherwise + * + * Since: 0.6.0 + * + **/ +bool +hb_ot_layout_table_find_feature (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t feature_tag, + unsigned int *feature_index /* OUT */) +{ + static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + unsigned int num_features = g.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) + { + if (feature_tag == g.get_feature_tag (i)) { + if (feature_index) *feature_index = i; + return true; + } + } + + if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; + return false; +} + + +/** + * hb_ot_layout_script_get_language_tags: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @start_offset: offset of the first language tag to retrieve + * @language_count: (inout) (optional): Input = the maximum number of language tags to return; + * Output = the actual number of language tags returned (may be zero) + * @language_tags: (out) (array length=language_count): Array of language tags found in the table + * + * Fetches a list of language tags in the given face's GSUB or GPOS table, underneath + * the specified script index. The list returned will begin at the offset provided. + * + * Return value: Total number of language tags. + * + * Since: 0.6.0 + * + **/ +unsigned int +hb_ot_layout_script_get_language_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int start_offset, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */) +{ + const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); + + return s.get_lang_sys_tags (start_offset, language_count, language_tags); +} + + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_layout_script_find_language: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_tag: The #hb_tag_t of the requested language + * @language_index: The index of the requested language + * + * Fetches the index of a given language tag in the specified face's GSUB table + * or GPOS table, underneath the specified script tag. + * + * Return value: `true` if the language tag is found, `false` otherwise + * + * Since: 0.6.0 + * Deprecated: 2.0.0 + **/ +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) +{ + return hb_ot_layout_script_select_language (face, + table_tag, + script_index, + 1, + &language_tag, + language_index); +} +#endif + + +/** + * hb_ot_layout_script_select_language2: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_count: The number of languages in the specified script + * @language_tags: The array of language tags + * @language_index: (out): The index of the chosen language + * @chosen_language: (out): #hb_tag_t of the chosen language + * + * Fetches the index of the first language tag fom @language_tags that is present + * in the specified face's GSUB or GPOS table, underneath the specified script + * index. + * + * If none of the given language tags is found, `false` is returned and + * @language_index is set to #HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX and + * @chosen_language is set to #HB_TAG_NONE. + * + * Return value: `true` if one of the given language tags is found, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_ot_layout_script_select_language2 (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_count, + const hb_tag_t *language_tags, + unsigned int *language_index /* OUT */, + hb_tag_t *chosen_language /* OUT */) +{ + static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX), ""); + const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); + unsigned int i; + + for (i = 0; i < language_count; i++) + { + if (s.find_lang_sys_index (language_tags[i], language_index)) + { + if (chosen_language) + *chosen_language = language_tags[i]; + return true; + } + } + + /* try finding 'dflt' */ + if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) + { + if (chosen_language) + *chosen_language = HB_OT_TAG_DEFAULT_LANGUAGE; + return false; + } + + if (language_index) + *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; + if (chosen_language) + *chosen_language = HB_TAG_NONE; + return false; +} + +/** + * hb_ot_layout_script_select_language: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_count: The number of languages in the specified script + * @language_tags: The array of language tags + * @language_index: (out): The index of the requested language + * + * Fetches the index of the first language tag fom @language_tags that is present + * in the specified face's GSUB or GPOS table, underneath the specified script + * index. + * + * If none of the given language tags is found, `false` is returned and + * @language_index is set to the default language index. + * + * Return value: `true` if one of the given language tags is found, `false` otherwise + * + * Since: 2.0.0 + **/ +hb_bool_t +hb_ot_layout_script_select_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_count, + const hb_tag_t *language_tags, + unsigned int *language_index /* OUT */) +{ + return hb_ot_layout_script_select_language2 (face, table_tag, + script_index, + language_count, language_tags, + language_index, nullptr); +} + +/** + * hb_ot_layout_language_get_required_feature_index: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_index: (out): The index of the requested feature + * + * Fetches the index of a requested feature in the given face's GSUB or GPOS table, + * underneath the specified script and language. + * + * Return value: `true` if the feature is found, `false` otherwise + * + * Since: 0.6.0 + * + **/ +hb_bool_t +hb_ot_layout_language_get_required_feature_index (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index /* OUT */) +{ + return hb_ot_layout_language_get_required_feature (face, + table_tag, + script_index, + language_index, + feature_index, + nullptr); +} + + +/** + * hb_ot_layout_language_get_required_feature: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_index: (out): The index of the requested feature + * @feature_tag: (out): The #hb_tag_t of the requested feature + * + * Fetches the tag of a requested feature index in the given face's GSUB or GPOS table, + * underneath the specified script and language. + * + * Return value: `true` if the feature is found, `false` otherwise + * + * Since: 0.9.30 + **/ +hb_bool_t +hb_ot_layout_language_get_required_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index /* OUT */, + hb_tag_t *feature_tag /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + unsigned int index = l.get_required_feature_index (); + if (feature_index) *feature_index = index; + if (feature_tag) *feature_tag = g.get_feature_tag (index); + + return l.has_required_feature (); +} + + +/** + * hb_ot_layout_language_get_feature_indexes: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * Output: the actual number of feature tags returned (may be zero) + * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query + * + * Fetches a list of all features in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. The list + * returned will begin at the offset provided. + * + * Return value: Total number of features. + * + * Since: 0.6.0 + * + **/ +unsigned int +hb_ot_layout_language_get_feature_indexes (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + return l.get_feature_indexes (start_offset, feature_count, feature_indexes); +} + + +/** + * hb_ot_layout_language_get_feature_tags: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @start_offset: offset of the first feature tag to retrieve + * @feature_count: (inout) (optional): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query + * + * Fetches a list of all features in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. The list + * returned will begin at the offset provided. + * + * Return value: Total number of feature tags. + * + * Since: 0.6.0 + * + **/ +unsigned int +hb_ot_layout_language_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + static_assert ((sizeof (unsigned int) == sizeof (hb_tag_t)), ""); + unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); + + if (feature_tags) { + unsigned int count = *feature_count; + for (unsigned int i = 0; i < count; i++) + feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); + } + + return ret; +} + + +/** + * hb_ot_layout_language_find_feature: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_tag: #hb_tag_t of the feature tag requested + * @feature_index: (out): The index of the requested feature + * + * Fetches the index of a given feature tag in the specified face's GSUB table + * or GPOS table, underneath the specified script and language. + * + * Return value: `true` if the feature is found, `false` otherwise + * + * Since: 0.6.0 + * + **/ +hb_bool_t +hb_ot_layout_language_find_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + hb_tag_t feature_tag, + unsigned int *feature_index /* OUT */) +{ + static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), ""); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + unsigned int num_features = l.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) { + unsigned int f_index = l.get_feature_index (i); + + if (feature_tag == g.get_feature_tag (f_index)) { + if (feature_index) *feature_index = f_index; + return true; + } + } + + if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; + return false; +} + + +/** + * hb_ot_layout_feature_get_lookups: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @feature_index: The index of the requested feature + * @start_offset: offset of the first lookup to retrieve + * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return; + * Output = the actual number of lookups returned (may be zero) + * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query + * + * Fetches a list of all lookups enumerated for the specified feature, in + * the specified face's GSUB table or GPOS table. The list returned will + * begin at the offset provided. + * + * Return value: Total number of lookups. + * + * Since: 0.9.7 + **/ +unsigned int +hb_ot_layout_feature_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */) +{ + return hb_ot_layout_feature_with_variations_get_lookups (face, + table_tag, + feature_index, + HB_OT_LAYOUT_NO_VARIATIONS_INDEX, + start_offset, + lookup_count, + lookup_indexes); +} + + +/** + * hb_ot_layout_table_get_lookup_count: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * + * Fetches the total number of lookups enumerated in the specified + * face's GSUB table or GPOS table. + * + * Return value: Total number of lookups. + * + * Since: 0.9.22 + **/ +unsigned int +hb_ot_layout_table_get_lookup_count (hb_face_t *face, + hb_tag_t table_tag) +{ + return get_gsubgpos_table (face, table_tag).get_lookup_count (); +} + + +struct hb_collect_features_context_t +{ + hb_collect_features_context_t (hb_face_t *face, + hb_tag_t table_tag, + hb_set_t *feature_indices_, + const hb_tag_t *features) + + : g (get_gsubgpos_table (face, table_tag)), + feature_indices (feature_indices_), + has_feature_filter (false), + script_count (0),langsys_count (0), feature_index_count (0) + { + compute_feature_filter (features); + } + + void compute_feature_filter (const hb_tag_t *features) + { + if (features == nullptr) + { + has_feature_filter = false; + return; + } + + has_feature_filter = true; + hb_set_t features_set; + for (; *features; features++) + features_set.add (*features); + + for (unsigned i = 0; i < g.get_feature_count (); i++) + { + hb_tag_t tag = g.get_feature_tag (i); + if (features_set.has (tag)) + feature_indices_filter.add(i); + } + } + + bool visited (const OT::Script &s) + { + /* We might have Null() object here. Don't want to involve + * that in the memoize. So, detect empty objects and return. */ + if (unlikely (!s.has_default_lang_sys () && + !s.get_lang_sys_count ())) + return true; + + if (script_count++ > HB_MAX_SCRIPTS) + return true; + + return visited (s, visited_script); + } + bool visited (const OT::LangSys &l) + { + /* We might have Null() object here. Don't want to involve + * that in the memoize. So, detect empty objects and return. */ + if (unlikely (!l.has_required_feature () && + !l.get_feature_count ())) + return true; + + if (langsys_count++ > HB_MAX_LANGSYS) + return true; + + return visited (l, visited_langsys); + } + + bool visited_feature_indices (unsigned count) + { + feature_index_count += count; + return feature_index_count > HB_MAX_FEATURE_INDICES; + } + + private: + template <typename T> + bool visited (const T &p, hb_set_t &visited_set) + { + hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g); + if (visited_set.has (delta)) + return true; + + visited_set.add (delta); + return false; + } + + public: + const OT::GSUBGPOS &g; + hb_set_t *feature_indices; + hb_set_t feature_indices_filter; + bool has_feature_filter; + + private: + hb_set_t visited_script; + hb_set_t visited_langsys; + unsigned int script_count; + unsigned int langsys_count; + unsigned int feature_index_count; +}; + +static void +langsys_collect_features (hb_collect_features_context_t *c, + const OT::LangSys &l) +{ + if (c->visited (l)) return; + + if (!c->has_feature_filter) + { + /* All features. */ + if (l.has_required_feature () && !c->visited_feature_indices (1)) + c->feature_indices->add (l.get_required_feature_index ()); + + // TODO(garretrieger): filter out indices >= feature count? + if (!c->visited_feature_indices (l.featureIndex.len)) + l.add_feature_indexes_to (c->feature_indices); + } + else + { + if (c->feature_indices_filter.is_empty()) return; + unsigned int num_features = l.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) + { + unsigned int feature_index = l.get_feature_index (i); + if (!c->feature_indices_filter.has (feature_index)) continue; + + c->feature_indices->add (feature_index); + c->feature_indices_filter.del (feature_index); + } + } +} + +static void +script_collect_features (hb_collect_features_context_t *c, + const OT::Script &s, + const hb_tag_t *languages) +{ + if (c->visited (s)) return; + + if (!languages) + { + /* All languages. */ + if (s.has_default_lang_sys ()) + langsys_collect_features (c, + s.get_default_lang_sys ()); + + + unsigned int count = s.get_lang_sys_count (); + for (unsigned int language_index = 0; language_index < count; language_index++) + langsys_collect_features (c, + s.get_lang_sys (language_index)); + } + else + { + for (; *languages; languages++) + { + unsigned int language_index; + if (s.find_lang_sys_index (*languages, &language_index)) + langsys_collect_features (c, + s.get_lang_sys (language_index)); + + } + } +} + + +/** + * hb_ot_layout_collect_features: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @scripts: (nullable) (array zero-terminated=1): The array of scripts to collect features for, + * terminated by %HB_TAG_NONE + * @languages: (nullable) (array zero-terminated=1): The array of languages to collect features for, + * terminated by %HB_TAG_NONE + * @features: (nullable) (array zero-terminated=1): The array of features to collect, + * terminated by %HB_TAG_NONE + * @feature_indexes: (out): The set of feature indexes found for the query + * + * Fetches a list of all feature indexes in the specified face's GSUB table + * or GPOS table, underneath the specified scripts, languages, and features. + * If no list of scripts is provided, all scripts will be queried. If no list + * of languages is provided, all languages will be queried. If no list of + * features is provided, all features will be queried. + * + * Since: 1.8.5 + **/ +void +hb_ot_layout_collect_features (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *scripts, + const hb_tag_t *languages, + const hb_tag_t *features, + hb_set_t *feature_indexes /* OUT */) +{ + hb_collect_features_context_t c (face, table_tag, feature_indexes, features); + if (!scripts) + { + /* All scripts. */ + unsigned int count = c.g.get_script_count (); + for (unsigned int script_index = 0; script_index < count; script_index++) + script_collect_features (&c, + c.g.get_script (script_index), + languages); + } + else + { + for (; *scripts; scripts++) + { + unsigned int script_index; + if (c.g.find_script_index (*scripts, &script_index)) + script_collect_features (&c, + c.g.get_script (script_index), + languages); + } + } +} + +/** + * hb_ot_layout_collect_features_map: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @script_index: The index of the requested script tag + * @language_index: The index of the requested language tag + * @feature_map: (out): The map of feature tag to feature index. + * + * Fetches the mapping from feature tags to feature indexes for + * the specified script and language. + * + * Since: 8.1.0 + **/ +void +hb_ot_layout_collect_features_map (hb_face_t *face, + hb_tag_t table_tag, + unsigned script_index, + unsigned language_index, + hb_map_t *feature_map /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); + + unsigned int count = l.get_feature_indexes (0, nullptr, nullptr); + feature_map->alloc (count); + + /* Loop in reverse, such that earlier entries win. That emulates + * a linear search, which seems to be what other implementations do. + * We found that with arialuni_t.ttf, the "ur" language system has + * duplicate features, and the earlier ones work but not later ones. + */ + for (unsigned int i = count; i; i--) + { + unsigned feature_index = 0; + unsigned feature_count = 1; + l.get_feature_indexes (i - 1, &feature_count, &feature_index); + if (!feature_count) + break; + hb_tag_t feature_tag = g.get_feature_tag (feature_index); + feature_map->set (feature_tag, feature_index); + } +} + + +/** + * hb_ot_layout_collect_lookups: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @scripts: (nullable) (array zero-terminated=1): The array of scripts to collect lookups for, + * terminated by %HB_TAG_NONE + * @languages: (nullable) (array zero-terminated=1): The array of languages to collect lookups for, + * terminated by %HB_TAG_NONE + * @features: (nullable) (array zero-terminated=1): The array of features to collect lookups for, + * terminated by %HB_TAG_NONE + * @lookup_indexes: (out): The array of lookup indexes found for the query + * + * Fetches a list of all feature-lookup indexes in the specified face's GSUB + * table or GPOS table, underneath the specified scripts, languages, and + * features. If no list of scripts is provided, all scripts will be queried. + * If no list of languages is provided, all languages will be queried. If no + * list of features is provided, all features will be queried. + * + * Since: 0.9.8 + **/ +void +hb_ot_layout_collect_lookups (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *scripts, + const hb_tag_t *languages, + const hb_tag_t *features, + hb_set_t *lookup_indexes /* OUT */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + hb_set_t feature_indexes; + hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes); + + for (auto feature_index : feature_indexes) + g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes); + + g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes); +} + + +#ifndef HB_NO_LAYOUT_COLLECT_GLYPHS +/** + * hb_ot_layout_lookup_collect_glyphs: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @lookup_index: The index of the feature lookup to query + * @glyphs_before: (out): Array of glyphs preceding the substitution range + * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup + * @glyphs_after: (out): Array of glyphs following the substitution range + * @glyphs_output: (out): Array of glyphs that would be the substituted output of the lookup + * + * Fetches a list of all glyphs affected by the specified lookup in the + * specified face's GSUB table or GPOS table. + * + * Since: 0.9.7 + **/ +void +hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, + hb_tag_t table_tag, + unsigned int lookup_index, + hb_set_t *glyphs_before, /* OUT. May be NULL */ + hb_set_t *glyphs_input, /* OUT. May be NULL */ + hb_set_t *glyphs_after, /* OUT. May be NULL */ + hb_set_t *glyphs_output /* OUT. May be NULL */) +{ + OT::hb_collect_glyphs_context_t c (face, + glyphs_before, + glyphs_input, + glyphs_after, + glyphs_output); + + switch (table_tag) + { + case HB_OT_TAG_GSUB: + { + const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); + l.collect_glyphs (&c); + return; + } + case HB_OT_TAG_GPOS: + { + const OT::PosLookup& l = face->table.GPOS->table->get_lookup (lookup_index); + l.collect_glyphs (&c); + return; + } + } +} +#endif + + +/* Variations support */ + + +/** + * hb_ot_layout_table_find_feature_variations: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @coords: The variation coordinates to query + * @num_coords: The number of variation coordinates + * @variations_index: (out): The array of feature variations found for the query + * + * Fetches a list of feature variations in the specified face's GSUB table + * or GPOS table, at the specified variation coordinates. + * + * Return value: `true` if feature variations were found, `false` otherwise. + * + * Since: 1.4.0 + * + **/ +hb_bool_t +hb_ot_layout_table_find_feature_variations (hb_face_t *face, + hb_tag_t table_tag, + const int *coords, + unsigned int num_coords, + unsigned int *variations_index /* out */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + return g.find_variations_index (coords, num_coords, variations_index); +} + + +/** + * hb_ot_layout_feature_with_variations_get_lookups: + * @face: #hb_face_t to work upon + * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS + * @feature_index: The index of the feature to query + * @variations_index: The index of the feature variation to query + * @start_offset: offset of the first lookup to retrieve + * @lookup_count: (inout) (optional): Input = the maximum number of lookups to return; + * Output = the actual number of lookups returned (may be zero) + * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query + * + * Fetches a list of all lookups enumerated for the specified feature, in + * the specified face's GSUB table or GPOS table, enabled at the specified + * variations index. The list returned will begin at the offset provided. + * + * Return value: Total number of lookups. + * + * Since: 1.4.0 + * + **/ +unsigned int +hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int variations_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */) +{ + static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), ""); + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + const OT::Feature &f = g.get_feature_variation (feature_index, variations_index); + + return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); +} + + +/* + * OT::GSUB + */ + + +/** + * hb_ot_layout_has_substitution: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any GSUB substitutions. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.6.0 + * + **/ +hb_bool_t +hb_ot_layout_has_substitution (hb_face_t *face) +{ + return face->table.GSUB->table->has_data (); +} + + +/** + * hb_ot_layout_lookup_would_substitute: + * @face: #hb_face_t to work upon + * @lookup_index: The index of the lookup to query + * @glyphs: The sequence of glyphs to query for substitution + * @glyphs_length: The length of the glyph sequence + * @zero_context: #hb_bool_t indicating whether pre-/post-context are disallowed + * in substitutions + * + * Tests whether a specified lookup in the specified face would + * trigger a substitution on the given glyph sequence. + * + * Return value: `true` if a substitution would be triggered, `false` otherwise + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_ot_layout_lookup_would_substitute (hb_face_t *face, + unsigned int lookup_index, + const hb_codepoint_t *glyphs, + unsigned int glyphs_length, + hb_bool_t zero_context) +{ + auto &gsub = face->table.GSUB; + if (unlikely (lookup_index >= gsub->lookup_count)) return false; + OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); + + const OT::SubstLookup& l = gsub->table->get_lookup (lookup_index); + auto *accel = gsub->get_accel (lookup_index); + return accel && l.would_apply (&c, accel); +} + + +/** + * hb_ot_layout_substitute_start: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called before substitution lookups are performed, to ensure that glyph + * class and other properties are set on the glyphs in the buffer. + * + **/ +void +hb_ot_layout_substitute_start (hb_font_t *font, + hb_buffer_t *buffer) +{ + _hb_ot_layout_set_glyph_props (font, buffer); +} + +/** + * hb_ot_layout_lookup_substitute_closure: + * @face: #hb_face_t to work upon + * @lookup_index: index of the feature lookup to query + * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookup + * + * Compute the transitive closure of glyphs needed for a + * specified lookup. + * + * Since: 0.9.7 + **/ +void +hb_ot_layout_lookup_substitute_closure (hb_face_t *face, + unsigned int lookup_index, + hb_set_t *glyphs /* OUT */) +{ + hb_map_t done_lookups_glyph_count; + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set; + OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set); + + const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); + + l.closure (&c, lookup_index); +} + +/** + * hb_ot_layout_lookups_substitute_closure: + * @face: #hb_face_t to work upon + * @lookups: The set of lookups to query + * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookups + * + * Compute the transitive closure of glyphs needed for all of the + * provided lookups. + * + * Since: 1.8.1 + **/ +void +hb_ot_layout_lookups_substitute_closure (hb_face_t *face, + const hb_set_t *lookups, + hb_set_t *glyphs /* OUT */) +{ + hb_map_t done_lookups_glyph_count; + hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set; + OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set); + const GSUB& gsub = *face->table.GSUB->table; + + unsigned int iteration_count = 0; + unsigned int glyphs_length; + do + { + c.reset_lookup_visit_count (); + glyphs_length = glyphs->get_population (); + if (lookups) + { + for (auto lookup_index : *lookups) + gsub.get_lookup (lookup_index).closure (&c, lookup_index); + } + else + { + for (unsigned int i = 0; i < gsub.get_lookup_count (); i++) + gsub.get_lookup (i).closure (&c, i); + } + } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES && + glyphs_length != glyphs->get_population ()); +} + +/* + * GPOS + */ + + +/** + * hb_ot_layout_has_positioning: + * @face: #hb_face_t to work upon + * + * Tests whether the specified face includes any GPOS positioning. + * + * Return value: `true` if the face has GPOS data, `false` otherwise + * + **/ +hb_bool_t +hb_ot_layout_has_positioning (hb_face_t *face) +{ + return face->table.GPOS->table->has_data (); +} + +/** + * hb_ot_layout_position_start: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called before positioning lookups are performed, to ensure that glyph + * attachment types and glyph-attachment chains are set for the glyphs in the buffer. + * + **/ +void +hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) +{ + GPOS::position_start (font, buffer); +} + + +/** + * hb_ot_layout_position_finish_advances: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called after positioning lookups are performed, to finish glyph advances. + * + **/ +void +hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer) +{ + GPOS::position_finish_advances (font, buffer); +} + +/** + * hb_ot_layout_position_finish_offsets: + * @font: #hb_font_t to use + * @buffer: #hb_buffer_t buffer to work upon + * + * Called after positioning lookups are performed, to finish glyph offsets. + * + **/ +void +hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) +{ + GPOS::position_finish_offsets (font, buffer); +} + + +#ifndef HB_NO_LAYOUT_FEATURE_PARAMS +/** + * hb_ot_layout_get_size_params: + * @face: #hb_face_t to work upon + * @design_size: (out): The design size of the face + * @subfamily_id: (out): The identifier of the face within the font subfamily + * @subfamily_name_id: (out): The ‘name’ table name ID of the face within the font subfamily + * @range_start: (out): The minimum size of the recommended size range for the face + * @range_end: (out): The maximum size of the recommended size range for the face + * + * Fetches optical-size feature data (i.e., the `size` feature from GPOS). Note that + * the subfamily_id and the subfamily name string (accessible via the subfamily_name_id) + * as used here are defined as pertaining only to fonts within a font family that differ + * specifically in their respective size ranges; other ways to differentiate fonts within + * a subfamily are not covered by the `size` feature. + * + * For more information on this distinction, see the [`size` feature documentation]( + * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size). + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 0.9.10 + **/ +hb_bool_t +hb_ot_layout_get_size_params (hb_face_t *face, + unsigned int *design_size, /* OUT. May be NULL */ + unsigned int *subfamily_id, /* OUT. May be NULL */ + hb_ot_name_id_t *subfamily_name_id, /* OUT. May be NULL */ + unsigned int *range_start, /* OUT. May be NULL */ + unsigned int *range_end /* OUT. May be NULL */) +{ + const GPOS &gpos = *face->table.GPOS->table; + const hb_tag_t tag = HB_TAG ('s','i','z','e'); + + unsigned int num_features = gpos.get_feature_count (); + for (unsigned int i = 0; i < num_features; i++) + { + if (tag == gpos.get_feature_tag (i)) + { + const OT::Feature &f = gpos.get_feature (i); + const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); + + if (params.designSize) + { + if (design_size) *design_size = params.designSize; + if (subfamily_id) *subfamily_id = params.subfamilyID; + if (subfamily_name_id) *subfamily_name_id = params.subfamilyNameID; + if (range_start) *range_start = params.rangeStart; + if (range_end) *range_end = params.rangeEnd; + + return true; + } + } + } + + if (design_size) *design_size = 0; + if (subfamily_id) *subfamily_id = 0; + if (subfamily_name_id) *subfamily_name_id = HB_OT_NAME_ID_INVALID; + if (range_start) *range_start = 0; + if (range_end) *range_end = 0; + + return false; +} + + +/** + * hb_ot_layout_feature_get_name_ids: + * @face: #hb_face_t to work upon + * @table_tag: table tag to query, "GSUB" or "GPOS". + * @feature_index: index of feature to query. + * @label_id: (out) (optional): The ‘name’ table name ID that specifies a string + * for a user-interface label for this feature. (May be NULL.) + * @tooltip_id: (out) (optional): The ‘name’ table name ID that specifies a string + * that an application can use for tooltip text for this + * feature. (May be NULL.) + * @sample_id: (out) (optional): The ‘name’ table name ID that specifies sample text + * that illustrates the effect of this feature. (May be NULL.) + * @num_named_parameters: (out) (optional): Number of named parameters. (May be zero.) + * @first_param_id: (out) (optional): The first ‘name’ table name ID used to specify + * strings for user-interface labels for the feature + * parameters. (Must be zero if numParameters is zero.) + * + * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or + * "Character Variant" ('cvXX') features. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.0.0 + **/ +hb_bool_t +hb_ot_layout_feature_get_name_ids (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + hb_ot_name_id_t *label_id, /* OUT. May be NULL */ + hb_ot_name_id_t *tooltip_id, /* OUT. May be NULL */ + hb_ot_name_id_t *sample_id, /* OUT. May be NULL */ + unsigned int *num_named_parameters, /* OUT. May be NULL */ + hb_ot_name_id_t *first_param_id /* OUT. May be NULL */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + + hb_tag_t feature_tag = g.get_feature_tag (feature_index); + const OT::Feature &f = g.get_feature (feature_index); + + const OT::FeatureParams &feature_params = f.get_feature_params (); + if (&feature_params != &Null (OT::FeatureParams)) + { + const OT::FeatureParamsStylisticSet& ss_params = + feature_params.get_stylistic_set_params (feature_tag); + if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */ + { + if (label_id) *label_id = ss_params.uiNameID; + // ssXX features don't have the rest + if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID; + if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID; + if (num_named_parameters) *num_named_parameters = 0; + if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; + return true; + } + const OT::FeatureParamsCharacterVariants& cv_params = + feature_params.get_character_variants_params (feature_tag); + if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */ + { + if (label_id) *label_id = cv_params.featUILableNameID; + if (tooltip_id) *tooltip_id = cv_params.featUITooltipTextNameID; + if (sample_id) *sample_id = cv_params.sampleTextNameID; + if (num_named_parameters) *num_named_parameters = cv_params.numNamedParameters; + if (first_param_id) *first_param_id = cv_params.firstParamUILabelNameID; + return true; + } + } + + if (label_id) *label_id = HB_OT_NAME_ID_INVALID; + if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID; + if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID; + if (num_named_parameters) *num_named_parameters = 0; + if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; + return false; +} +/** + * hb_ot_layout_feature_get_characters: + * @face: #hb_face_t to work upon + * @table_tag: table tag to query, "GSUB" or "GPOS". + * @feature_index: index of feature to query. + * @start_offset: offset of the first character to retrieve + * @char_count: (inout) (optional): Input = the maximum number of characters to return; + * Output = the actual number of characters returned (may be zero) + * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. + * The Unicode codepoints of the characters for which this feature provides + * glyph variants. + * + * Fetches a list of the characters defined as having a variant under the specified + * "Character Variant" ("cvXX") feature tag. + * + * Return value: Number of total sample characters in the cvXX feature. + * + * Since: 2.0.0 + **/ +unsigned int +hb_ot_layout_feature_get_characters (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int start_offset, + unsigned int *char_count, /* IN/OUT. May be NULL */ + hb_codepoint_t *characters /* OUT. May be NULL */) +{ + const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); + return g.get_feature (feature_index) + .get_feature_params () + .get_character_variants_params(g.get_feature_tag (feature_index)) + .get_characters (start_offset, char_count, characters); +} +#endif + + +/* + * Parts of different types are implemented here such that they have direct + * access to GSUB/GPOS lookups. + */ + + +struct GSUBProxy +{ + static constexpr unsigned table_index = 0u; + static constexpr bool always_inplace = false; + typedef OT::SubstLookup Lookup; + + GSUBProxy (hb_face_t *face) : + accel (*face->table.GSUB) {} + + const GSUB::accelerator_t &accel; +}; + +struct GPOSProxy +{ + static constexpr unsigned table_index = 1u; + static constexpr bool always_inplace = true; + typedef OT::PosLookup Lookup; + + GPOSProxy (hb_face_t *face) : + accel (*face->table.GPOS) {} + + const GPOS::accelerator_t &accel; +}; + + +static inline bool +apply_forward (OT::hb_ot_apply_context_t *c, + const OT::hb_ot_layout_lookup_accelerator_t &accel, + unsigned subtable_count) +{ + bool use_cache = accel.cache_enter (c); + + bool ret = false; + hb_buffer_t *buffer = c->buffer; + while (buffer->idx < buffer->len && buffer->successful) + { + bool applied = false; + if (accel.digest.may_have (buffer->cur().codepoint) && + (buffer->cur().mask & c->lookup_mask) && + c->check_glyph_property (&buffer->cur(), c->lookup_props)) + { + applied = accel.apply (c, subtable_count, use_cache); + } + + if (applied) + ret = true; + else + (void) buffer->next_glyph (); + } + + if (use_cache) + accel.cache_leave (c); + + return ret; +} + +static inline bool +apply_backward (OT::hb_ot_apply_context_t *c, + const OT::hb_ot_layout_lookup_accelerator_t &accel, + unsigned subtable_count) +{ + bool ret = false; + hb_buffer_t *buffer = c->buffer; + do + { + if (accel.digest.may_have (buffer->cur().codepoint) && + (buffer->cur().mask & c->lookup_mask) && + c->check_glyph_property (&buffer->cur(), c->lookup_props)) + ret |= accel.apply (c, subtable_count, false); + + /* The reverse lookup doesn't "advance" cursor (for good reason). */ + buffer->idx--; + + } + while ((int) buffer->idx >= 0); + return ret; +} + +template <typename Proxy> +static inline bool +apply_string (OT::hb_ot_apply_context_t *c, + const typename Proxy::Lookup &lookup, + const OT::hb_ot_layout_lookup_accelerator_t &accel) +{ + hb_buffer_t *buffer = c->buffer; + unsigned subtable_count = lookup.get_subtable_count (); + + if (unlikely (!buffer->len || !c->lookup_mask)) + return false; + + bool ret = false; + + c->set_lookup_props (lookup.get_props ()); + + if (likely (!lookup.is_reverse ())) + { + /* in/out forward substitution/positioning */ + if (!Proxy::always_inplace) + buffer->clear_output (); + + buffer->idx = 0; + ret = apply_forward (c, accel, subtable_count); + + if (!Proxy::always_inplace) + buffer->sync (); + } + else + { + /* in-place backward substitution/positioning */ + assert (!buffer->have_output); + buffer->idx = buffer->len - 1; + ret = apply_backward (c, accel, subtable_count); + } + + return ret; +} + +template <typename Proxy> +inline void hb_ot_map_t::apply (const Proxy &proxy, + const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) const +{ + const unsigned int table_index = proxy.table_index; + unsigned int i = 0; + OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob ()); + c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>); + + for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) + { + const stage_map_t *stage = &stages[table_index][stage_index]; + for (; i < stage->last_lookup; i++) + { + auto &lookup = lookups[table_index][i]; + + unsigned int lookup_index = lookup.index; + + auto *accel = proxy.accel.get_accel (lookup_index); + if (unlikely (!accel)) continue; + + if (buffer->messaging () && + !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue; + + /* c.digest is a digest of all the current glyphs in the buffer + * (plus some past glyphs). + * + * Only try applying the lookup if there is any overlap. */ + if (accel->digest.may_have (c.digest)) + { + c.set_lookup_index (lookup_index); + c.set_lookup_mask (lookup.mask, false); + c.set_auto_zwj (lookup.auto_zwj, false); + c.set_auto_zwnj (lookup.auto_zwnj, false); + c.set_random (lookup.random); + c.set_per_syllable (lookup.per_syllable, false); + /* apply_string's set_lookup_props initializes the iterators. */ + + apply_string<Proxy> (&c, + proxy.accel.table->get_lookup (lookup_index), + *accel); + } + else if (buffer->messaging ()) + (void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag)); + + if (buffer->messaging ()) + (void) buffer->message (font, "end lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag)); + } + + if (stage->pause_func) + { + if (stage->pause_func (plan, font, buffer)) + { + /* Refresh working buffer digest since buffer changed. */ + c.digest = buffer->digest (); + } + } + } +} + +void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const +{ + GSUBProxy proxy (font->face); + if (buffer->messaging () && + !buffer->message (font, "start table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]))) return; + apply (proxy, plan, font, buffer); + if (buffer->messaging ()) + (void) buffer->message (font, "end table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0])); +} + +void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const +{ + GPOSProxy proxy (font->face); + if (buffer->messaging () && + !buffer->message (font, "start table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]))) return; + apply (proxy, plan, font, buffer); + if (buffer->messaging ()) + (void) buffer->message (font, "end table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1])); +} + +void +hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c, + const OT::SubstLookup &lookup, + const OT::hb_ot_layout_lookup_accelerator_t &accel) +{ + apply_string<GSUBProxy> (c, lookup, accel); +} + +#ifndef HB_NO_BASE + +static void +choose_base_tags (hb_script_t script, + hb_language_t language, + hb_tag_t *script_tag, + hb_tag_t *language_tag) +{ + hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; + unsigned script_count = ARRAY_LENGTH (script_tags); + + hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; + unsigned language_count = ARRAY_LENGTH (language_tags); + + hb_ot_tags_from_script_and_language (script, language, + &script_count, script_tags, + &language_count, language_tags); + + *script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT; + *language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE; +} + +/** + * hb_ot_layout_get_font_extents: + * @font: a font + * @direction: text direction. + * @script_tag: script tag. + * @language_tag: language tag. + * @extents: (out) (nullable): font extents if found. + * + * Fetches script/language-specific font extents. These values are + * looked up in the `BASE` table's `MinMax` records. + * + * If no such extents are found, the default extents for the font are + * fetched. As such, the return value of this function can for the + * most part be ignored. Note that the per-script/language extents + * do not have a line-gap value, and the line-gap is set to zero in + * that case. + * + * Return value: `true` if found script/language-specific font extents. + * + * Since: 8.0.0 + **/ +hb_bool_t +hb_ot_layout_get_font_extents (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_font_extents_t *extents) +{ + hb_position_t min, max; + if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE, + &min, &max)) + { + if (extents) + { + extents->ascender = max; + extents->descender = min; + extents->line_gap = 0; + } + return true; + } + + hb_font_get_extents_for_direction (font, direction, extents); + return false; +} + +/** + * hb_ot_layout_get_font_extents2: + * @font: a font + * @direction: text direction. + * @script: script. + * @language: (nullable): language. + * @extents: (out) (nullable): font extents if found. + * + * Fetches script/language-specific font extents. These values are + * looked up in the `BASE` table's `MinMax` records. + * + * If no such extents are found, the default extents for the font are + * fetched. As such, the return value of this function can for the + * most part be ignored. Note that the per-script/language extents + * do not have a line-gap value, and the line-gap is set to zero in + * that case. + * + * This function is like hb_ot_layout_get_font_extents() but takes + * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. + * + * Return value: `true` if found script/language-specific font extents. + * + * Since: 8.0.0 + **/ +hb_bool_t +hb_ot_layout_get_font_extents2 (hb_font_t *font, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_font_extents_t *extents) +{ + hb_tag_t script_tag, language_tag; + choose_base_tags (script, language, &script_tag, &language_tag); + return hb_ot_layout_get_font_extents (font, + direction, + script_tag, + language_tag, + extents); +} + +/** + * hb_ot_layout_get_horizontal_baseline_tag_for_script: + * @script: a script tag. + * + * Fetches the dominant horizontal baseline tag used by @script. + * + * Return value: dominant baseline tag for the @script. + * + * Since: 4.0.0 + **/ +hb_ot_layout_baseline_tag_t +hb_ot_layout_get_horizontal_baseline_tag_for_script (hb_script_t script) +{ + /* Keep in sync with hb_ot_layout_get_baseline_with_fallback */ + switch ((int) script) + { + /* Unicode-1.1 additions */ + case HB_SCRIPT_BENGALI: + case HB_SCRIPT_DEVANAGARI: + case HB_SCRIPT_GUJARATI: + case HB_SCRIPT_GURMUKHI: + /* Unicode-2.0 additions */ + case HB_SCRIPT_TIBETAN: + /* Unicode-4.0 additions */ + case HB_SCRIPT_LIMBU: + /* Unicode-4.1 additions */ + case HB_SCRIPT_SYLOTI_NAGRI: + /* Unicode-5.0 additions */ + case HB_SCRIPT_PHAGS_PA: + /* Unicode-5.2 additions */ + case HB_SCRIPT_MEETEI_MAYEK: + /* Unicode-6.1 additions */ + case HB_SCRIPT_SHARADA: + case HB_SCRIPT_TAKRI: + /* Unicode-7.0 additions */ + case HB_SCRIPT_MODI: + case HB_SCRIPT_SIDDHAM: + case HB_SCRIPT_TIRHUTA: + /* Unicode-9.0 additions */ + case HB_SCRIPT_MARCHEN: + case HB_SCRIPT_NEWA: + /* Unicode-10.0 additions */ + case HB_SCRIPT_SOYOMBO: + case HB_SCRIPT_ZANABAZAR_SQUARE: + /* Unicode-11.0 additions */ + case HB_SCRIPT_DOGRA: + case HB_SCRIPT_GUNJALA_GONDI: + /* Unicode-12.0 additions */ + case HB_SCRIPT_NANDINAGARI: + return HB_OT_LAYOUT_BASELINE_TAG_HANGING; + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HANGUL: + case HB_SCRIPT_HAN: + case HB_SCRIPT_HIRAGANA: + case HB_SCRIPT_KATAKANA: + /* Unicode-3.0 additions */ + case HB_SCRIPT_BOPOMOFO: + /* Unicode-9.0 additions */ + case HB_SCRIPT_TANGUT: + /* Unicode-10.0 additions */ + case HB_SCRIPT_NUSHU: + /* Unicode-13.0 additions */ + case HB_SCRIPT_KHITAN_SMALL_SCRIPT: + return HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT; + + default: + return HB_OT_LAYOUT_BASELINE_TAG_ROMAN; + } +} + +/** + * hb_ot_layout_get_baseline: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script_tag: script tag. + * @language_tag: language tag, currently unused. + * @coord: (out) (nullable): baseline value if found. + * + * Fetches a baseline value from the face. + * + * Return value: `true` if found baseline value in the font. + * + * Since: 2.6.0 + **/ +hb_bool_t +hb_ot_layout_get_baseline (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT. May be NULL. */) +{ + return font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord); +} + +/** + * hb_ot_layout_get_baseline2: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script: script. + * @language: (nullable): language, currently unused. + * @coord: (out) (nullable): baseline value if found. + * + * Fetches a baseline value from the face. + * + * This function is like hb_ot_layout_get_baseline() but takes + * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. + * + * Return value: `true` if found baseline value in the font. + * + * Since: 8.0.0 + **/ +hb_bool_t +hb_ot_layout_get_baseline2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT. May be NULL. */) +{ + hb_tag_t script_tag, language_tag; + choose_base_tags (script, language, &script_tag, &language_tag); + return hb_ot_layout_get_baseline (font, + baseline_tag, + direction, + script_tag, + language_tag, + coord); +} + +/** + * hb_ot_layout_get_baseline_with_fallback: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script_tag: script tag. + * @language_tag: language tag, currently unused. + * @coord: (out): baseline value if found. + * + * Fetches a baseline value from the face, and synthesizes + * it if the font does not have it. + * + * Since: 4.0.0 + **/ +void +hb_ot_layout_get_baseline_with_fallback (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT */) +{ + if (hb_ot_layout_get_baseline (font, + baseline_tag, + direction, + script_tag, + language_tag, + coord)) + return; + + /* Synthesize missing baselines. + * See https://www.w3.org/TR/css-inline-3/#baseline-synthesis-fonts + */ + switch (baseline_tag) + { + case HB_OT_LAYOUT_BASELINE_TAG_ROMAN: + *coord = 0; // FIXME origin ? + break; + + case HB_OT_LAYOUT_BASELINE_TAG_MATH: + { + hb_codepoint_t glyph; + hb_glyph_extents_t extents; + if (HB_DIRECTION_IS_HORIZONTAL (direction) && + (hb_font_get_nominal_glyph (font, 0x2212u, &glyph) || + hb_font_get_nominal_glyph (font, '-', &glyph)) && + hb_font_get_glyph_extents (font, glyph, &extents)) + { + *coord = extents.y_bearing + extents.height / 2; + } + else + { + hb_position_t x_height = font->y_scale / 2; +#ifndef HB_NO_METRICS + hb_ot_metrics_get_position_with_fallback (font, HB_OT_METRICS_TAG_X_HEIGHT, &x_height); +#endif + *coord = x_height / 2; + } + } + break; + + case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT: + case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT: + { + hb_position_t embox_top, embox_bottom; + + hb_ot_layout_get_baseline_with_fallback (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, + direction, + script_tag, + language_tag, + &embox_top); + hb_ot_layout_get_baseline_with_fallback (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, + direction, + script_tag, + language_tag, + &embox_bottom); + + if (baseline_tag == HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT) + *coord = embox_top + (embox_bottom - embox_top) / 10; + else + *coord = embox_bottom + (embox_top - embox_bottom) / 10; + } + break; + + case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT: + if (hb_ot_layout_get_baseline (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, + direction, + script_tag, + language_tag, + coord)) + *coord += HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale; + else + { + hb_font_extents_t font_extents; + hb_font_get_extents_for_direction (font, direction, &font_extents); + *coord = font_extents.ascender; + } + break; + + case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT: + if (hb_ot_layout_get_baseline (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, + direction, + script_tag, + language_tag, + coord)) + *coord -= HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale; + else + { + hb_font_extents_t font_extents; + hb_font_get_extents_for_direction (font, direction, &font_extents); + *coord = font_extents.descender; + } + break; + + case HB_OT_LAYOUT_BASELINE_TAG_HANGING: + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + { + hb_codepoint_t ch; + hb_codepoint_t glyph; + hb_glyph_extents_t extents; + + /* Keep in sync with hb_ot_layout_get_horizontal_baseline_for_script */ + switch ((int) script_tag) + { + /* Unicode-1.1 additions */ + case HB_SCRIPT_BENGALI: ch = 0x0995u; break; + case HB_SCRIPT_DEVANAGARI: ch = 0x0915u; break; + case HB_SCRIPT_GUJARATI: ch = 0x0a95u; break; + case HB_SCRIPT_GURMUKHI: ch = 0x0a15u; break; + /* Unicode-2.0 additions */ + case HB_SCRIPT_TIBETAN: ch = 0x0f40u; break; + /* Unicode-4.0 additions */ + case HB_SCRIPT_LIMBU: ch = 0x1901u; break; + /* Unicode-4.1 additions */ + case HB_SCRIPT_SYLOTI_NAGRI: ch = 0xa807u; break; + /* Unicode-5.0 additions */ + case HB_SCRIPT_PHAGS_PA: ch = 0xa840u; break; + /* Unicode-5.2 additions */ + case HB_SCRIPT_MEETEI_MAYEK: ch = 0xabc0u; break; + /* Unicode-6.1 additions */ + case HB_SCRIPT_SHARADA: ch = 0x11191u; break; + case HB_SCRIPT_TAKRI: ch = 0x1168cu; break; + /* Unicode-7.0 additions */ + case HB_SCRIPT_MODI: ch = 0x1160eu;break; + case HB_SCRIPT_SIDDHAM: ch = 0x11590u; break; + case HB_SCRIPT_TIRHUTA: ch = 0x1148fu; break; + /* Unicode-9.0 additions */ + case HB_SCRIPT_MARCHEN: ch = 0x11c72u; break; + case HB_SCRIPT_NEWA: ch = 0x1140eu; break; + /* Unicode-10.0 additions */ + case HB_SCRIPT_SOYOMBO: ch = 0x11a5cu; break; + case HB_SCRIPT_ZANABAZAR_SQUARE: ch = 0x11a0bu; break; + /* Unicode-11.0 additions */ + case HB_SCRIPT_DOGRA: ch = 0x1180au; break; + case HB_SCRIPT_GUNJALA_GONDI: ch = 0x11d6cu; break; + /* Unicode-12.0 additions */ + case HB_SCRIPT_NANDINAGARI: ch = 0x119b0u; break; + default: ch = 0; break; + } + + if (ch && + hb_font_get_nominal_glyph (font, ch, &glyph) && + hb_font_get_glyph_extents (font, glyph, &extents)) + *coord = extents.y_bearing; + else + *coord = font->y_scale * 6 / 10; // FIXME makes assumptions about origin + } + else + *coord = font->x_scale * 6 / 10; // FIXME makes assumptions about origin + break; + + case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL: + { + hb_position_t top, bottom; + hb_ot_layout_get_baseline_with_fallback (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT, + direction, + script_tag, + language_tag, + &top); + hb_ot_layout_get_baseline_with_fallback (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, + direction, + script_tag, + language_tag, + &bottom); + *coord = (top + bottom) / 2; + + } + break; + + case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL: + { + hb_position_t top, bottom; + hb_ot_layout_get_baseline_with_fallback (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT, + direction, + script_tag, + language_tag, + &top); + hb_ot_layout_get_baseline_with_fallback (font, + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT, + direction, + script_tag, + language_tag, + &bottom); + *coord = (top + bottom) / 2; + + } + break; + + case _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE: + default: + *coord = 0; + break; + } +} + +/** + * hb_ot_layout_get_baseline_with_fallback2: + * @font: a font + * @baseline_tag: a baseline tag + * @direction: text direction. + * @script: script. + * @language: (nullable): language, currently unused. + * @coord: (out): baseline value if found. + * + * Fetches a baseline value from the face, and synthesizes + * it if the font does not have it. + * + * This function is like hb_ot_layout_get_baseline_with_fallback() but takes + * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t. + * + * Since: 8.0.0 + **/ +void +hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT */) +{ + hb_tag_t script_tag, language_tag; + choose_base_tags (script, language, &script_tag, &language_tag); + hb_ot_layout_get_baseline_with_fallback (font, + baseline_tag, + direction, + script_tag, + language_tag, + coord); +} + +#endif + + +struct hb_get_glyph_alternates_dispatch_t : + hb_dispatch_context_t<hb_get_glyph_alternates_dispatch_t, unsigned> +{ + static return_t default_return_value () { return 0; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + private: + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.get_glyph_alternates (std::forward<Ts> (ds)...) ) + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( default_return_value () ) + public: + template <typename T, typename ...Ts> auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) ) +}; + +#ifndef HB_NO_LAYOUT_RARELY_USED +/** + * hb_ot_layout_lookup_get_glyph_alternates: + * @face: a face. + * @lookup_index: index of the feature lookup to query. + * @glyph: a glyph id. + * @start_offset: starting offset. + * @alternate_count: (inout) (optional): Input = the maximum number of alternate glyphs to return; + * Output = the actual number of alternate glyphs returned (may be zero). + * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer. + * Alternate glyphs associated with the glyph id. + * + * Fetches alternates of a glyph from a given GSUB lookup index. + * + * Return value: Total number of alternates found in the specific lookup index for the given glyph id. + * + * Since: 2.6.8 + **/ +HB_EXTERN unsigned +hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_codepoint_t glyph, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT. May be NULL. */, + hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) +{ + hb_get_glyph_alternates_dispatch_t c; + const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index); + auto ret = lookup.dispatch (&c, glyph, start_offset, alternate_count, alternate_glyphs); + if (!ret && alternate_count) *alternate_count = 0; + return ret; +} + + +struct hb_position_single_dispatch_t : + hb_dispatch_context_t<hb_position_single_dispatch_t, bool> +{ + static return_t default_return_value () { return false; } + bool stop_sublookup_iteration (return_t r) const { return r; } + + private: + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.position_single (std::forward<Ts> (ds)...) ) + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( default_return_value () ) + public: + template <typename T, typename ...Ts> auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) ) +}; + +/** + * hb_ot_layout_lookup_get_optical_bound: + * @font: a font. + * @lookup_index: index of the feature lookup to query. + * @direction: edge of the glyph to query. + * @glyph: a glyph id. + * + * Fetches the optical bound of a glyph positioned at the margin of text. + * The direction identifies which edge of the glyph to query. + * + * Return value: Adjustment value. Negative values mean the glyph will stick out of the margin. + * + * Since: 5.3.0 + **/ +hb_position_t +hb_ot_layout_lookup_get_optical_bound (hb_font_t *font, + unsigned lookup_index, + hb_direction_t direction, + hb_codepoint_t glyph) +{ + const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index); + hb_blob_t *blob = font->face->table.GPOS->get_blob (); + hb_glyph_position_t pos = {0}; + hb_position_single_dispatch_t c; + lookup.dispatch (&c, font, blob, direction, glyph, pos); + hb_position_t ret = 0; + switch (direction) + { + case HB_DIRECTION_LTR: + ret = pos.x_offset; + break; + case HB_DIRECTION_RTL: + ret = pos.x_advance - pos.x_offset; + break; + case HB_DIRECTION_TTB: + ret = pos.y_offset; + break; + case HB_DIRECTION_BTT: + ret = pos.y_advance - pos.y_offset; + break; + case HB_DIRECTION_INVALID: + default: + break; + } + return ret; +} +#endif + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-layout.h b/gfx/harfbuzz/src/hb-ot-layout.h new file mode 100644 index 0000000000..386b98d580 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout.h @@ -0,0 +1,549 @@ +/* + * Copyright © 2007,2008,2009 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): Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_LAYOUT_H +#define HB_OT_LAYOUT_H + +#include "hb.h" + +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/** + * HB_OT_TAG_BASE: + * + * OpenType [Baseline Table](https://docs.microsoft.com/en-us/typography/opentype/spec/base). + */ +#define HB_OT_TAG_BASE HB_TAG('B','A','S','E') +/** + * HB_OT_TAG_GDEF: + * + * OpenType [Glyph Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gdef). + */ +#define HB_OT_TAG_GDEF HB_TAG('G','D','E','F') +/** + * HB_OT_TAG_GSUB: + * + * OpenType [Glyph Substitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gsub). + */ +#define HB_OT_TAG_GSUB HB_TAG('G','S','U','B') +/** + * HB_OT_TAG_GPOS: + * + * OpenType [Glyph Positioning Table](https://docs.microsoft.com/en-us/typography/opentype/spec/gpos). + */ +#define HB_OT_TAG_GPOS HB_TAG('G','P','O','S') +/** + * HB_OT_TAG_JSTF: + * + * OpenType [Justification Table](https://docs.microsoft.com/en-us/typography/opentype/spec/jstf). + */ +#define HB_OT_TAG_JSTF HB_TAG('J','S','T','F') + + +/* + * Script & Language tags. + */ + +/** + * HB_OT_TAG_DEFAULT_SCRIPT: + * + * OpenType script tag, `DFLT`, for features that are not script-specific. + * + */ +#define HB_OT_TAG_DEFAULT_SCRIPT HB_TAG ('D', 'F', 'L', 'T') +/** + * HB_OT_TAG_DEFAULT_LANGUAGE: + * + * OpenType language tag, `dflt`. Not a valid language tag, but some fonts + * mistakenly use it. + */ +#define HB_OT_TAG_DEFAULT_LANGUAGE HB_TAG ('d', 'f', 'l', 't') + +/** + * HB_OT_MAX_TAGS_PER_SCRIPT: + * + * Maximum number of OpenType tags that can correspond to a give #hb_script_t. + * + * Since: 2.0.0 + **/ +#define HB_OT_MAX_TAGS_PER_SCRIPT 3u +/** + * HB_OT_MAX_TAGS_PER_LANGUAGE: + * + * Maximum number of OpenType tags that can correspond to a give #hb_language_t. + * + * Since: 2.0.0 + **/ +#define HB_OT_MAX_TAGS_PER_LANGUAGE 3u + +HB_EXTERN void +hb_ot_tags_from_script_and_language (hb_script_t script, + hb_language_t language, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */); + +HB_EXTERN hb_script_t +hb_ot_tag_to_script (hb_tag_t tag); + +HB_EXTERN hb_language_t +hb_ot_tag_to_language (hb_tag_t tag); + +HB_EXTERN void +hb_ot_tags_to_script_and_language (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_script_t *script /* OUT */, + hb_language_t *language /* OUT */); + + +/* + * GDEF + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_has_glyph_classes (hb_face_t *face); + +/** + * hb_ot_layout_glyph_class_t: + * @HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED: Glyphs not matching the other classifications + * @HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH: Spacing, single characters, capable of accepting marks + * @HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE: Glyphs that represent ligation of multiple characters + * @HB_OT_LAYOUT_GLYPH_CLASS_MARK: Non-spacing, combining glyphs that represent marks + * @HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT: Spacing glyphs that represent part of a single character + * + * The GDEF classes defined for glyphs. + * + **/ +typedef enum { + HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED = 0, + HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH = 1, + HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE = 2, + HB_OT_LAYOUT_GLYPH_CLASS_MARK = 3, + HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT = 4 +} hb_ot_layout_glyph_class_t; + +HB_EXTERN hb_ot_layout_glyph_class_t +hb_ot_layout_get_glyph_class (hb_face_t *face, + hb_codepoint_t glyph); + +HB_EXTERN void +hb_ot_layout_get_glyphs_in_class (hb_face_t *face, + hb_ot_layout_glyph_class_t klass, + hb_set_t *glyphs /* OUT */); + +/* Not that useful. Provides list of attach points for a glyph that a + * client may want to cache */ +HB_EXTERN unsigned int +hb_ot_layout_get_attach_points (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *point_count /* IN/OUT */, + unsigned int *point_array /* OUT */); + +/* Ligature caret positions */ +HB_EXTERN unsigned int +hb_ot_layout_get_ligature_carets (hb_font_t *font, + hb_direction_t direction, + hb_codepoint_t glyph, + unsigned int start_offset, + unsigned int *caret_count /* IN/OUT */, + hb_position_t *caret_array /* OUT */); + + +/* + * GSUB/GPOS feature query and enumeration interface + */ + +/** + * HB_OT_LAYOUT_NO_SCRIPT_INDEX: + * + * Special value for script index indicating unsupported script. + */ +#define HB_OT_LAYOUT_NO_SCRIPT_INDEX 0xFFFFu +/** + * HB_OT_LAYOUT_NO_FEATURE_INDEX: + * + * Special value for feature index indicating unsupported feature. + */ +#define HB_OT_LAYOUT_NO_FEATURE_INDEX 0xFFFFu +/** + * HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX: + * + * Special value for language index indicating default or unsupported language. + */ +#define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX 0xFFFFu +/** + * HB_OT_LAYOUT_NO_VARIATIONS_INDEX: + * + * Special value for variations index indicating unsupported variation. + */ +#define HB_OT_LAYOUT_NO_VARIATIONS_INDEX 0xFFFFFFFFu + +HB_EXTERN unsigned int +hb_ot_layout_table_get_script_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_table_find_script (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t script_tag, + unsigned int *script_index /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_table_select_script (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_count, + const hb_tag_t *script_tags, + unsigned int *script_index /* OUT */, + hb_tag_t *chosen_script /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_table_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_script_get_language_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int start_offset, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_script_select_language (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_count, + const hb_tag_t *language_tags, + unsigned int *language_index /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_script_select_language2 (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_count, + const hb_tag_t *language_tags, + unsigned int *language_index /* OUT */, + hb_tag_t *chosen_language /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_language_get_required_feature_index (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_language_get_required_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int *feature_index /* OUT */, + hb_tag_t *feature_tag /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_language_get_feature_indexes (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + unsigned int *feature_indexes /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_language_get_feature_tags (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + unsigned int start_offset, + unsigned int *feature_count /* IN/OUT */, + hb_tag_t *feature_tags /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_language_find_feature (hb_face_t *face, + hb_tag_t table_tag, + unsigned int script_index, + unsigned int language_index, + hb_tag_t feature_tag, + unsigned int *feature_index /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_feature_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */); + +HB_EXTERN unsigned int +hb_ot_layout_table_get_lookup_count (hb_face_t *face, + hb_tag_t table_tag); + +HB_EXTERN void +hb_ot_layout_collect_features (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *scripts, + const hb_tag_t *languages, + const hb_tag_t *features, + hb_set_t *feature_indexes /* OUT */); + +HB_EXTERN void +hb_ot_layout_collect_features_map (hb_face_t *face, + hb_tag_t table_tag, + unsigned script_index, + unsigned language_index, + hb_map_t *feature_map /* OUT */); + +HB_EXTERN void +hb_ot_layout_collect_lookups (hb_face_t *face, + hb_tag_t table_tag, + const hb_tag_t *scripts, + const hb_tag_t *languages, + const hb_tag_t *features, + hb_set_t *lookup_indexes /* OUT */); + +HB_EXTERN void +hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, + hb_tag_t table_tag, + unsigned int lookup_index, + hb_set_t *glyphs_before, /* OUT. May be NULL */ + hb_set_t *glyphs_input, /* OUT. May be NULL */ + hb_set_t *glyphs_after, /* OUT. May be NULL */ + hb_set_t *glyphs_output /* OUT. May be NULL */); + + +/* Variations support */ + +HB_EXTERN hb_bool_t +hb_ot_layout_table_find_feature_variations (hb_face_t *face, + hb_tag_t table_tag, + const int *coords, + unsigned int num_coords, + unsigned int *variations_index /* out */); + +HB_EXTERN unsigned int +hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int variations_index, + unsigned int start_offset, + unsigned int *lookup_count /* IN/OUT */, + unsigned int *lookup_indexes /* OUT */); + + +/* + * GSUB + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_has_substitution (hb_face_t *face); + +HB_EXTERN unsigned +hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face, + unsigned lookup_index, + hb_codepoint_t glyph, + unsigned start_offset, + unsigned *alternate_count /* IN/OUT */, + hb_codepoint_t *alternate_glyphs /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_layout_lookup_would_substitute (hb_face_t *face, + unsigned int lookup_index, + const hb_codepoint_t *glyphs, + unsigned int glyphs_length, + hb_bool_t zero_context); + +HB_EXTERN void +hb_ot_layout_lookup_substitute_closure (hb_face_t *face, + unsigned int lookup_index, + hb_set_t *glyphs + /*TODO , hb_bool_t inclusive */); + +HB_EXTERN void +hb_ot_layout_lookups_substitute_closure (hb_face_t *face, + const hb_set_t *lookups, + hb_set_t *glyphs); + + +/* + * GPOS + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_has_positioning (hb_face_t *face); + +/* Optical 'size' feature info. Returns true if found. + * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */ +HB_EXTERN hb_bool_t +hb_ot_layout_get_size_params (hb_face_t *face, + unsigned int *design_size, /* OUT. May be NULL */ + unsigned int *subfamily_id, /* OUT. May be NULL */ + hb_ot_name_id_t *subfamily_name_id, /* OUT. May be NULL */ + unsigned int *range_start, /* OUT. May be NULL */ + unsigned int *range_end /* OUT. May be NULL */); + +HB_EXTERN hb_position_t +hb_ot_layout_lookup_get_optical_bound (hb_font_t *font, + unsigned lookup_index, + hb_direction_t direction, + hb_codepoint_t glyph); + + +/* + * GSUB/GPOS + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_feature_get_name_ids (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + hb_ot_name_id_t *label_id /* OUT. May be NULL */, + hb_ot_name_id_t *tooltip_id /* OUT. May be NULL */, + hb_ot_name_id_t *sample_id /* OUT. May be NULL */, + unsigned int *num_named_parameters /* OUT. May be NULL */, + hb_ot_name_id_t *first_param_id /* OUT. May be NULL */); + + +HB_EXTERN unsigned int +hb_ot_layout_feature_get_characters (hb_face_t *face, + hb_tag_t table_tag, + unsigned int feature_index, + unsigned int start_offset, + unsigned int *char_count /* IN/OUT. May be NULL */, + hb_codepoint_t *characters /* OUT. May be NULL */); + + +/* + * BASE + */ + +HB_EXTERN hb_bool_t +hb_ot_layout_get_font_extents (hb_font_t *font, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_font_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_ot_layout_get_font_extents2 (hb_font_t *font, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_font_extents_t *extents); + +/** + * hb_ot_layout_baseline_tag_t: + * @HB_OT_LAYOUT_BASELINE_TAG_ROMAN: The baseline used by alphabetic scripts such as Latin, Cyrillic and Greek. + * In vertical writing mode, the alphabetic baseline for characters rotated 90 degrees clockwise. + * (This would not apply to alphabetic characters that remain upright in vertical writing mode, since these + * characters are not rotated.) + * @HB_OT_LAYOUT_BASELINE_TAG_HANGING: The hanging baseline. In horizontal direction, this is the horizontal + * line from which syllables seem, to hang in Tibetan and other similar scripts. In vertical writing mode, + * for Tibetan (or some other similar script) characters rotated 90 degrees clockwise. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT: Ideographic character face bottom or left edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT: Ideographic character face top or right edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL: The center of the ideographic character face. Since: 4.0.0 + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT: Ideographic em-box bottom or left edge, + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT: Ideographic em-box top or right edge baseline, + * @HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL: The center of the ideographic em-box. Since: 4.0.0 + * if the direction is horizontal or vertical, respectively. + * @HB_OT_LAYOUT_BASELINE_TAG_MATH: The baseline about which mathematical characters are centered. + * In vertical writing mode when mathematical characters rotated 90 degrees clockwise, are centered. + * + * Baseline tags from [Baseline Tags](https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags) registry. + * + * Since: 2.6.0 + */ +typedef enum { + HB_OT_LAYOUT_BASELINE_TAG_ROMAN = HB_TAG ('r','o','m','n'), + HB_OT_LAYOUT_BASELINE_TAG_HANGING = HB_TAG ('h','a','n','g'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT = HB_TAG ('i','c','f','b'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT = HB_TAG ('i','c','f','t'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL = HB_TAG ('I','c','f','c'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT = HB_TAG ('i','d','e','o'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT = HB_TAG ('i','d','t','p'), + HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL = HB_TAG ('I','d','c','e'), + HB_OT_LAYOUT_BASELINE_TAG_MATH = HB_TAG ('m','a','t','h'), + + /*< private >*/ + _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_layout_baseline_tag_t; + +HB_EXTERN hb_ot_layout_baseline_tag_t +hb_ot_layout_get_horizontal_baseline_tag_for_script (hb_script_t script); + +HB_EXTERN hb_bool_t +hb_ot_layout_get_baseline (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT. May be NULL. */); + +HB_EXTERN hb_bool_t +hb_ot_layout_get_baseline2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT. May be NULL. */); + +HB_EXTERN void +hb_ot_layout_get_baseline_with_fallback (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_tag_t script_tag, + hb_tag_t language_tag, + hb_position_t *coord /* OUT */); + +HB_EXTERN void +hb_ot_layout_get_baseline_with_fallback2 (hb_font_t *font, + hb_ot_layout_baseline_tag_t baseline_tag, + hb_direction_t direction, + hb_script_t script, + hb_language_t language, + hb_position_t *coord /* OUT */); + +HB_END_DECLS + +#endif /* HB_OT_LAYOUT_H */ diff --git a/gfx/harfbuzz/src/hb-ot-layout.hh b/gfx/harfbuzz/src/hb-ot-layout.hh new file mode 100644 index 0000000000..d71889331d --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-layout.hh @@ -0,0 +1,611 @@ +/* + * 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_LAYOUT_HH +#define HB_OT_LAYOUT_HH + +#include "hb.hh" + +#include "hb-font.hh" +#include "hb-buffer.hh" +#include "hb-open-type.hh" +#include "hb-ot-shape.hh" +#include "hb-set-digest.hh" + + +struct hb_ot_shape_plan_t; + + +/* + * kern + */ + +HB_INTERNAL bool +hb_ot_layout_has_kerning (hb_face_t *face); + +HB_INTERNAL bool +hb_ot_layout_has_machine_kerning (hb_face_t *face); + +HB_INTERNAL bool +hb_ot_layout_has_cross_kerning (hb_face_t *face); + +HB_INTERNAL void +hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +/* Private API corresponding to hb-ot-layout.h: */ + +HB_INTERNAL bool +hb_ot_layout_table_find_feature (hb_face_t *face, + hb_tag_t table_tag, + hb_tag_t feature_tag, + unsigned int *feature_index); + + +/* + * GDEF + */ + +enum hb_ot_layout_glyph_props_flags_t +{ + /* The following three match LookupFlags::Ignore* numbers. */ + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH = 0x02u, + HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE = 0x04u, + HB_OT_LAYOUT_GLYPH_PROPS_MARK = 0x08u, + + /* The following are used internally; not derived from GDEF. */ + HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED = 0x10u, + HB_OT_LAYOUT_GLYPH_PROPS_LIGATED = 0x20u, + HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED = 0x40u, + + HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE = HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED | + HB_OT_LAYOUT_GLYPH_PROPS_LIGATED | + HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED +}; +HB_MARK_AS_FLAG_T (hb_ot_layout_glyph_props_flags_t); + + +/* + * GSUB/GPOS + */ + + +/* Should be called before all the substitute_lookup's are done. */ +HB_INTERNAL void +hb_ot_layout_substitute_start (hb_font_t *font, + hb_buffer_t *buffer); + +namespace OT { + struct hb_ot_apply_context_t; + struct hb_ot_layout_lookup_accelerator_t; +namespace Layout { +namespace GSUB_impl { + struct SubstLookup; +} +} +} + +HB_INTERNAL void +hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c, + const OT::Layout::GSUB_impl::SubstLookup &lookup, + const OT::hb_ot_layout_lookup_accelerator_t &accel); + + +/* Should be called before all the position_lookup's are done. */ +HB_INTERNAL void +hb_ot_layout_position_start (hb_font_t *font, + hb_buffer_t *buffer); + +/* Should be called after all the position_lookup's are done, to fini advances. */ +HB_INTERNAL void +hb_ot_layout_position_finish_advances (hb_font_t *font, + hb_buffer_t *buffer); + +/* Should be called after hb_ot_layout_position_finish_advances, to fini offsets. */ +HB_INTERNAL void +hb_ot_layout_position_finish_offsets (hb_font_t *font, + hb_buffer_t *buffer); + + +/* + * Buffer var routines. + */ + +/* buffer var allocations, used during the entire shaping process */ +#define unicode_props() var2.u16[0] + +/* buffer var allocations, used during the GSUB/GPOS processing */ +#define glyph_props() var1.u16[0] /* GDEF glyph properties */ +#define lig_props() var1.u8[2] /* GSUB/GPOS ligature tracking */ +#define syllable() var1.u8[3] /* GSUB/GPOS shaping boundaries */ + + +/* Loop over syllables. Based on foreach_cluster(). */ +#define foreach_syllable(buffer, start, end) \ + for (unsigned int \ + _count = buffer->len, \ + start = 0, end = _count ? _hb_next_syllable (buffer, 0) : 0; \ + start < _count; \ + start = end, end = _hb_next_syllable (buffer, start)) + +static inline unsigned int +_hb_next_syllable (hb_buffer_t *buffer, unsigned int start) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + + unsigned int syllable = info[start].syllable(); + while (++start < count && syllable == info[start].syllable()) + ; + + return start; +} + + +/* unicode_props */ + +/* Design: + * unicode_props() is a two-byte number. The low byte includes: + * - General_Category: 5 bits. + * - A bit each for: + * * Is it Default_Ignorable(); we have a modified Default_Ignorable(). + * * Whether it's one of the four Mongolian Free Variation Selectors, + * CGJ, or other characters that are hidden but should not be ignored + * like most other Default_Ignorable()s do during matching. + * * Whether it's a grapheme continuation. + * + * The high-byte has different meanings, switched by the Gen-Cat: + * - For Mn,Mc,Me: the modified Combining_Class. + * - For Cf: whether it's ZWJ, ZWNJ, or something else. + * - For Ws: index of which space character this is, if space fallback + * is needed, ie. we don't set this by default, only if asked to. + */ + +enum hb_unicode_props_flags_t { + UPROPS_MASK_GEN_CAT = 0x001Fu, + UPROPS_MASK_IGNORABLE = 0x0020u, + UPROPS_MASK_HIDDEN = 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..4, or TAG characters */ + UPROPS_MASK_CONTINUATION=0x0080u, + + /* If GEN_CAT=FORMAT, top byte masks: */ + UPROPS_MASK_Cf_ZWJ = 0x0100u, + UPROPS_MASK_Cf_ZWNJ = 0x0200u +}; +HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t); + +static inline void +_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_buffer_t *buffer) +{ + hb_unicode_funcs_t *unicode = buffer->unicode; + unsigned int u = info->codepoint; + unsigned int gen_cat = (unsigned int) unicode->general_category (u); + unsigned int props = gen_cat; + + if (u >= 0x80u) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII; + + if (unlikely (unicode->is_default_ignorable (u))) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES; + props |= UPROPS_MASK_IGNORABLE; + if (u == 0x200Cu) props |= UPROPS_MASK_Cf_ZWNJ; + else if (u == 0x200Du) props |= UPROPS_MASK_Cf_ZWJ; + /* Mongolian Free Variation Selectors need to be remembered + * because although we need to hide them like default-ignorables, + * they need to non-ignorable during shaping. This is similar to + * what we do for joiners in Indic-like shapers, but since the + * FVSes are GC=Mn, we have use a separate bit to remember them. + * Fixes: + * https://github.com/harfbuzz/harfbuzz/issues/234 */ + else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x180Bu, 0x180Du, 0x180Fu, 0x180Fu))) props |= UPROPS_MASK_HIDDEN; + /* TAG characters need similar treatment. Fixes: + * https://github.com/harfbuzz/harfbuzz/issues/463 */ + else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN; + /* COMBINING GRAPHEME JOINER should not be skipped; at least some times. + * https://github.com/harfbuzz/harfbuzz/issues/554 */ + else if (unlikely (u == 0x034Fu)) + { + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_CGJ; + props |= UPROPS_MASK_HIDDEN; + } + } + + if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (gen_cat))) + { + props |= UPROPS_MASK_CONTINUATION; + props |= unicode->modified_combining_class (u)<<8; + } + } + + info->unicode_props() = props; +} + +static inline void +_hb_glyph_info_set_general_category (hb_glyph_info_t *info, + hb_unicode_general_category_t gen_cat) +{ + /* Clears top-byte. */ + info->unicode_props() = (unsigned int) gen_cat | (info->unicode_props() & (0xFF & ~UPROPS_MASK_GEN_CAT)); +} + +static inline hb_unicode_general_category_t +_hb_glyph_info_get_general_category (const hb_glyph_info_t *info) +{ + return (hb_unicode_general_category_t) (info->unicode_props() & UPROPS_MASK_GEN_CAT); +} + +static inline bool +_hb_glyph_info_is_unicode_mark (const hb_glyph_info_t *info) +{ + return HB_UNICODE_GENERAL_CATEGORY_IS_MARK (info->unicode_props() & UPROPS_MASK_GEN_CAT); +} +static inline void +_hb_glyph_info_set_modified_combining_class (hb_glyph_info_t *info, + unsigned int modified_class) +{ + if (unlikely (!_hb_glyph_info_is_unicode_mark (info))) + return; + info->unicode_props() = (modified_class<<8) | (info->unicode_props() & 0xFF); +} +static inline unsigned int +_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0; +} +#define info_cc(info) (_hb_glyph_info_get_modified_combining_class (&(info))) + +static inline bool +_hb_glyph_info_is_unicode_space (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_get_general_category (info) == + HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR; +} +static inline void +_hb_glyph_info_set_unicode_space_fallback_type (hb_glyph_info_t *info, hb_unicode_funcs_t::space_t s) +{ + if (unlikely (!_hb_glyph_info_is_unicode_space (info))) + return; + info->unicode_props() = (((unsigned int) s)<<8) | (info->unicode_props() & 0xFF); +} +static inline hb_unicode_funcs_t::space_t +_hb_glyph_info_get_unicode_space_fallback_type (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_space (info) ? + (hb_unicode_funcs_t::space_t) (info->unicode_props()>>8) : + hb_unicode_funcs_t::NOT_SPACE; +} + +static inline bool _hb_glyph_info_substituted (const hb_glyph_info_t *info); + +static inline bool +_hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info) +{ + return (info->unicode_props() & UPROPS_MASK_IGNORABLE) && + !_hb_glyph_info_substituted (info); +} +static inline bool +_hb_glyph_info_is_default_ignorable_and_not_hidden (const hb_glyph_info_t *info) +{ + return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_HIDDEN)) + == UPROPS_MASK_IGNORABLE) && + !_hb_glyph_info_substituted (info); +} +static inline void +_hb_glyph_info_unhide (hb_glyph_info_t *info) +{ + info->unicode_props() &= ~ UPROPS_MASK_HIDDEN; +} + +static inline void +_hb_glyph_info_set_continuation (hb_glyph_info_t *info) +{ + info->unicode_props() |= UPROPS_MASK_CONTINUATION; +} +static inline void +_hb_glyph_info_reset_continuation (hb_glyph_info_t *info) +{ + info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION; +} +static inline bool +_hb_glyph_info_is_continuation (const hb_glyph_info_t *info) +{ + return info->unicode_props() & UPROPS_MASK_CONTINUATION; +} + +static inline bool +_hb_grapheme_group_func (const hb_glyph_info_t& a HB_UNUSED, + const hb_glyph_info_t& b) +{ return _hb_glyph_info_is_continuation (&b); } + +#define foreach_grapheme(buffer, start, end) \ + foreach_group (buffer, start, end, _hb_grapheme_group_func) + +static inline void +_hb_ot_layout_reverse_graphemes (hb_buffer_t *buffer) +{ + buffer->reverse_groups (_hb_grapheme_group_func, + buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); +} + +static inline bool +_hb_glyph_info_is_unicode_format (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_get_general_category (info) == + HB_UNICODE_GENERAL_CATEGORY_FORMAT; +} +static inline bool +_hb_glyph_info_is_zwnj (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWNJ); +} +static inline bool +_hb_glyph_info_is_zwj (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & UPROPS_MASK_Cf_ZWJ); +} +static inline bool +_hb_glyph_info_is_joiner (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_is_unicode_format (info) && (info->unicode_props() & (UPROPS_MASK_Cf_ZWNJ|UPROPS_MASK_Cf_ZWJ)); +} +static inline void +_hb_glyph_info_flip_joiners (hb_glyph_info_t *info) +{ + if (!_hb_glyph_info_is_unicode_format (info)) + return; + info->unicode_props() ^= UPROPS_MASK_Cf_ZWNJ | UPROPS_MASK_Cf_ZWJ; +} + +/* lig_props: aka lig_id / lig_comp + * + * When a ligature is formed: + * + * - The ligature glyph and any marks in between all the same newly allocated + * lig_id, + * - The ligature glyph will get lig_num_comps set to the number of components + * - The marks get lig_comp > 0, reflecting which component of the ligature + * they were applied to. + * - This is used in GPOS to attach marks to the right component of a ligature + * in MarkLigPos, + * - Note that when marks are ligated together, much of the above is skipped + * and the current lig_id reused. + * + * When a multiple-substitution is done: + * + * - All resulting glyphs will have lig_id = 0, + * - The resulting glyphs will have lig_comp = 0, 1, 2, ... respectively. + * - This is used in GPOS to attach marks to the first component of a + * multiple substitution in MarkBasePos. + * + * The numbers are also used in GPOS to do mark-to-mark positioning only + * to marks that belong to the same component of the same ligature. + */ + +static inline void +_hb_glyph_info_clear_lig_props (hb_glyph_info_t *info) +{ + info->lig_props() = 0; +} + +#define IS_LIG_BASE 0x10 + +static inline void +_hb_glyph_info_set_lig_props_for_ligature (hb_glyph_info_t *info, + unsigned int lig_id, + unsigned int lig_num_comps) +{ + info->lig_props() = (lig_id << 5) | IS_LIG_BASE | (lig_num_comps & 0x0F); +} + +static inline void +_hb_glyph_info_set_lig_props_for_mark (hb_glyph_info_t *info, + unsigned int lig_id, + unsigned int lig_comp) +{ + info->lig_props() = (lig_id << 5) | (lig_comp & 0x0F); +} + +static inline void +_hb_glyph_info_set_lig_props_for_component (hb_glyph_info_t *info, unsigned int comp) +{ + _hb_glyph_info_set_lig_props_for_mark (info, 0, comp); +} + +static inline unsigned int +_hb_glyph_info_get_lig_id (const hb_glyph_info_t *info) +{ + return info->lig_props() >> 5; +} + +static inline bool +_hb_glyph_info_ligated_internal (const hb_glyph_info_t *info) +{ + return info->lig_props() & IS_LIG_BASE; +} + +static inline unsigned int +_hb_glyph_info_get_lig_comp (const hb_glyph_info_t *info) +{ + if (_hb_glyph_info_ligated_internal (info)) + return 0; + else + return info->lig_props() & 0x0F; +} + +static inline unsigned int +_hb_glyph_info_get_lig_num_comps (const hb_glyph_info_t *info) +{ + if ((info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE) && + _hb_glyph_info_ligated_internal (info)) + return info->lig_props() & 0x0F; + else + return 1; +} + +static inline uint8_t +_hb_allocate_lig_id (hb_buffer_t *buffer) +{ + uint8_t lig_id = buffer->next_serial () & 0x07; + if (unlikely (!lig_id)) + lig_id = _hb_allocate_lig_id (buffer); /* in case of overflow */ + return lig_id; +} + +/* glyph_props: */ + +static inline void +_hb_glyph_info_set_glyph_props (hb_glyph_info_t *info, unsigned int props) +{ + info->glyph_props() = props; +} + +static inline unsigned int +_hb_glyph_info_get_glyph_props (const hb_glyph_info_t *info) +{ + return info->glyph_props(); +} + +static inline bool +_hb_glyph_info_is_base_glyph (const hb_glyph_info_t *info) +{ + return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH; +} + +static inline bool +_hb_glyph_info_is_ligature (const hb_glyph_info_t *info) +{ + return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE; +} + +static inline bool +_hb_glyph_info_is_mark (const hb_glyph_info_t *info) +{ + return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK; +} + +static inline bool +_hb_glyph_info_substituted (const hb_glyph_info_t *info) +{ + return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED; +} + +static inline bool +_hb_glyph_info_ligated (const hb_glyph_info_t *info) +{ + return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATED; +} + +static inline bool +_hb_glyph_info_multiplied (const hb_glyph_info_t *info) +{ + return info->glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED; +} + +static inline bool +_hb_glyph_info_ligated_and_didnt_multiply (const hb_glyph_info_t *info) +{ + return _hb_glyph_info_ligated (info) && !_hb_glyph_info_multiplied (info); +} + +static inline void +_hb_glyph_info_clear_ligated_and_multiplied (hb_glyph_info_t *info) +{ + info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_LIGATED | + HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED); +} + +static inline void +_hb_glyph_info_clear_substituted (hb_glyph_info_t *info) +{ + info->glyph_props() &= ~(HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED); +} + +static inline bool +_hb_clear_substitution_flags (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + _hb_glyph_info_clear_substituted (&info[i]); + return false; +} + + +/* Allocation / deallocation. */ + +static inline void +_hb_buffer_allocate_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, unicode_props); +} + +static inline void +_hb_buffer_deallocate_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, unicode_props); +} + +static inline void +_hb_buffer_assert_unicode_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ASSERT_VAR (buffer, unicode_props); +} + +static inline void +_hb_buffer_allocate_gsubgpos_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props); + HB_BUFFER_ALLOCATE_VAR (buffer, lig_props); +} + +static inline void +_hb_buffer_deallocate_gsubgpos_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props); + HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_props); +} + +static inline void +_hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer) +{ + HB_BUFFER_ASSERT_VAR (buffer, glyph_props); + HB_BUFFER_ASSERT_VAR (buffer, lig_props); +} + +/* Make sure no one directly touches our props... */ +#undef unicode_props0 +#undef unicode_props1 +#undef lig_props +#undef glyph_props + +#endif /* HB_OT_LAYOUT_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-map.cc b/gfx/harfbuzz/src/hb-ot-map.cc new file mode 100644 index 0000000000..fac73eb34e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-map.cc @@ -0,0 +1,394 @@ +/* + * 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_OT_SHAPE + +#include "hb-ot-map.hh" +#include "hb-ot-shape.hh" +#include "hb-ot-layout.hh" + + +void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const +{ + for (unsigned int i = 0; i < lookups[table_index].length; i++) + lookups_out->add (lookups[table_index][i].index); +} + + +hb_ot_map_builder_t::hb_ot_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t &props_) +{ + hb_memset (this, 0, sizeof (*this)); + + feature_infos.init (); + for (unsigned int table_index = 0; table_index < 2; table_index++) + stages[table_index].init (); + + face = face_; + props = props_; + + /* Fetch script/language indices for GSUB/GPOS. We need these later to skip + * features not available in either table and not waste precious bits for them. */ + + unsigned int script_count = HB_OT_MAX_TAGS_PER_SCRIPT; + unsigned int language_count = HB_OT_MAX_TAGS_PER_LANGUAGE; + hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT]; + hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; + + hb_ot_tags_from_script_and_language (props.script, + props.language, + &script_count, + script_tags, + &language_count, + language_tags); + + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + hb_tag_t table_tag = table_tags[table_index]; + found_script[table_index] = (bool) hb_ot_layout_table_select_script (face, + table_tag, + script_count, + script_tags, + &script_index[table_index], + &chosen_script[table_index]); + hb_ot_layout_script_select_language (face, + table_tag, + script_index[table_index], + language_count, + language_tags, + &language_index[table_index]); + } +} + +hb_ot_map_builder_t::~hb_ot_map_builder_t () +{ + feature_infos.fini (); + for (unsigned int table_index = 0; table_index < 2; table_index++) + stages[table_index].fini (); +} + +void hb_ot_map_builder_t::add_feature (hb_tag_t tag, + hb_ot_map_feature_flags_t flags, + unsigned int value) +{ + if (unlikely (!tag)) return; + feature_info_t *info = feature_infos.push(); + info->tag = tag; + info->seq = feature_infos.length; + info->max_value = value; + info->flags = flags; + info->default_value = (flags & F_GLOBAL) ? value : 0; + info->stage[0] = current_stage[0]; + info->stage[1] = current_stage[1]; +} + +bool hb_ot_map_builder_t::has_feature (hb_tag_t tag) +{ + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + if (hb_ot_layout_language_find_feature (face, + table_tags[table_index], + script_index[table_index], + language_index[table_index], + tag, + nullptr)) + return true; + } + return false; +} + +void +hb_ot_map_builder_t::add_lookups (hb_ot_map_t &m, + unsigned int table_index, + unsigned int feature_index, + unsigned int variations_index, + hb_mask_t mask, + bool auto_zwnj, + bool auto_zwj, + bool random, + bool per_syllable, + hb_tag_t feature_tag) +{ + unsigned int lookup_indices[32]; + unsigned int offset, len; + unsigned int table_lookup_count; + + table_lookup_count = hb_ot_layout_table_get_lookup_count (face, table_tags[table_index]); + + offset = 0; + do { + len = ARRAY_LENGTH (lookup_indices); + hb_ot_layout_feature_with_variations_get_lookups (face, + table_tags[table_index], + feature_index, + variations_index, + offset, &len, + lookup_indices); + + for (unsigned int i = 0; i < len; i++) + { + if (lookup_indices[i] >= table_lookup_count) + continue; + hb_ot_map_t::lookup_map_t *lookup = m.lookups[table_index].push (); + lookup->mask = mask; + lookup->index = lookup_indices[i]; + lookup->auto_zwnj = auto_zwnj; + lookup->auto_zwj = auto_zwj; + lookup->random = random; + lookup->per_syllable = per_syllable; + lookup->feature_tag = feature_tag; + } + + offset += len; + } while (len == ARRAY_LENGTH (lookup_indices)); +} + + +void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func) +{ + stage_info_t *s = stages[table_index].push (); + s->index = current_stage[table_index]; + s->pause_func = pause_func; + + current_stage[table_index]++; +} + +void +hb_ot_map_builder_t::compile (hb_ot_map_t &m, + const hb_ot_shape_plan_key_t &key) +{ + unsigned int global_bit_shift = 8 * sizeof (hb_mask_t) - 1; + unsigned int global_bit_mask = 1u << global_bit_shift; + + m.global_mask = global_bit_mask; + + unsigned int required_feature_index[2]; + hb_tag_t required_feature_tag[2]; + /* We default to applying required feature in stage 0. If the required + * feature has a tag that is known to the shaper, we apply required feature + * in the stage for that tag. + */ + unsigned int required_feature_stage[2] = {0, 0}; + + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + m.chosen_script[table_index] = chosen_script[table_index]; + m.found_script[table_index] = found_script[table_index]; + + hb_ot_layout_language_get_required_feature (face, + table_tags[table_index], + script_index[table_index], + language_index[table_index], + &required_feature_index[table_index], + &required_feature_tag[table_index]); + } + + /* Sort features and merge duplicates */ + if (feature_infos.length) + { + if (!is_simple) + feature_infos.qsort (); + auto *f = feature_infos.arrayZ; + unsigned int j = 0; + unsigned count = feature_infos.length; + for (unsigned int i = 1; i < count; i++) + if (f[i].tag != f[j].tag) + f[++j] = f[i]; + else { + if (f[i].flags & F_GLOBAL) { + f[j].flags |= F_GLOBAL; + f[j].max_value = f[i].max_value; + f[j].default_value = f[i].default_value; + } else { + if (f[j].flags & F_GLOBAL) + f[j].flags ^= F_GLOBAL; + f[j].max_value = hb_max (f[j].max_value, f[i].max_value); + /* Inherit default_value from j */ + } + f[j].flags |= (f[i].flags & F_HAS_FALLBACK); + f[j].stage[0] = hb_min (f[j].stage[0], f[i].stage[0]); + f[j].stage[1] = hb_min (f[j].stage[1], f[i].stage[1]); + } + feature_infos.shrink (j + 1); + } + + hb_map_t feature_indices[2]; + for (unsigned int table_index = 0; table_index < 2; table_index++) + hb_ot_layout_collect_features_map (face, + table_tags[table_index], + script_index[table_index], + language_index[table_index], + &feature_indices[table_index]); + + /* Allocate bits now */ + static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), ""); + unsigned int next_bit = hb_popcount (HB_GLYPH_FLAG_DEFINED) + 1; + + unsigned count = feature_infos.length; + for (unsigned int i = 0; i < count; i++) + { + const feature_info_t *info = &feature_infos[i]; + + unsigned int bits_needed; + + if ((info->flags & F_GLOBAL) && info->max_value == 1) + /* Uses the global bit */ + bits_needed = 0; + else + /* Limit bits per feature. */ + bits_needed = hb_min (HB_OT_MAP_MAX_BITS, hb_bit_storage (info->max_value)); + + if (!info->max_value || next_bit + bits_needed >= global_bit_shift) + continue; /* Feature disabled, or not enough bits. */ + + bool found = false; + unsigned int feature_index[2]; + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + if (required_feature_tag[table_index] == info->tag) + required_feature_stage[table_index] = info->stage[table_index]; + + hb_codepoint_t *index; + if (feature_indices[table_index].has (info->tag, &index)) + { + feature_index[table_index] = *index; + found = true; + } + else + feature_index[table_index] = HB_OT_LAYOUT_NO_FEATURE_INDEX; + } + if (!found && (info->flags & F_GLOBAL_SEARCH)) + { + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + found |= (bool) hb_ot_layout_table_find_feature (face, + table_tags[table_index], + info->tag, + &feature_index[table_index]); + } + } + if (!found && !(info->flags & F_HAS_FALLBACK)) + continue; + + + hb_ot_map_t::feature_map_t *map = m.features.push (); + + map->tag = info->tag; + map->index[0] = feature_index[0]; + map->index[1] = feature_index[1]; + map->stage[0] = info->stage[0]; + map->stage[1] = info->stage[1]; + map->auto_zwnj = !(info->flags & F_MANUAL_ZWNJ); + map->auto_zwj = !(info->flags & F_MANUAL_ZWJ); + map->random = !!(info->flags & F_RANDOM); + map->per_syllable = !!(info->flags & F_PER_SYLLABLE); + if ((info->flags & F_GLOBAL) && info->max_value == 1) { + /* Uses the global bit */ + map->shift = global_bit_shift; + map->mask = global_bit_mask; + } else { + map->shift = next_bit; + map->mask = (1u << (next_bit + bits_needed)) - (1u << next_bit); + next_bit += bits_needed; + m.global_mask |= (info->default_value << map->shift) & map->mask; + } + map->_1_mask = (1u << map->shift) & map->mask; + map->needs_fallback = !found; + } + //feature_infos.shrink (0); /* Done with these */ + if (is_simple) + m.features.qsort (); + + add_gsub_pause (nullptr); + add_gpos_pause (nullptr); + + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + /* Collect lookup indices for features */ + auto &lookups = m.lookups[table_index]; + + unsigned int stage_index = 0; + unsigned int last_num_lookups = 0; + for (unsigned stage = 0; stage < current_stage[table_index]; stage++) + { + if (required_feature_index[table_index] != HB_OT_LAYOUT_NO_FEATURE_INDEX && + required_feature_stage[table_index] == stage) + add_lookups (m, table_index, + required_feature_index[table_index], + key.variations_index[table_index], + global_bit_mask); + + for (auto &feature : m.features) + { + if (feature.stage[table_index] == stage) + add_lookups (m, table_index, + feature.index[table_index], + key.variations_index[table_index], + feature.mask, + feature.auto_zwnj, + feature.auto_zwj, + feature.random, + feature.per_syllable, + feature.tag); + } + + /* Sort lookups and merge duplicates */ + if (last_num_lookups + 1 < lookups.length) + { + lookups.as_array ().sub_array (last_num_lookups, lookups.length - last_num_lookups).qsort (); + + unsigned int j = last_num_lookups; + for (unsigned int i = j + 1; i < lookups.length; i++) + if (lookups.arrayZ[i].index != lookups.arrayZ[j].index) + lookups.arrayZ[++j] = lookups.arrayZ[i]; + else + { + lookups.arrayZ[j].mask |= lookups.arrayZ[i].mask; + lookups.arrayZ[j].auto_zwnj &= lookups.arrayZ[i].auto_zwnj; + lookups.arrayZ[j].auto_zwj &= lookups.arrayZ[i].auto_zwj; + } + lookups.shrink (j + 1); + } + + last_num_lookups = lookups.length; + + if (stage_index < stages[table_index].length && stages[table_index][stage_index].index == stage) { + hb_ot_map_t::stage_map_t *stage_map = m.stages[table_index].push (); + stage_map->last_lookup = last_num_lookups; + stage_map->pause_func = stages[table_index][stage_index].pause_func; + + stage_index++; + } + } + } +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-map.hh b/gfx/harfbuzz/src/hb-ot-map.hh new file mode 100644 index 0000000000..8af8129ceb --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-map.hh @@ -0,0 +1,298 @@ +/* + * Copyright © 2009,2010 Red Hat, Inc. + * Copyright © 2010,2011,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_MAP_HH +#define HB_OT_MAP_HH + +#include "hb-buffer.hh" + + +#define HB_OT_MAP_MAX_BITS 8u +#define HB_OT_MAP_MAX_VALUE ((1u << HB_OT_MAP_MAX_BITS) - 1u) + +struct hb_ot_shape_plan_t; + +static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS}; + +struct hb_ot_map_t +{ + friend struct hb_ot_map_builder_t; + + public: + + struct feature_map_t { + hb_tag_t tag; /* should be first for our bsearch to work */ + unsigned int index[2]; /* GSUB/GPOS */ + unsigned int stage[2]; /* GSUB/GPOS */ + unsigned int shift; + hb_mask_t mask; + hb_mask_t _1_mask; /* mask for value=1, for quick access */ + unsigned int needs_fallback : 1; + unsigned int auto_zwnj : 1; + unsigned int auto_zwj : 1; + unsigned int random : 1; + unsigned int per_syllable : 1; + + int cmp (const hb_tag_t tag_) const + { return tag_ < tag ? -1 : tag_ > tag ? 1 : 0; } + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const feature_map_t *a = (const feature_map_t *) pa; + const feature_map_t *b = (const feature_map_t *) pb; + return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; + } + }; + + struct lookup_map_t { + unsigned short index; + unsigned short auto_zwnj : 1; + unsigned short auto_zwj : 1; + unsigned short random : 1; + unsigned short per_syllable : 1; + hb_mask_t mask; + hb_tag_t feature_tag; + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const lookup_map_t *a = (const lookup_map_t *) pa; + const lookup_map_t *b = (const lookup_map_t *) pb; + return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; + } + }; + + /* Pause functions return true if new glyph indices might have been + * added to the buffer. This is used to update buffer digest. */ + typedef bool (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer); + + struct stage_map_t { + unsigned int last_lookup; /* Cumulative */ + pause_func_t pause_func; + }; + + void init () + { + hb_memset (this, 0, sizeof (*this)); + + features.init0 (); + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + lookups[table_index].init0 (); + stages[table_index].init0 (); + } + } + void fini () + { + features.fini (); + for (unsigned int table_index = 0; table_index < 2; table_index++) + { + lookups[table_index].fini (); + stages[table_index].fini (); + } + } + + hb_mask_t get_global_mask () const { return global_mask; } + + hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = nullptr) const + { + const feature_map_t *map = features.bsearch (feature_tag); + if (shift) *shift = map ? map->shift : 0; + return map ? map->mask : 0; + } + + bool needs_fallback (hb_tag_t feature_tag) const + { + const feature_map_t *map = features.bsearch (feature_tag); + return map ? map->needs_fallback : false; + } + + hb_mask_t get_1_mask (hb_tag_t feature_tag) const + { + const feature_map_t *map = features.bsearch (feature_tag); + return map ? map->_1_mask : 0; + } + + unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const + { + const feature_map_t *map = features.bsearch (feature_tag); + return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX; + } + + unsigned int get_feature_stage (unsigned int table_index, hb_tag_t feature_tag) const + { + const feature_map_t *map = features.bsearch (feature_tag); + return map ? map->stage[table_index] : UINT_MAX; + } + + hb_array_t<const hb_ot_map_t::lookup_map_t> + get_stage_lookups (unsigned int table_index, unsigned int stage) const + { + if (unlikely (stage > stages[table_index].length)) + return hb_array<const hb_ot_map_t::lookup_map_t> (nullptr, 0); + + unsigned int start = stage ? stages[table_index][stage - 1].last_lookup : 0; + unsigned int end = stage < stages[table_index].length ? stages[table_index][stage].last_lookup : lookups[table_index].length; + return lookups[table_index].as_array ().sub_array (start, end - start); + } + + HB_INTERNAL void collect_lookups (unsigned int table_index, hb_set_t *lookups) const; + template <typename Proxy> + HB_INTERNAL void apply (const Proxy &proxy, + const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; + + public: + hb_tag_t chosen_script[2]; + bool found_script[2]; + + private: + + hb_mask_t global_mask = 0; + + hb_sorted_vector_t<feature_map_t> features; + hb_vector_t<lookup_map_t> lookups[2]; /* GSUB/GPOS */ + hb_vector_t<stage_map_t> stages[2]; /* GSUB/GPOS */ +}; + +enum hb_ot_map_feature_flags_t +{ + F_NONE = 0x0000u, + F_GLOBAL = 0x0001u, /* Feature applies to all characters; results in no mask allocated for it. */ + F_HAS_FALLBACK = 0x0002u, /* Has fallback implementation, so include mask bit even if feature not found. */ + F_MANUAL_ZWNJ = 0x0004u, /* Don't skip over ZWNJ when matching **context**. */ + F_MANUAL_ZWJ = 0x0008u, /* Don't skip over ZWJ when matching **input**. */ + F_MANUAL_JOINERS = F_MANUAL_ZWNJ | F_MANUAL_ZWJ, + F_GLOBAL_MANUAL_JOINERS= F_GLOBAL | F_MANUAL_JOINERS, + F_GLOBAL_HAS_FALLBACK = F_GLOBAL | F_HAS_FALLBACK, + F_GLOBAL_SEARCH = 0x0010u, /* If feature not found in LangSys, look for it in global feature list and pick one. */ + F_RANDOM = 0x0020u, /* Randomly select a glyph from an AlternateSubstFormat1 subtable. */ + F_PER_SYLLABLE = 0x0040u /* Contain lookup application to within syllable. */ +}; +HB_MARK_AS_FLAG_T (hb_ot_map_feature_flags_t); + + +struct hb_ot_map_feature_t +{ + hb_tag_t tag; + hb_ot_map_feature_flags_t flags; +}; + +struct hb_ot_shape_plan_key_t; + +struct hb_ot_map_builder_t +{ + public: + + HB_INTERNAL hb_ot_map_builder_t (hb_face_t *face_, + const hb_segment_properties_t &props_); + + HB_INTERNAL ~hb_ot_map_builder_t (); + + HB_INTERNAL void add_feature (hb_tag_t tag, + hb_ot_map_feature_flags_t flags=F_NONE, + unsigned int value=1); + + HB_INTERNAL bool has_feature (hb_tag_t tag); + + void add_feature (const hb_ot_map_feature_t &feat) + { add_feature (feat.tag, feat.flags); } + + void enable_feature (hb_tag_t tag, + hb_ot_map_feature_flags_t flags=F_NONE, + unsigned int value=1) + { add_feature (tag, F_GLOBAL | flags, value); } + + void disable_feature (hb_tag_t tag) + { add_feature (tag, F_GLOBAL, 0); } + + void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func) + { add_pause (0, pause_func); } + void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func) + { add_pause (1, pause_func); } + + HB_INTERNAL void compile (hb_ot_map_t &m, + const hb_ot_shape_plan_key_t &key); + + private: + + HB_INTERNAL void add_lookups (hb_ot_map_t &m, + unsigned int table_index, + unsigned int feature_index, + unsigned int variations_index, + hb_mask_t mask, + bool auto_zwnj = true, + bool auto_zwj = true, + bool random = false, + bool per_syllable = false, + hb_tag_t feature_tag = HB_TAG(' ',' ',' ',' ')); + + struct feature_info_t { + hb_tag_t tag; + unsigned int seq; /* sequence#, used for stable sorting only */ + unsigned int max_value; + hb_ot_map_feature_flags_t flags; + unsigned int default_value; /* for non-global features, what should the unset glyphs take */ + unsigned int stage[2]; /* GSUB/GPOS */ + + 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; + return (a->tag != b->tag) ? (a->tag < b->tag ? -1 : 1) : + (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); + } + }; + + struct stage_info_t { + unsigned int index; + hb_ot_map_t::pause_func_t pause_func; + }; + + HB_INTERNAL void add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func); + + public: + + hb_face_t *face; + hb_segment_properties_t props; + bool is_simple; + + hb_tag_t chosen_script[2]; + bool found_script[2]; + unsigned int script_index[2], language_index[2]; + + private: + + unsigned int current_stage[2]; /* GSUB/GPOS */ + hb_vector_t<feature_info_t> feature_infos; + hb_vector_t<stage_info_t> stages[2]; /* GSUB/GPOS */ +}; + + + +#endif /* HB_OT_MAP_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-math-table.hh b/gfx/harfbuzz/src/hb-ot-math-table.hh new file mode 100644 index 0000000000..32e497aef6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-math-table.hh @@ -0,0 +1,1139 @@ +/* + * Copyright © 2016 Igalia S.L. + * + * 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. + * + * Igalia Author(s): Frédéric Wang + */ + +#ifndef HB_OT_MATH_TABLE_HH +#define HB_OT_MATH_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-math.h" + +namespace OT { + + +struct MathValueRecord +{ + hb_position_t get_x_value (hb_font_t *font, const void *base) const + { return font->em_scale_x (value) + (base+deviceTable).get_x_delta (font); } + hb_position_t get_y_value (hb_font_t *font, const void *base) const + { return font->em_scale_y (value) + (base+deviceTable).get_y_delta (font); } + + MathValueRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + out->deviceTable.serialize_copy (c, deviceTable, base, 0, hb_serialize_context_t::Head); + + return_trace (out); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && deviceTable.sanitize (c, base)); + } + + protected: + HBINT16 value; /* The X or Y value in design units */ + Offset16To<Device> deviceTable; /* Offset to the device table - from the + * beginning of parent table. May be NULL. + * Suggested format for device table is 1. */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct MathConstants +{ + MathConstants* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + + HBINT16 *p = c->allocate_size<HBINT16> (HBINT16::static_size * 2); + if (unlikely (!p)) return_trace (nullptr); + hb_memcpy (p, percentScaleDown, HBINT16::static_size * 2); + + HBUINT16 *m = c->allocate_size<HBUINT16> (HBUINT16::static_size * 2); + if (unlikely (!m)) return_trace (nullptr); + hb_memcpy (m, minHeight, HBUINT16::static_size * 2); + + unsigned count = ARRAY_LENGTH (mathValueRecords); + for (unsigned i = 0; i < count; i++) + if (!c->copy (mathValueRecords[i], this)) + return_trace (nullptr); + + if (!c->embed (radicalDegreeBottomRaisePercent)) return_trace (nullptr); + return_trace (out); + } + + bool sanitize_math_value_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + + unsigned int count = ARRAY_LENGTH (mathValueRecords); + for (unsigned int i = 0; i < count; i++) + if (!mathValueRecords[i].sanitize (c, this)) + return_trace (false); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && sanitize_math_value_records (c)); + } + + hb_position_t get_value (hb_ot_math_constant_t constant, + hb_font_t *font) const + { + switch (constant) { + + case HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN: + case HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN: + return percentScaleDown[constant - HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN]; + + case HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT: + case HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT: + return font->em_scale_y (minHeight[constant - HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT]); + + case HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE: + case HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE: + case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP: + case HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT: + return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_x_value (font, this); + + case HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT: + case HB_OT_MATH_CONSTANT_AXIS_HEIGHT: + case HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT: + case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: + case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP: + case HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: + case HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN: + case HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN: + case HB_OT_MATH_CONSTANT_MATH_LEADING: + case HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER: + case HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER: + case HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN: + case HB_OT_MATH_CONSTANT_STACK_GAP_MIN: + case HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP: + case HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN: + case HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP: + case HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN: + case HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN: + case HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX: + case HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP: + case HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED: + case HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER: + case HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS: + case HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP: + case HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN: + case HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN: + return mathValueRecords[constant - HB_OT_MATH_CONSTANT_MATH_LEADING].get_y_value (font, this); + + case HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT: + return radicalDegreeBottomRaisePercent; + + default: + return 0; + } + } + + protected: + HBINT16 percentScaleDown[2]; + HBUINT16 minHeight[2]; + MathValueRecord mathValueRecords[51]; + HBINT16 radicalDegreeBottomRaisePercent; + + public: + DEFINE_SIZE_STATIC (214); +}; + +struct MathItalicsCorrectionInfo +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = c->plan->_glyphset_mathed; + 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<hb_codepoint_t> new_coverage; + + hb_zip (this+coverage, italicsCorrection) + | hb_filter (glyphset, hb_first) + | hb_filter (serialize_math_record_array (c->serializer, out->italicsCorrection, 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 (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + coverage.sanitize (c, this) && + italicsCorrection.sanitize (c, this)); + } + + hb_position_t get_value (hb_codepoint_t glyph, + hb_font_t *font) const + { + unsigned int index = (this+coverage).get_coverage (glyph); + return italicsCorrection[index].get_x_value (font, this); + } + + protected: + Offset16To<Coverage> coverage; /* Offset to Coverage table - + * from the beginning of + * MathItalicsCorrectionInfo + * table. */ + Array16Of<MathValueRecord> italicsCorrection; /* Array of MathValueRecords + * defining italics correction + * values for each + * covered glyph. */ + + public: + DEFINE_SIZE_ARRAY (4, italicsCorrection); +}; + +struct MathTopAccentAttachment +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = c->plan->_glyphset_mathed; + 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<hb_codepoint_t> new_coverage; + + hb_zip (this+topAccentCoverage, topAccentAttachment) + | hb_filter (glyphset, hb_first) + | hb_filter (serialize_math_record_array (c->serializer, out->topAccentAttachment, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->topAccentCoverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + topAccentCoverage.sanitize (c, this) && + topAccentAttachment.sanitize (c, this)); + } + + hb_position_t get_value (hb_codepoint_t glyph, + hb_font_t *font) const + { + unsigned int index = (this+topAccentCoverage).get_coverage (glyph); + if (index == NOT_COVERED) + return font->get_glyph_h_advance (glyph) / 2; + return topAccentAttachment[index].get_x_value (font, this); + } + + protected: + Offset16To<Coverage> topAccentCoverage; /* Offset to Coverage table - + * from the beginning of + * MathTopAccentAttachment + * table. */ + Array16Of<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords + * defining top accent + * attachment points for each + * covered glyph. */ + + public: + DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment); +}; + +struct MathKern +{ + MathKern* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + + if (unlikely (!c->embed (heightCount))) return_trace (nullptr); + + unsigned count = 2 * heightCount + 1; + for (unsigned i = 0; i < count; i++) + if (!c->copy (mathValueRecordsZ.arrayZ[i], this)) + return_trace (nullptr); + + return_trace (out); + } + + bool sanitize_math_value_records (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + unsigned int count = 2 * heightCount + 1; + for (unsigned int i = 0; i < count; i++) + if (!mathValueRecordsZ.arrayZ[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + c->check_array (mathValueRecordsZ.arrayZ, 2 * heightCount + 1) && + sanitize_math_value_records (c)); + } + + hb_position_t get_value (hb_position_t correction_height, hb_font_t *font) const + { + const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ; + const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount; + int sign = font->y_scale < 0 ? -1 : +1; + + /* The description of the MathKern table is a ambiguous, but interpreting + * "between the two heights found at those indexes" for 0 < i < len as + * + * correctionHeight[i-1] < correction_height <= correctionHeight[i] + * + * makes the result consistent with the limit cases and we can just use the + * binary search algorithm of std::upper_bound: + */ + unsigned int i = 0; + unsigned int count = heightCount; + while (count > 0) + { + unsigned int half = count / 2; + hb_position_t height = correctionHeight[i + half].get_y_value (font, this); + if (sign * height < sign * correction_height) + { + i += half + 1; + count -= half + 1; + } else + count = half; + } + return kernValue[i].get_x_value (font, this); + } + + unsigned int get_entries (unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries, /* OUT */ + hb_font_t *font) const + { + const MathValueRecord* correctionHeight = mathValueRecordsZ.arrayZ; + const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount; + const unsigned int entriesCount = heightCount + 1; + + if (entries_count) + { + unsigned int start = hb_min (start_offset, entriesCount); + unsigned int end = hb_min (start + *entries_count, entriesCount); + *entries_count = end - start; + + for (unsigned int i = 0; i < *entries_count; i++) { + unsigned int j = start + i; + + hb_position_t max_height; + if (j == heightCount) { + max_height = INT32_MAX; + } else { + max_height = correctionHeight[j].get_y_value (font, this); + } + + kern_entries[i] = {max_height, kernValue[j].get_x_value (font, this)}; + } + } + return entriesCount; + } + + protected: + HBUINT16 heightCount; + UnsizedArrayOf<MathValueRecord> + mathValueRecordsZ; + /* Array of correction heights at + * which the kern value changes. + * Sorted by the height value in + * design units (heightCount entries), + * Followed by: + * Array of kern values corresponding + * to heights. (heightCount+1 entries). + */ + + public: + DEFINE_SIZE_ARRAY (2, mathValueRecordsZ); +}; + +struct MathKernInfoRecord +{ + MathKernInfoRecord* copy (hb_serialize_context_t *c, const void *base) const + { + TRACE_SERIALIZE (this); + auto *out = c->embed (this); + if (unlikely (!out)) return_trace (nullptr); + + unsigned count = ARRAY_LENGTH (mathKern); + for (unsigned i = 0; i < count; i++) + out->mathKern[i].serialize_copy (c, mathKern[i], base, 0, hb_serialize_context_t::Head); + + return_trace (out); + } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + + unsigned int count = ARRAY_LENGTH (mathKern); + for (unsigned int i = 0; i < count; i++) + if (unlikely (!mathKern[i].sanitize (c, base))) + return_trace (false); + + return_trace (true); + } + + hb_position_t get_kerning (hb_ot_math_kern_t kern, + hb_position_t correction_height, + hb_font_t *font, + const void *base) const + { + unsigned int idx = kern; + if (unlikely (idx >= ARRAY_LENGTH (mathKern))) return 0; + return (base+mathKern[idx]).get_value (correction_height, font); + } + + unsigned int get_kernings (hb_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries, /* OUT */ + hb_font_t *font, + const void *base) const + { + unsigned int idx = kern; + if (unlikely (idx >= ARRAY_LENGTH (mathKern)) || !mathKern[idx]) { + if (entries_count) *entries_count = 0; + return 0; + } + return (base+mathKern[idx]).get_entries (start_offset, + entries_count, + kern_entries, + font); + } + + protected: + /* Offset to MathKern table for each corner - + * from the beginning of MathKernInfo table. May be NULL. */ + Offset16To<MathKern> mathKern[4]; + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct MathKernInfo +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = c->plan->_glyphset_mathed; + 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<hb_codepoint_t> new_coverage; + + hb_zip (this+mathKernCoverage, mathKernInfoRecords) + | hb_filter (glyphset, hb_first) + | hb_filter (serialize_math_record_array (c->serializer, out->mathKernInfoRecords, this), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->mathKernCoverage.serialize_serialize (c->serializer, new_coverage.iter ()); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mathKernCoverage.sanitize (c, this) && + mathKernInfoRecords.sanitize (c, this)); + } + + hb_position_t get_kerning (hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height, + hb_font_t *font) const + { + unsigned int index = (this+mathKernCoverage).get_coverage (glyph); + return mathKernInfoRecords[index].get_kerning (kern, correction_height, font, this); + } + + unsigned int get_kernings (hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries, /* OUT */ + hb_font_t *font) const + { + unsigned int index = (this+mathKernCoverage).get_coverage (glyph); + return mathKernInfoRecords[index].get_kernings (kern, + start_offset, + entries_count, + kern_entries, + font, + this); + } + + protected: + Offset16To<Coverage> + mathKernCoverage; + /* Offset to Coverage table - + * from the beginning of the + * MathKernInfo table. */ + Array16Of<MathKernInfoRecord> + mathKernInfoRecords; + /* Array of MathKernInfoRecords, + * per-glyph information for + * mathematical positioning + * of subscripts and + * superscripts. */ + + public: + DEFINE_SIZE_ARRAY (4, mathKernInfoRecords); +}; + +struct MathGlyphInfo +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + out->mathItalicsCorrectionInfo.serialize_subset (c, mathItalicsCorrectionInfo, this); + out->mathTopAccentAttachment.serialize_subset (c, mathTopAccentAttachment, this); + + const hb_set_t &glyphset = c->plan->_glyphset_mathed; + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + hb_iter (this+extendedShapeCoverage) + | hb_take (c->plan->source->get_num_glyphs ()) + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + if (it) out->extendedShapeCoverage.serialize_serialize (c->serializer, it); + else out->extendedShapeCoverage = 0; + + out->mathKernInfo.serialize_subset (c, mathKernInfo, this); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + mathItalicsCorrectionInfo.sanitize (c, this) && + mathTopAccentAttachment.sanitize (c, this) && + extendedShapeCoverage.sanitize (c, this) && + mathKernInfo.sanitize (c, this)); + } + + hb_position_t + get_italics_correction (hb_codepoint_t glyph, hb_font_t *font) const + { return (this+mathItalicsCorrectionInfo).get_value (glyph, font); } + + hb_position_t + get_top_accent_attachment (hb_codepoint_t glyph, hb_font_t *font) const + { return (this+mathTopAccentAttachment).get_value (glyph, font); } + + bool is_extended_shape (hb_codepoint_t glyph) const + { return (this+extendedShapeCoverage).get_coverage (glyph) != NOT_COVERED; } + + hb_position_t get_kerning (hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height, + hb_font_t *font) const + { return (this+mathKernInfo).get_kerning (glyph, kern, correction_height, font); } + + hb_position_t get_kernings (hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries, /* OUT */ + hb_font_t *font) const + { return (this+mathKernInfo).get_kernings (glyph, + kern, + start_offset, + entries_count, + kern_entries, + font); } + + protected: + /* Offset to MathItalicsCorrectionInfo table - + * from the beginning of MathGlyphInfo table. */ + Offset16To<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo; + + /* Offset to MathTopAccentAttachment table - + * from the beginning of MathGlyphInfo table. */ + Offset16To<MathTopAccentAttachment> mathTopAccentAttachment; + + /* Offset to coverage table for Extended Shape glyphs - + * from the beginning of MathGlyphInfo table. When the left or right glyph of + * a box is an extended shape variant, the (ink) box (and not the default + * position defined by values in MathConstants table) should be used for + * vertical positioning purposes. May be NULL.. */ + Offset16To<Coverage> extendedShapeCoverage; + + /* Offset to MathKernInfo table - + * from the beginning of MathGlyphInfo table. */ + Offset16To<MathKernInfo> mathKernInfo; + + public: + DEFINE_SIZE_STATIC (8); +}; + +struct MathGlyphVariantRecord +{ + friend struct MathGlyphConstruction; + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + const hb_map_t& glyph_map = *c->plan->glyph_map; + return_trace (c->serializer->check_assign (out->variantGlyph, glyph_map.get (variantGlyph), HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void closure_glyphs (hb_set_t *variant_glyphs) const + { variant_glyphs->add (variantGlyph); } + + protected: + HBGlyphID16 variantGlyph; /* Glyph ID for the variant. */ + HBUINT16 advanceMeasurement; /* Advance width/height, in design units, of the + * variant, in the direction of requested + * glyph extension. */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct PartFlags : HBUINT16 +{ + enum Flags { + Extender = 0x0001u, /* If set, the part can be skipped or repeated. */ + + Defined = 0x0001u, /* All defined flags. */ + }; + + public: + DEFINE_SIZE_STATIC (2); +}; + +struct MathGlyphPartRecord +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + const hb_map_t& glyph_map = *c->plan->glyph_map; + return_trace (c->serializer->check_assign (out->glyph, glyph_map.get (glyph), HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void extract (hb_ot_math_glyph_part_t &out, + int64_t mult, + hb_font_t *font) const + { + out.glyph = glyph; + + out.start_connector_length = font->em_mult (startConnectorLength, mult); + out.end_connector_length = font->em_mult (endConnectorLength, mult); + out.full_advance = font->em_mult (fullAdvance, mult); + + static_assert ((unsigned int) HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER == + (unsigned int) PartFlags::Extender, ""); + + out.flags = (hb_ot_math_glyph_part_flags_t) + (unsigned int) + (partFlags & PartFlags::Defined); + } + + void closure_glyphs (hb_set_t *variant_glyphs) const + { variant_glyphs->add (glyph); } + + protected: + HBGlyphID16 glyph; /* Glyph ID for the part. */ + HBUINT16 startConnectorLength; + /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the beginning of the glyph, in the + * direction of the extension. */ + HBUINT16 endConnectorLength; + /* Advance width/ height of the straight bar + * connector material, in design units, is at + * the end of the glyph, in the direction of + * the extension. */ + HBUINT16 fullAdvance; /* Full advance width/height for this part, + * in the direction of the extension. + * In design units. */ + PartFlags partFlags; /* Part qualifiers. */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +struct MathGlyphAssembly +{ + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + + if (!c->serializer->copy (italicsCorrection, this)) return_trace (false); + if (!c->serializer->copy<HBUINT16> (partRecords.len)) return_trace (false); + + for (const auto& record : partRecords.iter ()) + if (!record.subset (c)) return_trace (false); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + italicsCorrection.sanitize (c, this) && + partRecords.sanitize (c)); + } + + unsigned int get_parts (hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts /* OUT */, + hb_position_t *italics_correction /* OUT */) const + { + if (parts_count) + { + int64_t mult = font->dir_mult (direction); + for (auto _ : hb_zip (partRecords.as_array ().sub_array (start_offset, parts_count), + hb_array (parts, *parts_count))) + _.first.extract (_.second, mult, font); + } + + if (italics_correction) + *italics_correction = italicsCorrection.get_x_value (font, this); + + return partRecords.len; + } + + void closure_glyphs (hb_set_t *variant_glyphs) const + { + for (const auto& _ : partRecords.iter ()) + _.closure_glyphs (variant_glyphs); + } + + protected: + MathValueRecord + italicsCorrection; + /* Italics correction of this + * MathGlyphAssembly. Should not + * depend on the assembly size. */ + Array16Of<MathGlyphPartRecord> + partRecords; /* Array of part records, from + * left to right and bottom to + * top. */ + + public: + DEFINE_SIZE_ARRAY (6, partRecords); +}; + +struct MathGlyphConstruction +{ + 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->glyphAssembly.serialize_subset (c, glyphAssembly, this); + + if (!c->serializer->check_assign (out->mathGlyphVariantRecord.len, mathGlyphVariantRecord.len, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + for (const auto& record : mathGlyphVariantRecord.iter ()) + if (!record.subset (c)) return_trace (false); + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + glyphAssembly.sanitize (c, this) && + mathGlyphVariantRecord.sanitize (c)); + } + + const MathGlyphAssembly &get_assembly () const { return this+glyphAssembly; } + + unsigned int get_variants (hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */) const + { + if (variants_count) + { + int64_t mult = font->dir_mult (direction); + for (auto _ : hb_zip (mathGlyphVariantRecord.as_array ().sub_array (start_offset, variants_count), + hb_array (variants, *variants_count))) + _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)}; + } + return mathGlyphVariantRecord.len; + } + + void closure_glyphs (hb_set_t *variant_glyphs) const + { + (this+glyphAssembly).closure_glyphs (variant_glyphs); + + for (const auto& _ : mathGlyphVariantRecord.iter ()) + _.closure_glyphs (variant_glyphs); + } + + protected: + /* Offset to MathGlyphAssembly table for this shape - from the beginning of + MathGlyphConstruction table. May be NULL. */ + Offset16To<MathGlyphAssembly> glyphAssembly; + + /* MathGlyphVariantRecords for alternative variants of the glyphs. */ + Array16Of<MathGlyphVariantRecord> mathGlyphVariantRecord; + + public: + DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord); +}; + +struct MathVariants +{ + void closure_glyphs (const hb_set_t *glyph_set, + hb_set_t *variant_glyphs) const + { + const hb_array_t<const Offset16To<MathGlyphConstruction>> glyph_construction_offsets = glyphConstruction.as_array (vertGlyphCount + horizGlyphCount); + + if (vertGlyphCoverage) + { + const auto vert_offsets = glyph_construction_offsets.sub_array (0, vertGlyphCount); + + hb_zip (this+vertGlyphCoverage, vert_offsets) + | hb_filter (glyph_set, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); }) + ; + } + + if (horizGlyphCoverage) + { + const auto hori_offsets = glyph_construction_offsets.sub_array (vertGlyphCount, horizGlyphCount); + + hb_zip (this+horizGlyphCoverage, hori_offsets) + | hb_filter (glyph_set, hb_first) + | hb_map (hb_second) + | hb_map (hb_add (this)) + | hb_apply ([=] (const MathGlyphConstruction &_) { _.closure_glyphs (variant_glyphs); }) + ; + } + } + + void collect_coverage_and_indices (hb_sorted_vector_t<hb_codepoint_t>& new_coverage, + const Offset16To<Coverage>& coverage, + unsigned i, + unsigned end_index, + hb_set_t& indices, + const hb_set_t& glyphset, + const hb_map_t& glyph_map) const + { + if (!coverage) return; + + for (const auto _ : (this+coverage).iter ()) + { + if (i >= end_index) return; + if (glyphset.has (_)) + { + unsigned new_gid = glyph_map.get (_); + new_coverage.push (new_gid); + indices.add (i); + } + i++; + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = c->plan->_glyphset_mathed; + 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); + if (!c->serializer->check_assign (out->minConnectorOverlap, minConnectorOverlap, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + hb_sorted_vector_t<hb_codepoint_t> new_vert_coverage; + hb_sorted_vector_t<hb_codepoint_t> new_hori_coverage; + hb_set_t indices; + collect_coverage_and_indices (new_vert_coverage, vertGlyphCoverage, 0, vertGlyphCount, indices, glyphset, glyph_map); + collect_coverage_and_indices (new_hori_coverage, horizGlyphCoverage, vertGlyphCount, vertGlyphCount + horizGlyphCount, indices, glyphset, glyph_map); + + if (!c->serializer->check_assign (out->vertGlyphCount, new_vert_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + if (!c->serializer->check_assign (out->horizGlyphCount, new_hori_coverage.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + for (unsigned i : indices.iter ()) + { + auto *o = c->serializer->embed (glyphConstruction[i]); + if (!o) return_trace (false); + o->serialize_subset (c, glyphConstruction[i], this); + } + + if (new_vert_coverage) + out->vertGlyphCoverage.serialize_serialize (c->serializer, new_vert_coverage.iter ()); + + if (new_hori_coverage) + out->horizGlyphCoverage.serialize_serialize (c->serializer, new_hori_coverage.iter ()); + return_trace (true); + } + + bool sanitize_offsets (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + unsigned int count = vertGlyphCount + horizGlyphCount; + for (unsigned int i = 0; i < count; i++) + if (!glyphConstruction.arrayZ[i].sanitize (c, this)) return_trace (false); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + vertGlyphCoverage.sanitize (c, this) && + horizGlyphCoverage.sanitize (c, this) && + hb_barrier () && + c->check_array (glyphConstruction.arrayZ, vertGlyphCount + horizGlyphCount) && + sanitize_offsets (c)); + } + + hb_position_t get_min_connector_overlap (hb_direction_t direction, + hb_font_t *font) const + { return font->em_scale_dir (minConnectorOverlap, direction); } + + unsigned int get_glyph_variants (hb_codepoint_t glyph, + hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */) const + { return get_glyph_construction (glyph, direction, font) + .get_variants (direction, font, start_offset, variants_count, variants); } + + unsigned int get_glyph_parts (hb_codepoint_t glyph, + hb_direction_t direction, + hb_font_t *font, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts /* OUT */, + hb_position_t *italics_correction /* OUT */) const + { return get_glyph_construction (glyph, direction, font) + .get_assembly () + .get_parts (direction, font, + start_offset, parts_count, parts, + italics_correction); } + + private: + const MathGlyphConstruction & + get_glyph_construction (hb_codepoint_t glyph, + hb_direction_t direction, + hb_font_t *font HB_UNUSED) const + { + bool vertical = HB_DIRECTION_IS_VERTICAL (direction); + unsigned int count = vertical ? vertGlyphCount : horizGlyphCount; + const Offset16To<Coverage> &coverage = vertical ? vertGlyphCoverage + : horizGlyphCoverage; + + unsigned int index = (this+coverage).get_coverage (glyph); + if (unlikely (index >= count)) return Null (MathGlyphConstruction); + + if (!vertical) + index += vertGlyphCount; + + return this+glyphConstruction[index]; + } + + protected: + HBUINT16 minConnectorOverlap; + /* Minimum overlap of connecting + * glyphs during glyph construction, + * in design units. */ + Offset16To<Coverage> vertGlyphCoverage; + /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + Offset16To<Coverage> horizGlyphCoverage; + /* Offset to Coverage table - + * from the beginning of MathVariants + * table. */ + HBUINT16 vertGlyphCount; /* Number of glyphs for which + * information is provided for + * vertically growing variants. */ + HBUINT16 horizGlyphCount;/* Number of glyphs for which + * information is provided for + * horizontally growing variants. */ + + /* Array of offsets to MathGlyphConstruction tables - from the beginning of + the MathVariants table, for shapes growing in vertical/horizontal + direction. */ + UnsizedArrayOf<Offset16To<MathGlyphConstruction>> + glyphConstruction; + + public: + DEFINE_SIZE_ARRAY (10, glyphConstruction); +}; + + +/* + * MATH -- Mathematical typesetting + * https://docs.microsoft.com/en-us/typography/opentype/spec/math + */ + +struct MATH +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_MATH; + + bool has_data () const { return version.to_int (); } + + void closure_glyphs (hb_set_t *glyph_set) const + { + if (mathVariants) + { + hb_set_t variant_glyphs; + (this+mathVariants).closure_glyphs (glyph_set, &variant_glyphs); + hb_set_union (glyph_set, &variant_glyphs); + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + out->mathConstants.serialize_copy (c->serializer, mathConstants, this, 0, hb_serialize_context_t::Head); + out->mathGlyphInfo.serialize_subset (c, mathGlyphInfo, this); + out->mathVariants.serialize_subset (c, mathVariants, this); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + likely (version.major == 1) && + hb_barrier () && + mathConstants.sanitize (c, this) && + mathGlyphInfo.sanitize (c, this) && + mathVariants.sanitize (c, this)); + } + + hb_position_t get_constant (hb_ot_math_constant_t constant, + hb_font_t *font) const + { return (this+mathConstants).get_value (constant, font); } + + const MathGlyphInfo &get_glyph_info () const { return this+mathGlyphInfo; } + + const MathVariants &get_variants () const { return this+mathVariants; } + + protected: + FixedVersion<>version; /* Version of the MATH table + * initially set to 0x00010000u */ + Offset16To<MathConstants> + mathConstants; /* MathConstants table */ + Offset16To<MathGlyphInfo> + mathGlyphInfo; /* MathGlyphInfo table */ + Offset16To<MathVariants> + mathVariants; /* MathVariants table */ + + public: + DEFINE_SIZE_STATIC (10); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_MATH_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-math.cc b/gfx/harfbuzz/src/hb-ot-math.cc new file mode 100644 index 0000000000..876ad258e3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-math.cc @@ -0,0 +1,338 @@ +/* + * Copyright © 2016 Igalia S.L. + * + * 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. + * + * Igalia Author(s): Frédéric Wang + */ + +#include "hb.hh" + +#ifndef HB_NO_MATH + +#include "hb-ot-math-table.hh" + + +/** + * SECTION:hb-ot-math + * @title: hb-ot-math + * @short_description: OpenType Math information + * @include: hb-ot.h + * + * Functions for fetching mathematics layout data from OpenType fonts. + * + * HarfBuzz itself does not implement a math layout solution. The + * functions and types provided can be used by client programs to access + * the font data necessary for typesetting OpenType Math layout. + * + **/ + + +/* + * OT::MATH + */ + +/** + * hb_ot_math_has_data: + * @face: #hb_face_t to test + * + * Tests whether a face has a `MATH` table. + * + * Return value: `true` if the table is found, `false` otherwise + * + * Since: 1.3.3 + **/ +hb_bool_t +hb_ot_math_has_data (hb_face_t *face) +{ + return face->table.MATH->has_data (); +} + +/** + * hb_ot_math_get_constant: + * @font: #hb_font_t to work upon + * @constant: #hb_ot_math_constant_t the constant to retrieve + * + * Fetches the specified math constant. For most constants, the value returned + * is an #hb_position_t. + * + * However, if the requested constant is #HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN, + * #HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN or + * #HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT, then the return value is + * an integer between 0 and 100 representing that percentage. + * + * Return value: the requested constant or zero + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_constant (hb_font_t *font, + hb_ot_math_constant_t constant) +{ + return font->face->table.MATH->get_constant(constant, font); +} + +/** + * hb_ot_math_get_glyph_italics_correction: + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value + * + * Fetches an italics-correction value (if one exists) for the specified + * glyph index. + * + * Return value: the italics correction of the glyph or zero + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_glyph_italics_correction (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->face->table.MATH->get_glyph_info().get_italics_correction (glyph, font); +} + +/** + * hb_ot_math_get_glyph_top_accent_attachment: + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value + * + * Fetches a top-accent-attachment value (if one exists) for the specified + * glyph index. + * + * For any glyph that does not have a top-accent-attachment value - that is, + * a glyph not covered by the `MathTopAccentAttachment` table (or, when + * @font has no `MathTopAccentAttachment` table or no `MATH` table, any + * glyph) - the function synthesizes a value, returning the position at + * one-half the glyph's advance width. + * + * Return value: the top accent attachment of the glyph or 0.5 * the advance + * width of @glyph + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font, + hb_codepoint_t glyph) +{ + return font->face->table.MATH->get_glyph_info().get_top_accent_attachment (glyph, font); +} + +/** + * hb_ot_math_is_glyph_extended_shape: + * @face: #hb_face_t to work upon + * @glyph: The glyph index to test + * + * Tests whether the given glyph index is an extended shape in the face. + * + * Return value: `true` if the glyph is an extended shape, `false` otherwise + * + * Since: 1.3.3 + **/ +hb_bool_t +hb_ot_math_is_glyph_extended_shape (hb_face_t *face, + hb_codepoint_t glyph) +{ + return face->table.MATH->get_glyph_info().is_extended_shape (glyph); +} + +/** + * hb_ot_math_get_glyph_kerning: + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the value + * @kern: The #hb_ot_math_kern_t from which to retrieve the value + * @correction_height: the correction height to use to determine the kerning. + * + * Fetches the math kerning (cut-ins) value for the specified font, glyph index, and + * @kern. + * + * If the MathKern table is found, the function examines it to find a height + * value that is greater or equal to @correction_height. If such a height + * value is found, corresponding kerning value from the table is returned. If + * no such height value is found, the last kerning value is returned. + * + * Return value: requested kerning value or zero + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_glyph_kerning (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height) +{ + return font->face->table.MATH->get_glyph_info().get_kerning (glyph, + kern, + correction_height, + font); +} + +/** + * hb_ot_math_get_glyph_kernings: + * @font: #hb_font_t to work upon + * @glyph: The glyph index from which to retrieve the kernings + * @kern: The #hb_ot_math_kern_t from which to retrieve the kernings + * @start_offset: offset of the first kern entry to retrieve + * @entries_count: (inout) (optional): Input = the maximum number of kern entries to return; + * Output = the actual number of kern entries returned + * @kern_entries: (out caller-allocates) (array length=entries_count): array of kern entries returned + * + * Fetches the raw MathKern (cut-in) data for the specified font, glyph index, + * and @kern. The corresponding list of kern values and correction heights is + * returned as a list of #hb_ot_math_kern_entry_t structs. + * + * See also #hb_ot_math_get_glyph_kerning, which handles selecting the + * appropriate kern value for a given correction height. + * + * <note>For a glyph with @n defined kern values (where @n > 0), there are only + * @n−1 defined correction heights, as each correction height defines a boundary + * past which the next kern value should be selected. Therefore, only the + * #hb_ot_math_kern_entry_t.kern_value of the uppermost #hb_ot_math_kern_entry_t + * actually comes from the font; its corresponding + * #hb_ot_math_kern_entry_t.max_correction_height is always set to + * <code>INT32_MAX</code>.</note> + * + * Return value: the total number of kern values available or zero + * + * Since: 3.4.0 + **/ +unsigned int +hb_ot_math_get_glyph_kernings (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries /* OUT */) +{ + return font->face->table.MATH->get_glyph_info().get_kernings (glyph, + kern, + start_offset, + entries_count, + kern_entries, + font); +} + +/** + * hb_ot_math_get_glyph_variants: + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: The direction of the stretching (horizontal or vertical) + * @start_offset: offset of the first variant to retrieve + * @variants_count: (inout): Input = the maximum number of variants to return; + * Output = the actual number of variants returned + * @variants: (out) (array length=variants_count): array of variants returned + * + * Fetches the MathGlyphConstruction for the specified font, glyph index, and + * direction. The corresponding list of size variants is returned as a list of + * #hb_ot_math_glyph_variant_t structs. + * + * <note>The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered.</note> + * + * Return value: the total number of size variants available or zero + * + * Since: 1.3.3 + **/ +unsigned int +hb_ot_math_get_glyph_variants (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */) +{ + return font->face->table.MATH->get_variants().get_glyph_variants (glyph, direction, font, + start_offset, + variants_count, + variants); +} + +/** + * hb_ot_math_get_min_connector_overlap: + * @font: #hb_font_t to work upon + * @direction: direction of the stretching (horizontal or vertical) + * + * Fetches the MathVariants table for the specified font and returns the + * minimum overlap of connecting glyphs that are required to draw a glyph + * assembly in the specified direction. + * + * <note>The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered.</note> + * + * Return value: requested minimum connector overlap or zero + * + * Since: 1.3.3 + **/ +hb_position_t +hb_ot_math_get_min_connector_overlap (hb_font_t *font, + hb_direction_t direction) +{ + return font->face->table.MATH->get_variants().get_min_connector_overlap (direction, font); +} + +/** + * hb_ot_math_get_glyph_assembly: + * @font: #hb_font_t to work upon + * @glyph: The index of the glyph to stretch + * @direction: direction of the stretching (horizontal or vertical) + * @start_offset: offset of the first glyph part to retrieve + * @parts_count: (inout): Input = maximum number of glyph parts to return; + * Output = actual number of parts returned + * @parts: (out) (array length=parts_count): the glyph parts returned + * @italics_correction: (out): italics correction of the glyph assembly + * + * Fetches the GlyphAssembly for the specified font, glyph index, and direction. + * Returned are a list of #hb_ot_math_glyph_part_t glyph parts that can be + * used to draw the glyph and an italics-correction value (if one is defined + * in the font). + * + * <note>The @direction parameter is only used to select between horizontal + * or vertical directions for the construction. Even though all #hb_direction_t + * values are accepted, only the result of #HB_DIRECTION_IS_HORIZONTAL is + * considered.</note> + * + * Return value: the total number of parts in the glyph assembly + * + * Since: 1.3.3 + **/ +unsigned int +hb_ot_math_get_glyph_assembly (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts, /* OUT */ + hb_position_t *italics_correction /* OUT */) +{ + return font->face->table.MATH->get_variants().get_glyph_parts (glyph, + direction, + font, + start_offset, + parts_count, + parts, + italics_correction); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-math.h b/gfx/harfbuzz/src/hb-ot-math.h new file mode 100644 index 0000000000..1378a0639a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-math.h @@ -0,0 +1,333 @@ +/* + * Copyright © 2016 Igalia S.L. + * + * 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. + * + * Igalia Author(s): Frédéric Wang + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_MATH_H +#define HB_OT_MATH_H + +#include "hb.h" + +HB_BEGIN_DECLS + + +/* + * MATH + */ + +/** + * HB_OT_TAG_MATH: + * + * OpenType [Mathematical Typesetting Table](https://docs.microsoft.com/en-us/typography/opentype/spec/math). + * + * Since: 1.3.3 + */ +#define HB_OT_TAG_MATH HB_TAG('M','A','T','H') + +/** + * HB_OT_TAG_MATH_SCRIPT: + * + * OpenType script tag, `math`, for features specific to math shaping. + * + * <note>#HB_OT_TAG_MATH_SCRIPT is not a valid #hb_script_t and should only be + * used with functions that accept raw OpenType script tags, such as + * #hb_ot_layout_collect_features. In other cases, #HB_SCRIPT_MATH should be + * used instead.</note> + * + * Since: 3.4.0 + */ +#define HB_OT_TAG_MATH_SCRIPT HB_TAG('m','a','t','h') + +/* Types */ + +/** + * hb_ot_math_constant_t: + * @HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN: scriptPercentScaleDown + * @HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN: scriptScriptPercentScaleDown + * @HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT: delimitedSubFormulaMinHeight + * @HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT: displayOperatorMinHeight + * @HB_OT_MATH_CONSTANT_MATH_LEADING: mathLeading + * @HB_OT_MATH_CONSTANT_AXIS_HEIGHT: axisHeight + * @HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT: accentBaseHeight + * @HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT: flattenedAccentBaseHeight + * @HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN: subscriptShiftDown + * @HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX: subscriptTopMax + * @HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN: subscriptBaselineDropMin + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP: superscriptShiftUp + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED: superscriptShiftUpCramped + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN: superscriptBottomMin + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX: superscriptBaselineDropMax + * @HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN: subSuperscriptGapMin + * @HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT: superscriptBottomMaxWithSubscript + * @HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT: spaceAfterScript + * @HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN: upperLimitGapMin + * @HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN: upperLimitBaselineRiseMin + * @HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN: lowerLimitGapMin + * @HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN: lowerLimitBaselineDropMin + * @HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP: stackTopShiftUp + * @HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP: stackTopDisplayStyleShiftUp + * @HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN: stackBottomShiftDown + * @HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN: stackBottomDisplayStyleShiftDown + * @HB_OT_MATH_CONSTANT_STACK_GAP_MIN: stackGapMin + * @HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN: stackDisplayStyleGapMin + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP: stretchStackTopShiftUp + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN: stretchStackBottomShiftDown + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN: stretchStackGapAboveMin + * @HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN: stretchStackGapBelowMin + * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP: fractionNumeratorShiftUp + * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP: fractionNumeratorDisplayStyleShiftUp + * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN: fractionDenominatorShiftDown + * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN: fractionDenominatorDisplayStyleShiftDown + * @HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN: fractionNumeratorGapMin + * @HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN: fractionNumDisplayStyleGapMin + * @HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS: fractionRuleThickness + * @HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN: fractionDenominatorGapMin + * @HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN: fractionDenomDisplayStyleGapMin + * @HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP: skewedFractionHorizontalGap + * @HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP: skewedFractionVerticalGap + * @HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP: overbarVerticalGap + * @HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS: overbarRuleThickness + * @HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER: overbarExtraAscender + * @HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP: underbarVerticalGap + * @HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS: underbarRuleThickness + * @HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER: underbarExtraDescender + * @HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP: radicalVerticalGap + * @HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP: radicalDisplayStyleVerticalGap + * @HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS: radicalRuleThickness + * @HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER: radicalExtraAscender + * @HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE: radicalKernBeforeDegree + * @HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE: radicalKernAfterDegree + * @HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT: radicalDegreeBottomRaisePercent + * + * The 'MATH' table constants, refer to + * [OpenType documentation](https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathconstants-table) + * For more explanations. + * + * Since: 1.3.3 + */ +typedef enum { + HB_OT_MATH_CONSTANT_SCRIPT_PERCENT_SCALE_DOWN = 0, + HB_OT_MATH_CONSTANT_SCRIPT_SCRIPT_PERCENT_SCALE_DOWN = 1, + HB_OT_MATH_CONSTANT_DELIMITED_SUB_FORMULA_MIN_HEIGHT = 2, + HB_OT_MATH_CONSTANT_DISPLAY_OPERATOR_MIN_HEIGHT = 3, + HB_OT_MATH_CONSTANT_MATH_LEADING = 4, + HB_OT_MATH_CONSTANT_AXIS_HEIGHT = 5, + HB_OT_MATH_CONSTANT_ACCENT_BASE_HEIGHT = 6, + HB_OT_MATH_CONSTANT_FLATTENED_ACCENT_BASE_HEIGHT = 7, + HB_OT_MATH_CONSTANT_SUBSCRIPT_SHIFT_DOWN = 8, + HB_OT_MATH_CONSTANT_SUBSCRIPT_TOP_MAX = 9, + HB_OT_MATH_CONSTANT_SUBSCRIPT_BASELINE_DROP_MIN = 10, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP = 11, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_SHIFT_UP_CRAMPED = 12, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MIN = 13, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BASELINE_DROP_MAX = 14, + HB_OT_MATH_CONSTANT_SUB_SUPERSCRIPT_GAP_MIN = 15, + HB_OT_MATH_CONSTANT_SUPERSCRIPT_BOTTOM_MAX_WITH_SUBSCRIPT = 16, + HB_OT_MATH_CONSTANT_SPACE_AFTER_SCRIPT = 17, + HB_OT_MATH_CONSTANT_UPPER_LIMIT_GAP_MIN = 18, + HB_OT_MATH_CONSTANT_UPPER_LIMIT_BASELINE_RISE_MIN = 19, + HB_OT_MATH_CONSTANT_LOWER_LIMIT_GAP_MIN = 20, + HB_OT_MATH_CONSTANT_LOWER_LIMIT_BASELINE_DROP_MIN = 21, + HB_OT_MATH_CONSTANT_STACK_TOP_SHIFT_UP = 22, + HB_OT_MATH_CONSTANT_STACK_TOP_DISPLAY_STYLE_SHIFT_UP = 23, + HB_OT_MATH_CONSTANT_STACK_BOTTOM_SHIFT_DOWN = 24, + HB_OT_MATH_CONSTANT_STACK_BOTTOM_DISPLAY_STYLE_SHIFT_DOWN = 25, + HB_OT_MATH_CONSTANT_STACK_GAP_MIN = 26, + HB_OT_MATH_CONSTANT_STACK_DISPLAY_STYLE_GAP_MIN = 27, + HB_OT_MATH_CONSTANT_STRETCH_STACK_TOP_SHIFT_UP = 28, + HB_OT_MATH_CONSTANT_STRETCH_STACK_BOTTOM_SHIFT_DOWN = 29, + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_ABOVE_MIN = 30, + HB_OT_MATH_CONSTANT_STRETCH_STACK_GAP_BELOW_MIN = 31, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_SHIFT_UP = 32, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_DISPLAY_STYLE_SHIFT_UP = 33, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_SHIFT_DOWN = 34, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_DISPLAY_STYLE_SHIFT_DOWN = 35, + HB_OT_MATH_CONSTANT_FRACTION_NUMERATOR_GAP_MIN = 36, + HB_OT_MATH_CONSTANT_FRACTION_NUM_DISPLAY_STYLE_GAP_MIN = 37, + HB_OT_MATH_CONSTANT_FRACTION_RULE_THICKNESS = 38, + HB_OT_MATH_CONSTANT_FRACTION_DENOMINATOR_GAP_MIN = 39, + HB_OT_MATH_CONSTANT_FRACTION_DENOM_DISPLAY_STYLE_GAP_MIN = 40, + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_HORIZONTAL_GAP = 41, + HB_OT_MATH_CONSTANT_SKEWED_FRACTION_VERTICAL_GAP = 42, + HB_OT_MATH_CONSTANT_OVERBAR_VERTICAL_GAP = 43, + HB_OT_MATH_CONSTANT_OVERBAR_RULE_THICKNESS = 44, + HB_OT_MATH_CONSTANT_OVERBAR_EXTRA_ASCENDER = 45, + HB_OT_MATH_CONSTANT_UNDERBAR_VERTICAL_GAP = 46, + HB_OT_MATH_CONSTANT_UNDERBAR_RULE_THICKNESS = 47, + HB_OT_MATH_CONSTANT_UNDERBAR_EXTRA_DESCENDER = 48, + HB_OT_MATH_CONSTANT_RADICAL_VERTICAL_GAP = 49, + HB_OT_MATH_CONSTANT_RADICAL_DISPLAY_STYLE_VERTICAL_GAP = 50, + HB_OT_MATH_CONSTANT_RADICAL_RULE_THICKNESS = 51, + HB_OT_MATH_CONSTANT_RADICAL_EXTRA_ASCENDER = 52, + HB_OT_MATH_CONSTANT_RADICAL_KERN_BEFORE_DEGREE = 53, + HB_OT_MATH_CONSTANT_RADICAL_KERN_AFTER_DEGREE = 54, + HB_OT_MATH_CONSTANT_RADICAL_DEGREE_BOTTOM_RAISE_PERCENT = 55 +} hb_ot_math_constant_t; + +/** + * hb_ot_math_kern_t: + * @HB_OT_MATH_KERN_TOP_RIGHT: The top right corner of the glyph. + * @HB_OT_MATH_KERN_TOP_LEFT: The top left corner of the glyph. + * @HB_OT_MATH_KERN_BOTTOM_RIGHT: The bottom right corner of the glyph. + * @HB_OT_MATH_KERN_BOTTOM_LEFT: The bottom left corner of the glyph. + * + * The math kerning-table types defined for the four corners + * of a glyph. + * + * Since: 1.3.3 + */ +typedef enum { + HB_OT_MATH_KERN_TOP_RIGHT = 0, + HB_OT_MATH_KERN_TOP_LEFT = 1, + HB_OT_MATH_KERN_BOTTOM_RIGHT = 2, + HB_OT_MATH_KERN_BOTTOM_LEFT = 3 +} hb_ot_math_kern_t; + +/** + * hb_ot_math_kern_entry_t: + * @max_correction_height: The maximum height at which this entry should be used + * @kern_value: The kern value of the entry + * + * Data type to hold math kerning (cut-in) information for a glyph. + * + * Since: 3.4.0 + */ +typedef struct { + hb_position_t max_correction_height; + hb_position_t kern_value; +} hb_ot_math_kern_entry_t; + +/** + * hb_ot_math_glyph_variant_t: + * @glyph: The glyph index of the variant + * @advance: The advance width of the variant + * + * Data type to hold math-variant information for a glyph. + * + * Since: 1.3.3 + */ +typedef struct hb_ot_math_glyph_variant_t { + hb_codepoint_t glyph; + hb_position_t advance; +} hb_ot_math_glyph_variant_t; + +/** + * hb_ot_math_glyph_part_flags_t: + * @HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER: This is an extender glyph part that + * can be repeated to reach the desired length. + * + * Flags for math glyph parts. + * + * Since: 1.3.3 + */ +typedef enum { /*< flags >*/ + HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER = 0x00000001u /* Extender glyph */ +} hb_ot_math_glyph_part_flags_t; + +/** + * hb_ot_math_glyph_part_t: + * @glyph: The glyph index of the variant part + * @start_connector_length: The length of the connector on the starting side of the variant part + * @end_connector_length: The length of the connector on the ending side of the variant part + * @full_advance: The total advance of the part + * @flags: #hb_ot_math_glyph_part_flags_t flags for the part + * + * Data type to hold information for a "part" component of a math-variant glyph. + * Large variants for stretchable math glyphs (such as parentheses) can be constructed + * on the fly from parts. + * + * Since: 1.3.3 + */ +typedef struct hb_ot_math_glyph_part_t { + hb_codepoint_t glyph; + hb_position_t start_connector_length; + hb_position_t end_connector_length; + hb_position_t full_advance; + hb_ot_math_glyph_part_flags_t flags; +} hb_ot_math_glyph_part_t; + +/* Methods */ + +HB_EXTERN hb_bool_t +hb_ot_math_has_data (hb_face_t *face); + +HB_EXTERN hb_position_t +hb_ot_math_get_constant (hb_font_t *font, + hb_ot_math_constant_t constant); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_italics_correction (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_top_accent_attachment (hb_font_t *font, + hb_codepoint_t glyph); + +HB_EXTERN hb_bool_t +hb_ot_math_is_glyph_extended_shape (hb_face_t *face, + hb_codepoint_t glyph); + +HB_EXTERN hb_position_t +hb_ot_math_get_glyph_kerning (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + hb_position_t correction_height); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_kernings (hb_font_t *font, + hb_codepoint_t glyph, + hb_ot_math_kern_t kern, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT */ + hb_ot_math_kern_entry_t *kern_entries /* OUT */); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_variants (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *variants_count, /* IN/OUT */ + hb_ot_math_glyph_variant_t *variants /* OUT */); + +HB_EXTERN hb_position_t +hb_ot_math_get_min_connector_overlap (hb_font_t *font, + hb_direction_t direction); + +HB_EXTERN unsigned int +hb_ot_math_get_glyph_assembly (hb_font_t *font, + hb_codepoint_t glyph, + hb_direction_t direction, + unsigned int start_offset, + unsigned int *parts_count, /* IN/OUT */ + hb_ot_math_glyph_part_t *parts, /* OUT */ + hb_position_t *italics_correction /* OUT */); + + +HB_END_DECLS + +#endif /* HB_OT_MATH_H */ diff --git a/gfx/harfbuzz/src/hb-ot-maxp-table.hh b/gfx/harfbuzz/src/hb-ot-maxp-table.hh new file mode 100644 index 0000000000..8f000526b9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh @@ -0,0 +1,156 @@ +/* + * 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 HB_OT_MAXP_TABLE_HH +#define HB_OT_MAXP_TABLE_HH + +#include "hb-open-type.hh" + +namespace OT { + + +/* + * maxp -- Maximum Profile + * https://docs.microsoft.com/en-us/typography/opentype/spec/maxp + */ + +#define HB_OT_TAG_maxp HB_TAG('m','a','x','p') + +struct maxpV1Tail +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT16 maxPoints; /* Maximum points in a non-composite glyph. */ + HBUINT16 maxContours; /* Maximum contours in a non-composite glyph. */ + HBUINT16 maxCompositePoints; /* Maximum points in a composite glyph. */ + HBUINT16 maxCompositeContours; /* Maximum contours in a composite glyph. */ + HBUINT16 maxZones; /* 1 if instructions do not use the twilight zone (Z0), + * or 2 if instructions do use Z0; should be set to 2 in + * most cases. */ + HBUINT16 maxTwilightPoints; /* Maximum points used in Z0. */ + HBUINT16 maxStorage; /* Number of Storage Area locations. */ + HBUINT16 maxFunctionDefs; /* Number of FDEFs, equal to the highest function number + 1. */ + HBUINT16 maxInstructionDefs; /* Number of IDEFs. */ + HBUINT16 maxStackElements; /* Maximum stack depth. (This includes Font and CVT + * Programs, as well as the instructions for each glyph.) */ + HBUINT16 maxSizeOfInstructions; /* Maximum byte count for glyph instructions. */ + HBUINT16 maxComponentElements; /* Maximum number of components referenced at + * "top level" for any composite glyph. */ + HBUINT16 maxComponentDepth; /* Maximum levels of recursion; 1 for simple components. */ + public: + DEFINE_SIZE_STATIC (26); +}; + + +struct maxp +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_maxp; + + unsigned int get_num_glyphs () const { return numGlyphs; } + + void set_num_glyphs (unsigned int count) + { + numGlyphs = count; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + hb_barrier (); + if (version.major == 1) + { + const maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*this); + return_trace (v1.sanitize (c)); + } + return_trace (likely (version.major == 0 && version.minor == 0x5000u)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + maxp *maxp_prime = c->serializer->embed (this); + if (unlikely (!maxp_prime)) return_trace (false); + + maxp_prime->numGlyphs = hb_min (c->plan->num_output_glyphs (), 0xFFFFu); + if (maxp_prime->version.major == 1) + { + hb_barrier (); + const maxpV1Tail *src_v1 = &StructAfter<maxpV1Tail> (*this); + maxpV1Tail *dest_v1 = c->serializer->embed<maxpV1Tail> (src_v1); + if (unlikely (!dest_v1)) return_trace (false); + + if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) + drop_hint_fields (dest_v1); + + if (c->plan->normalized_coords) + instancing_update_fields (c->plan->head_maxp_info, dest_v1); + } + + return_trace (true); + } + + void instancing_update_fields (head_maxp_info_t& maxp_info, maxpV1Tail* dest_v1) const + { + dest_v1->maxPoints = maxp_info.maxPoints; + dest_v1->maxContours = maxp_info.maxContours; + dest_v1->maxCompositePoints = maxp_info.maxCompositePoints; + dest_v1->maxCompositeContours = maxp_info.maxCompositeContours; + dest_v1->maxComponentElements = maxp_info.maxComponentElements; + dest_v1->maxComponentDepth = maxp_info.maxComponentDepth; + } + + static void drop_hint_fields (maxpV1Tail* dest_v1) + { + dest_v1->maxZones = 1; + dest_v1->maxTwilightPoints = 0; + dest_v1->maxStorage = 0; + dest_v1->maxFunctionDefs = 0; + dest_v1->maxInstructionDefs = 0; + dest_v1->maxStackElements = 0; + dest_v1->maxSizeOfInstructions = 0; + } + + protected: + FixedVersion<>version;/* Version of the maxp table (0.5 or 1.0), + * 0x00005000u or 0x00010000u. */ + HBUINT16 numGlyphs; + /* The number of glyphs in the font. */ +/*maxpV1Tail v1Tail[HB_VAR_ARRAY]; */ + public: + DEFINE_SIZE_STATIC (6); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_MAXP_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-meta-table.hh b/gfx/harfbuzz/src/hb-ot-meta-table.hh new file mode 100644 index 0000000000..658db584c7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-meta-table.hh @@ -0,0 +1,131 @@ +/* + * 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_OT_META_TABLE_HH +#define HB_OT_META_TABLE_HH + +#include "hb-open-type.hh" + +/* + * meta -- Metadata Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6meta.html + */ +#define HB_OT_TAG_meta HB_TAG ('m','e','t','a') + + +namespace OT { + + +struct DataMap +{ + int cmp (hb_tag_t a) const { return tag.cmp (a); } + + hb_tag_t get_tag () const { return tag; } + + hb_blob_t *reference_entry (hb_blob_t *meta_blob) const + { return hb_blob_create_sub_blob (meta_blob, dataZ, dataLength); } + + bool sanitize (hb_sanitize_context_t *c, const void *base) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + hb_barrier () && + dataZ.sanitize (c, base, dataLength))); + } + + protected: + Tag tag; /* A tag indicating the type of metadata. */ + NNOffset32To<UnsizedArrayOf<HBUINT8>> + dataZ; /* Offset in bytes from the beginning of the + * metadata table to the data for this tag. */ + HBUINT32 dataLength; /* Length of the data. The data is not required to + * be padded to any byte boundary. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct meta +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_meta; + + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { table = hb_sanitize_context_t ().reference_table<meta> (face); } + ~accelerator_t () { table.destroy (); } + + hb_blob_t *reference_entry (hb_tag_t tag) const + { return table->dataMaps.lsearch (tag).reference_entry (table.get_blob ()); } + + unsigned int get_entries (unsigned int start_offset, + unsigned int *count, + hb_ot_meta_tag_t *entries) const + { + if (count) + { + + table->dataMaps.as_array ().sub_array (start_offset, count) + | hb_map (&DataMap::get_tag) + | hb_map ([](hb_tag_t tag) { return (hb_ot_meta_tag_t) tag; }) + | hb_sink (hb_array (entries, *count)) + ; + } + return table->dataMaps.len; + } + + private: + hb_blob_ptr_t<meta> table; + }; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + hb_barrier () && + version == 1 && + dataMaps.sanitize (c, this))); + } + + protected: + HBUINT32 version; /* Version number of the metadata table — set to 1. */ + HBUINT32 flags; /* Flags — currently unused; set to 0. */ + HBUINT32 dataOffset; + /* Per Apple specification: + * Offset from the beginning of the table to the data. + * Per OT specification: + * Reserved. Not used; should be set to 0. */ + Array32Of<DataMap> + dataMaps;/* Array of data map records. */ + public: + DEFINE_SIZE_ARRAY (16, dataMaps); +}; + +struct meta_accelerator_t : meta::accelerator_t { + meta_accelerator_t (hb_face_t *face) : meta::accelerator_t (face) {} +}; + +} /* namespace OT */ + + +#endif /* HB_OT_META_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-meta.cc b/gfx/harfbuzz/src/hb-ot-meta.cc new file mode 100644 index 0000000000..35c8eb523f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-meta.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" + +#ifndef HB_NO_META + +#include "hb-ot-meta-table.hh" + +/** + * SECTION:hb-ot-meta + * @title: hb-ot-meta + * @short_description: OpenType Metadata + * @include: hb-ot.h + * + * Functions for fetching metadata from fonts. + **/ + +/** + * hb_ot_meta_get_entry_tags: + * @face: a face object + * @start_offset: iteration's start offset + * @entries_count:(inout) (optional): buffer size as input, filled size as output + * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer + * + * Fetches all available feature types. + * + * Return value: Number of all available feature types. + * + * Since: 2.6.0 + **/ +unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */) +{ + return face->table.meta->get_entries (start_offset, entries_count, entries); +} + +/** + * hb_ot_meta_reference_entry: + * @face: a #hb_face_t object. + * @meta_tag: tag of metadata you like to have. + * + * It fetches metadata entry of a given tag from a font. + * + * Returns: (transfer full): A blob containing the blob. + * + * Since: 2.6.0 + **/ +hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag) +{ + return face->table.meta->reference_entry (meta_tag); +} + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-meta.h b/gfx/harfbuzz/src/hb-ot-meta.h new file mode 100644 index 0000000000..7748eb4958 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-meta.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_META_H +#define HB_OT_META_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_ot_meta_tag_t: + * @HB_OT_META_TAG_DESIGN_LANGUAGES: Design languages. Text, using only + * Basic Latin (ASCII) characters. Indicates languages and/or scripts + * for the user audiences that the font was primarily designed for. + * @HB_OT_META_TAG_SUPPORTED_LANGUAGES: Supported languages. Text, using + * only Basic Latin (ASCII) characters. Indicates languages and/or scripts + * that the font is declared to be capable of supporting. + * + * Known metadata tags from https://docs.microsoft.com/en-us/typography/opentype/spec/meta + * + * Since: 2.6.0 + **/ +typedef enum { +/* + HB_OT_META_TAG_APPL = HB_TAG ('a','p','p','l'), + HB_OT_META_TAG_BILD = HB_TAG ('b','i','l','d'), +*/ + HB_OT_META_TAG_DESIGN_LANGUAGES = HB_TAG ('d','l','n','g'), + HB_OT_META_TAG_SUPPORTED_LANGUAGES = HB_TAG ('s','l','n','g'), + + /*< private >*/ + _HB_OT_META_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_meta_tag_t; + +HB_EXTERN unsigned int +hb_ot_meta_get_entry_tags (hb_face_t *face, + unsigned int start_offset, + unsigned int *entries_count, /* IN/OUT. May be NULL. */ + hb_ot_meta_tag_t *entries /* OUT. May be NULL. */); + +HB_EXTERN hb_blob_t * +hb_ot_meta_reference_entry (hb_face_t *face, hb_ot_meta_tag_t meta_tag); + +HB_END_DECLS + +#endif /* HB_OT_META_H */ diff --git a/gfx/harfbuzz/src/hb-ot-metrics.cc b/gfx/harfbuzz/src/hb-ot-metrics.cc new file mode 100644 index 0000000000..e314d946b6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-metrics.cc @@ -0,0 +1,436 @@ +/* + * 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. + */ + +#include "hb.hh" + +#include "hb-ot-var-mvar-table.hh" +#include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. +#include "hb-ot-os2-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-metrics.hh" +#include "hb-ot-face.hh" + + +/** + * SECTION:hb-ot-metrics + * @title: hb-ot-metrics + * @short_description: OpenType Metrics + * @include: hb-ot.h + * + * Functions for fetching metrics from fonts. + **/ + +static float +_fix_ascender_descender (float value, hb_ot_metrics_tag_t metrics_tag) +{ + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_ASCENDER) + return fabs ((double) value); + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER || + metrics_tag == HB_OT_METRICS_TAG_VERTICAL_DESCENDER) + return -fabs ((double) value); + return value; +} + +/* The common part of _get_position logic needed on hb-ot-font and here + to be able to have slim builds without the not always needed parts */ +bool +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { +#ifndef HB_NO_VAR +#define GET_VAR face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords) +#else +#define GET_VAR .0f +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_x (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag)))), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_y (_fix_ascender_descender ( \ + face->table.TABLE->ATTR + GET_VAR, metrics_tag)))), true)) + + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoAscender)) || + GET_METRIC_Y (hhea, ascender); + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoDescender)) || + GET_METRIC_Y (hhea, descender); + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + return (face->table.OS2->use_typo_metrics () && GET_METRIC_Y (OS2, sTypoLineGap)) || + GET_METRIC_Y (hhea, lineGap); + +#ifndef HB_NO_VERTICAL + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: return GET_METRIC_X (vhea, ascender); + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: return GET_METRIC_X (vhea, descender); + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return GET_METRIC_X (vhea, lineGap); +#endif + +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: assert (0); return false; + } +} + +#ifndef HB_NO_METRICS + +#if 0 +static bool +_get_gasp (hb_face_t *face, float *result, hb_ot_metrics_tag_t metrics_tag) +{ + const OT::GaspRange& range = face->table.gasp->get_gasp_range (metrics_tag - HB_TAG ('g','s','p','0')); + if (&range == &Null (OT::GaspRange)) return false; + if (result) *result = range.rangeMaxPPEM + font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); + return true; +} +#endif + +/* Private tags for https://github.com/harfbuzz/harfbuzz/issues/1866 */ +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2 HB_TAG ('O','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA HB_TAG ('H','a','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2 HB_TAG ('O','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA HB_TAG ('H','d','s','c') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2 HB_TAG ('O','l','g','p') +#define _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA HB_TAG ('H','l','g','p') + +/** + * hb_ot_metrics_get_position: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * @position: (out) (optional): result of metrics value from the font. + * + * Fetches metrics value corresponding to @metrics_tag from @font. + * + * Returns: Whether found the requested metrics in the font. + * Since: 2.6.0 + **/ +hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */) +{ + hb_face_t *face = font->face; + switch ((unsigned) metrics_tag) + { + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: return _hb_ot_metrics_get_position_common (font, metrics_tag, position); +#ifndef HB_NO_VAR +#define GET_VAR hb_ot_metrics_get_variation (font, metrics_tag) +#else +#define GET_VAR 0 +#endif +#define GET_METRIC_X(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR))), true)) +#define GET_METRIC_Y(TABLE, ATTR) \ + (face->table.TABLE->has_data () && \ + ((void) (position && (*position = font->em_scalef_y (face->table.TABLE->ATTR + GET_VAR))), true)) + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: return GET_METRIC_Y (OS2, usWinAscent); + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: return GET_METRIC_Y (OS2, usWinDescent); + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: + { + unsigned mult = 1u; + + if (font->slant) + { + unsigned rise = face->table.hhea->caretSlopeRise; + unsigned upem = face->get_upem (); + mult = (rise && rise < upem) ? hb_min (upem / rise, 256u) : 1u; + } + + if (metrics_tag == HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE) + { + bool ret = GET_METRIC_Y (hhea, caretSlopeRise); + + if (position) + *position *= mult; + + return ret; + } + else + { + hb_position_t rise = 0; + + if (font->slant && position && GET_METRIC_Y (hhea, caretSlopeRise)) + rise = *position; + + bool ret = GET_METRIC_X (hhea, caretSlopeRun); + + if (position) + { + *position *= mult; + + if (font->slant) + *position += roundf (mult * font->slant_xy * rise); + } + + return ret; + } + } + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: return GET_METRIC_X (hhea, caretOffset); + +#ifndef HB_NO_VERTICAL + case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: return GET_METRIC_X (vhea, caretSlopeRise); + case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: return GET_METRIC_Y (vhea, caretSlopeRun); + case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: return GET_METRIC_Y (vhea, caretOffset); +#endif + case HB_OT_METRICS_TAG_X_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sxHeight); + case HB_OT_METRICS_TAG_CAP_HEIGHT: return GET_METRIC_Y (OS2->v2 (), sCapHeight); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySubscriptXSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySubscriptYSize); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySubscriptXOffset); + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySubscriptYOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: return GET_METRIC_X (OS2, ySuperscriptXSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: return GET_METRIC_Y (OS2, ySuperscriptYSize); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: return GET_METRIC_X (OS2, ySuperscriptXOffset); + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: return GET_METRIC_Y (OS2, ySuperscriptYOffset); + case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: return GET_METRIC_Y (OS2, yStrikeoutSize); + case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: return GET_METRIC_Y (OS2, yStrikeoutPosition); + case HB_OT_METRICS_TAG_UNDERLINE_SIZE: return GET_METRIC_Y (post->table, underlineThickness); + case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: return GET_METRIC_Y (post->table, underlinePosition); + + /* Private tags */ + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_OS2: return GET_METRIC_Y (OS2, sTypoAscender); + case _HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER_HHEA: return GET_METRIC_Y (hhea, ascender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_OS2: return GET_METRIC_Y (OS2, sTypoDescender); + case _HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER_HHEA: return GET_METRIC_Y (hhea, descender); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_OS2: return GET_METRIC_Y (OS2, sTypoLineGap); + case _HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP_HHEA: return GET_METRIC_Y (hhea, lineGap); +#undef GET_METRIC_Y +#undef GET_METRIC_X +#undef GET_VAR + default: return false; + } +} + +/** + * hb_ot_metrics_get_position_with_fallback: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * @position: (out) (optional): result of metrics value from the font. + * + * Fetches metrics value corresponding to @metrics_tag from @font, + * and synthesizes a value if it the value is missing in the font. + * + * Since: 4.0.0 + **/ +void +hb_ot_metrics_get_position_with_fallback (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT */) +{ + hb_font_extents_t font_extents; + hb_codepoint_t glyph; + hb_glyph_extents_t extents; + + if (hb_ot_metrics_get_position (font, metrics_tag, position)) + { + if ((metrics_tag != HB_OT_METRICS_TAG_STRIKEOUT_SIZE && + metrics_tag != HB_OT_METRICS_TAG_UNDERLINE_SIZE) || + *position != 0) + return; + } + + switch (metrics_tag) + { + case HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + *position = font_extents.ascender; + break; + + case HB_OT_METRICS_TAG_VERTICAL_ASCENDER: + hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); + *position = font_extents.ascender; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: + case HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + *position = font_extents.descender; + break; + + case HB_OT_METRICS_TAG_VERTICAL_DESCENDER: + hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); + *position = font_extents.ascender; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + *position = font_extents.line_gap; + break; + + case HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: + hb_font_get_extents_for_direction (font, HB_DIRECTION_TTB, &font_extents); + *position = font_extents.line_gap; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: + case HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: + *position = 1; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: + case HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: + *position = 0; + break; + + case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: + case HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: + *position = 0; + break; + + case HB_OT_METRICS_TAG_X_HEIGHT: + if (hb_font_get_nominal_glyph (font, 'x', &glyph) && + hb_font_get_glyph_extents (font, glyph, &extents)) + *position = extents.y_bearing; + else + *position = font->y_scale / 2; + break; + + case HB_OT_METRICS_TAG_CAP_HEIGHT: + if (hb_font_get_nominal_glyph (font, 'O', &glyph) && + hb_font_get_glyph_extents (font, glyph, &extents)) + *position = extents.height + 2 * extents.y_bearing; + else + *position = font->y_scale * 2 / 3; + break; + + case HB_OT_METRICS_TAG_STRIKEOUT_SIZE: + case HB_OT_METRICS_TAG_UNDERLINE_SIZE: + *position = font->y_scale / 18; + break; + + case HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: + { + hb_position_t ascender; + hb_ot_metrics_get_position_with_fallback (font, + HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, + &ascender); + *position = ascender / 2; + } + break; + + case HB_OT_METRICS_TAG_UNDERLINE_OFFSET: + *position = - font->y_scale / 18; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: + *position = font->x_scale * 10 / 12; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: + *position = font->y_scale * 10 / 12; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: + *position = 0; + break; + + case HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: + case HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: + *position = font->y_scale / 5; + break; + + case _HB_OT_METRICS_TAG_MAX_VALUE: + default: + *position = 0; + break; + } +} + +#ifndef HB_NO_VAR +/** + * hb_ot_metrics_get_variation: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * + * Fetches metrics value corresponding to @metrics_tag from @font with the + * current font variation settings applied. + * + * Returns: The requested metric value. + * + * Since: 2.6.0 + **/ +float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->face->table.MVAR->get_var (metrics_tag, font->coords, font->num_coords); +} + +/** + * hb_ot_metrics_get_x_variation: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * + * Fetches horizontal metrics value corresponding to @metrics_tag from @font + * with the current font variation settings applied. + * + * Returns: The requested metric value. + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_x (hb_ot_metrics_get_variation (font, metrics_tag)); +} + +/** + * hb_ot_metrics_get_y_variation: + * @font: an #hb_font_t object. + * @metrics_tag: tag of metrics value you like to fetch. + * + * Fetches vertical metrics value corresponding to @metrics_tag from @font with + * the current font variation settings applied. + * + * Returns: The requested metric value. + * + * Since: 2.6.0 + **/ +hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag) +{ + return font->em_scalef_y (hb_ot_metrics_get_variation (font, metrics_tag)); +} +#endif + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-metrics.h b/gfx/harfbuzz/src/hb-ot-metrics.h new file mode 100644 index 0000000000..30de500088 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-metrics.h @@ -0,0 +1,129 @@ +/* + * 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_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_METRICS_H +#define HB_OT_METRICS_H + +#include "hb.h" +#include "hb-ot-name.h" + +HB_BEGIN_DECLS + + +/** + * hb_ot_metrics_tag_t: + * @HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER: horizontal ascender. + * @HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER: horizontal descender. + * @HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP: horizontal line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT: horizontal clipping ascent. + * @HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT: horizontal clipping descent. + * @HB_OT_METRICS_TAG_VERTICAL_ASCENDER: vertical ascender. + * @HB_OT_METRICS_TAG_VERTICAL_DESCENDER: vertical descender. + * @HB_OT_METRICS_TAG_VERTICAL_LINE_GAP: vertical line gap. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE: horizontal caret rise. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: horizontal caret run. + * @HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: horizontal caret offset. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RISE: vertical caret rise. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_RUN: vertical caret run. + * @HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET: vertical caret offset. + * @HB_OT_METRICS_TAG_X_HEIGHT: x height. + * @HB_OT_METRICS_TAG_CAP_HEIGHT: cap height. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE: subscript em x size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE: subscript em y size. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET: subscript em x offset. + * @HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET: subscript em y offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE: superscript em x size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE: superscript em y size. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET: superscript em x offset. + * @HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET: superscript em y offset. + * @HB_OT_METRICS_TAG_STRIKEOUT_SIZE: strikeout size. + * @HB_OT_METRICS_TAG_STRIKEOUT_OFFSET: strikeout offset. + * @HB_OT_METRICS_TAG_UNDERLINE_SIZE: underline size. + * @HB_OT_METRICS_TAG_UNDERLINE_OFFSET: underline offset. + * + * Metric tags corresponding to [MVAR Value + * Tags](https://docs.microsoft.com/en-us/typography/opentype/spec/mvar#value-tags) + * + * Since: 2.6.0 + **/ +typedef enum { + HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER = HB_TAG ('h','a','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER = HB_TAG ('h','d','s','c'), + HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP = HB_TAG ('h','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT = HB_TAG ('h','c','l','a'), + HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT = HB_TAG ('h','c','l','d'), + HB_OT_METRICS_TAG_VERTICAL_ASCENDER = HB_TAG ('v','a','s','c'), + HB_OT_METRICS_TAG_VERTICAL_DESCENDER = HB_TAG ('v','d','s','c'), + HB_OT_METRICS_TAG_VERTICAL_LINE_GAP = HB_TAG ('v','l','g','p'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RISE = HB_TAG ('h','c','r','s'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN = HB_TAG ('h','c','r','n'), + HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET = HB_TAG ('h','c','o','f'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RISE = HB_TAG ('v','c','r','s'), + HB_OT_METRICS_TAG_VERTICAL_CARET_RUN = HB_TAG ('v','c','r','n'), + HB_OT_METRICS_TAG_VERTICAL_CARET_OFFSET = HB_TAG ('v','c','o','f'), + HB_OT_METRICS_TAG_X_HEIGHT = HB_TAG ('x','h','g','t'), + HB_OT_METRICS_TAG_CAP_HEIGHT = HB_TAG ('c','p','h','t'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE = HB_TAG ('s','b','x','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE = HB_TAG ('s','b','y','s'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET = HB_TAG ('s','b','x','o'), + HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET = HB_TAG ('s','b','y','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE = HB_TAG ('s','p','x','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE = HB_TAG ('s','p','y','s'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET = HB_TAG ('s','p','x','o'), + HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET = HB_TAG ('s','p','y','o'), + HB_OT_METRICS_TAG_STRIKEOUT_SIZE = HB_TAG ('s','t','r','s'), + HB_OT_METRICS_TAG_STRIKEOUT_OFFSET = HB_TAG ('s','t','r','o'), + HB_OT_METRICS_TAG_UNDERLINE_SIZE = HB_TAG ('u','n','d','s'), + HB_OT_METRICS_TAG_UNDERLINE_OFFSET = HB_TAG ('u','n','d','o'), + + /*< private >*/ + _HB_OT_METRICS_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_metrics_tag_t; + +HB_EXTERN hb_bool_t +hb_ot_metrics_get_position (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); + +HB_EXTERN void +hb_ot_metrics_get_position_with_fallback (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT */); + +HB_EXTERN float +hb_ot_metrics_get_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_x_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_EXTERN hb_position_t +hb_ot_metrics_get_y_variation (hb_font_t *font, hb_ot_metrics_tag_t metrics_tag); + +HB_END_DECLS + +#endif /* HB_OT_METRICS_H */ diff --git a/gfx/harfbuzz/src/hb-ot-metrics.hh b/gfx/harfbuzz/src/hb-ot-metrics.hh new file mode 100644 index 0000000000..19a5e9ed41 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-metrics.hh @@ -0,0 +1,35 @@ +/* + * 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_METRICS_HH +#define HB_OT_METRICS_HH + +#include "hb.hh" + +HB_INTERNAL bool +_hb_ot_metrics_get_position_common (hb_font_t *font, + hb_ot_metrics_tag_t metrics_tag, + hb_position_t *position /* OUT. May be NULL. */); + +#endif /* HB_OT_METRICS_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-name-language-static.hh b/gfx/harfbuzz/src/hb-ot-name-language-static.hh new file mode 100644 index 0000000000..0e0f2d632a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-name-language-static.hh @@ -0,0 +1,456 @@ +/* + * 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_OT_NAME_LANGUAGE_STATIC_HH +#define HB_OT_NAME_LANGUAGE_STATIC_HH + +#include "hb-ot-name-language.hh" + +/* Following two tables were generated by joining FreeType, FontConfig, + * and OpenType specification language lists, then filled in missing + * entries using: + * https://docs.microsoft.com/en-us/windows/desktop/intl/language-identifier-constants-and-strings + */ + +struct hb_ot_language_map_t +{ + int cmp (unsigned int key) const + { return key < code ? -1 : key > code ? +1 : 0; } + + uint16_t code; + char lang[6]; +}; + +static const hb_ot_language_map_t +_hb_ms_language_map[] = +{ + {0x0001, "ar"}, /* ??? */ + {0x0004, "zh"}, /* ??? */ + {0x0009, "en"}, /* ??? */ + {0x0401, "ar"}, /* Arabic (Saudi Arabia) */ + {0x0402, "bg"}, /* Bulgarian (Bulgaria) */ + {0x0403, "ca"}, /* Catalan (Catalan) */ + {0x0404, "zh-tw"}, /* Chinese (Taiwan) */ + {0x0405, "cs"}, /* Czech (Czech Republic) */ + {0x0406, "da"}, /* Danish (Denmark) */ + {0x0407, "de"}, /* German (Germany) */ + {0x0408, "el"}, /* Greek (Greece) */ + {0x0409, "en"}, /* English (United States) */ + {0x040A, "es"}, /* Spanish (Traditional Sort) (Spain) */ + {0x040B, "fi"}, /* Finnish (Finland) */ + {0x040C, "fr"}, /* French (France) */ + {0x040D, "he"}, /* Hebrew (Israel) */ + {0x040E, "hu"}, /* Hungarian (Hungary) */ + {0x040F, "is"}, /* Icelandic (Iceland) */ + {0x0410, "it"}, /* Italian (Italy) */ + {0x0411, "ja"}, /* Japanese (Japan) */ + {0x0412, "ko"}, /* Korean (Korea) */ + {0x0413, "nl"}, /* Dutch (Netherlands) */ + {0x0414, "no"}, /* Norwegian (Bokmal) (Norway) */ + {0x0415, "pl"}, /* Polish (Poland) */ + {0x0416, "pt"}, /* Portuguese (Brazil) */ + {0x0417, "rm"}, /* Romansh (Switzerland) */ + {0x0418, "ro"}, /* Romanian (Romania) */ + {0x0419, "ru"}, /* Russian (Russia) */ + {0x041A, "hr"}, /* Croatian (Croatia) */ + {0x041B, "sk"}, /* Slovak (Slovakia) */ + {0x041C, "sq"}, /* Albanian (Albania) */ + {0x041D, "sv"}, /* Swedish (Sweden) */ + {0x041E, "th"}, /* Thai (Thailand) */ + {0x041F, "tr"}, /* Turkish (Turkey) */ + {0x0420, "ur"}, /* Urdu (Islamic Republic of Pakistan) */ + {0x0421, "id"}, /* Indonesian (Indonesia) */ + {0x0422, "uk"}, /* Ukrainian (Ukraine) */ + {0x0423, "be"}, /* Belarusian (Belarus) */ + {0x0424, "sl"}, /* Slovenian (Slovenia) */ + {0x0425, "et"}, /* Estonian (Estonia) */ + {0x0426, "lv"}, /* Latvian (Latvia) */ + {0x0427, "lt"}, /* Lithuanian (Lithuania) */ + {0x0428, "tg"}, /* Tajik (Cyrillic) (Tajikistan) */ + {0x0429, "fa"}, /* Persian (Iran) */ + {0x042A, "vi"}, /* Vietnamese (Vietnam) */ + {0x042B, "hy"}, /* Armenian (Armenia) */ + {0x042C, "az"}, /* Azeri (Latin) (Azerbaijan) */ + {0x042D, "eu"}, /* Basque (Basque) */ + {0x042E, "hsb"}, /* Upper Sorbian (Germany) */ + {0x042F, "mk"}, /* Macedonian (FYROM) (Former Yugoslav Republic of Macedonia) */ + {0x0430, "st"}, /* ??? */ + {0x0431, "ts"}, /* ??? */ + {0x0432, "tn"}, /* Setswana (South Africa) */ + {0x0433, "ven"}, /* ??? */ + {0x0434, "xh"}, /* isiXhosa (South Africa) */ + {0x0435, "zu"}, /* isiZulu (South Africa) */ + {0x0436, "af"}, /* Afrikaans (South Africa) */ + {0x0437, "ka"}, /* Georgian (Georgia) */ + {0x0438, "fo"}, /* Faroese (Faroe Islands) */ + {0x0439, "hi"}, /* Hindi (India) */ + {0x043A, "mt"}, /* Maltese (Malta) */ + {0x043B, "se"}, /* Sami (Northern) (Norway) */ + {0x043C, "ga"}, /* ??? */ + {0x043D, "yi"}, /* ??? */ + {0x043E, "ms"}, /* Malay (Malaysia) */ + {0x043F, "kk"}, /* Kazakh (Kazakhstan) */ + {0x0440, "ky"}, /* Kyrgyz (Kyrgyzstan) */ + {0x0441, "sw"}, /* Kiswahili (Kenya) */ + {0x0442, "tk"}, /* Turkmen (Turkmenistan) */ + {0x0443, "uz"}, /* Uzbek (Latin) (Uzbekistan) */ + {0x0444, "tt"}, /* Tatar (Russia) */ + {0x0445, "bn"}, /* Bengali (India) */ + {0x0446, "pa"}, /* Punjabi (India) */ + {0x0447, "gu"}, /* Gujarati (India) */ + {0x0448, "or"}, /* Odia (formerly Oriya) (India) */ + {0x0449, "ta"}, /* Tamil (India) */ + {0x044A, "te"}, /* Telugu (India) */ + {0x044B, "kn"}, /* Kannada (India) */ + {0x044C, "ml"}, /* Malayalam (India) */ + {0x044D, "as"}, /* Assamese (India) */ + {0x044E, "mr"}, /* Marathi (India) */ + {0x044F, "sa"}, /* Sanskrit (India) */ + {0x0450, "mn"}, /* Mongolian (Cyrillic) (Mongolia) */ + {0x0451, "bo"}, /* Tibetan (PRC) */ + {0x0452, "cy"}, /* Welsh (United Kingdom) */ + {0x0453, "km"}, /* Khmer (Cambodia) */ + {0x0454, "lo"}, /* Lao (Lao P.D.R.) */ + {0x0455, "my"}, /* ??? */ + {0x0456, "gl"}, /* Galician (Galician) */ + {0x0457, "kok"}, /* Konkani (India) */ + {0x0458, "mni"}, /* ??? */ + {0x0459, "sd"}, /* ??? */ + {0x045A, "syr"}, /* Syriac (Syria) */ + {0x045B, "si"}, /* Sinhala (Sri Lanka) */ + {0x045C, "chr"}, /* ??? */ + {0x045D, "iu"}, /* Inuktitut (Canada) */ + {0x045E, "am"}, /* Amharic (Ethiopia) */ + {0x0460, "ks"}, /* ??? */ + {0x0461, "ne"}, /* Nepali (Nepal) */ + {0x0462, "fy"}, /* Frisian (Netherlands) */ + {0x0463, "ps"}, /* Pashto (Afghanistan) */ + {0x0464, "phi"}, /* Filipino (Philippines) */ + {0x0465, "div"}, /* Divehi (Maldives) */ + {0x0468, "ha"}, /* Hausa (Latin) (Nigeria) */ + {0x046A, "yo"}, /* Yoruba (Nigeria) */ + {0x046B, "quz"}, /* Quechua (Bolivia) */ + {0x046C, "nso"}, /* Sesotho sa Leboa (South Africa) */ + {0x046D, "ba"}, /* Bashkir (Russia) */ + {0x046E, "lb"}, /* Luxembourgish (Luxembourg) */ + {0x046F, "kl"}, /* Greenlandic (Greenland) */ + {0x0470, "ibo"}, /* Igbo (Nigeria) */ + {0x0471, "kau"}, /* ??? */ + {0x0472, "om"}, /* ??? */ + {0x0473, "ti"}, /* ??? */ + {0x0474, "gn"}, /* ??? */ + {0x0475, "haw"}, /* ??? */ + {0x0476, "la"}, /* ??? */ + {0x0477, "so"}, /* ??? */ + {0x0478, "ii"}, /* Yi (PRC) */ + {0x0479, "pap"}, /* ??? */ + {0x047A, "arn"}, /* Mapudungun (Chile) */ + {0x047C, "moh"}, /* Mohawk (Mohawk) */ + {0x047E, "br"}, /* Breton (France) */ + {0x0480, "ug"}, /* Uighur (PRC) */ + {0x0481, "mi"}, /* Maori (New Zealand) */ + {0x0482, "oc"}, /* Occitan (France) */ + {0x0483, "co"}, /* Corsican (France) */ + {0x0484, "gsw"}, /* Alsatian (France) */ + {0x0485, "sah"}, /* Yakut (Russia) */ + {0x0486, "qut"}, /* K'iche (Guatemala) */ + {0x0487, "rw"}, /* Kinyarwanda (Rwanda) */ + {0x0488, "wo"}, /* Wolof (Senegal) */ + {0x048C, "fa"}, /* Dari (Afghanistan) */ + {0x0801, "ar"}, /* Arabic (Iraq) */ + {0x0804, "zh-cn"}, /* Chinese (People’s Republic of China) */ + {0x0807, "de"}, /* German (Switzerland) */ + {0x0809, "en"}, /* English (United Kingdom) */ + {0x080A, "es"}, /* Spanish (Mexico) */ + {0x080C, "fr"}, /* French (Belgium) */ + {0x0810, "it"}, /* Italian (Switzerland) */ + {0x0812, "ko"}, /* ??? */ + {0x0813, "nl"}, /* Dutch (Belgium) */ + {0x0814, "nn"}, /* Norwegian (Nynorsk) (Norway) */ + {0x0816, "pt"}, /* Portuguese (Portugal) */ + {0x0818, "mo"}, /* ??? */ + {0x0819, "ru"}, /* ??? */ + {0x081A, "sr"}, /* Serbian (Latin) (Serbia) */ + {0x081D, "sv"}, /* Sweden (Finland) */ + {0x0820, "ur"}, /* ??? */ + {0x0827, "lt"}, /* ??? */ + {0x082C, "az"}, /* Azeri (Cyrillic) (Azerbaijan) */ + {0x082E, "dsb"}, /* Lower Sorbian (Germany) */ +//{0x083B, ""}, /* Sami (Northern) (Sweden) */ + {0x083C, "gd"}, /* Irish (Ireland) */ + {0x083E, "ms"}, /* Malay (Brunei Darussalam) */ + {0x0843, "uz"}, /* Uzbek (Cyrillic) (Uzbekistan) */ + {0x0845, "bn"}, /* Bengali (Bangladesh) */ + {0x0846, "ar"}, /* ??? */ + {0x0850, "mn"}, /* Mongolian (Traditional) (People’s Republic of China) */ + {0x0851, "dz"}, /* ??? */ + {0x085D, "iu"}, /* Inuktitut (Latin) (Canada) */ + {0x085F, "tzm"}, /* Tamazight (Latin) (Algeria) */ + {0x0861, "ne"}, /* ??? */ +//{0x086B, ""}, /* Quechua (Ecuador) */ + {0x0873, "ti"}, /* ??? */ + {0x0C01, "ar"}, /* Arabic (Egypt) */ + {0x0C04, "zh-hk"}, /* Chinese (Hong Kong S.A.R.) */ + {0x0C07, "de"}, /* German (Austria) */ + {0x0C09, "en"}, /* English (Australia) */ + {0x0C0A, "es"}, /* Spanish (Modern Sort) (Spain) */ + {0x0C0C, "fr"}, /* French (Canada) */ + {0x0C1A, "sr"}, /* Serbian (Cyrillic) (Serbia) */ + {0x0C3B, "se"}, /* Sami (Northern) (Finland) */ +//{0x0C6B, ""}, /* Quechua (Peru) */ + {0x1001, "ar"}, /* Arabic (Libya) */ + {0x1004, "zh-sg"}, /* Chinese (Singapore) */ + {0x1007, "de"}, /* German (Luxembourg) */ + {0x1009, "en"}, /* English (Canada) */ + {0x100A, "es"}, /* Spanish (Guatemala) */ + {0x100C, "fr"}, /* French (Switzerland) */ + {0x101A, "hr"}, /* Croatian (Latin) (Bosnia and Herzegovina) */ + {0x103B, "smj"}, /* Sami (Lule) (Norway) */ + {0x1401, "ar"}, /* Arabic (Algeria) */ +//{0x1404, ""}, /* Chinese (Macao S.A.R.) */ + {0x1407, "de"}, /* German (Liechtenstein) */ + {0x1409, "en"}, /* English (New Zealand) */ + {0x140A, "es"}, /* Spanish (Costa Rica) */ + {0x140C, "fr"}, /* French (Luxembourg) */ + {0x141A, "bs"}, /* Bosnian (Latin) (Bosnia and Herzegovina) */ +//{0x143B, ""}, /* Sami (Lule) (Sweden) */ + {0x1801, "ar"}, /* Arabic (Morocco) */ + {0x1809, "en"}, /* English (Ireland) */ + {0x180A, "es"}, /* Spanish (Panama) */ + {0x180C, "fr"}, /* French (Principality of Monaco) */ +//{0x181A, ""}, /* Serbian (Latin) (Bosnia and Herzegovina) */ + {0x183B, "sma"}, /* Sami (Southern) (Norway) */ + {0x1C01, "ar"}, /* Arabic (Tunisia) */ + {0x1C09, "en"}, /* English (South Africa) */ + {0x1C0A, "es"}, /* Spanish (Dominican Republic) */ + {0x1C0C, "fr"}, /* ??? */ +//{0x1C1A, ""}, /* Serbian (Cyrillic) (Bosnia and Herzegovina) */ +//{0x1C3B, ""}, /* Sami (Southern) (Sweden) */ + {0x2001, "ar"}, /* Arabic (Oman) */ + {0x2009, "en"}, /* English (Jamaica) */ + {0x200A, "es"}, /* Spanish (Venezuela) */ + {0x200C, "fr"}, /* ??? */ + {0x201A, "bs"}, /* Bosnian (Cyrillic) (Bosnia and Herzegovina) */ + {0x203B, "sms"}, /* Sami (Skolt) (Finland) */ + {0x2401, "ar"}, /* Arabic (Yemen) */ + {0x2409, "en"}, /* English (Caribbean) */ + {0x240A, "es"}, /* Spanish (Colombia) */ + {0x240C, "fr"}, /* ??? */ + {0x243B, "smn"}, /* Sami (Inari) (Finland) */ + {0x2801, "ar"}, /* Arabic (Syria) */ + {0x2809, "en"}, /* English (Belize) */ + {0x280A, "es"}, /* Spanish (Peru) */ + {0x280C, "fr"}, /* ??? */ + {0x2C01, "ar"}, /* Arabic (Jordan) */ + {0x2C09, "en"}, /* English (Trinidad and Tobago) */ + {0x2C0A, "es"}, /* Spanish (Argentina) */ + {0x2C0C, "fr"}, /* ??? */ + {0x3001, "ar"}, /* Arabic (Lebanon) */ + {0x3009, "en"}, /* English (Zimbabwe) */ + {0x300A, "es"}, /* Spanish (Ecuador) */ + {0x300C, "fr"}, /* ??? */ + {0x3401, "ar"}, /* Arabic (Kuwait) */ + {0x3409, "en"}, /* English (Republic of the Philippines) */ + {0x340A, "es"}, /* Spanish (Chile) */ + {0x340C, "fr"}, /* ??? */ + {0x3801, "ar"}, /* Arabic (U.A.E.) */ + {0x380A, "es"}, /* Spanish (Uruguay) */ + {0x380C, "fr"}, /* ??? */ + {0x3C01, "ar"}, /* Arabic (Bahrain) */ + {0x3C09, "en"}, /* ??? */ + {0x3C0A, "es"}, /* Spanish (Paraguay) */ + {0x3C0C, "fr"}, /* ??? */ + {0x4001, "ar"}, /* Arabic (Qatar) */ + {0x4009, "en"}, /* English (India) */ + {0x400A, "es"}, /* Spanish (Bolivia) */ + {0x4409, "en"}, /* English (Malaysia) */ + {0x440A, "es"}, /* Spanish (El Salvador) */ + {0x4809, "en"}, /* English (Singapore) */ + {0x480A, "es"}, /* Spanish (Honduras) */ + {0x4C0A, "es"}, /* Spanish (Nicaragua) */ + {0x500A, "es"}, /* Spanish (Puerto Rico) */ + {0x540A, "es"}, /* Spanish (United States) */ + {0xE40A, "es"}, /* ??? */ + {0xE40C, "fr"}, /* ??? */ +}; + +static const hb_ot_language_map_t +_hb_mac_language_map[] = +{ + { 0, "en"}, /* English */ + { 1, "fr"}, /* French */ + { 2, "de"}, /* German */ + { 3, "it"}, /* Italian */ + { 4, "nl"}, /* Dutch */ + { 5, "sv"}, /* Swedish */ + { 6, "es"}, /* Spanish */ + { 7, "da"}, /* Danish */ + { 8, "pt"}, /* Portuguese */ + { 9, "no"}, /* Norwegian */ + { 10, "he"}, /* Hebrew */ + { 11, "ja"}, /* Japanese */ + { 12, "ar"}, /* Arabic */ + { 13, "fi"}, /* Finnish */ + { 14, "el"}, /* Greek */ + { 15, "is"}, /* Icelandic */ + { 16, "mt"}, /* Maltese */ + { 17, "tr"}, /* Turkish */ + { 18, "hr"}, /* Croatian */ + { 19, "zh-tw"}, /* Chinese (Traditional) */ + { 20, "ur"}, /* Urdu */ + { 21, "hi"}, /* Hindi */ + { 22, "th"}, /* Thai */ + { 23, "ko"}, /* Korean */ + { 24, "lt"}, /* Lithuanian */ + { 25, "pl"}, /* Polish */ + { 26, "hu"}, /* Hungarian */ + { 27, "et"}, /* Estonian */ + { 28, "lv"}, /* Latvian */ +//{ 29, ""}, /* Sami */ + { 30, "fo"}, /* Faroese */ + { 31, "fa"}, /* Farsi/Persian */ + { 32, "ru"}, /* Russian */ + { 33, "zh-cn"}, /* Chinese (Simplified) */ + { 34, "nl"}, /* Flemish */ + { 35, "ga"}, /* Irish Gaelic */ + { 36, "sq"}, /* Albanian */ + { 37, "ro"}, /* Romanian */ + { 38, "cs"}, /* Czech */ + { 39, "sk"}, /* Slovak */ + { 40, "sl"}, /* Slovenian */ + { 41, "yi"}, /* Yiddish */ + { 42, "sr"}, /* Serbian */ + { 43, "mk"}, /* Macedonian */ + { 44, "bg"}, /* Bulgarian */ + { 45, "uk"}, /* Ukrainian */ + { 46, "be"}, /* Byelorussian */ + { 47, "uz"}, /* Uzbek */ + { 48, "kk"}, /* Kazakh */ + { 49, "az"}, /* Azerbaijani (Cyrillic script) */ + { 50, "az"}, /* Azerbaijani (Arabic script) */ + { 51, "hy"}, /* Armenian */ + { 52, "ka"}, /* Georgian */ + { 53, "mo"}, /* Moldavian */ + { 54, "ky"}, /* Kirghiz */ + { 55, "tg"}, /* Tajiki */ + { 56, "tk"}, /* Turkmen */ + { 57, "mn"}, /* Mongolian (Mongolian script) */ + { 58, "mn"}, /* Mongolian (Cyrillic script) */ + { 59, "ps"}, /* Pashto */ + { 60, "ku"}, /* Kurdish */ + { 61, "ks"}, /* Kashmiri */ + { 62, "sd"}, /* Sindhi */ + { 63, "bo"}, /* Tibetan */ + { 64, "ne"}, /* Nepali */ + { 65, "sa"}, /* Sanskrit */ + { 66, "mr"}, /* Marathi */ + { 67, "bn"}, /* Bengali */ + { 68, "as"}, /* Assamese */ + { 69, "gu"}, /* Gujarati */ + { 70, "pa"}, /* Punjabi */ + { 71, "or"}, /* Oriya */ + { 72, "ml"}, /* Malayalam */ + { 73, "kn"}, /* Kannada */ + { 74, "ta"}, /* Tamil */ + { 75, "te"}, /* Telugu */ + { 76, "si"}, /* Sinhalese */ + { 77, "my"}, /* Burmese */ + { 78, "km"}, /* Khmer */ + { 79, "lo"}, /* Lao */ + { 80, "vi"}, /* Vietnamese */ + { 81, "id"}, /* Indonesian */ + { 82, "tl"}, /* Tagalog */ + { 83, "ms"}, /* Malay (Roman script) */ + { 84, "ms"}, /* Malay (Arabic script) */ + { 85, "am"}, /* Amharic */ + { 86, "ti"}, /* Tigrinya */ + { 87, "om"}, /* Galla */ + { 88, "so"}, /* Somali */ + { 89, "sw"}, /* Swahili */ + { 90, "rw"}, /* Kinyarwanda/Ruanda */ + { 91, "rn"}, /* Rundi */ + { 92, "ny"}, /* Nyanja/Chewa */ + { 93, "mg"}, /* Malagasy */ + { 94, "eo"}, /* Esperanto */ + {128, "cy"}, /* Welsh */ + {129, "eu"}, /* Basque */ + {130, "ca"}, /* Catalan */ + {131, "la"}, /* Latin */ + {132, "qu"}, /* Quechua */ + {133, "gn"}, /* Guarani */ + {134, "ay"}, /* Aymara */ + {135, "tt"}, /* Tatar */ + {136, "ug"}, /* Uighur */ + {137, "dz"}, /* Dzongkha */ + {138, "jw"}, /* Javanese (Roman script) */ + {139, "su"}, /* Sundanese (Roman script) */ + {140, "gl"}, /* Galician */ + {141, "af"}, /* Afrikaans */ + {142, "br"}, /* Breton */ + {143, "iu"}, /* Inuktitut */ + {144, "gd"}, /* Scottish Gaelic */ + {145, "gv"}, /* Manx Gaelic */ + {146, "ga"}, /* Irish Gaelic (with dot above) */ + {147, "to"}, /* Tongan */ + {148, "el"}, /* Greek (polytonic) */ + {149, "ik"}, /* Greenlandic */ + {150, "az"}, /* Azerbaijani (Roman script) */ +}; + + +static hb_language_t +_hb_ot_name_language_for (unsigned int code, + const hb_ot_language_map_t *array, + unsigned int len) +{ +#ifdef HB_NO_OT_NAME_LANGUAGE + return HB_LANGUAGE_INVALID; +#endif + auto *entry = hb_bsearch (code, array, len); + + if (entry) + return hb_language_from_string (entry->lang, -1); + + return HB_LANGUAGE_INVALID; +} + +hb_language_t +_hb_ot_name_language_for_ms_code (unsigned int code) +{ + return _hb_ot_name_language_for (code, + _hb_ms_language_map, + ARRAY_LENGTH (_hb_ms_language_map)); +} + +hb_language_t +_hb_ot_name_language_for_mac_code (unsigned int code) +{ + return _hb_ot_name_language_for (code, + _hb_mac_language_map, + ARRAY_LENGTH (_hb_mac_language_map)); +} + +#endif /* HB_OT_NAME_LANGUAGE_STATIC_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-name-language.hh b/gfx/harfbuzz/src/hb-ot-name-language.hh new file mode 100644 index 0000000000..903076c0d5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-name-language.hh @@ -0,0 +1,40 @@ +/* + * 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_OT_NAME_LANGUAGE_HH +#define HB_OT_NAME_LANGUAGE_HH + +#include "hb.hh" + + +HB_INTERNAL hb_language_t +_hb_ot_name_language_for_ms_code (unsigned int code); + +HB_INTERNAL hb_language_t +_hb_ot_name_language_for_mac_code (unsigned int code); + + +#endif /* HB_OT_NAME_LANGUAGE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-name-table.hh b/gfx/harfbuzz/src/hb-ot-name-table.hh new file mode 100644 index 0000000000..85653224cc --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-name-table.hh @@ -0,0 +1,32 @@ +/* + * 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 HB_OT_NAME_TABLE_HH +#define HB_OT_NAME_TABLE_HH + +#include "OT/name/name.hh" + +#endif /* HB_OT_NAME_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-name.cc b/gfx/harfbuzz/src/hb-ot-name.cc new file mode 100644 index 0000000000..6adf1e8fbe --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-name.cc @@ -0,0 +1,184 @@ +/* + * 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.hh" + +#ifndef HB_NO_NAME + +#include "hb-ot-name-table.hh" + +#include "hb-utf.hh" + + +/** + * SECTION:hb-ot-name + * @title: hb-ot-name + * @short_description: OpenType font name information + * @include: hb-ot.h + * + * Functions for fetching name strings from OpenType fonts. + **/ + + +/** + * hb_ot_name_list_names: + * @face: font face. + * @num_entries: (out) (optional): number of returned entries. + * + * Enumerates all available name IDs and language combinations. Returned + * array is owned by the @face and should not be modified. It can be + * used as long as @face is alive. + * + * Returns: (transfer none) (array length=num_entries): Array of available name entries. + * Since: 2.1.0 + **/ +const hb_ot_name_entry_t * +hb_ot_name_list_names (hb_face_t *face, + unsigned int *num_entries /* OUT */) +{ + const OT::name_accelerator_t &name = *face->table.name; + if (num_entries) *num_entries = name.names.length; + return (const hb_ot_name_entry_t *) name.names; +} + +template <typename utf_t> +static inline unsigned int +hb_ot_name_get_utf (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + typename utf_t::codepoint_t *text /* OUT */) +{ + const OT::name_accelerator_t &name = *face->table.name; + + if (!language) + language = hb_language_from_string ("en", 2); + + unsigned int width; + int idx = name.get_index (name_id, language, &width); + if (idx != -1) + { + hb_bytes_t bytes = name.get_name (idx); + + if (width == 2) /* UTF16-BE */ + return OT::hb_ot_name_convert_utf<hb_utf16_be_t, utf_t> (bytes, text_size, text); + + if (width == 1) /* ASCII */ + return OT::hb_ot_name_convert_utf<hb_ascii_t, utf_t> (bytes, text_size, text); + } + + if (text_size) + { + if (*text_size) + *text = 0; + *text_size = 0; + } + return 0; +} + +/** + * hb_ot_name_get_utf8: + * @face: font face. + * @name_id: OpenType name identifier to fetch. + * @language: language to fetch the name for. + * @text_size: (inout) (optional): input size of @text buffer, and output size of + * text written to buffer. + * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into. + * + * Fetches a font name from the OpenType 'name' table. + * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. + * Returns string in UTF-8 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. + * + * Returns: full length of the requested string, or 0 if not found. + * Since: 2.1.0 + **/ +unsigned int +hb_ot_name_get_utf8 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + char *text /* OUT */) +{ + return hb_ot_name_get_utf<hb_utf8_t> (face, name_id, language, text_size, + (hb_utf8_t::codepoint_t *) text); +} + +/** + * hb_ot_name_get_utf16: + * @face: font face. + * @name_id: OpenType name identifier to fetch. + * @language: language to fetch the name for. + * @text_size: (inout) (optional): input size of @text buffer, and output size of + * text written to buffer. + * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into. + * + * Fetches a font name from the OpenType 'name' table. + * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. + * Returns string in UTF-16 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. + * + * Returns: full length of the requested string, or 0 if not found. + * Since: 2.1.0 + **/ +unsigned int +hb_ot_name_get_utf16 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint16_t *text /* OUT */) +{ + return hb_ot_name_get_utf<hb_utf16_t> (face, name_id, language, text_size, text); +} + +/** + * hb_ot_name_get_utf32: + * @face: font face. + * @name_id: OpenType name identifier to fetch. + * @language: language to fetch the name for. + * @text_size: (inout) (optional): input size of @text buffer, and output size of + * text written to buffer. + * @text: (out caller-allocates) (array length=text_size): buffer to write fetched name into. + * + * Fetches a font name from the OpenType 'name' table. + * If @language is #HB_LANGUAGE_INVALID, English ("en") is assumed. + * Returns string in UTF-32 encoding. A NUL terminator is always written + * for convenience, and isn't included in the output @text_size. + * + * Returns: full length of the requested string, or 0 if not found. + * Since: 2.1.0 + **/ +unsigned int +hb_ot_name_get_utf32 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint32_t *text /* OUT */) +{ + return hb_ot_name_get_utf<hb_utf32_t> (face, name_id, language, text_size, text); +} + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-name.h b/gfx/harfbuzz/src/hb-ot-name.h new file mode 100644 index 0000000000..03e664bb93 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-name.h @@ -0,0 +1,164 @@ +/* + * 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_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_NAME_H +#define HB_OT_NAME_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_ot_name_id_predefined_t: + * @HB_OT_NAME_ID_COPYRIGHT: Copyright notice + * @HB_OT_NAME_ID_FONT_FAMILY: Font Family name + * @HB_OT_NAME_ID_FONT_SUBFAMILY: Font Subfamily name + * @HB_OT_NAME_ID_UNIQUE_ID: Unique font identifier + * @HB_OT_NAME_ID_FULL_NAME: Full font name that reflects + * all family and relevant subfamily descriptors + * @HB_OT_NAME_ID_VERSION_STRING: Version string + * @HB_OT_NAME_ID_POSTSCRIPT_NAME: PostScript name for the font + * @HB_OT_NAME_ID_TRADEMARK: Trademark + * @HB_OT_NAME_ID_MANUFACTURER: Manufacturer Name + * @HB_OT_NAME_ID_DESIGNER: Designer + * @HB_OT_NAME_ID_DESCRIPTION: Description + * @HB_OT_NAME_ID_VENDOR_URL: URL of font vendor + * @HB_OT_NAME_ID_DESIGNER_URL: URL of typeface designer + * @HB_OT_NAME_ID_LICENSE: License Description + * @HB_OT_NAME_ID_LICENSE_URL: URL where additional licensing + * information can be found + * @HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY: Typographic Family name + * @HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY: Typographic Subfamily name + * @HB_OT_NAME_ID_MAC_FULL_NAME: Compatible Full Name for MacOS + * @HB_OT_NAME_ID_SAMPLE_TEXT: Sample text + * @HB_OT_NAME_ID_CID_FINDFONT_NAME: PostScript CID findfont name + * @HB_OT_NAME_ID_WWS_FAMILY: WWS Family Name + * @HB_OT_NAME_ID_WWS_SUBFAMILY: WWS Subfamily Name + * @HB_OT_NAME_ID_LIGHT_BACKGROUND: Light Background Palette + * @HB_OT_NAME_ID_DARK_BACKGROUND: Dark Background Palette + * @HB_OT_NAME_ID_VARIATIONS_PS_PREFIX: Variations PostScript Name Prefix + * @HB_OT_NAME_ID_INVALID: Value to represent a nonexistent name ID. + * + * An enum type representing the pre-defined name IDs. + * + * For more information on these fields, see the + * [OpenType spec](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids). + * + * Since: 7.0.0 + **/ +typedef enum +{ + HB_OT_NAME_ID_COPYRIGHT = 0, + HB_OT_NAME_ID_FONT_FAMILY = 1, + HB_OT_NAME_ID_FONT_SUBFAMILY = 2, + HB_OT_NAME_ID_UNIQUE_ID = 3, + HB_OT_NAME_ID_FULL_NAME = 4, + HB_OT_NAME_ID_VERSION_STRING = 5, + HB_OT_NAME_ID_POSTSCRIPT_NAME = 6, + HB_OT_NAME_ID_TRADEMARK = 7, + HB_OT_NAME_ID_MANUFACTURER = 8, + HB_OT_NAME_ID_DESIGNER = 9, + HB_OT_NAME_ID_DESCRIPTION = 10, + HB_OT_NAME_ID_VENDOR_URL = 11, + HB_OT_NAME_ID_DESIGNER_URL = 12, + HB_OT_NAME_ID_LICENSE = 13, + HB_OT_NAME_ID_LICENSE_URL = 14, +/*HB_OT_NAME_ID_RESERVED = 15,*/ + HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY = 16, + HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY = 17, + HB_OT_NAME_ID_MAC_FULL_NAME = 18, + HB_OT_NAME_ID_SAMPLE_TEXT = 19, + HB_OT_NAME_ID_CID_FINDFONT_NAME = 20, + HB_OT_NAME_ID_WWS_FAMILY = 21, + HB_OT_NAME_ID_WWS_SUBFAMILY = 22, + HB_OT_NAME_ID_LIGHT_BACKGROUND = 23, + HB_OT_NAME_ID_DARK_BACKGROUND = 24, + HB_OT_NAME_ID_VARIATIONS_PS_PREFIX = 25, + + HB_OT_NAME_ID_INVALID = 0xFFFF +} hb_ot_name_id_predefined_t; + +/** + * hb_ot_name_id_t: + * + * An integral type representing an OpenType 'name' table name identifier. + * There are predefined name IDs, as well as name IDs return from other + * API. These can be used to fetch name strings from a font face. + * + * Since: 2.0.0 + **/ +typedef unsigned int hb_ot_name_id_t; + + +/** + * hb_ot_name_entry_t: + * @name_id: name ID + * @language: language + * + * Structure representing a name ID in a particular language. + * + * Since: 2.1.0 + **/ +typedef struct hb_ot_name_entry_t { + hb_ot_name_id_t name_id; + /*< private >*/ + hb_var_int_t var; + /*< public >*/ + hb_language_t language; +} hb_ot_name_entry_t; + +HB_EXTERN const hb_ot_name_entry_t * +hb_ot_name_list_names (hb_face_t *face, + unsigned int *num_entries /* OUT */); + + +HB_EXTERN unsigned int +hb_ot_name_get_utf8 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + char *text /* OUT */); + +HB_EXTERN unsigned int +hb_ot_name_get_utf16 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint16_t *text /* OUT */); + +HB_EXTERN unsigned int +hb_ot_name_get_utf32 (hb_face_t *face, + hb_ot_name_id_t name_id, + hb_language_t language, + unsigned int *text_size /* IN/OUT */, + uint32_t *text /* OUT */); + + +HB_END_DECLS + +#endif /* HB_OT_NAME_H */ diff --git a/gfx/harfbuzz/src/hb-ot-os2-table.hh b/gfx/harfbuzz/src/hb-ot-os2-table.hh new file mode 100644 index 0000000000..8c2e696f56 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-os2-table.hh @@ -0,0 +1,405 @@ +/* + * Copyright © 2011,2012 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 + */ + +#ifndef HB_OT_OS2_TABLE_HH +#define HB_OT_OS2_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-os2-unicode-ranges.hh" +#include "hb-ot-var-mvar-table.hh" + +#include "hb-set.hh" + +/* + * OS/2 and Windows Metrics + * https://docs.microsoft.com/en-us/typography/opentype/spec/os2 + */ +#define HB_OT_TAG_OS2 HB_TAG('O','S','/','2') + + +namespace OT { + +struct OS2V1Tail +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT32 ulCodePageRange1; + HBUINT32 ulCodePageRange2; + public: + DEFINE_SIZE_STATIC (8); +}; + +struct OS2V2Tail +{ + bool has_data () const { return sxHeight || sCapHeight; } + + const OS2V2Tail * operator -> () const { return this; } + OS2V2Tail * operator -> () { return this; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBINT16 sxHeight; + HBINT16 sCapHeight; + HBUINT16 usDefaultChar; + HBUINT16 usBreakChar; + HBUINT16 usMaxContext; + public: + DEFINE_SIZE_STATIC (10); +}; + +struct OS2V5Tail +{ + inline bool get_optical_size (unsigned int *lower, unsigned int *upper) const + { + unsigned int lower_optical_size = usLowerOpticalPointSize; + unsigned int upper_optical_size = usUpperOpticalPointSize; + + /* Per https://docs.microsoft.com/en-us/typography/opentype/spec/os2#lps */ + if (lower_optical_size < upper_optical_size && + lower_optical_size >= 1 && lower_optical_size <= 0xFFFE && + upper_optical_size >= 2 && upper_optical_size <= 0xFFFF) + { + *lower = lower_optical_size; + *upper = upper_optical_size; + return true; + } + return false; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + public: + HBUINT16 usLowerOpticalPointSize; + HBUINT16 usUpperOpticalPointSize; + public: + DEFINE_SIZE_STATIC (4); +}; + +struct OS2 +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_OS2; + + bool has_data () const { return usWeightClass || usWidthClass || usFirstCharIndex || usLastCharIndex; } + + const OS2V1Tail &v1 () const { return version >= 1 ? v1X : Null (OS2V1Tail); } + const OS2V2Tail &v2 () const { return version >= 2 ? v2X : Null (OS2V2Tail); } + const OS2V5Tail &v5 () const { return version >= 5 ? v5X : Null (OS2V5Tail); } + + enum selection_flag_t { + ITALIC = 1u<<0, + UNDERSCORE = 1u<<1, + NEGATIVE = 1u<<2, + OUTLINED = 1u<<3, + STRIKEOUT = 1u<<4, + BOLD = 1u<<5, + REGULAR = 1u<<6, + USE_TYPO_METRICS = 1u<<7, + WWS = 1u<<8, + OBLIQUE = 1u<<9 + }; + + bool is_italic () const { return fsSelection & ITALIC; } + bool is_oblique () const { return fsSelection & OBLIQUE; } + bool use_typo_metrics () const { return fsSelection & USE_TYPO_METRICS; } + + enum width_class_t { + FWIDTH_ULTRA_CONDENSED = 1, /* 50% */ + FWIDTH_EXTRA_CONDENSED = 2, /* 62.5% */ + FWIDTH_CONDENSED = 3, /* 75% */ + FWIDTH_SEMI_CONDENSED = 4, /* 87.5% */ + FWIDTH_NORMAL = 5, /* 100% */ + FWIDTH_SEMI_EXPANDED = 6, /* 112.5% */ + FWIDTH_EXPANDED = 7, /* 125% */ + FWIDTH_EXTRA_EXPANDED = 8, /* 150% */ + FWIDTH_ULTRA_EXPANDED = 9 /* 200% */ + }; + + float get_width () const + { + switch (usWidthClass) { + case FWIDTH_ULTRA_CONDENSED:return 50.f; + case FWIDTH_EXTRA_CONDENSED:return 62.5f; + case FWIDTH_CONDENSED: return 75.f; + case FWIDTH_SEMI_CONDENSED: return 87.5f; + default: + case FWIDTH_NORMAL: return 100.f; + case FWIDTH_SEMI_EXPANDED: return 112.5f; + case FWIDTH_EXPANDED: return 125.f; + case FWIDTH_EXTRA_EXPANDED: return 150.f; + case FWIDTH_ULTRA_EXPANDED: return 200.f; + } + } + + float map_wdth_to_widthclass(float width) const + { + if (width < 50) return 1.0f; + if (width > 200) return 9.0f; + + float ratio = (width - 50) / 12.5f; + int a = (int) floorf (ratio); + int b = (int) ceilf (ratio); + + /* follow this maping: + * https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass + */ + if (b <= 6) // 50-125 + { + if (a == b) return a + 1.0f; + } + else if (b == 7) // no mapping for 137.5 + { + a = 6; + b = 8; + } + else if (b == 8) + { + if (a == b) return 8.0f; // 150 + a = 6; + } + else + { + if (a == b && a == 12) return 9.0f; //200 + b = 12; + a = 8; + } + + float va = 50 + a * 12.5f; + float vb = 50 + b * 12.5f; + + float ret = a + (width - va) / (vb - va); + if (a <= 6) ret += 1.0f; + return ret; + } + + static unsigned calc_avg_char_width (const hb_hashmap_t<hb_codepoint_t, hb_pair_t<unsigned, int>>& hmtx_map) + { + unsigned num = 0; + unsigned total_width = 0; + for (const auto& _ : hmtx_map.values_ref ()) + { + unsigned width = _.first; + if (width) + { + total_width += width; + num++; + } + } + + return num ? (unsigned) roundf (total_width / num) : 0; + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + OS2 *os2_prime = c->serializer->embed (this); + if (unlikely (!os2_prime)) return_trace (false); + +#ifndef HB_NO_VAR + if (c->plan->normalized_coords) + { + auto &MVAR = *c->plan->source->table.MVAR; + auto *table = os2_prime; + + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER, sTypoAscender); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER, sTypoDescender); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP, sTypoLineGap); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_ASCENT, usWinAscent); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_HORIZONTAL_CLIPPING_DESCENT, usWinDescent); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_SIZE, ySubscriptXSize); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_SIZE, ySubscriptYSize); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_X_OFFSET, ySubscriptXOffset); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUBSCRIPT_EM_Y_OFFSET, ySubscriptYOffset); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_SIZE, ySuperscriptXSize); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_SIZE, ySuperscriptYSize); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_X_OFFSET, ySuperscriptXOffset); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_SUPERSCRIPT_EM_Y_OFFSET, ySuperscriptYOffset); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_SIZE, yStrikeoutSize); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_STRIKEOUT_OFFSET, yStrikeoutPosition); + + if (os2_prime->version >= 2) + { + hb_barrier (); + auto *table = & const_cast<OS2V2Tail &> (os2_prime->v2 ()); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_X_HEIGHT, sxHeight); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_CAP_HEIGHT, sCapHeight); + } + + unsigned avg_char_width = calc_avg_char_width (c->plan->hmtx_map); + if (!c->serializer->check_assign (os2_prime->xAvgCharWidth, avg_char_width, + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + } +#endif + + Triple *axis_range; + if (c->plan->user_axes_location.has (HB_TAG ('w','g','h','t'), &axis_range)) + { + unsigned weight_class = static_cast<unsigned> (roundf (hb_clamp (axis_range->middle, 1.0f, 1000.0f))); + if (os2_prime->usWeightClass != weight_class) + os2_prime->usWeightClass = weight_class; + } + + if (c->plan->user_axes_location.has (HB_TAG ('w','d','t','h'), &axis_range)) + { + unsigned width_class = static_cast<unsigned> (roundf (map_wdth_to_widthclass (axis_range->middle))); + if (os2_prime->usWidthClass != width_class) + os2_prime->usWidthClass = width_class; + } + + if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES) + return_trace (true); + + os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_min ()); + os2_prime->usLastCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_max ()); + + _update_unicode_ranges (&c->plan->unicodes, os2_prime->ulUnicodeRange); + + return_trace (true); + } + + void _update_unicode_ranges (const hb_set_t *codepoints, + HBUINT32 ulUnicodeRange[4]) const + { + HBUINT32 newBits[4]; + for (unsigned int i = 0; i < 4; i++) + newBits[i] = 0; + + /* This block doesn't show up in profiles. If it ever did, + * we can rewrite it to iterate over OS/2 ranges and use + * set iteration to check if the range matches. */ + for (auto cp : *codepoints) + { + unsigned int bit = _hb_ot_os2_get_unicode_range_bit (cp); + if (bit < 128) + { + unsigned int block = bit / 32; + unsigned int bit_in_block = bit % 32; + unsigned int mask = 1 << bit_in_block; + newBits[block] = newBits[block] | mask; + } + if (cp >= 0x10000 && cp <= 0x110000) + { + /* the spec says that bit 57 ("Non Plane 0") implies that there's + at least one codepoint beyond the BMP; so I also include all + the non-BMP codepoints here */ + newBits[1] = newBits[1] | (1 << 25); + } + } + + for (unsigned int i = 0; i < 4; i++) + ulUnicodeRange[i] = ulUnicodeRange[i] & newBits[i]; // set bits only if set in the original + } + + /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 + * https://docs.microsoft.com/en-us/typography/legacy/legacy_arabic_fonts */ + enum font_page_t + { + FONT_PAGE_NONE = 0, + FONT_PAGE_HEBREW = 0xB100, /* Hebrew Windows 3.1 font page */ + FONT_PAGE_SIMP_ARABIC = 0xB200, /* Simplified Arabic Windows 3.1 font page */ + FONT_PAGE_TRAD_ARABIC = 0xB300, /* Traditional Arabic Windows 3.1 font page */ + FONT_PAGE_OEM_ARABIC = 0xB400, /* OEM Arabic Windows 3.1 font page */ + FONT_PAGE_SIMP_FARSI = 0xBA00, /* Simplified Farsi Windows 3.1 font page */ + FONT_PAGE_TRAD_FARSI = 0xBB00, /* Traditional Farsi Windows 3.1 font page */ + FONT_PAGE_THAI = 0xDE00 /* Thai Windows 3.1 font page */ + }; + font_page_t get_font_page () const + { return (font_page_t) (version == 0 ? fsSelection & 0xFF00 : 0); } + + unsigned get_size () const + { + unsigned result = min_size; + if (version >= 1) result += v1X.get_size (); + if (version >= 2) result += v2X.get_size (); + if (version >= 5) result += v5X.get_size (); + return result; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) return_trace (false); + hb_barrier (); + if (unlikely (version >= 1 && !v1X.sanitize (c))) return_trace (false); + if (unlikely (version >= 2 && !v2X.sanitize (c))) return_trace (false); + if (unlikely (version >= 5 && !v5X.sanitize (c))) return_trace (false); + return_trace (true); + } + + public: + HBUINT16 version; + HBINT16 xAvgCharWidth; + HBUINT16 usWeightClass; + HBUINT16 usWidthClass; + HBUINT16 fsType; + HBINT16 ySubscriptXSize; + HBINT16 ySubscriptYSize; + HBINT16 ySubscriptXOffset; + HBINT16 ySubscriptYOffset; + HBINT16 ySuperscriptXSize; + HBINT16 ySuperscriptYSize; + HBINT16 ySuperscriptXOffset; + HBINT16 ySuperscriptYOffset; + HBINT16 yStrikeoutSize; + HBINT16 yStrikeoutPosition; + HBINT16 sFamilyClass; + HBUINT8 panose[10]; + HBUINT32 ulUnicodeRange[4]; + Tag achVendID; + HBUINT16 fsSelection; + HBUINT16 usFirstCharIndex; + HBUINT16 usLastCharIndex; + HBINT16 sTypoAscender; + HBINT16 sTypoDescender; + HBINT16 sTypoLineGap; + HBUINT16 usWinAscent; + HBUINT16 usWinDescent; + OS2V1Tail v1X; + OS2V2Tail v2X; + OS2V5Tail v5X; + public: + DEFINE_SIZE_MIN (78); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_OS2_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh b/gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh new file mode 100644 index 0000000000..01e6a46e63 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-os2-unicode-ranges.hh @@ -0,0 +1,231 @@ +/* + * 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_OS2_UNICODE_RANGES_HH +#define HB_OT_OS2_UNICODE_RANGES_HH + +#include "hb.hh" + +namespace OT { + +struct OS2Range +{ + int cmp (hb_codepoint_t key) const + { return (key < first) ? -1 : key <= last ? 0 : +1; } + + hb_codepoint_t first; + hb_codepoint_t last; + unsigned int bit; +}; + +/* Note: The contents of this array was generated using gen-os2-unicode-ranges.py. */ +static const OS2Range _hb_os2_unicode_ranges[] = +{ + { 0x0, 0x7F, 0}, // Basic Latin + { 0x80, 0xFF, 1}, // Latin-1 Supplement + { 0x100, 0x17F, 2}, // Latin Extended-A + { 0x180, 0x24F, 3}, // Latin Extended-B + { 0x250, 0x2AF, 4}, // IPA Extensions + { 0x2B0, 0x2FF, 5}, // Spacing Modifier Letters + { 0x300, 0x36F, 6}, // Combining Diacritical Marks + { 0x370, 0x3FF, 7}, // Greek and Coptic + { 0x400, 0x4FF, 9}, // Cyrillic + { 0x500, 0x52F, 9}, // Cyrillic Supplement + { 0x530, 0x58F, 10}, // Armenian + { 0x590, 0x5FF, 11}, // Hebrew + { 0x600, 0x6FF, 13}, // Arabic + { 0x700, 0x74F, 71}, // Syriac + { 0x750, 0x77F, 13}, // Arabic Supplement + { 0x780, 0x7BF, 72}, // Thaana + { 0x7C0, 0x7FF, 14}, // NKo + { 0x900, 0x97F, 15}, // Devanagari + { 0x980, 0x9FF, 16}, // Bengali + { 0xA00, 0xA7F, 17}, // Gurmukhi + { 0xA80, 0xAFF, 18}, // Gujarati + { 0xB00, 0xB7F, 19}, // Oriya + { 0xB80, 0xBFF, 20}, // Tamil + { 0xC00, 0xC7F, 21}, // Telugu + { 0xC80, 0xCFF, 22}, // Kannada + { 0xD00, 0xD7F, 23}, // Malayalam + { 0xD80, 0xDFF, 73}, // Sinhala + { 0xE00, 0xE7F, 24}, // Thai + { 0xE80, 0xEFF, 25}, // Lao + { 0xF00, 0xFFF, 70}, // Tibetan + { 0x1000, 0x109F, 74}, // Myanmar + { 0x10A0, 0x10FF, 26}, // Georgian + { 0x1100, 0x11FF, 28}, // Hangul Jamo + { 0x1200, 0x137F, 75}, // Ethiopic + { 0x1380, 0x139F, 75}, // Ethiopic Supplement + { 0x13A0, 0x13FF, 76}, // Cherokee + { 0x1400, 0x167F, 77}, // Unified Canadian Aboriginal Syllabics + { 0x1680, 0x169F, 78}, // Ogham + { 0x16A0, 0x16FF, 79}, // Runic + { 0x1700, 0x171F, 84}, // Tagalog + { 0x1720, 0x173F, 84}, // Hanunoo + { 0x1740, 0x175F, 84}, // Buhid + { 0x1760, 0x177F, 84}, // Tagbanwa + { 0x1780, 0x17FF, 80}, // Khmer + { 0x1800, 0x18AF, 81}, // Mongolian + { 0x1900, 0x194F, 93}, // Limbu + { 0x1950, 0x197F, 94}, // Tai Le + { 0x1980, 0x19DF, 95}, // New Tai Lue + { 0x19E0, 0x19FF, 80}, // Khmer Symbols + { 0x1A00, 0x1A1F, 96}, // Buginese + { 0x1B00, 0x1B7F, 27}, // Balinese + { 0x1B80, 0x1BBF, 112}, // Sundanese + { 0x1C00, 0x1C4F, 113}, // Lepcha + { 0x1C50, 0x1C7F, 114}, // Ol Chiki + { 0x1D00, 0x1D7F, 4}, // Phonetic Extensions + { 0x1D80, 0x1DBF, 4}, // Phonetic Extensions Supplement + { 0x1DC0, 0x1DFF, 6}, // Combining Diacritical Marks Supplement + { 0x1E00, 0x1EFF, 29}, // Latin Extended Additional + { 0x1F00, 0x1FFF, 30}, // Greek Extended + { 0x2000, 0x206F, 31}, // General Punctuation + { 0x2070, 0x209F, 32}, // Superscripts And Subscripts + { 0x20A0, 0x20CF, 33}, // Currency Symbols + { 0x20D0, 0x20FF, 34}, // Combining Diacritical Marks For Symbols + { 0x2100, 0x214F, 35}, // Letterlike Symbols + { 0x2150, 0x218F, 36}, // Number Forms + { 0x2190, 0x21FF, 37}, // Arrows + { 0x2200, 0x22FF, 38}, // Mathematical Operators + { 0x2300, 0x23FF, 39}, // Miscellaneous Technical + { 0x2400, 0x243F, 40}, // Control Pictures + { 0x2440, 0x245F, 41}, // Optical Character Recognition + { 0x2460, 0x24FF, 42}, // Enclosed Alphanumerics + { 0x2500, 0x257F, 43}, // Box Drawing + { 0x2580, 0x259F, 44}, // Block Elements + { 0x25A0, 0x25FF, 45}, // Geometric Shapes + { 0x2600, 0x26FF, 46}, // Miscellaneous Symbols + { 0x2700, 0x27BF, 47}, // Dingbats + { 0x27C0, 0x27EF, 38}, // Miscellaneous Mathematical Symbols-A + { 0x27F0, 0x27FF, 37}, // Supplemental Arrows-A + { 0x2800, 0x28FF, 82}, // Braille Patterns + { 0x2900, 0x297F, 37}, // Supplemental Arrows-B + { 0x2980, 0x29FF, 38}, // Miscellaneous Mathematical Symbols-B + { 0x2A00, 0x2AFF, 38}, // Supplemental Mathematical Operators + { 0x2B00, 0x2BFF, 37}, // Miscellaneous Symbols and Arrows + { 0x2C00, 0x2C5F, 97}, // Glagolitic + { 0x2C60, 0x2C7F, 29}, // Latin Extended-C + { 0x2C80, 0x2CFF, 8}, // Coptic + { 0x2D00, 0x2D2F, 26}, // Georgian Supplement + { 0x2D30, 0x2D7F, 98}, // Tifinagh + { 0x2D80, 0x2DDF, 75}, // Ethiopic Extended + { 0x2DE0, 0x2DFF, 9}, // Cyrillic Extended-A + { 0x2E00, 0x2E7F, 31}, // Supplemental Punctuation + { 0x2E80, 0x2EFF, 59}, // CJK Radicals Supplement + { 0x2F00, 0x2FDF, 59}, // Kangxi Radicals + { 0x2FF0, 0x2FFF, 59}, // Ideographic Description Characters + { 0x3000, 0x303F, 48}, // CJK Symbols And Punctuation + { 0x3040, 0x309F, 49}, // Hiragana + { 0x30A0, 0x30FF, 50}, // Katakana + { 0x3100, 0x312F, 51}, // Bopomofo + { 0x3130, 0x318F, 52}, // Hangul Compatibility Jamo + { 0x3190, 0x319F, 59}, // Kanbun + { 0x31A0, 0x31BF, 51}, // Bopomofo Extended + { 0x31C0, 0x31EF, 61}, // CJK Strokes + { 0x31F0, 0x31FF, 50}, // Katakana Phonetic Extensions + { 0x3200, 0x32FF, 54}, // Enclosed CJK Letters And Months + { 0x3300, 0x33FF, 55}, // CJK Compatibility + { 0x3400, 0x4DBF, 59}, // CJK Unified Ideographs Extension A + { 0x4DC0, 0x4DFF, 99}, // Yijing Hexagram Symbols + { 0x4E00, 0x9FFF, 59}, // CJK Unified Ideographs + { 0xA000, 0xA48F, 83}, // Yi Syllables + { 0xA490, 0xA4CF, 83}, // Yi Radicals + { 0xA500, 0xA63F, 12}, // Vai + { 0xA640, 0xA69F, 9}, // Cyrillic Extended-B + { 0xA700, 0xA71F, 5}, // Modifier Tone Letters + { 0xA720, 0xA7FF, 29}, // Latin Extended-D + { 0xA800, 0xA82F, 100}, // Syloti Nagri + { 0xA840, 0xA87F, 53}, // Phags-pa + { 0xA880, 0xA8DF, 115}, // Saurashtra + { 0xA900, 0xA92F, 116}, // Kayah Li + { 0xA930, 0xA95F, 117}, // Rejang + { 0xAA00, 0xAA5F, 118}, // Cham + { 0xAC00, 0xD7AF, 56}, // Hangul Syllables + { 0xD800, 0xDFFF, 57}, // Non-Plane 0 * + { 0xE000, 0xF8FF, 60}, // Private Use Area (plane 0) + { 0xF900, 0xFAFF, 61}, // CJK Compatibility Ideographs + { 0xFB00, 0xFB4F, 62}, // Alphabetic Presentation Forms + { 0xFB50, 0xFDFF, 63}, // Arabic Presentation Forms-A + { 0xFE00, 0xFE0F, 91}, // Variation Selectors + { 0xFE10, 0xFE1F, 65}, // Vertical Forms + { 0xFE20, 0xFE2F, 64}, // Combining Half Marks + { 0xFE30, 0xFE4F, 65}, // CJK Compatibility Forms + { 0xFE50, 0xFE6F, 66}, // Small Form Variants + { 0xFE70, 0xFEFF, 67}, // Arabic Presentation Forms-B + { 0xFF00, 0xFFEF, 68}, // Halfwidth And Fullwidth Forms + { 0xFFF0, 0xFFFF, 69}, // Specials + { 0x10000, 0x1007F, 101}, // Linear B Syllabary + { 0x10080, 0x100FF, 101}, // Linear B Ideograms + { 0x10100, 0x1013F, 101}, // Aegean Numbers + { 0x10140, 0x1018F, 102}, // Ancient Greek Numbers + { 0x10190, 0x101CF, 119}, // Ancient Symbols + { 0x101D0, 0x101FF, 120}, // Phaistos Disc + { 0x10280, 0x1029F, 121}, // Lycian + { 0x102A0, 0x102DF, 121}, // Carian + { 0x10300, 0x1032F, 85}, // Old Italic + { 0x10330, 0x1034F, 86}, // Gothic + { 0x10380, 0x1039F, 103}, // Ugaritic + { 0x103A0, 0x103DF, 104}, // Old Persian + { 0x10400, 0x1044F, 87}, // Deseret + { 0x10450, 0x1047F, 105}, // Shavian + { 0x10480, 0x104AF, 106}, // Osmanya + { 0x10800, 0x1083F, 107}, // Cypriot Syllabary + { 0x10900, 0x1091F, 58}, // Phoenician + { 0x10920, 0x1093F, 121}, // Lydian + { 0x10A00, 0x10A5F, 108}, // Kharoshthi + { 0x12000, 0x123FF, 110}, // Cuneiform + { 0x12400, 0x1247F, 110}, // Cuneiform Numbers and Punctuation + { 0x1D000, 0x1D0FF, 88}, // Byzantine Musical Symbols + { 0x1D100, 0x1D1FF, 88}, // Musical Symbols + { 0x1D200, 0x1D24F, 88}, // Ancient Greek Musical Notation + { 0x1D300, 0x1D35F, 109}, // Tai Xuan Jing Symbols + { 0x1D360, 0x1D37F, 111}, // Counting Rod Numerals + { 0x1D400, 0x1D7FF, 89}, // Mathematical Alphanumeric Symbols + { 0x1F000, 0x1F02F, 122}, // Mahjong Tiles + { 0x1F030, 0x1F09F, 122}, // Domino Tiles + { 0x20000, 0x2A6DF, 59}, // CJK Unified Ideographs Extension B + { 0x2F800, 0x2FA1F, 61}, // CJK Compatibility Ideographs Supplement + { 0xE0000, 0xE007F, 92}, // Tags + { 0xE0100, 0xE01EF, 91}, // Variation Selectors Supplement + { 0xF0000, 0xFFFFD, 90}, // Private Use (plane 15) + {0x100000, 0x10FFFD, 90}, // Private Use (plane 16) +}; + +/** + * _hb_ot_os2_get_unicode_range_bit: + * Returns the bit to be set in os/2 ulUnicodeOS2Range for a given codepoint. + **/ +static unsigned int +_hb_ot_os2_get_unicode_range_bit (hb_codepoint_t cp) +{ + auto *range = hb_sorted_array (_hb_os2_unicode_ranges).bsearch (cp); + return range ? range->bit : (unsigned) -1; +} + +} /* namespace OT */ + +#endif /* HB_OT_OS2_UNICODE_RANGES_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-post-macroman.hh b/gfx/harfbuzz/src/hb-ot-post-macroman.hh new file mode 100644 index 0000000000..b4df8aaeea --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-post-macroman.hh @@ -0,0 +1,294 @@ +/* + * 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_OT_POST_MACROMAN_HH +#if 0 /* Make checks happy. */ +#define HB_OT_POST_MACROMAN_HH +#include "hb.hh" +#endif + + +_S(".notdef") +_S(".null") +_S("nonmarkingreturn") +_S("space") +_S("exclam") +_S("quotedbl") +_S("numbersign") +_S("dollar") +_S("percent") +_S("ampersand") +_S("quotesingle") +_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("grave") +_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("Adieresis") +_S("Aring") +_S("Ccedilla") +_S("Eacute") +_S("Ntilde") +_S("Odieresis") +_S("Udieresis") +_S("aacute") +_S("agrave") +_S("acircumflex") +_S("adieresis") +_S("atilde") +_S("aring") +_S("ccedilla") +_S("eacute") +_S("egrave") +_S("ecircumflex") +_S("edieresis") +_S("iacute") +_S("igrave") +_S("icircumflex") +_S("idieresis") +_S("ntilde") +_S("oacute") +_S("ograve") +_S("ocircumflex") +_S("odieresis") +_S("otilde") +_S("uacute") +_S("ugrave") +_S("ucircumflex") +_S("udieresis") +_S("dagger") +_S("degree") +_S("cent") +_S("sterling") +_S("section") +_S("bullet") +_S("paragraph") +_S("germandbls") +_S("registered") +_S("copyright") +_S("trademark") +_S("acute") +_S("dieresis") +_S("notequal") +_S("AE") +_S("Oslash") +_S("infinity") +_S("plusminus") +_S("lessequal") +_S("greaterequal") +_S("yen") +_S("mu") +_S("partialdiff") +_S("summation") +_S("product") +_S("pi") +_S("integral") +_S("ordfeminine") +_S("ordmasculine") +_S("Omega") +_S("ae") +_S("oslash") +_S("questiondown") +_S("exclamdown") +_S("logicalnot") +_S("radical") +_S("florin") +_S("approxequal") +_S("Delta") +_S("guillemotleft") +_S("guillemotright") +_S("ellipsis") +_S("nonbreakingspace") +_S("Agrave") +_S("Atilde") +_S("Otilde") +_S("OE") +_S("oe") +_S("endash") +_S("emdash") +_S("quotedblleft") +_S("quotedblright") +_S("quoteleft") +_S("quoteright") +_S("divide") +_S("lozenge") +_S("ydieresis") +_S("Ydieresis") +_S("fraction") +_S("currency") +_S("guilsinglleft") +_S("guilsinglright") +_S("fi") +_S("fl") +_S("daggerdbl") +_S("periodcentered") +_S("quotesinglbase") +_S("quotedblbase") +_S("perthousand") +_S("Acircumflex") +_S("Ecircumflex") +_S("Aacute") +_S("Edieresis") +_S("Egrave") +_S("Iacute") +_S("Icircumflex") +_S("Idieresis") +_S("Igrave") +_S("Oacute") +_S("Ocircumflex") +_S("apple") +_S("Ograve") +_S("Uacute") +_S("Ucircumflex") +_S("Ugrave") +_S("dotlessi") +_S("circumflex") +_S("tilde") +_S("macron") +_S("breve") +_S("dotaccent") +_S("ring") +_S("cedilla") +_S("hungarumlaut") +_S("ogonek") +_S("caron") +_S("Lslash") +_S("lslash") +_S("Scaron") +_S("scaron") +_S("Zcaron") +_S("zcaron") +_S("brokenbar") +_S("Eth") +_S("eth") +_S("Yacute") +_S("yacute") +_S("Thorn") +_S("thorn") +_S("minus") +_S("multiply") +_S("onesuperior") +_S("twosuperior") +_S("threesuperior") +_S("onehalf") +_S("onequarter") +_S("threequarters") +_S("franc") +_S("Gbreve") +_S("gbreve") +_S("Idotaccent") +_S("Scedilla") +_S("scedilla") +_S("Cacute") +_S("cacute") +_S("Ccaron") +_S("ccaron") +_S("dcroat") + + +#endif /* HB_OT_POST_MACROMAN_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-post-table-v2subset.hh b/gfx/harfbuzz/src/hb-ot-post-table-v2subset.hh new file mode 100644 index 0000000000..d44233610a --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-post-table-v2subset.hh @@ -0,0 +1,142 @@ +/* + * Copyright © 2021 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 HB_OT_POST_TABLE_V2SUBSET_HH +#define HB_OT_POST_TABLE_V2SUBSET_HH + +#include "hb-open-type.hh" +#include "hb-ot-post-table.hh" + +/* + * post -- PostScript + * https://docs.microsoft.com/en-us/typography/opentype/spec/post + */ + +namespace OT { +template<typename Iterator> +HB_INTERNAL bool postV2Tail::serialize (hb_serialize_context_t *c, + Iterator it, + const void* _post) const +{ + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!c->check_success (out))) return_trace (false); + if (!out->glyphNameIndex.serialize (c, + it + | hb_map (hb_second))) + return_trace (false); + + hb_set_t copied_indices; + for (const auto& _ : + it ) + { + unsigned glyph_id = _.first; + unsigned new_index = _.second; + + if (new_index < 258) continue; + if (copied_indices.has (new_index)) continue; + copied_indices.add (new_index); + + hb_bytes_t s = reinterpret_cast<const post::accelerator_t*> (_post)->find_glyph_name (glyph_id); + HBUINT8 *o = c->allocate_size<HBUINT8> (HBUINT8::static_size * (s.length + 1)); + if (unlikely (!o)) return_trace (false); + if (!c->check_assign (o[0], s.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + hb_memcpy (o+1, s.arrayZ, HBUINT8::static_size * s.length); + } + + return_trace (true); +} + +HB_INTERNAL bool postV2Tail::subset (hb_subset_context_t *c) const +{ + TRACE_SUBSET (this); + + const hb_map_t &reverse_glyph_map = *c->plan->reverse_glyph_map; + unsigned num_glyphs = c->plan->num_output_glyphs (); + hb_map_t old_new_index_map, old_gid_new_index_map; + unsigned i = 0; + + post::accelerator_t _post (c->plan->source); + + hb_hashmap_t<hb_bytes_t, uint32_t, true> glyph_name_to_new_index; + + old_new_index_map.alloc (num_glyphs); + old_gid_new_index_map.alloc (num_glyphs); + glyph_name_to_new_index.alloc (num_glyphs); + + for (hb_codepoint_t new_gid = 0; new_gid < num_glyphs; new_gid++) + { + hb_codepoint_t old_gid = reverse_glyph_map.get (new_gid); + unsigned old_index = glyphNameIndex[old_gid]; + + unsigned new_index; + const uint32_t *new_index2; + if (old_index <= 257) + new_index = old_index; + else if (old_new_index_map.has (old_index, &new_index2)) + new_index = *new_index2; + else + { + hb_bytes_t s = _post.find_glyph_name (old_gid); + new_index = glyph_name_to_new_index.get (s); + if (new_index == (unsigned)-1) + { + int standard_glyph_index = -1; + for (unsigned i = 0; i < format1_names_length; i++) + { + if (s == format1_names (i)) + { + standard_glyph_index = i; + break; + } + } + + if (standard_glyph_index == -1) + { + new_index = 258 + i; + i++; + } + else + { new_index = standard_glyph_index; } + glyph_name_to_new_index.set (s, new_index); + } + old_new_index_map.set (old_index, new_index); + } + old_gid_new_index_map.set (old_gid, new_index); + } + + auto index_iter = + + hb_range (num_glyphs) + | hb_map (reverse_glyph_map) + | hb_map_retains_sorting ([&](hb_codepoint_t old_gid) + { + unsigned new_index = old_gid_new_index_map.get (old_gid); + return hb_pair_t<unsigned, unsigned> (old_gid, new_index); + }) + ; + + return_trace (serialize (c->serializer, index_iter, &_post)); +} + +} /* namespace OT */ +#endif /* HB_OT_POST_TABLE_V2SUBSET_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-post-table.hh b/gfx/harfbuzz/src/hb-ot-post-table.hh new file mode 100644 index 0000000000..8132dcfb91 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-post-table.hh @@ -0,0 +1,352 @@ +/* + * 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): Behdad Esfahbod + */ + +#ifndef HB_OT_POST_TABLE_HH +#define HB_OT_POST_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-var-mvar-table.hh" + +#define HB_STRING_ARRAY_NAME format1_names +#define HB_STRING_ARRAY_LIST "hb-ot-post-macroman.hh" +#include "hb-string-array.hh" +#undef HB_STRING_ARRAY_LIST +#undef HB_STRING_ARRAY_NAME + +/* + * post -- PostScript + * https://docs.microsoft.com/en-us/typography/opentype/spec/post + */ +#define HB_OT_TAG_post HB_TAG('p','o','s','t') + + +namespace OT { + + +struct postV2Tail +{ + friend struct post; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (glyphNameIndex.sanitize (c)); + } + + template<typename Iterator> + bool serialize (hb_serialize_context_t *c, + Iterator it, + const void* _post) const; + + bool subset (hb_subset_context_t *c) const; + + protected: + Array16Of<HBUINT16> glyphNameIndex; /* This is not an offset, but is the + * ordinal number of the glyph in 'post' + * string tables. */ +/*UnsizedArrayOf<HBUINT8> + namesX;*/ /* Glyph names with length bytes [variable] + * (a Pascal string). */ + + public: + DEFINE_SIZE_ARRAY (2, glyphNameIndex); +}; + +struct post +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_post; + + bool serialize (hb_serialize_context_t *c, bool glyph_names) const + { + TRACE_SERIALIZE (this); + post *post_prime = c->allocate_min<post> (); + if (unlikely (!post_prime)) return_trace (false); + + hb_memcpy (post_prime, this, post::min_size); + if (!glyph_names) + return_trace (c->check_assign (post_prime->version.major, 3, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); // Version 3 does not have any glyph names. + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *post_prime = c->serializer->start_embed<post> (); + + bool glyph_names = c->plan->flags & HB_SUBSET_FLAGS_GLYPH_NAMES; + if (!serialize (c->serializer, glyph_names)) + return_trace (false); + +#ifndef HB_NO_VAR + if (c->plan->normalized_coords) + { + auto &MVAR = *c->plan->source->table.MVAR; + auto *table = post_prime; + + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_UNDERLINE_SIZE, underlineThickness); + HB_ADD_MVAR_VAR (HB_OT_METRICS_TAG_UNDERLINE_OFFSET, underlinePosition); + } +#endif + + Triple *axis_range; + if (c->plan->user_axes_location.has (HB_TAG ('s','l','n','t'), &axis_range)) + { + float italic_angle = hb_max (-90.f, hb_min (axis_range->middle, 90.f)); + if (post_prime->italicAngle.to_float () != italic_angle) + post_prime->italicAngle.set_float (italic_angle); + } + + if (glyph_names && version.major == 2) + { + hb_barrier (); + return_trace (v2X.subset (c)); + } + + return_trace (true); + } + + struct accelerator_t + { + friend struct postV2Tail; + + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table<post> (face); + unsigned int table_length = table.get_length (); + + version = table->version.to_int (); + if (version != 0x00020000) return; + hb_barrier (); + + const postV2Tail &v2 = table->v2X; + + glyphNameIndex = &v2.glyphNameIndex; + pool = &StructAfter<uint8_t> (v2.glyphNameIndex); + + const uint8_t *end = (const uint8_t *) (const void *) table + table_length; + index_to_offset.alloc (hb_min (face->get_num_glyphs (), table_length / 8)); + for (const uint8_t *data = pool; + index_to_offset.length < 65535 && data < end && data + *data < end; + data += 1 + *data) + index_to_offset.push (data - pool); + } + ~accelerator_t () + { + hb_free (gids_sorted_by_name.get_acquire ()); + table.destroy (); + } + + bool get_glyph_name (hb_codepoint_t glyph, + char *buf, unsigned int buf_len) const + { + hb_bytes_t s = find_glyph_name (glyph); + if (!s.length) return false; + if (!buf_len) return true; + unsigned int len = hb_min (buf_len - 1, s.length); + strncpy (buf, s.arrayZ, len); + buf[len] = '\0'; + return true; + } + + bool get_glyph_from_name (const char *name, int len, + hb_codepoint_t *glyph) const + { + unsigned int count = get_glyph_count (); + if (unlikely (!count)) return false; + + if (len < 0) len = strlen (name); + + if (unlikely (!len)) return false; + + retry: + uint16_t *gids = gids_sorted_by_name.get_acquire (); + + if (unlikely (!gids)) + { + gids = (uint16_t *) hb_malloc (count * sizeof (gids[0])); + if (unlikely (!gids)) + return false; /* Anything better?! */ + + for (unsigned int i = 0; i < count; i++) + gids[i] = i; + hb_qsort (gids, count, sizeof (gids[0]), cmp_gids, (void *) this); + + if (unlikely (!gids_sorted_by_name.cmpexch (nullptr, gids))) + { + hb_free (gids); + goto retry; + } + } + + hb_bytes_t st (name, len); + auto* gid = hb_bsearch (st, gids, count, sizeof (gids[0]), cmp_key, (void *) this); + if (gid) + { + *glyph = *gid; + return true; + } + + return false; + } + + hb_blob_ptr_t<post> table; + + protected: + + unsigned int get_glyph_count () const + { + if (version == 0x00010000) + { + hb_barrier (); + return format1_names_length; + } + + if (version == 0x00020000) + { + hb_barrier (); + return glyphNameIndex->len; + } + + return 0; + } + + static int cmp_gids (const void *pa, const void *pb, void *arg) + { + const accelerator_t *thiz = (const accelerator_t *) arg; + uint16_t a = * (const uint16_t *) pa; + uint16_t b = * (const uint16_t *) pb; + return thiz->find_glyph_name (b).cmp (thiz->find_glyph_name (a)); + } + + static int cmp_key (const void *pk, const void *po, void *arg) + { + const accelerator_t *thiz = (const accelerator_t *) arg; + const hb_bytes_t *key = (const hb_bytes_t *) pk; + uint16_t o = * (const uint16_t *) po; + return thiz->find_glyph_name (o).cmp (*key); + } + + hb_bytes_t find_glyph_name (hb_codepoint_t glyph) const + { + if (version == 0x00010000) + { + hb_barrier (); + if (glyph >= format1_names_length) + return hb_bytes_t (); + + return format1_names (glyph); + } + + if (version != 0x00020000) + return hb_bytes_t (); + hb_barrier (); + + if (glyph >= glyphNameIndex->len) + return hb_bytes_t (); + + unsigned int index = glyphNameIndex->arrayZ[glyph]; + if (index < format1_names_length) + return format1_names (index); + index -= format1_names_length; + + if (index >= index_to_offset.length) + return hb_bytes_t (); + unsigned int offset = index_to_offset[index]; + + const uint8_t *data = pool + offset; + unsigned int name_length = *data; + data++; + + return hb_bytes_t ((const char *) data, name_length); + } + + private: + uint32_t version; + const Array16Of<HBUINT16> *glyphNameIndex = nullptr; + hb_vector_t<uint32_t> index_to_offset; + const uint8_t *pool = nullptr; + hb_atomic_ptr_t<uint16_t *> gids_sorted_by_name; + }; + + bool has_data () const { return version.to_int (); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + (version.to_int () == 0x00010000 || + (version.to_int () == 0x00020000 && v2X.sanitize (c)) || + version.to_int () == 0x00030000)); + } + + public: + FixedVersion<>version; /* 0x00010000 for version 1.0 + * 0x00020000 for version 2.0 + * 0x00025000 for version 2.5 (deprecated) + * 0x00030000 for version 3.0 */ + F16DOT16 italicAngle; /* Italic angle in counter-clockwise degrees + * from the vertical. Zero for upright text, + * negative for text that leans to the right + * (forward). */ + FWORD underlinePosition; /* This is the suggested distance of the top + * of the underline from the baseline + * (negative values indicate below baseline). + * The PostScript definition of this FontInfo + * dictionary key (the y coordinate of the + * center of the stroke) is not used for + * historical reasons. The value of the + * PostScript key may be calculated by + * subtracting half the underlineThickness + * from the value of this field. */ + FWORD underlineThickness; /* Suggested values for the underline + thickness. */ + HBUINT32 isFixedPitch; /* Set to 0 if the font is proportionally + * spaced, non-zero if the font is not + * proportionally spaced (i.e. monospaced). */ + HBUINT32 minMemType42; /* Minimum memory usage when an OpenType font + * is downloaded. */ + HBUINT32 maxMemType42; /* Maximum memory usage when an OpenType font + * is downloaded. */ + HBUINT32 minMemType1; /* Minimum memory usage when an OpenType font + * is downloaded as a Type 1 font. */ + HBUINT32 maxMemType1; /* Maximum memory usage when an OpenType font + * is downloaded as a Type 1 font. */ + postV2Tail v2X; + DEFINE_SIZE_MIN (32); +}; + +struct post_accelerator_t : post::accelerator_t { + post_accelerator_t (hb_face_t *face) : post::accelerator_t (face) {} +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_POST_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc new file mode 100644 index 0000000000..b2eedb027b --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc @@ -0,0 +1,615 @@ +/* + * 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" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shape-fallback.hh" +#include "hb-kern.hh" + +static unsigned int +recategorize_combining_class (hb_codepoint_t u, + unsigned int klass) +{ + if (klass >= 200) + return klass; + + /* Thai / Lao need some per-character work. */ + if ((u & ~0xFF) == 0x0E00u) + { + if (unlikely (klass == 0)) + { + switch (u) + { + case 0x0E31u: + case 0x0E34u: + case 0x0E35u: + case 0x0E36u: + case 0x0E37u: + case 0x0E47u: + case 0x0E4Cu: + case 0x0E4Du: + case 0x0E4Eu: + klass = HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + break; + + case 0x0EB1u: + case 0x0EB4u: + case 0x0EB5u: + case 0x0EB6u: + case 0x0EB7u: + case 0x0EBBu: + case 0x0ECCu: + case 0x0ECDu: + klass = HB_UNICODE_COMBINING_CLASS_ABOVE; + break; + + case 0x0EBCu: + klass = HB_UNICODE_COMBINING_CLASS_BELOW; + break; + } + } else { + /* Thai virama is below-right */ + if (u == 0x0E3Au) + klass = HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT; + } + } + + switch (klass) + { + + /* Hebrew */ + + case HB_MODIFIED_COMBINING_CLASS_CCC10: /* sheva */ + case HB_MODIFIED_COMBINING_CLASS_CCC11: /* hataf segol */ + case HB_MODIFIED_COMBINING_CLASS_CCC12: /* hataf patah */ + case HB_MODIFIED_COMBINING_CLASS_CCC13: /* hataf qamats */ + case HB_MODIFIED_COMBINING_CLASS_CCC14: /* hiriq */ + case HB_MODIFIED_COMBINING_CLASS_CCC15: /* tsere */ + case HB_MODIFIED_COMBINING_CLASS_CCC16: /* segol */ + case HB_MODIFIED_COMBINING_CLASS_CCC17: /* patah */ + case HB_MODIFIED_COMBINING_CLASS_CCC18: /* qamats & qamats qatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC20: /* qubuts */ + case HB_MODIFIED_COMBINING_CLASS_CCC22: /* meteg */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC23: /* rafe */ + return HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC24: /* shin dot */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + + case HB_MODIFIED_COMBINING_CLASS_CCC25: /* sin dot */ + case HB_MODIFIED_COMBINING_CLASS_CCC19: /* holam & holam haser for vav */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT; + + case HB_MODIFIED_COMBINING_CLASS_CCC26: /* point varika */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC21: /* dagesh */ + break; + + + /* Arabic and Syriac */ + + case HB_MODIFIED_COMBINING_CLASS_CCC27: /* fathatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC28: /* dammatan */ + case HB_MODIFIED_COMBINING_CLASS_CCC30: /* fatha */ + case HB_MODIFIED_COMBINING_CLASS_CCC31: /* damma */ + case HB_MODIFIED_COMBINING_CLASS_CCC33: /* shadda */ + case HB_MODIFIED_COMBINING_CLASS_CCC34: /* sukun */ + case HB_MODIFIED_COMBINING_CLASS_CCC35: /* superscript alef */ + case HB_MODIFIED_COMBINING_CLASS_CCC36: /* superscript alaph */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC29: /* kasratan */ + case HB_MODIFIED_COMBINING_CLASS_CCC32: /* kasra */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + + /* Thai */ + + case HB_MODIFIED_COMBINING_CLASS_CCC103: /* sara u / sara uu */ + return HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT; + + case HB_MODIFIED_COMBINING_CLASS_CCC107: /* mai */ + return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT; + + + /* Lao */ + + case HB_MODIFIED_COMBINING_CLASS_CCC118: /* sign u / sign uu */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC122: /* mai */ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + + /* Tibetan */ + + case HB_MODIFIED_COMBINING_CLASS_CCC129: /* sign aa */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + case HB_MODIFIED_COMBINING_CLASS_CCC130: /* sign i*/ + return HB_UNICODE_COMBINING_CLASS_ABOVE; + + case HB_MODIFIED_COMBINING_CLASS_CCC132: /* sign u */ + return HB_UNICODE_COMBINING_CLASS_BELOW; + + } + + return klass; +} + +void +_hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) { + unsigned int combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]); + combining_class = recategorize_combining_class (info[i].codepoint, combining_class); + _hb_glyph_info_set_modified_combining_class (&info[i], combining_class); + } +} + + +static void +zero_mark_advances (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = start; i < end; i++) + if (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) + { + if (adjust_offsets_when_zeroing) + { + buffer->pos[i].x_offset -= buffer->pos[i].x_advance; + buffer->pos[i].y_offset -= buffer->pos[i].y_advance; + } + buffer->pos[i].x_advance = 0; + buffer->pos[i].y_advance = 0; + } +} + +static inline void +position_mark (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer, + hb_glyph_extents_t &base_extents, + unsigned int i, + unsigned int combining_class) +{ + hb_glyph_extents_t mark_extents; + if (!font->get_glyph_extents (buffer->info[i].codepoint, &mark_extents)) + return; + + hb_position_t y_gap = font->y_scale / 16; + + hb_glyph_position_t &pos = buffer->pos[i]; + pos.x_offset = pos.y_offset = 0; + + + /* We don't position LEFT and RIGHT marks. */ + + /* X positioning */ + switch (combining_class) + { + case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: + case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: + if (buffer->props.direction == HB_DIRECTION_LTR) { + pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing; + break; + } else if (buffer->props.direction == HB_DIRECTION_RTL) { + pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing; + break; + } + HB_FALLTHROUGH; + + default: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: + case HB_UNICODE_COMBINING_CLASS_BELOW: + case HB_UNICODE_COMBINING_CLASS_ABOVE: + /* Center align. */ + pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2 - mark_extents.x_bearing; + break; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: + /* Left align. */ + pos.x_offset += base_extents.x_bearing - mark_extents.x_bearing; + break; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: + case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: + case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: + /* Right align. */ + pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width - mark_extents.x_bearing; + break; + } + + /* Y positioning */ + switch (combining_class) + { + case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: + case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_BELOW: + case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: + /* Add gap, fall-through. */ + base_extents.height -= y_gap; + HB_FALLTHROUGH; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: + pos.y_offset = base_extents.y_bearing + base_extents.height - mark_extents.y_bearing; + /* Never shift up "below" marks. */ + if ((y_gap > 0) == (pos.y_offset > 0)) + { + base_extents.height -= pos.y_offset; + pos.y_offset = 0; + } + base_extents.height += mark_extents.height; + break; + + case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: + case HB_UNICODE_COMBINING_CLASS_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: + /* Add gap, fall-through. */ + base_extents.y_bearing += y_gap; + base_extents.height -= y_gap; + HB_FALLTHROUGH; + + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: + case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: + pos.y_offset = base_extents.y_bearing - (mark_extents.y_bearing + mark_extents.height); + /* Don't shift down "above" marks too much. */ + if ((y_gap > 0) != (pos.y_offset > 0)) + { + int correction = -pos.y_offset / 2; + base_extents.y_bearing += correction; + base_extents.height -= correction; + pos.y_offset += correction; + } + base_extents.y_bearing -= mark_extents.height; + base_extents.height += mark_extents.height; + break; + } +} + +static inline void +position_around_base (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int base, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + hb_direction_t horiz_dir = HB_DIRECTION_INVALID; + + buffer->unsafe_to_break (base, end); + + hb_glyph_extents_t base_extents; + if (!font->get_glyph_extents (buffer->info[base].codepoint, + &base_extents)) + { + /* If extents don't work, zero marks and go home. */ + zero_mark_advances (buffer, base + 1, end, adjust_offsets_when_zeroing); + return; + } + base_extents.y_bearing += buffer->pos[base].y_offset; + /* Use horizontal advance for horizontal positioning. + * Generally a better idea. Also works for zero-ink glyphs. See: + * https://github.com/harfbuzz/harfbuzz/issues/1532 */ + base_extents.x_bearing = 0; + base_extents.width = font->get_glyph_h_advance (buffer->info[base].codepoint); + + unsigned int lig_id = _hb_glyph_info_get_lig_id (&buffer->info[base]); + /* Use integer for num_lig_components such that it doesn't convert to unsigned + * when we divide or multiply by it. */ + int num_lig_components = _hb_glyph_info_get_lig_num_comps (&buffer->info[base]); + + hb_position_t x_offset = 0, y_offset = 0; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { + x_offset -= buffer->pos[base].x_advance; + y_offset -= buffer->pos[base].y_advance; + } + + hb_glyph_extents_t component_extents = base_extents; + int last_lig_component = -1; + unsigned int last_combining_class = 255; + hb_glyph_extents_t cluster_extents = base_extents; /* Initialization is just to shut gcc up. */ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = base + 1; i < end; i++) + if (_hb_glyph_info_get_modified_combining_class (&info[i])) + { + if (num_lig_components > 1) { + unsigned int this_lig_id = _hb_glyph_info_get_lig_id (&info[i]); + int this_lig_component = _hb_glyph_info_get_lig_comp (&info[i]) - 1; + /* Conditions for attaching to the last component. */ + if (!lig_id || lig_id != this_lig_id || this_lig_component >= num_lig_components) + this_lig_component = num_lig_components - 1; + if (last_lig_component != this_lig_component) + { + last_lig_component = this_lig_component; + last_combining_class = 255; + component_extents = base_extents; + if (unlikely (horiz_dir == HB_DIRECTION_INVALID)) { + if (HB_DIRECTION_IS_HORIZONTAL (plan->props.direction)) + horiz_dir = plan->props.direction; + else + horiz_dir = hb_script_get_horizontal_direction (plan->props.script); + } + if (horiz_dir == HB_DIRECTION_LTR) + component_extents.x_bearing += (this_lig_component * component_extents.width) / num_lig_components; + else + component_extents.x_bearing += ((num_lig_components - 1 - this_lig_component) * component_extents.width) / num_lig_components; + component_extents.width /= num_lig_components; + } + } + + unsigned int this_combining_class = _hb_glyph_info_get_modified_combining_class (&info[i]); + if (last_combining_class != this_combining_class) + { + last_combining_class = this_combining_class; + cluster_extents = component_extents; + } + + position_mark (plan, font, buffer, cluster_extents, i, this_combining_class); + + buffer->pos[i].x_advance = 0; + buffer->pos[i].y_advance = 0; + buffer->pos[i].x_offset += x_offset; + buffer->pos[i].y_offset += y_offset; + + } else { + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) { + x_offset -= buffer->pos[i].x_advance; + y_offset -= buffer->pos[i].y_advance; + } else { + x_offset += buffer->pos[i].x_advance; + y_offset += buffer->pos[i].y_advance; + } + } +} + +static inline void +position_cluster (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool adjust_offsets_when_zeroing) +{ + if (end - start < 2) + return; + + /* Find the base glyph */ + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = start; i < end; i++) + if (!_hb_glyph_info_is_unicode_mark (&info[i])) + { + /* Find mark glyphs */ + unsigned int j; + for (j = i + 1; j < end; j++) + if (!_hb_glyph_info_is_unicode_mark (&info[j])) + break; + + position_around_base (plan, font, buffer, i, j, adjust_offsets_when_zeroing); + + i = j - 1; + } +} + +void +_hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + bool adjust_offsets_when_zeroing) +{ +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + + if (!buffer->message (font, "start fallback mark")) + return; + + _hb_buffer_assert_gsubgpos_vars (buffer); + + unsigned int start = 0; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (likely (!_hb_glyph_info_is_unicode_mark (&info[i]))) { + position_cluster (plan, font, buffer, start, i, adjust_offsets_when_zeroing); + start = i; + } + position_cluster (plan, font, buffer, start, count, adjust_offsets_when_zeroing); + + (void) buffer->message (font, "end fallback mark"); +} + + +#ifndef HB_DISABLE_DEPRECATED +struct hb_ot_shape_fallback_kern_driver_t +{ + hb_ot_shape_fallback_kern_driver_t (hb_font_t *font_, + hb_buffer_t *buffer) : + font (font_), direction (buffer->props.direction) {} + + hb_position_t get_kerning (hb_codepoint_t first, hb_codepoint_t second) const + { + hb_position_t kern = 0; + font->get_glyph_kerning_for_direction (first, second, + direction, + &kern, &kern); + return kern; + } + + hb_font_t *font; + hb_direction_t direction; +}; +#endif + +/* Performs font-assisted kerning. */ +void +_hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ +#ifdef HB_NO_OT_SHAPE_FALLBACK + return; +#endif + +#ifndef HB_DISABLE_DEPRECATED + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ? + !font->has_glyph_h_kerning_func () : + !font->has_glyph_v_kerning_func ()) + return; + + if (!buffer->message (font, "start fallback kern")) + return; + + bool reverse = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + if (reverse) + buffer->reverse (); + + hb_ot_shape_fallback_kern_driver_t driver (font, buffer); + OT::hb_kern_machine_t<hb_ot_shape_fallback_kern_driver_t> machine (driver); + machine.kern (font, buffer, plan->kern_mask, false); + + if (reverse) + buffer->reverse (); + + (void) buffer->message (font, "end fallback kern"); +#endif +} + + +/* Adjusts width of various spaces. */ +void +_hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + bool horizontal = HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction); + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_is_unicode_space (&info[i]) && !_hb_glyph_info_ligated (&info[i])) + { + /* If font had no ASCII space and we used the invisible glyph, give it a 1/4 EM default advance. */ + if (buffer->invisible && info[i].codepoint == buffer->invisible) + { + if (horizontal) + pos[i].x_advance = +font->x_scale / 4; + else + pos[i].y_advance = -font->y_scale / 4; + } + + hb_unicode_funcs_t::space_t space_type = _hb_glyph_info_get_unicode_space_fallback_type (&info[i]); + hb_codepoint_t glyph; + typedef hb_unicode_funcs_t t; + switch (space_type) + { + case t::NOT_SPACE: /* Shouldn't happen. */ + case t::SPACE: + break; + + case t::SPACE_EM: + case t::SPACE_EM_2: + case t::SPACE_EM_3: + case t::SPACE_EM_4: + case t::SPACE_EM_5: + case t::SPACE_EM_6: + case t::SPACE_EM_16: + if (horizontal) + pos[i].x_advance = +(font->x_scale + ((int) space_type)/2) / (int) space_type; + else + pos[i].y_advance = -(font->y_scale + ((int) space_type)/2) / (int) space_type; + break; + + case t::SPACE_4_EM_18: + if (horizontal) + pos[i].x_advance = (int64_t) +font->x_scale * 4 / 18; + else + pos[i].y_advance = (int64_t) -font->y_scale * 4 / 18; + break; + + case t::SPACE_FIGURE: + for (char u = '0'; u <= '9'; u++) + if (font->get_nominal_glyph (u, &glyph)) + { + if (horizontal) + pos[i].x_advance = font->get_glyph_h_advance (glyph); + else + pos[i].y_advance = font->get_glyph_v_advance (glyph); + break; + } + break; + + case t::SPACE_PUNCTUATION: + if (font->get_nominal_glyph ('.', &glyph) || + font->get_nominal_glyph (',', &glyph)) + { + if (horizontal) + pos[i].x_advance = font->get_glyph_h_advance (glyph); + else + pos[i].y_advance = font->get_glyph_v_advance (glyph); + } + break; + + case t::SPACE_NARROW: + /* Half-space? + * Unicode doc https://unicode.org/charts/PDF/U2000.pdf says ~1/4 or 1/5 of EM. + * However, in my testing, many fonts have their regular space being about that + * size. To me, a percentage of the space width makes more sense. Half is as + * good as any. */ + if (horizontal) + pos[i].x_advance /= 2; + else + pos[i].y_advance /= 2; + break; + } + } +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shape-fallback.hh b/gfx/harfbuzz/src/hb-ot-shape-fallback.hh new file mode 100644 index 0000000000..5faf5f2dfb --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.hh @@ -0,0 +1,54 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_FALLBACK_HH +#define HB_OT_SHAPE_FALLBACK_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" + + +HB_INTERNAL void _hb_ot_shape_fallback_mark_position (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer, + bool adjust_offsets_when_zeroing); + +HB_INTERNAL void _hb_ot_shape_fallback_mark_position_recategorize_marks (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +HB_INTERNAL void _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +HB_INTERNAL void _hb_ot_shape_fallback_spaces (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_OT_SHAPE_FALLBACK_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc new file mode 100644 index 0000000000..69dbec0783 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc @@ -0,0 +1,488 @@ +/* + * 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" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shape-normalize.hh" +#include "hb-ot-shaper.hh" +#include "hb-ot-shape.hh" + + +/* + * HIGHLEVEL DESIGN: + * + * This file exports one main function: _hb_ot_shape_normalize(). + * + * This function closely reflects the Unicode Normalization Algorithm, + * yet it's different. + * + * Each shaper specifies whether it prefers decomposed (NFD) or composed (NFC). + * The logic however tries to use whatever the font can support. + * + * In general what happens is that: each grapheme is decomposed in a chain + * of 1:2 decompositions, marks reordered, and then recomposed if desired, + * so far it's like Unicode Normalization. However, the decomposition and + * recomposition only happens if the font supports the resulting characters. + * + * The goals are: + * + * - Try to render all canonically equivalent strings similarly. To really + * achieve this we have to always do the full decomposition and then + * selectively recompose from there. It's kinda too expensive though, so + * we skip some cases. For example, if composed is desired, we simply + * don't touch 1-character clusters that are supported by the font, even + * though their NFC may be different. + * + * - When a font has a precomposed character for a sequence but the 'ccmp' + * feature in the font is not adequate, use the precomposed character + * which typically has better mark positioning. + * + * - When a font does not support a combining mark, but supports it precomposed + * with previous base, use that. This needs the itemizer to have this + * knowledge too. We need to provide assistance to the itemizer. + * + * - When a font does not support a character but supports its canonical + * decomposition, well, use the decomposition. + * + * - The shapers can customize the compose and decompose functions to + * offload some of their requirements to the normalizer. For example, the + * Indic shaper may want to disallow recomposing of two matras. + */ + +static bool +decompose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_unicode (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + return (bool) c->unicode->compose (a, b, ab); +} + +static inline void +set_glyph (hb_glyph_info_t &info, hb_font_t *font) +{ + (void) font->get_nominal_glyph (info.codepoint, &info.glyph_index()); +} + +static inline void +output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph) +{ + /* This is very confusing indeed. */ + buffer->cur().glyph_index() = glyph; + (void) buffer->output_glyph (unichar); + _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer); +} + +static inline void +next_char (hb_buffer_t *buffer, hb_codepoint_t glyph) +{ + buffer->cur().glyph_index() = glyph; + (void) buffer->next_glyph (); +} + +static inline void +skip_char (hb_buffer_t *buffer) +{ + buffer->skip_glyph (); +} + +/* Returns 0 if didn't decompose, number of resulting characters otherwise. */ +static inline unsigned int +decompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint_t ab) +{ + hb_codepoint_t a = 0, b = 0, a_glyph = 0, b_glyph = 0; + hb_buffer_t * const buffer = c->buffer; + hb_font_t * const font = c->font; + + if (!c->decompose (c, ab, &a, &b) || + (b && !font->get_nominal_glyph (b, &b_glyph))) + return 0; + + bool has_a = (bool) font->get_nominal_glyph (a, &a_glyph); + if (shortest && has_a) { + /* Output a and b */ + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; + } + + if (unsigned ret = decompose (c, shortest, a)) { + if (b) { + output_char (buffer, b, b_glyph); + return ret + 1; + } + return ret; + } + + if (has_a) { + output_char (buffer, a, a_glyph); + if (likely (b)) { + output_char (buffer, b, b_glyph); + return 2; + } + return 1; + } + + return 0; +} + +static inline void +decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest) +{ + hb_buffer_t * const buffer = c->buffer; + hb_codepoint_t u = buffer->cur().codepoint; + hb_codepoint_t glyph = 0; + + if (shortest && c->font->get_nominal_glyph (u, &glyph, c->not_found)) + { + next_char (buffer, glyph); + return; + } + + if (decompose (c, shortest, u)) + { + skip_char (buffer); + return; + } + + if (!shortest && c->font->get_nominal_glyph (u, &glyph, c->not_found)) + { + next_char (buffer, glyph); + return; + } + + if (_hb_glyph_info_is_unicode_space (&buffer->cur())) + { + hb_codepoint_t space_glyph; + hb_unicode_funcs_t::space_t space_type = buffer->unicode->space_fallback_type (u); + if (space_type != hb_unicode_funcs_t::NOT_SPACE && + (c->font->get_nominal_glyph (0x0020, &space_glyph) || (space_glyph = buffer->invisible))) + { + _hb_glyph_info_set_unicode_space_fallback_type (&buffer->cur(), space_type); + next_char (buffer, space_glyph); + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK; + return; + } + } + + if (u == 0x2011u) + { + /* U+2011 is the only sensible character that is a no-break version of another character + * and not a space. The space ones are handled already. Handle this lone one. */ + hb_codepoint_t other_glyph; + if (c->font->get_nominal_glyph (0x2010u, &other_glyph)) + { + next_char (buffer, other_glyph); + return; + } + } + + next_char (buffer, glyph); /* glyph is initialized in earlier branches. */ +} + +static inline void +handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, + unsigned int end, + bool short_circuit HB_UNUSED) +{ + /* Currently if there's a variation-selector we give-up on normalization, it's just too hard. */ + hb_buffer_t * const buffer = c->buffer; + hb_font_t * const font = c->font; + for (; buffer->idx < end - 1 && buffer->successful;) { + if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) { + if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index())) + { + hb_codepoint_t unicode = buffer->cur().codepoint; + (void) buffer->replace_glyphs (2, 1, &unicode); + } + else + { + /* Just pass on the two characters separately, let GSUB do its magic. */ + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } + /* Skip any further variation selectors. */ + while (buffer->idx < end && + buffer->successful && + unlikely (buffer->unicode->is_variation_selector (buffer->cur().codepoint))) + { + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } + } + else + { + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } + } + if (likely (buffer->idx < end)) + { + set_glyph (buffer->cur(), font); + (void) buffer->next_glyph (); + } +} + +static inline void +decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit) +{ + hb_buffer_t * const buffer = c->buffer; + for (unsigned int i = buffer->idx; i < end && buffer->successful; i++) + if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) { + handle_variation_selector_cluster (c, end, short_circuit); + return; + } + + while (buffer->idx < end && buffer->successful) + decompose_current_character (c, short_circuit); +} + + +static int +compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + unsigned int a = _hb_glyph_info_get_modified_combining_class (pa); + unsigned int b = _hb_glyph_info_get_modified_combining_class (pb); + + return a < b ? -1 : a == b ? 0 : +1; +} + + +void +_hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + if (unlikely (!buffer->len)) return; + + _hb_buffer_assert_unicode_vars (buffer); + + hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference; + if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_AUTO) + { + if (plan->has_gpos_mark) + // https://github.com/harfbuzz/harfbuzz/issues/653#issuecomment-423905920 + //mode = HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED; + mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; + else + mode = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS; + } + + const hb_ot_shape_normalize_context_t c = { + plan, + buffer, + font, + buffer->unicode, + buffer->not_found, + plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode, + plan->shaper->compose ? plan->shaper->compose : compose_unicode + }; + + bool always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE; + bool might_short_circuit = always_short_circuit || + (mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED && + mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT); + unsigned int count; + + /* We do a fairly straightforward yet custom normalization process in three + * separate rounds: decompose, reorder, recompose (if desired). Currently + * this makes two buffer swaps. We can make it faster by moving the last + * two rounds into the inner loop for the first round, but it's more readable + * this way. */ + + + /* First round, decompose */ + + bool all_simple = true; + { + buffer->clear_output (); + count = buffer->len; + buffer->idx = 0; + do + { + unsigned int end; + for (end = buffer->idx + 1; end < count; end++) + if (_hb_glyph_info_is_unicode_mark (&buffer->info[end])) + break; + + if (end < count) + end--; /* Leave one base for the marks to cluster with. */ + + /* From idx to end are simple clusters. */ + if (might_short_circuit) + { + unsigned int done = font->get_nominal_glyphs (end - buffer->idx, + &buffer->cur().codepoint, + sizeof (buffer->info[0]), + &buffer->cur().glyph_index(), + sizeof (buffer->info[0])); + if (unlikely (!buffer->next_glyphs (done))) break; + } + while (buffer->idx < end && buffer->successful) + decompose_current_character (&c, might_short_circuit); + + if (buffer->idx == count || !buffer->successful) + break; + + all_simple = false; + + /* Find all the marks now. */ + for (end = buffer->idx + 1; end < count; end++) + if (!_hb_glyph_info_is_unicode_mark(&buffer->info[end])) + break; + + /* idx to end is one non-simple cluster. */ + decompose_multi_char_cluster (&c, end, always_short_circuit); + } + while (buffer->idx < count && buffer->successful); + buffer->sync (); + } + + + /* Second round, reorder (inplace) */ + + if (!all_simple && buffer->message(font, "start reorder")) + { + count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + if (_hb_glyph_info_get_modified_combining_class (&info[i]) == 0) + continue; + + unsigned int end; + for (end = i + 1; end < count; end++) + if (_hb_glyph_info_get_modified_combining_class (&info[end]) == 0) + break; + + /* We are going to do a O(n^2). Only do this if the sequence is short. */ + if (end - i > HB_OT_SHAPE_MAX_COMBINING_MARKS) { + i = end; + continue; + } + + buffer->sort (i, end, compare_combining_class); + + if (plan->shaper->reorder_marks) + plan->shaper->reorder_marks (plan, buffer, i, end); + + i = end; + } + (void) buffer->message(font, "end reorder"); + } + if (buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CGJ) + { + /* For all CGJ, check if it prevented any reordering at all. + * If it did NOT, then make it skippable. + * https://github.com/harfbuzz/harfbuzz/issues/554 + */ + unsigned count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i + 1 < count; i++) + if (info[i].codepoint == 0x034Fu/*CGJ*/ && + (info_cc(info[i+1]) == 0 || info_cc(info[i-1]) <= info_cc(info[i+1]))) + { + _hb_glyph_info_unhide (&info[i]); + } + } + + + /* Third round, recompose */ + + if (!all_simple && + buffer->successful && + (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS || + mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT)) + { + /* As noted in the comment earlier, we don't try to combine + * ccc=0 chars with their previous Starter. */ + + buffer->clear_output (); + count = buffer->len; + unsigned int starter = 0; + (void) buffer->next_glyph (); + while (buffer->idx < count /* No need for: && buffer->successful */) + { + hb_codepoint_t composed, glyph; + if (/* We don't try to compose a non-mark character with it's preceding starter. + * This is both an optimization to avoid trying to compose every two neighboring + * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul + * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */ + _hb_glyph_info_is_unicode_mark(&buffer->cur())) + { + if (/* If there's anything between the starter and this char, they should have CCC + * smaller than this character's. */ + (starter == buffer->out_len - 1 || + info_cc (buffer->prev()) < info_cc (buffer->cur())) && + /* And compose. */ + c.compose (&c, + buffer->out_info[starter].codepoint, + buffer->cur().codepoint, + &composed) && + /* And the font has glyph for the composite. */ + font->get_nominal_glyph (composed, &glyph)) + { + /* Composes. */ + if (unlikely (!buffer->next_glyph ())) break; /* Copy to out-buffer. */ + buffer->merge_out_clusters (starter, buffer->out_len); + buffer->out_len--; /* Remove the second composable. */ + /* Modify starter and carry on. */ + buffer->out_info[starter].codepoint = composed; + buffer->out_info[starter].glyph_index() = glyph; + _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer); + + continue; + } + } + + /* Blocked, or doesn't compose. */ + if (unlikely (!buffer->next_glyph ())) break; + + if (info_cc (buffer->prev()) == 0) + starter = buffer->out_len - 1; + } + buffer->sync (); + } +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shape-normalize.hh b/gfx/harfbuzz/src/hb-ot-shape-normalize.hh new file mode 100644 index 0000000000..12c78a2352 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.hh @@ -0,0 +1,71 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_NORMALIZE_HH +#define HB_OT_SHAPE_NORMALIZE_HH + +#include "hb.hh" + + +/* buffer var allocations, used during the normalization process */ +#define glyph_index() var1.u32 + +struct hb_ot_shape_plan_t; + +enum hb_ot_shape_normalization_mode_t { + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED, + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* Never composes base-to-base */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* Always fully decomposes and then recompose back */ + + HB_OT_SHAPE_NORMALIZATION_MODE_AUTO, /* See hb-ot-shape-normalize.cc for logic. */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_AUTO +}; + +HB_INTERNAL void _hb_ot_shape_normalize (const hb_ot_shape_plan_t *shaper, + hb_buffer_t *buffer, + hb_font_t *font); + + +struct hb_ot_shape_normalize_context_t +{ + const hb_ot_shape_plan_t *plan; + hb_buffer_t *buffer; + hb_font_t *font; + hb_unicode_funcs_t *unicode; + const hb_codepoint_t not_found; + bool (*decompose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + bool (*compose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); +}; + + +#endif /* HB_OT_SHAPE_NORMALIZE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shape.cc b/gfx/harfbuzz/src/hb-ot-shape.cc new file mode 100644 index 0000000000..90f596ae79 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape.cc @@ -0,0 +1,1318 @@ +/* + * Copyright © 2009,2010 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#ifdef HB_NO_OT_LAYOUT +#error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." +#endif + +#include "hb-shaper-impl.hh" + +#include "hb-ot-shape.hh" +#include "hb-ot-shaper.hh" +#include "hb-ot-shape-fallback.hh" +#include "hb-ot-shape-normalize.hh" + +#include "hb-ot-face.hh" + +#include "hb-set.hh" + +#include "hb-aat-layout.hh" + +static inline bool +_hb_codepoint_is_regional_indicator (hb_codepoint_t u) +{ return hb_in_range<hb_codepoint_t> (u, 0x1F1E6u, 0x1F1FFu); } + +#ifndef HB_NO_AAT_SHAPE +static inline bool +_hb_apply_morx (hb_face_t *face, const hb_segment_properties_t &props) +{ + /* https://github.com/harfbuzz/harfbuzz/issues/2124 */ + return hb_aat_layout_has_substitution (face) && + (HB_DIRECTION_IS_HORIZONTAL (props.direction) || !hb_ot_layout_has_substitution (face)); +} +#endif + +/** + * SECTION:hb-ot-shape + * @title: hb-ot-shape + * @short_description: OpenType shaping support + * @include: hb-ot.h + * + * Support functions for OpenType shaping related queries. + **/ + + +static void +hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, + const hb_feature_t *user_features, + unsigned int num_user_features); + +hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face, + const hb_segment_properties_t &props) : + face (face), + props (props), + map (face, props) +#ifndef HB_NO_AAT_SHAPE + , apply_morx (_hb_apply_morx (face, props)) +#endif +{ + shaper = hb_ot_shaper_categorize (this); + + script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; + script_fallback_mark_positioning = shaper->fallback_position; + +#ifndef HB_NO_AAT_SHAPE + /* https://github.com/harfbuzz/harfbuzz/issues/1528 */ + if (apply_morx && shaper != &_hb_ot_shaper_default) + shaper = &_hb_ot_shaper_dumber; +#endif +} + +void +hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, + const hb_ot_shape_plan_key_t &key) +{ + plan.props = props; + plan.shaper = shaper; + map.compile (plan.map, key); + +#ifndef HB_NO_OT_SHAPE_FRACTIONS + plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); + plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); + plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); + plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); +#endif + + plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); + plan.has_vert = !!plan.map.get_1_mask (HB_TAG ('v','e','r','t')); + + hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? + HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); +#ifndef HB_NO_OT_KERN + plan.kern_mask = plan.map.get_mask (kern_tag); + plan.requested_kerning = !!plan.kern_mask; +#endif +#ifndef HB_NO_AAT_SHAPE + plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); + plan.requested_tracking = !!plan.trak_mask; +#endif + + bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; + bool disable_gpos = plan.shaper->gpos_tag && + plan.shaper->gpos_tag != plan.map.chosen_script[1]; + + /* + * Decide who provides glyph classes. GDEF or Unicode. + */ + + if (!hb_ot_layout_has_glyph_classes (face)) + plan.fallback_glyph_classes = true; + + /* + * Decide who does substitutions. GSUB, morx, or fallback. + */ + +#ifndef HB_NO_AAT_SHAPE + plan.apply_morx = apply_morx; +#endif + + /* + * Decide who does positioning. GPOS, kerx, kern, or fallback. + */ + +#ifndef HB_NO_AAT_SHAPE + bool has_kerx = hb_aat_layout_has_positioning (face); + bool has_gsub = !apply_morx && hb_ot_layout_has_substitution (face); +#endif + bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); + if (false) + ; +#ifndef HB_NO_AAT_SHAPE + /* Prefer GPOS over kerx if GSUB is present; + * https://github.com/harfbuzz/harfbuzz/issues/3008 */ + else if (has_kerx && !(has_gsub && has_gpos)) + plan.apply_kerx = true; +#endif + else if (has_gpos) + plan.apply_gpos = true; + + if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) + { +#ifndef HB_NO_AAT_SHAPE + if (has_kerx) + plan.apply_kerx = true; + else +#endif +#ifndef HB_NO_OT_KERN + if (hb_ot_layout_has_kerning (face)) + plan.apply_kern = true; +#endif + } + + plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); + + plan.zero_marks = script_zero_marks && + !plan.apply_kerx && + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_machine_kerning (face) +#endif + ); + plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); + + plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && + !plan.apply_kerx && + (!plan.apply_kern +#ifndef HB_NO_OT_KERN + || !hb_ot_layout_has_cross_kerning (face) +#endif + ); + + plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && + script_fallback_mark_positioning; + +#ifndef HB_NO_AAT_SHAPE + /* If we're using morx shaping, we cancel mark position adjustment because + Apple Color Emoji assumes this will NOT be done when forming emoji sequences; + https://github.com/harfbuzz/harfbuzz/issues/2967. */ + if (plan.apply_morx) + plan.adjust_mark_positioning_when_zeroing = false; + + /* Currently we always apply trak. */ + plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); +#endif +} + +bool +hb_ot_shape_plan_t::init0 (hb_face_t *face, + const hb_shape_plan_key_t *key) +{ + map.init (); + + hb_ot_shape_planner_t planner (face, + key->props); + + hb_ot_shape_collect_features (&planner, + key->user_features, + key->num_user_features); + + planner.compile (*this, key->ot); + + if (shaper->data_create) + { + data = shaper->data_create (this); + if (unlikely (!data)) + { + map.fini (); + return false; + } + } + + return true; +} + +void +hb_ot_shape_plan_t::fini () +{ + if (shaper->data_destroy) + shaper->data_destroy (const_cast<void *> (data)); + + map.fini (); +} + +void +hb_ot_shape_plan_t::substitute (hb_font_t *font, + hb_buffer_t *buffer) const +{ + map.substitute (this, font, buffer); +} + +void +hb_ot_shape_plan_t::position (hb_font_t *font, + hb_buffer_t *buffer) const +{ + if (this->apply_gpos) + map.position (this, font, buffer); +#ifndef HB_NO_AAT_SHAPE + else if (this->apply_kerx) + hb_aat_layout_position (this, font, buffer); +#endif + +#ifndef HB_NO_OT_KERN + if (this->apply_kern) + hb_ot_layout_kern (this, font, buffer); +#endif + else if (this->apply_fallback_kern) + _hb_ot_shape_fallback_kern (this, font, buffer); + +#ifndef HB_NO_AAT_SHAPE + if (this->apply_trak) + hb_aat_layout_track (this, font, buffer); +#endif +} + + +static const hb_ot_map_feature_t +common_features[] = +{ + {HB_TAG('a','b','v','m'), F_GLOBAL}, + {HB_TAG('b','l','w','m'), F_GLOBAL}, + {HB_TAG('c','c','m','p'), F_GLOBAL}, + {HB_TAG('l','o','c','l'), F_GLOBAL}, + {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('r','l','i','g'), F_GLOBAL}, +}; + + +static const hb_ot_map_feature_t +horizontal_features[] = +{ + {HB_TAG('c','a','l','t'), F_GLOBAL}, + {HB_TAG('c','l','i','g'), F_GLOBAL}, + {HB_TAG('c','u','r','s'), F_GLOBAL}, + {HB_TAG('d','i','s','t'), F_GLOBAL}, + {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, + {HB_TAG('l','i','g','a'), F_GLOBAL}, + {HB_TAG('r','c','l','t'), F_GLOBAL}, +}; + +static void +hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, + const hb_feature_t *user_features, + unsigned int num_user_features) +{ + hb_ot_map_builder_t *map = &planner->map; + + map->is_simple = true; + + map->enable_feature (HB_TAG('r','v','r','n')); + map->add_gsub_pause (nullptr); + + switch (planner->props.direction) + { + case HB_DIRECTION_LTR: + map->enable_feature (HB_TAG ('l','t','r','a')); + map->enable_feature (HB_TAG ('l','t','r','m')); + break; + case HB_DIRECTION_RTL: + map->enable_feature (HB_TAG ('r','t','l','a')); + map->add_feature (HB_TAG ('r','t','l','m')); + break; + case HB_DIRECTION_TTB: + case HB_DIRECTION_BTT: + case HB_DIRECTION_INVALID: + default: + break; + } + +#ifndef HB_NO_OT_SHAPE_FRACTIONS + /* Automatic fractions. */ + map->add_feature (HB_TAG ('f','r','a','c')); + map->add_feature (HB_TAG ('n','u','m','r')); + map->add_feature (HB_TAG ('d','n','o','m')); +#endif + + /* Random! */ + map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); + +#ifndef HB_NO_AAT_SHAPE + /* Tracking. We enable dummy feature here just to allow disabling + * AAT 'trak' table using features. + * https://github.com/harfbuzz/harfbuzz/issues/1303 */ + map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); +#endif + + map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */ + map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */ + + if (planner->shaper->collect_features) + { + map->is_simple = false; + planner->shaper->collect_features (planner); + } + + map->enable_feature (HB_TAG ('B','u','z','z')); /* Considered required. */ + map->enable_feature (HB_TAG ('B','U','Z','Z')); /* Considered discretionary. */ + + for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) + map->add_feature (common_features[i]); + + if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction)) + for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) + map->add_feature (horizontal_features[i]); + else + { + /* We only apply `vert` feature. See: + * https://github.com/harfbuzz/harfbuzz/commit/d71c0df2d17f4590d5611239577a6cb532c26528 + * https://lists.freedesktop.org/archives/harfbuzz/2013-August/003490.html */ + + /* We really want to find a 'vert' feature if there's any in the font, no + * matter which script/langsys it is listed (or not) under. + * See various bugs referenced from: + * https://github.com/harfbuzz/harfbuzz/issues/63 */ + map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH); + } + + if (num_user_features) + map->is_simple = false; + for (unsigned int i = 0; i < num_user_features; i++) + { + const hb_feature_t *feature = &user_features[i]; + map->add_feature (feature->tag, + (feature->start == HB_FEATURE_GLOBAL_START && + feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE, + feature->value); + } + + if (planner->shaper->override_features) + planner->shaper->override_features (planner); +} + + +/* + * shaper face data + */ + +struct hb_ot_face_data_t {}; + +hb_ot_face_data_t * +_hb_ot_shaper_face_data_create (hb_face_t *face) +{ + return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) +{ +} + + +/* + * shaper font data + */ + +struct hb_ot_font_data_t {}; + +hb_ot_font_data_t * +_hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +struct hb_ot_shape_context_t +{ + hb_ot_shape_plan_t *plan; + hb_font_t *font; + hb_face_t *face; + hb_buffer_t *buffer; + const hb_feature_t *user_features; + unsigned int num_user_features; + + /* Transient stuff */ + hb_direction_t target_direction; +}; + + + +/* Main shaper */ + + +/* Prepare */ + +static void +hb_set_unicode_props (hb_buffer_t *buffer) +{ + /* Implement enough of Unicode Graphemes here that shaping + * in reverse-direction wouldn't break graphemes. Namely, + * we mark all marks and ZWJ and ZWJ,Extended_Pictographic + * sequences as continuations. The foreach_grapheme() + * macro uses this bit. + * + * https://www.unicode.org/reports/tr29/#Regex_Definitions + */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + _hb_glyph_info_set_unicode_props (&info[i], buffer); + + unsigned gen_cat = _hb_glyph_info_get_general_category (&info[i]); + if (FLAG_UNSAFE (gen_cat) & + (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | + FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR))) + continue; + + /* Marks are already set as continuation by the above line. + * Handle Emoji_Modifier and ZWJ-continuation. */ + if (unlikely (gen_cat == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && + hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) + { + _hb_glyph_info_set_continuation (&info[i]); + } + /* Regional_Indicators are hairy as hell... + * https://github.com/harfbuzz/harfbuzz/issues/2265 */ + else if (unlikely (i && _hb_codepoint_is_regional_indicator (info[i].codepoint))) + { + if (_hb_codepoint_is_regional_indicator (info[i - 1].codepoint) && + !_hb_glyph_info_is_continuation (&info[i - 1])) + _hb_glyph_info_set_continuation (&info[i]); + } +#ifndef HB_NO_EMOJI_SEQUENCES + else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) + { + _hb_glyph_info_set_continuation (&info[i]); + if (i + 1 < count && + _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) + { + i++; + _hb_glyph_info_set_unicode_props (&info[i], buffer); + _hb_glyph_info_set_continuation (&info[i]); + } + } +#endif + /* Or part of the Other_Grapheme_Extend that is not marks. + * As of Unicode 15 that is just: + * + * 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER + * FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK + * E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG + * + * ZWNJ is special, we don't want to merge it as there's no need, and keeping + * it separate results in more granular clusters. + * Tags are used for Emoji sub-region flag sequences: + * https://github.com/harfbuzz/harfbuzz/issues/1556 + * Katakana ones were requested: + * https://github.com/harfbuzz/harfbuzz/issues/3844 + */ + else if (unlikely (hb_in_ranges<hb_codepoint_t> (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu))) + _hb_glyph_info_set_continuation (&info[i]); + } +} + +static void +hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) +{ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + + if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || + buffer->context_len[0] || + !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) + return; + + if (!font->has_glyph (0x25CCu)) + return; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + _hb_glyph_info_set_unicode_props (&dottedcircle, buffer); + + buffer->clear_output (); + + buffer->idx = 0; + hb_glyph_info_t info = dottedcircle; + info.cluster = buffer->cur().cluster; + info.mask = buffer->cur().mask; + (void) buffer->output_info (info); + + buffer->sync (); +} + +static void +hb_form_clusters (hb_buffer_t *buffer) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) + return; + + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + foreach_grapheme (buffer, start, end) + buffer->merge_clusters (start, end); + else + foreach_grapheme (buffer, start, end) + buffer->unsafe_to_break (start, end); +} + +static void +hb_ensure_native_direction (hb_buffer_t *buffer) +{ + hb_direction_t direction = buffer->props.direction; + hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); + + /* Numeric runs in natively-RTL scripts are actually native-LTR, so we reset + * the horiz_dir if the run contains at least one decimal-number char, and no + * letter chars (ideally we should be checking for chars with strong + * directionality but hb-unicode currently lacks bidi categories). + * + * This allows digit sequences in Arabic etc to be shaped in "native" + * direction, so that features like ligatures will work as intended. + * + * https://github.com/harfbuzz/harfbuzz/issues/501 + * + * Similar thing about Regional_Indicators; They are bidi=L, but Script=Common. + * If they are present in a run of natively-RTL text, they get assigned a script + * with natively RTL direction, which would result in wrong shaping if we + * assign such native RTL direction to them then. Detect that as well. + * + * https://github.com/harfbuzz/harfbuzz/issues/3314 + */ + if (unlikely (horiz_dir == HB_DIRECTION_RTL && direction == HB_DIRECTION_LTR)) + { + bool found_number = false, found_letter = false, found_ri = false; + const auto* info = buffer->info; + const auto count = buffer->len; + for (unsigned i = 0; i < count; i++) + { + auto gc = _hb_glyph_info_get_general_category (&info[i]); + if (gc == HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + found_number = true; + else if (HB_UNICODE_GENERAL_CATEGORY_IS_LETTER (gc)) + { + found_letter = true; + break; + } + else if (_hb_codepoint_is_regional_indicator (info[i].codepoint)) + found_ri = true; + } + if ((found_number || found_ri) && !found_letter) + horiz_dir = HB_DIRECTION_LTR; + } + + /* 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_ot_layout_reverse_graphemes (buffer); + buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); + } +} + + +/* + * Substitute + */ + +#ifndef HB_NO_VERTICAL +static hb_codepoint_t +hb_vert_char_for (hb_codepoint_t u) +{ + switch (u >> 8) + { + case 0x20: switch (u) { + case 0x2013u: return 0xfe32u; // EN DASH + case 0x2014u: return 0xfe31u; // EM DASH + case 0x2025u: return 0xfe30u; // TWO DOT LEADER + case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS + } break; + case 0x30: switch (u) { + case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA + case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP + case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET + case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET + case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET + case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET + case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET + case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET + case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET + case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET + case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET + case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET + case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET + case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET + case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET + case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET + } break; + case 0xfe: switch (u) { + case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE + } break; + case 0xff: switch (u) { + case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK + case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS + case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS + case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA + case 0xff1au: return 0xfe13u; // FULLWIDTH COLON + case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON + case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK + case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET + case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET + case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE + case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET + case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET + } break; + } + + return u; +} +#endif + +static inline void +hb_ot_rotate_chars (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + + if (HB_DIRECTION_IS_BACKWARD (c->target_direction)) + { + hb_unicode_funcs_t *unicode = buffer->unicode; + hb_mask_t rtlm_mask = c->plan->rtlm_mask; + + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + else + info[i].mask |= rtlm_mask; + } + } + +#ifndef HB_NO_VERTICAL + if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert) + { + for (unsigned int i = 0; i < count; i++) { + hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint); + if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint))) + info[i].codepoint = codepoint; + } + } +#endif +} + +static inline void +hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) +{ +#ifdef HB_NO_OT_SHAPE_FRACTIONS + return; +#endif + + if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || + !c->plan->has_frac) + return; + + hb_buffer_t *buffer = c->buffer; + + hb_mask_t pre_mask, post_mask; + if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) + { + pre_mask = c->plan->numr_mask | c->plan->frac_mask; + post_mask = c->plan->frac_mask | c->plan->dnom_mask; + } + else + { + pre_mask = c->plan->frac_mask | c->plan->dnom_mask; + post_mask = c->plan->numr_mask | c->plan->frac_mask; + } + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */ + { + unsigned int start = i, end = i + 1; + while (start && + _hb_glyph_info_get_general_category (&info[start - 1]) == + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + start--; + while (end < count && + _hb_glyph_info_get_general_category (&info[end]) == + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) + end++; + if (start == i || end == i + 1) + { + if (start == i) + buffer->unsafe_to_concat (start, start + 1); + if (end == i + 1) + buffer->unsafe_to_concat (end - 1, end); + continue; + } + + buffer->unsafe_to_break (start, end); + + for (unsigned int j = start; j < i; j++) + info[j].mask |= pre_mask; + info[i].mask |= c->plan->frac_mask; + for (unsigned int j = i + 1; j < end; j++) + info[j].mask |= post_mask; + + i = end - 1; + } + } +} + +static inline void +hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c) +{ + hb_ot_map_t *map = &c->plan->map; + hb_buffer_t *buffer = c->buffer; + + hb_mask_t global_mask = map->get_global_mask (); + buffer->reset_masks (global_mask); +} + +static inline void +hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) +{ + hb_ot_map_t *map = &c->plan->map; + hb_buffer_t *buffer = c->buffer; + + hb_ot_shape_setup_masks_fraction (c); + + if (c->plan->shaper->setup_masks) + c->plan->shaper->setup_masks (c->plan, buffer, c->font); + + for (unsigned int i = 0; i < c->num_user_features; i++) + { + const hb_feature_t *feature = &c->user_features[i]; + if (!(feature->start == HB_FEATURE_GLOBAL_START && feature->end == HB_FEATURE_GLOBAL_END)) { + unsigned int shift; + hb_mask_t mask = map->get_mask (feature->tag, &shift); + buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); + } + } +} + +static void +hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES)) + return; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int i = 0; + for (i = 0; i < count; i++) + if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) + pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; +} + +static void +hb_ot_hide_default_ignorables (hb_buffer_t *buffer, + hb_font_t *font) +{ + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || + (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) + return; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + + hb_codepoint_t invisible = buffer->invisible; + if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) && + (invisible || font->get_nominal_glyph (' ', &invisible))) + { + /* Replace default-ignorables with a zero-advance invisible glyph. */ + for (unsigned int i = 0; i < count; i++) + { + if (_hb_glyph_info_is_default_ignorable (&info[i])) + info[i].codepoint = invisible; + } + } + else + buffer->delete_glyphs_inplace (_hb_glyph_info_is_default_ignorable); +} + + +static inline void +hb_ot_map_glyphs_fast (hb_buffer_t *buffer) +{ + /* Normalization process sets up glyph_index(), we just copy it. */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].codepoint = info[i].glyph_index(); + + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; +} + +static inline void +hb_synthesize_glyph_classes (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + { + hb_ot_layout_glyph_props_flags_t klass; + + /* Never mark default-ignorables as marks. + * They won't get in the way of lookups anyway, + * but having them as mark will cause them to be skipped + * over if the lookup-flag says so, but at least for the + * Mongolian variation selectors, looks like Uniscribe + * marks them as non-mark. Some Mongolian fonts without + * GDEF rely on this. Another notable character that + * this applies to is COMBINING GRAPHEME JOINER. */ + klass = (_hb_glyph_info_get_general_category (&info[i]) != + HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK || + _hb_glyph_info_is_default_ignorable (&info[i])) ? + HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : + HB_OT_LAYOUT_GLYPH_PROPS_MARK; + _hb_glyph_info_set_glyph_props (&info[i], klass); + } +} + +static inline void +hb_ot_substitute_default (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + hb_ot_rotate_chars (c); + + HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); + + _hb_ot_shape_normalize (c->plan, buffer, c->font); + + hb_ot_shape_setup_masks (c); + + /* This is unfortunate to go here, but necessary... */ + if (c->plan->fallback_mark_positioning) + _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer); + + hb_ot_map_glyphs_fast (buffer); + + HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); +} + +static inline void +hb_ot_substitute_plan (const hb_ot_shape_context_t *c) +{ + hb_buffer_t *buffer = c->buffer; + + hb_ot_layout_substitute_start (c->font, buffer); + + if (c->plan->fallback_glyph_classes) + hb_synthesize_glyph_classes (c->buffer); + +#ifndef HB_NO_AAT_SHAPE + if (unlikely (c->plan->apply_morx)) + hb_aat_layout_substitute (c->plan, c->font, c->buffer, + c->user_features, c->num_user_features); + else +#endif + c->plan->substitute (c->font, buffer); +} + +static inline void +hb_ot_substitute_pre (const hb_ot_shape_context_t *c) +{ + hb_ot_substitute_default (c); + + _hb_buffer_allocate_gsubgpos_vars (c->buffer); + + hb_ot_substitute_plan (c); + +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx && c->plan->apply_gpos) + hb_aat_layout_remove_deleted_glyphs (c->buffer); +#endif +} + +static inline void +hb_ot_substitute_post (const hb_ot_shape_context_t *c) +{ +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx && !c->plan->apply_gpos) + hb_aat_layout_remove_deleted_glyphs (c->buffer); +#endif + + hb_ot_hide_default_ignorables (c->buffer, c->font); + + if (c->plan->shaper->postprocess_glyphs && + c->buffer->message(c->font, "start postprocess-glyphs")) { + c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); + (void) c->buffer->message(c->font, "end postprocess-glyphs"); + } +} + + +/* + * Position + */ + +static inline void +adjust_mark_offsets (hb_glyph_position_t *pos) +{ + pos->x_offset -= pos->x_advance; + pos->y_offset -= pos->y_advance; +} + +static inline void +zero_mark_width (hb_glyph_position_t *pos) +{ + pos->x_advance = 0; + pos->y_advance = 0; +} + +static inline void +zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (_hb_glyph_info_is_mark (&info[i])) + { + if (adjust_offsets) + adjust_mark_offsets (&buffer->pos[i]); + zero_mark_width (&buffer->pos[i]); + } +} + +static inline void +hb_ot_position_default (const hb_ot_shape_context_t *c) +{ + hb_direction_t direction = c->buffer->props.direction; + unsigned int count = c->buffer->len; + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + + if (HB_DIRECTION_IS_HORIZONTAL (direction)) + { + c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), + &pos[0].x_advance, sizeof(pos[0])); + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->subtract_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + } + else + { + c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), + &pos[0].y_advance, sizeof(pos[0])); + for (unsigned int i = 0; i < count; i++) + { + c->font->subtract_glyph_v_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + } + } + if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) + _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); +} + +static inline void +hb_ot_position_plan (const hb_ot_shape_context_t *c) +{ + unsigned int count = c->buffer->len; + hb_glyph_info_t *info = c->buffer->info; + hb_glyph_position_t *pos = c->buffer->pos; + + /* If the font has no GPOS and direction is forward, then when + * zeroing mark widths, we shift the mark with it, such that the + * mark is positioned hanging over the previous glyph. When + * direction is backward we don't shift and it will end up + * hanging over the next glyph after the final reordering. + * + * Note: If fallback positioning happens, we don't care about + * this as it will be overridden. + */ + bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing && + HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); + + /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ + + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->add_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + + hb_ot_layout_position_start (c->font, c->buffer); + + if (c->plan->zero_marks) + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + break; + } + + c->plan->position (c->font, c->buffer); + + if (c->plan->zero_marks) + switch (c->plan->shaper->zero_width_marks) + { + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: + zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); + break; + + default: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: + case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: + break; + } + + /* Finish off. Has to follow a certain order. */ + hb_ot_layout_position_finish_advances (c->font, c->buffer); + hb_ot_zero_width_default_ignorables (c->buffer); +#ifndef HB_NO_AAT_SHAPE + if (c->plan->apply_morx) + hb_aat_layout_zero_width_deleted_glyphs (c->buffer); +#endif + hb_ot_layout_position_finish_offsets (c->font, c->buffer); + + /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ + if (c->font->has_glyph_h_origin_func ()) + for (unsigned int i = 0; i < count; i++) + c->font->subtract_glyph_h_origin (info[i].codepoint, + &pos[i].x_offset, + &pos[i].y_offset); + + if (c->plan->fallback_mark_positioning) + _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, + adjust_offsets_when_zeroing); +} + +static inline void +hb_ot_position (const hb_ot_shape_context_t *c) +{ + c->buffer->clear_positions (); + + hb_ot_position_default (c); + + hb_ot_position_plan (c); + + if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) + hb_buffer_reverse (c->buffer); + + _hb_buffer_deallocate_gsubgpos_vars (c->buffer); +} + +static inline void +hb_propagate_flags (hb_buffer_t *buffer) +{ + /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. + * Simplifies using them. */ + + if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_GLYPH_FLAGS)) + return; + + /* If we are producing SAFE_TO_INSERT_TATWEEL, then do two things: + * + * - If the places that the Arabic shaper marked as SAFE_TO_INSERT_TATWEEL, + * are UNSAFE_TO_BREAK, then clear the SAFE_TO_INSERT_TATWEEL, + * - Any place that is SAFE_TO_INSERT_TATWEEL, is also now UNSAFE_TO_BREAK. + * + * We couldn't make this interaction earlier. It has to be done here. + */ + bool flip_tatweel = buffer->flags & HB_BUFFER_FLAG_PRODUCE_SAFE_TO_INSERT_TATWEEL; + + bool clear_concat = (buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) == 0; + + hb_glyph_info_t *info = buffer->info; + + foreach_cluster (buffer, start, end) + { + unsigned int mask = 0; + for (unsigned int i = start; i < end; i++) + mask |= info[i].mask & HB_GLYPH_FLAG_DEFINED; + + if (flip_tatweel) + { + if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) + mask &= ~HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL; + if (mask & HB_GLYPH_FLAG_SAFE_TO_INSERT_TATWEEL) + mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK | HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + } + + if (clear_concat) + mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_CONCAT; + + for (unsigned int i = start; i < end; i++) + info[i].mask = mask; + } +} + +/* Pull it all together! */ + +static void +hb_ot_shape_internal (hb_ot_shape_context_t *c) +{ + /* Save the original direction, we use it later. */ + c->target_direction = c->buffer->props.direction; + + _hb_buffer_allocate_unicode_vars (c->buffer); + + hb_ot_shape_initialize_masks (c); + hb_set_unicode_props (c->buffer); + hb_insert_dotted_circle (c->buffer, c->font); + + hb_form_clusters (c->buffer); + + hb_ensure_native_direction (c->buffer); + + if (c->plan->shaper->preprocess_text && + c->buffer->message(c->font, "start preprocess-text")) + { + c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); + (void) c->buffer->message(c->font, "end preprocess-text"); + } + + hb_ot_substitute_pre (c); + hb_ot_position (c); + hb_ot_substitute_post (c); + + hb_propagate_flags (c->buffer); + + _hb_buffer_deallocate_unicode_vars (c->buffer); + + c->buffer->props.direction = c->target_direction; + + c->buffer->leave (); +} + + +hb_bool_t +_hb_ot_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_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features}; + hb_ot_shape_internal (&c); + + return true; +} + + +/** + * hb_ot_shape_plan_collect_lookups: + * @shape_plan: #hb_shape_plan_t to query + * @table_tag: GSUB or GPOS + * @lookup_indexes: (out): The #hb_set_t set of lookups returned + * + * Computes the complete set of GSUB or GPOS lookups that are applicable + * under a given @shape_plan. + * + * Since: 0.9.7 + **/ +void +hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, + hb_tag_t table_tag, + hb_set_t *lookup_indexes /* OUT */) +{ + shape_plan->ot.collect_lookups (table_tag, lookup_indexes); +} + + +/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ +static void +add_char (hb_font_t *font, + hb_unicode_funcs_t *unicode, + hb_bool_t mirror, + hb_codepoint_t u, + hb_set_t *glyphs) +{ + hb_codepoint_t glyph; + if (font->get_nominal_glyph (u, &glyph)) + glyphs->add (glyph); + if (mirror) + { + hb_codepoint_t m = unicode->mirroring (u); + if (m != u && font->get_nominal_glyph (m, &glyph)) + glyphs->add (glyph); + } +} + + +/** + * hb_ot_shape_glyphs_closure: + * @font: #hb_font_t to work upon + * @buffer: The input buffer to compute from + * @features: (array length=num_features): The features enabled on the buffer + * @num_features: The number of features enabled on the buffer + * @glyphs: (out): The #hb_set_t set of glyphs comprising the transitive closure of the query + * + * Computes the transitive closure of glyphs needed for a specified + * input buffer under the given font and feature list. The closure is + * computed as a set, not as a list. + * + * Since: 0.9.2 + **/ +void +hb_ot_shape_glyphs_closure (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + hb_set_t *glyphs) +{ + const char *shapers[] = {"ot", nullptr}; + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, + features, num_features, shapers); + + bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs); + + hb_set_t *lookups = hb_set_create (); + hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups); + hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs); + + hb_set_destroy (lookups); + + hb_shape_plan_destroy (shape_plan); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shape.h b/gfx/harfbuzz/src/hb-ot-shape.h new file mode 100644 index 0000000000..afdff72833 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2013 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): Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_SHAPE_H +#define HB_OT_SHAPE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/* TODO port to shape-plan / set. */ +HB_EXTERN void +hb_ot_shape_glyphs_closure (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + hb_set_t *glyphs); + +HB_EXTERN void +hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, + hb_tag_t table_tag, + hb_set_t *lookup_indexes /* OUT */); + +HB_END_DECLS + +#endif /* HB_OT_SHAPE_H */ diff --git a/gfx/harfbuzz/src/hb-ot-shape.hh b/gfx/harfbuzz/src/hb-ot-shape.hh new file mode 100644 index 0000000000..f84aa5c49e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shape.hh @@ -0,0 +1,171 @@ +/* + * Copyright © 2010 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_OT_SHAPE_HH +#define HB_OT_SHAPE_HH + +#include "hb.hh" + +#include "hb-ot-map.hh" +#include "hb-aat-map.hh" + + +struct hb_ot_shape_plan_key_t +{ + unsigned int variations_index[2]; + + void init (hb_face_t *face, + const int *coords, + unsigned num_coords) + { + for (unsigned int table_index = 0; table_index < 2; table_index++) + hb_ot_layout_table_find_feature_variations (face, + table_tags[table_index], + coords, + num_coords, + &variations_index[table_index]); + } + + bool equal (const hb_ot_shape_plan_key_t *other) + { + return 0 == hb_memcmp (this, other, sizeof (*this)); + } +}; + + +struct hb_shape_plan_key_t; + +struct hb_ot_shape_plan_t +{ + ~hb_ot_shape_plan_t () { fini (); } + + hb_segment_properties_t props; + const struct hb_ot_shaper_t *shaper; + hb_ot_map_t map; + const void *data; +#ifndef HB_NO_OT_SHAPE_FRACTIONS + hb_mask_t frac_mask, numr_mask, dnom_mask; +#else + static constexpr hb_mask_t frac_mask = 0; + static constexpr hb_mask_t numr_mask = 0; + static constexpr hb_mask_t dnom_mask = 0; +#endif + hb_mask_t rtlm_mask; +#ifndef HB_NO_OT_KERN + hb_mask_t kern_mask; +#else + static constexpr hb_mask_t kern_mask = 0; +#endif +#ifndef HB_NO_AAT_SHAPE + hb_mask_t trak_mask; +#else + static constexpr hb_mask_t trak_mask = 0; +#endif + +#ifndef HB_NO_OT_KERN + bool requested_kerning : 1; +#else + static constexpr bool requested_kerning = false; +#endif +#ifndef HB_NO_AAT_SHAPE + bool requested_tracking : 1; +#else + static constexpr bool requested_tracking = false; +#endif +#ifndef HB_NO_OT_SHAPE_FRACTIONS + bool has_frac : 1; +#else + static constexpr bool has_frac = false; +#endif + bool has_vert : 1; + bool has_gpos_mark : 1; + bool zero_marks : 1; + bool fallback_glyph_classes : 1; + bool fallback_mark_positioning : 1; + bool adjust_mark_positioning_when_zeroing : 1; + + bool apply_gpos : 1; +#ifndef HB_NO_OT_KERN + bool apply_kern : 1; +#else + static constexpr bool apply_kern = false; +#endif + bool apply_fallback_kern : 1; +#ifndef HB_NO_AAT_SHAPE + bool apply_kerx : 1; + bool apply_morx : 1; + bool apply_trak : 1; +#else + static constexpr bool apply_kerx = false; + static constexpr bool apply_morx = false; + static constexpr bool apply_trak = false; +#endif + + void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const + { + unsigned int table_index; + switch (table_tag) { + case HB_OT_TAG_GSUB: table_index = 0; break; + case HB_OT_TAG_GPOS: table_index = 1; break; + default: return; + } + map.collect_lookups (table_index, lookups); + } + + HB_INTERNAL bool init0 (hb_face_t *face, + const hb_shape_plan_key_t *key); + HB_INTERNAL void fini (); + + HB_INTERNAL void substitute (hb_font_t *font, hb_buffer_t *buffer) const; + HB_INTERNAL void position (hb_font_t *font, hb_buffer_t *buffer) const; +}; + +struct hb_shape_plan_t; + +struct hb_ot_shape_planner_t +{ + /* In the order that they are filled in. */ + hb_face_t *face; + hb_segment_properties_t props; + hb_ot_map_builder_t map; +#ifndef HB_NO_AAT_SHAPE + bool apply_morx : 1; +#else + static constexpr bool apply_morx = false; +#endif + bool script_zero_marks : 1; + bool script_fallback_mark_positioning : 1; + const struct hb_ot_shaper_t *shaper; + + HB_INTERNAL hb_ot_shape_planner_t (hb_face_t *face, + const hb_segment_properties_t &props); + + HB_INTERNAL void compile (hb_ot_shape_plan_t &plan, + const hb_ot_shape_plan_key_t &key); +}; + + +#endif /* HB_OT_SHAPE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh b/gfx/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh new file mode 100644 index 0000000000..66a8bfbd28 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-arabic-fallback.hh @@ -0,0 +1,383 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_ARABIC_FALLBACK_HH +#define HB_OT_SHAPER_ARABIC_FALLBACK_HH + +#include "hb.hh" + +#include "hb-ot-shape.hh" +#include "hb-ot-layout-gsub-table.hh" + + +/* Features ordered the same as the entries in shaping_table rows, + * followed by rlig. Don't change. + * + * We currently support one subtable per lookup, and one lookup + * per feature. But we allow duplicate features, so we use that! + */ +static const hb_tag_t arabic_fallback_features[] = +{ + HB_TAG('i','n','i','t'), + HB_TAG('m','e','d','i'), + HB_TAG('f','i','n','a'), + HB_TAG('i','s','o','l'), + HB_TAG('r','l','i','g'), + HB_TAG('r','l','i','g'), + HB_TAG('r','l','i','g'), +}; + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + unsigned int feature_index) +{ + OT::HBGlyphID16 glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + OT::HBGlyphID16 substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1]; + unsigned int num_glyphs = 0; + + /* Populate arrays */ + for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++) + { + hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index]; + hb_codepoint_t u_glyph, s_glyph; + + if (!s || + !hb_font_get_glyph (font, u, 0, &u_glyph) || + !hb_font_get_glyph (font, s, 0, &s_glyph) || + u_glyph == s_glyph || + u_glyph > 0xFFFFu || s_glyph > 0xFFFFu) + continue; + + glyphs[num_glyphs] = u_glyph; + substitutes[num_glyphs] = s_glyph; + + num_glyphs++; + } + + if (!num_glyphs) + return nullptr; + + /* Bubble-sort or something equally good! + * May not be good-enough for presidential candidate interviews, but good-enough for us... */ + hb_stable_sort (&glyphs[0], num_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID16::cmp, + &substitutes[0]); + + + /* Each glyph takes four bytes max, and there's some overhead. */ + char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128]; + hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); + bool ret = lookup->serialize_single (&c, + OT::LookupFlag::IgnoreMarks, + hb_sorted_array (glyphs, num_glyphs), + hb_array (substitutes, num_glyphs)); + c.end_serialize (); + + return ret && !c.in_error () ? c.copy<OT::SubstLookup> () : nullptr; +} + +template <typename T> +static OT::SubstLookup * +arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font, + const T &ligature_table, + unsigned lookup_flags) +{ + OT::HBGlyphID16 first_glyphs[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)]; + unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)]; + unsigned int num_first_glyphs = 0; + + /* We know that all our ligatures have the same number of components. */ + OT::HBGlyphID16 ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)]; + unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)]; + OT::HBGlyphID16 component_list[ARRAY_LENGTH_CONST (ligature_list) * + ARRAY_LENGTH_CONST (ligature_table[0].ligatures[0].components)]; + unsigned int num_ligatures = 0; + unsigned int num_components = 0; + + /* Populate arrays */ + + /* Sort out the first-glyphs */ + for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++) + { + hb_codepoint_t first_u = ligature_table[first_glyph_idx].first; + hb_codepoint_t first_glyph; + if (!hb_font_get_glyph (font, first_u, 0, &first_glyph)) + continue; + first_glyphs[num_first_glyphs] = first_glyph; + ligature_per_first_glyph_count_list[num_first_glyphs] = 0; + first_glyphs_indirection[num_first_glyphs] = first_glyph_idx; + num_first_glyphs++; + } + hb_stable_sort (&first_glyphs[0], num_first_glyphs, + (int(*)(const OT::HBUINT16*, const OT::HBUINT16 *)) OT::HBGlyphID16::cmp, + &first_glyphs_indirection[0]); + + /* Now that the first-glyphs are sorted, walk again, populate ligatures. */ + for (unsigned int i = 0; i < num_first_glyphs; i++) + { + unsigned int first_glyph_idx = first_glyphs_indirection[i]; + + for (unsigned int ligature_idx = 0; ligature_idx < ARRAY_LENGTH (ligature_table[0].ligatures); ligature_idx++) + { + hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[ligature_idx].ligature; + hb_codepoint_t ligature_glyph; + if (!hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph)) + continue; + + const auto &components = ligature_table[first_glyph_idx].ligatures[ligature_idx].components; + unsigned component_count = ARRAY_LENGTH_CONST (components); + + bool matched = true; + for (unsigned j = 0; j < component_count; j++) + { + hb_codepoint_t component_u = ligature_table[first_glyph_idx].ligatures[ligature_idx].components[j]; + hb_codepoint_t component_glyph; + if (!component_u || + !hb_font_get_nominal_glyph (font, component_u, &component_glyph)) + { + matched = false; + break; + } + + component_list[num_components++] = component_glyph; + } + if (!matched) + continue; + + component_count_list[num_ligatures] = 1 + component_count; + ligature_list[num_ligatures] = ligature_glyph; + + ligature_per_first_glyph_count_list[i]++; + + num_ligatures++; + } + } + + if (!num_ligatures) + return nullptr; + + + /* 16 bytes per ligature ought to be enough... */ + char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128]; + hb_serialize_context_t c (buf, sizeof (buf)); + OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> (); + bool ret = lookup->serialize_ligature (&c, + lookup_flags, + hb_sorted_array (first_glyphs, num_first_glyphs), + hb_array (ligature_per_first_glyph_count_list, num_first_glyphs), + hb_array (ligature_list, num_ligatures), + hb_array (component_count_list, num_ligatures), + hb_array (component_list, num_components)); + c.end_serialize (); + + return ret && !c.in_error () ? c.copy<OT::SubstLookup> () : nullptr; +} + +static OT::SubstLookup * +arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + unsigned int feature_index) +{ + if (feature_index < 4) + return arabic_fallback_synthesize_lookup_single (plan, font, feature_index); + else + { + switch (feature_index) { + case 4: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_3_table, OT::LookupFlag::IgnoreMarks); + case 5: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_table, OT::LookupFlag::IgnoreMarks); + case 6: return arabic_fallback_synthesize_lookup_ligature (plan, font, ligature_mark_table, 0); + } + } + assert (false); + return nullptr; +} + +#define ARABIC_FALLBACK_MAX_LOOKUPS ARRAY_LENGTH_CONST (arabic_fallback_features) + +struct arabic_fallback_plan_t +{ + unsigned int num_lookups; + bool free_lookups; + + hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + OT::hb_ot_layout_lookup_accelerator_t *accel_array[ARABIC_FALLBACK_MAX_LOOKUPS]; +}; + +#if defined(_WIN32) && !defined(HB_NO_WIN1256) +#define HB_WITH_WIN1256 +#endif + +#ifdef HB_WITH_WIN1256 +#include "hb-ot-shaper-arabic-win1256.hh" +#endif + +struct ManifestLookup +{ + public: + OT::Tag tag; + OT::Offset16To<OT::SubstLookup> lookupOffset; + public: + DEFINE_SIZE_STATIC (6); +}; +typedef OT::Array16Of<ManifestLookup> Manifest; + +static bool +arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan HB_UNUSED, + const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED) +{ +#ifdef HB_WITH_WIN1256 + /* Does this font look like it's Windows-1256-encoded? */ + hb_codepoint_t g; + if (!(hb_font_get_glyph (font, 0x0627u, 0, &g) && g == 199 /* ALEF */ && + hb_font_get_glyph (font, 0x0644u, 0, &g) && g == 225 /* LAM */ && + hb_font_get_glyph (font, 0x0649u, 0, &g) && g == 236 /* ALEF MAKSURA */ && + hb_font_get_glyph (font, 0x064Au, 0, &g) && g == 237 /* YEH */ && + hb_font_get_glyph (font, 0x0652u, 0, &g) && g == 250 /* SUKUN */)) + return false; + + const Manifest &manifest = reinterpret_cast<const Manifest&> (arabic_win1256_gsub_lookups.manifest); + static_assert (sizeof (arabic_win1256_gsub_lookups.manifestData) <= + ARABIC_FALLBACK_MAX_LOOKUPS * sizeof (ManifestLookup), ""); + + unsigned j = 0; + unsigned int count = manifest.len; + for (unsigned int i = 0; i < count; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (manifest[i].tag); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = const_cast<OT::SubstLookup*> (&(&manifest+manifest[i].lookupOffset)); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j] = OT::hb_ot_layout_lookup_accelerator_t::create (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = false; + + return j > 0; +#else + return false; +#endif +} + +static bool +arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan, + const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + static_assert ((ARRAY_LENGTH_CONST (arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS), ""); + unsigned int j = 0; + for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (arabic_fallback_features[i]); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j] = OT::hb_ot_layout_lookup_accelerator_t::create (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = true; + + return j > 0; +} + +static arabic_fallback_plan_t * +arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_calloc (1, sizeof (arabic_fallback_plan_t)); + if (unlikely (!fallback_plan)) + return const_cast<arabic_fallback_plan_t *> (&Null (arabic_fallback_plan_t)); + + fallback_plan->num_lookups = 0; + fallback_plan->free_lookups = false; + + /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms, + * in case the font has cmap entries for the presentation-forms characters. */ + if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font)) + return fallback_plan; + + /* See if this looks like a Windows-1256-encoded font. If it does, use a + * hand-coded GSUB table. */ + if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font)) + return fallback_plan; + + assert (fallback_plan->num_lookups == 0); + hb_free (fallback_plan); + return const_cast<arabic_fallback_plan_t *> (&Null (arabic_fallback_plan_t)); +} + +static void +arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) +{ + if (!fallback_plan || fallback_plan->num_lookups == 0) + return; + + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) + if (fallback_plan->lookup_array[i]) + { + hb_free (fallback_plan->accel_array[i]); + if (fallback_plan->free_lookups) + hb_free (fallback_plan->lookup_array[i]); + } + + hb_free (fallback_plan); +} + +static void +arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + OT::hb_ot_apply_context_t c (0, font, buffer, hb_blob_get_empty ()); + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) + if (fallback_plan->lookup_array[i]) { + c.set_lookup_mask (fallback_plan->mask_array[i]); + if (fallback_plan->accel_array[i]) + hb_ot_layout_substitute_lookup (&c, + *fallback_plan->lookup_array[i], + *fallback_plan->accel_array[i]); + } +} + + +#endif /* HB_OT_SHAPER_ARABIC_FALLBACK_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-arabic-joining-list.hh b/gfx/harfbuzz/src/hb-ot-shaper-arabic-joining-list.hh new file mode 100644 index 0000000000..a5a7b84af4 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-arabic-joining-list.hh @@ -0,0 +1,47 @@ +/* == Start of generated function == */ +/* + * The following function is generated by running: + * + * ./gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt + * + * on files with these headers: + * + * # ArabicShaping-15.1.0.txt + * # Date: 2023-01-05 + * # Scripts-15.1.0.txt + * # Date: 2023-07-28, 16:01:07 GMT + */ + +#ifndef HB_OT_SHAPER_ARABIC_JOINING_LIST_HH +#define HB_OT_SHAPER_ARABIC_JOINING_LIST_HH + +static bool +has_arabic_joining (hb_script_t script) +{ + /* List of scripts that have data in arabic-table. */ + switch ((int) script) + { + case HB_SCRIPT_ADLAM: + case HB_SCRIPT_ARABIC: + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_MANDAIC: + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_NKO: + case HB_SCRIPT_OLD_UYGHUR: + case HB_SCRIPT_PHAGS_PA: + case HB_SCRIPT_PSALTER_PAHLAVI: + case HB_SCRIPT_SOGDIAN: + case HB_SCRIPT_SYRIAC: + return true; + + default: + return false; + } +} + + +#endif /* HB_OT_SHAPER_ARABIC_JOINING_LIST_HH */ + +/* == End of generated function == */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-arabic-pua.hh b/gfx/harfbuzz/src/hb-ot-shaper-arabic-pua.hh new file mode 100644 index 0000000000..ba86772f84 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-arabic-pua.hh @@ -0,0 +1,118 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-arabic-pua.py + * + */ + +#ifndef HB_OT_SHAPER_ARABIC_PUA_HH +#define HB_OT_SHAPER_ARABIC_PUA_HH + +static const uint8_t +_hb_arabic_u8[464] = +{ + 84, 86, 85, 85, 85, 85, 85,213, 16, 34, 34, 34, 34, 34, 35, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 36, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 82, 16, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 7, + 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 0, 0, 0, 22, 0, 23, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 16, 34, 34, 34, 35, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 66, 16, 50, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68,101, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 71, 68, 68, 68, 68, 68, 68, 68,152,186, 76, 77, 68,254, 16, 50, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 5, 6, + 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 10, 0, + 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 13, 0, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 23, 23, 29, 30, 31, 32, 33, 0, 0, 0, 0, + 0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 40, + 41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47, 48, 49, 0, 0, 0, + 0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56, 57, 58, 0, 0, 0, + 0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 66, + 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, +}; +static const uint16_t +_hb_arabic_u16[720] = +{ + 0, 0, 0, 0, 0, 0, 0, 0,61728,61729,61730, 0, 0,61733, 0, 0, + 61736,61737,61738,61739,61790,61741,61742,61743,61872,61873,61874,61875,61876,61877,61878,61879, + 61880,61881,61754,61755, 0,61757, 0,61759, 0, 0, 0,61787,61788,61789, 0, 0, + 0, 0, 0,61731, 0, 0, 0, 0, 0, 0, 0,61732, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,61734, 0, 0, 0, 0, 0, 0, 0,61735, + 0, 0, 0, 0,61740, 0, 0, 0, 0, 0, 0,61755, 0, 0, 0,61759, + 0,61869,61765,61763,61883,61767,61882,61761,61770,61865,61772,61774,61777,61780,61783,61784, + 61785,61786,61792,61794,61796,61798,61800,61801,61802,61806,61810,61696,61696,61696,61696,61696, + 61791,61813,61816,61818,61820,61822,61921,61860,61861,61868,61864,61895,61896,61899,61892,61893, + 61898,61897,61894,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696,61696, 0, + 61744,61745,61746,61747,61748,61749,61750,61751,61752,61753, 0,61790,61790, 0, 0, 0, + 0, 0, 0, 0,61708,61709,61710,61711,61756,61758, 0, 0, 0, 0, 0, 0, + 0,61765,61766,61763,61764,61883,61883,61767,61768,61882,61871,61870,61870,61761,61762,61770, + 61770,61769,61769,61865,61866,61772,61772,61771,61771,61774,61774,61773,61773,61777,61776,61775, + 61775,61780,61779,61778,61778,61783,61782,61781,61781,61784,61784,61785,61785,61786,61786,61792, + 61792,61794,61794,61793,61793,61796,61796,61795,61795,61798,61798,61797,61797,61800,61800,61799, + 61799,61801,61801,61801,61801,61802,61802,61802,61802,61806,61805,61803,61804,61810,61809,61807, + 61808,61813,61813,61811,61812,61816,61816,61814,61815,61818,61818,61817,61817,61820,61820,61819, + 61819,61822,61822,61821,61821,61921,61921,61823,61823,61860,61859,61857,61858,61861,61861,61868, + 61867,61864,61863,61862,61862,61888,61889,61886,61887,61890,61891,61885,61884, 0, 0, 0, + 0, 0, 0, 0,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994,61995, + 62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0,62043, + 0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0,61990, + 0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0,62015, + 0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,62053, + 62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,61952,61952,61952,61952, + 62047,62130,62134,62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181, + 62186,62185,62182,61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007, + 62008,62009, 0,62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0, + 61954, 0,61981, 0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0, + 0, 0, 0,61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0, + 61983, 0, 0, 0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0, + 0,62098, 0, 0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0, + 62100, 0, 0, 0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0, + 62106, 0,62107, 0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088, + 62109,61978,62089,62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096, + 62097,62105, 0, 0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0, + 0,62021,62022,62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028, + 62027,62025,62026,62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037, + 62038,62048,62044,62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059, + 62060,62064,62063,62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073, + 62074,62114,62113,62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123, + 62124,62130,62129,62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139, + 62140,62146,62145,62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164, + 62163,62160,62159,62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0, +}; + +static inline unsigned +_hb_arabic_b2 (const uint8_t* a, unsigned i) +{ + return (a[i>>2]>>((i&3u)<<1))&3u; +} +static inline unsigned +_hb_arabic_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline uint_fast16_t +_hb_arabic_pua_simp_map (unsigned u) +{ + return u<65277u?_hb_arabic_u16[((_hb_arabic_u8[40+(((_hb_arabic_b4(8+_hb_arabic_u8,((_hb_arabic_b2(_hb_arabic_u8,u>>3>>4>>4))<<4)+((u>>3>>4)&15u)))<<4)+((u>>3)&15u))])<<3)+((u)&7u)]:0; +} +static inline uint_fast16_t +_hb_arabic_pua_trad_map (unsigned u) +{ + return u<65277u?_hb_arabic_u16[320+(((_hb_arabic_u8[208+(((_hb_arabic_b4(168+_hb_arabic_u8,((_hb_arabic_b4(136+_hb_arabic_u8,u>>2>>4>>4))<<4)+((u>>2>>4)&15u)))<<4)+((u>>2)&15u))])<<2)+((u)&3u))]:0; +} + +#endif /* HB_OT_SHAPER_ARABIC_PUA_HH */ + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-arabic-table.hh b/gfx/harfbuzz/src/hb-ot-shaper-arabic-table.hh new file mode 100644 index 0000000000..336a1391e9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-arabic-table.hh @@ -0,0 +1,556 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + * + * on files with these headers: + * + * # ArabicShaping-15.1.0.txt + * # Date: 2023-01-05 + * # Blocks-15.1.0.txt + * # Date: 2023-07-28, 15:47:20 GMT + * UnicodeData.txt does not have a header. + */ + +#ifndef HB_OT_SHAPER_ARABIC_TABLE_HH +#define HB_OT_SHAPER_ARABIC_TABLE_HH + + +#define A JOINING_GROUP_ALAPH +#define DR JOINING_GROUP_DALATH_RISH +#define C JOINING_TYPE_C +#define D JOINING_TYPE_D +#define L JOINING_TYPE_L +#define R JOINING_TYPE_R +#define T JOINING_TYPE_T +#define U JOINING_TYPE_U +#define X JOINING_TYPE_X + +static const uint8_t joining_table[] = +{ + +#define joining_offset_0x0600u 0 + + /* Arabic */ + + /* 0600 */ U,U,U,U,U,U,X,X,U,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0620 */ D,U,R,R,R,R,D,R,D,R,D,D,D,D,D,R,R,R,R,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 0640 */ C,D,D,D,D,D,D,D,R,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0660 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,D,D,X,R,R,R,U,R,R,R,D,D,D,D,D,D,D,D, + /* 0680 */ D,D,D,D,D,D,D,D,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,D,D,D,D,D,D, + /* 06A0 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 06C0 */ R,D,D,R,R,R,R,R,R,R,R,R,D,R,D,R,D,D,R,R,X,R,X,X,X,X,X,X,X,U,X,X, + /* 06E0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,R,R,X,X,X,X,X,X,X,X,X,X,D,D,D,X,X,D, + + /* Syriac */ + + /* 0700 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,T,A,X,D,D,D,DR,DR,R,R,R,D,D,D,D,R,D, + /* 0720 */ D,D,D,D,D,D,D,D,R,D,DR,D,R,D,D,DR,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0740 */ X,X,X,X,X,X,X,X,X,X,X,X,X,R,D,D, + + /* Arabic Supplement */ + + /* 0740 */ D,D,D,D,D,D,D,D,D,R,R,R,D,D,D,D, + /* 0760 */ D,D,D,D,D,D,D,D,D,D,D,R,R,D,D,D,D,R,D,R,R,D,D,D,R,R,D,D,D,D,D,D, + + /* FILLER */ + + /* 0780 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 07A0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* NKo */ + + /* 07C0 */ X,X,X,X,X,X,X,X,X,X,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 07E0 */ D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,C,X,X,X,X,X, + + /* FILLER */ + + /* 0800 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 0820 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Mandaic */ + + /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,R,R,R,X,X,X,X,X,X,X, + + /* Syriac Supplement */ + + /* 0860 */ D,U,D,D,D,D,U,R,D,R,R,X,X,X,X,X, + + /* Arabic Extended-B */ + + /* 0860 */ R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R, + /* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,X,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Arabic Extended-A */ + + /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,D,D,D,D,R,D,D,D,D,D,D, + /* 08C0 */ D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 08E0 */ X,X,U, + +#define joining_offset_0x1806u 739 + + /* Mongolian */ + + /* 1800 */ U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X, + /* 1880 */ U,U,U,U,U,T,T,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 18A0 */ D,D,D,D,D,D,D,D,D,X,D, + +#define joining_offset_0x200cu 904 + + /* General Punctuation */ + + /* 2000 */ U,C,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2020 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2040 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 2060 */ X,X,X,X,X,X,U,U,U,U, + +#define joining_offset_0xa840u 998 + + /* Phags-pa */ + + /* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U, + +#define joining_offset_0x10ac0u 1050 + + /* Manichaean */ + + /* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D, + /* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R, + +#define joining_offset_0x10b80u 1098 + + /* Psalter Pahlavi */ + + /* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U, + +#define joining_offset_0x10d00u 1146 + + /* Hanifi Rohingya */ + + /* 10D00 */ L,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 10D20 */ D,D,R,D, + +#define joining_offset_0x10f30u 1182 + + /* Sogdian */ + + /* 10F20 */ D,D,D,R,D,D,D,D,D,D,D,D,D,D,D,D, + /* 10F40 */ D,D,D,D,D,U,X,X,X,X,X,X,X,X,X,X,X,D,D,D,R,X,X,X,X,X,X,X,X,X,X,X, + /* 10F60 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Old Uyghur */ + + /* 10F60 */ D,D,D,D,R,R,D,D,D,D,D,D,D,D,D,D, + /* 10F80 */ D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + /* 10FA0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, + + /* Chorasmian */ + + /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, + /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, + +#define joining_offset_0x110bdu 1338 + + /* Kaithi */ + + /* 110A0 */ U,X,X, + /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, + +#define joining_offset_0x1e900u 1355 + + /* Adlam */ + + /* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, + /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, + +}; /* Table items: 1431; occupancy: 57% */ + + +static unsigned int +joining_type (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (hb_in_range<hb_codepoint_t> (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u]; + break; + + case 0x1u: + if (hb_in_range<hb_codepoint_t> (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u]; + break; + + case 0x2u: + if (hb_in_range<hb_codepoint_t> (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu]; + break; + + case 0xAu: + if (hb_in_range<hb_codepoint_t> (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u]; + break; + + case 0x10u: + if (hb_in_range<hb_codepoint_t> (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u]; + if (hb_in_range<hb_codepoint_t> (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; + if (hb_in_range<hb_codepoint_t> (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; + if (hb_in_range<hb_codepoint_t> (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; + break; + + case 0x11u: + if (hb_in_range<hb_codepoint_t> (u, 0x110BDu, 0x110CDu)) return joining_table[u - 0x110BDu + joining_offset_0x110bdu]; + break; + + case 0x1Eu: + if (hb_in_range<hb_codepoint_t> (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; + break; + + default: + break; + } + return X; +} + +#undef A +#undef DR +#undef C +#undef D +#undef L +#undef R +#undef T +#undef U +#undef X + + +static const uint16_t shaping_table[][4] = +{ + {0x0000u, 0x0000u, 0x0000u, 0xFE80u}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */ + {0x0000u, 0x0000u, 0xFE82u, 0xFE81u}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */ + {0x0000u, 0x0000u, 0xFE84u, 0xFE83u}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE86u, 0xFE85u}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE88u, 0xFE87u}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */ + {0xFE8Bu, 0xFE8Cu, 0xFE8Au, 0xFE89u}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */ + {0x0000u, 0x0000u, 0xFE8Eu, 0xFE8Du}, /* U+0627 ARABIC LETTER ALEF */ + {0xFE91u, 0xFE92u, 0xFE90u, 0xFE8Fu}, /* U+0628 ARABIC LETTER BEH */ + {0x0000u, 0x0000u, 0xFE94u, 0xFE93u}, /* U+0629 ARABIC LETTER TEH MARBUTA */ + {0xFE97u, 0xFE98u, 0xFE96u, 0xFE95u}, /* U+062A ARABIC LETTER TEH */ + {0xFE9Bu, 0xFE9Cu, 0xFE9Au, 0xFE99u}, /* U+062B ARABIC LETTER THEH */ + {0xFE9Fu, 0xFEA0u, 0xFE9Eu, 0xFE9Du}, /* U+062C ARABIC LETTER JEEM */ + {0xFEA3u, 0xFEA4u, 0xFEA2u, 0xFEA1u}, /* U+062D ARABIC LETTER HAH */ + {0xFEA7u, 0xFEA8u, 0xFEA6u, 0xFEA5u}, /* U+062E ARABIC LETTER KHAH */ + {0x0000u, 0x0000u, 0xFEAAu, 0xFEA9u}, /* U+062F ARABIC LETTER DAL */ + {0x0000u, 0x0000u, 0xFEACu, 0xFEABu}, /* U+0630 ARABIC LETTER THAL */ + {0x0000u, 0x0000u, 0xFEAEu, 0xFEADu}, /* U+0631 ARABIC LETTER REH */ + {0x0000u, 0x0000u, 0xFEB0u, 0xFEAFu}, /* U+0632 ARABIC LETTER ZAIN */ + {0xFEB3u, 0xFEB4u, 0xFEB2u, 0xFEB1u}, /* U+0633 ARABIC LETTER SEEN */ + {0xFEB7u, 0xFEB8u, 0xFEB6u, 0xFEB5u}, /* U+0634 ARABIC LETTER SHEEN */ + {0xFEBBu, 0xFEBCu, 0xFEBAu, 0xFEB9u}, /* U+0635 ARABIC LETTER SAD */ + {0xFEBFu, 0xFEC0u, 0xFEBEu, 0xFEBDu}, /* U+0636 ARABIC LETTER DAD */ + {0xFEC3u, 0xFEC4u, 0xFEC2u, 0xFEC1u}, /* U+0637 ARABIC LETTER TAH */ + {0xFEC7u, 0xFEC8u, 0xFEC6u, 0xFEC5u}, /* U+0638 ARABIC LETTER ZAH */ + {0xFECBu, 0xFECCu, 0xFECAu, 0xFEC9u}, /* U+0639 ARABIC LETTER AIN */ + {0xFECFu, 0xFED0u, 0xFECEu, 0xFECDu}, /* U+063A ARABIC LETTER GHAIN */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+063F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0640 */ + {0xFED3u, 0xFED4u, 0xFED2u, 0xFED1u}, /* U+0641 ARABIC LETTER FEH */ + {0xFED7u, 0xFED8u, 0xFED6u, 0xFED5u}, /* U+0642 ARABIC LETTER QAF */ + {0xFEDBu, 0xFEDCu, 0xFEDAu, 0xFED9u}, /* U+0643 ARABIC LETTER KAF */ + {0xFEDFu, 0xFEE0u, 0xFEDEu, 0xFEDDu}, /* U+0644 ARABIC LETTER LAM */ + {0xFEE3u, 0xFEE4u, 0xFEE2u, 0xFEE1u}, /* U+0645 ARABIC LETTER MEEM */ + {0xFEE7u, 0xFEE8u, 0xFEE6u, 0xFEE5u}, /* U+0646 ARABIC LETTER NOON */ + {0xFEEBu, 0xFEECu, 0xFEEAu, 0xFEE9u}, /* U+0647 ARABIC LETTER HEH */ + {0x0000u, 0x0000u, 0xFEEEu, 0xFEEDu}, /* U+0648 ARABIC LETTER WAW */ + {0xFBE8u, 0xFBE9u, 0xFEF0u, 0xFEEFu}, /* U+0649 ARABIC LETTER */ + {0xFEF3u, 0xFEF4u, 0xFEF2u, 0xFEF1u}, /* U+064A ARABIC LETTER YEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+064F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0650 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0651 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0652 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0653 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0654 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0655 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0656 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0657 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0658 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0659 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+065F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0660 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0661 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0662 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0663 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0664 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0665 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0666 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0667 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0668 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0669 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+066F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0670 */ + {0x0000u, 0x0000u, 0xFB51u, 0xFB50u}, /* U+0671 ARABIC LETTER ALEF WASLA */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0672 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0673 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0674 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0675 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0676 */ + {0x0000u, 0x0000u, 0x0000u, 0xFBDDu}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0678 */ + {0xFB68u, 0xFB69u, 0xFB67u, 0xFB66u}, /* U+0679 ARABIC LETTER TTEH */ + {0xFB60u, 0xFB61u, 0xFB5Fu, 0xFB5Eu}, /* U+067A ARABIC LETTER TTEHEH */ + {0xFB54u, 0xFB55u, 0xFB53u, 0xFB52u}, /* U+067B ARABIC LETTER BEEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+067C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+067D */ + {0xFB58u, 0xFB59u, 0xFB57u, 0xFB56u}, /* U+067E ARABIC LETTER PEH */ + {0xFB64u, 0xFB65u, 0xFB63u, 0xFB62u}, /* U+067F ARABIC LETTER TEHEH */ + {0xFB5Cu, 0xFB5Du, 0xFB5Bu, 0xFB5Au}, /* U+0680 ARABIC LETTER BEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0681 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0682 */ + {0xFB78u, 0xFB79u, 0xFB77u, 0xFB76u}, /* U+0683 ARABIC LETTER NYEH */ + {0xFB74u, 0xFB75u, 0xFB73u, 0xFB72u}, /* U+0684 ARABIC LETTER DYEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0685 */ + {0xFB7Cu, 0xFB7Du, 0xFB7Bu, 0xFB7Au}, /* U+0686 ARABIC LETTER TCHEH */ + {0xFB80u, 0xFB81u, 0xFB7Fu, 0xFB7Eu}, /* U+0687 ARABIC LETTER TCHEHEH */ + {0x0000u, 0x0000u, 0xFB89u, 0xFB88u}, /* U+0688 ARABIC LETTER DDAL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0689 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068B */ + {0x0000u, 0x0000u, 0xFB85u, 0xFB84u}, /* U+068C ARABIC LETTER DAHAL */ + {0x0000u, 0x0000u, 0xFB83u, 0xFB82u}, /* U+068D ARABIC LETTER DDAHAL */ + {0x0000u, 0x0000u, 0xFB87u, 0xFB86u}, /* U+068E ARABIC LETTER DUL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+068F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0690 */ + {0x0000u, 0x0000u, 0xFB8Du, 0xFB8Cu}, /* U+0691 ARABIC LETTER RREH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0692 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0693 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0694 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0695 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0696 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0697 */ + {0x0000u, 0x0000u, 0xFB8Bu, 0xFB8Au}, /* U+0698 ARABIC LETTER JEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+0699 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069A */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069B */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069C */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069D */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+069F */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A0 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A1 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A2 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A3 */ + {0xFB6Cu, 0xFB6Du, 0xFB6Bu, 0xFB6Au}, /* U+06A4 ARABIC LETTER VEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A5 */ + {0xFB70u, 0xFB71u, 0xFB6Fu, 0xFB6Eu}, /* U+06A6 ARABIC LETTER PEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A7 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06A8 */ + {0xFB90u, 0xFB91u, 0xFB8Fu, 0xFB8Eu}, /* U+06A9 ARABIC LETTER KEHEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AA */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AB */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AC */ + {0xFBD5u, 0xFBD6u, 0xFBD4u, 0xFBD3u}, /* U+06AD ARABIC LETTER NG */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06AE */ + {0xFB94u, 0xFB95u, 0xFB93u, 0xFB92u}, /* U+06AF ARABIC LETTER GAF */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B0 */ + {0xFB9Cu, 0xFB9Du, 0xFB9Bu, 0xFB9Au}, /* U+06B1 ARABIC LETTER NGOEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B2 */ + {0xFB98u, 0xFB99u, 0xFB97u, 0xFB96u}, /* U+06B3 ARABIC LETTER GUEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B4 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B5 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B6 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B7 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B8 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06B9 */ + {0x0000u, 0x0000u, 0xFB9Fu, 0xFB9Eu}, /* U+06BA ARABIC LETTER NOON GHUNNA */ + {0xFBA2u, 0xFBA3u, 0xFBA1u, 0xFBA0u}, /* U+06BB ARABIC LETTER RNOON */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BC */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BD */ + {0xFBACu, 0xFBADu, 0xFBABu, 0xFBAAu}, /* U+06BE ARABIC LETTER HEH DOACHASHMEE */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06BF */ + {0x0000u, 0x0000u, 0xFBA5u, 0xFBA4u}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */ + {0xFBA8u, 0xFBA9u, 0xFBA7u, 0xFBA6u}, /* U+06C1 ARABIC LETTER HEH GOAL */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C2 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C3 */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06C4 */ + {0x0000u, 0x0000u, 0xFBE1u, 0xFBE0u}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */ + {0x0000u, 0x0000u, 0xFBDAu, 0xFBD9u}, /* U+06C6 ARABIC LETTER OE */ + {0x0000u, 0x0000u, 0xFBD8u, 0xFBD7u}, /* U+06C7 ARABIC LETTER U */ + {0x0000u, 0x0000u, 0xFBDCu, 0xFBDBu}, /* U+06C8 ARABIC LETTER YU */ + {0x0000u, 0x0000u, 0xFBE3u, 0xFBE2u}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CA */ + {0x0000u, 0x0000u, 0xFBDFu, 0xFBDEu}, /* U+06CB ARABIC LETTER VE */ + {0xFBFEu, 0xFBFFu, 0xFBFDu, 0xFBFCu}, /* U+06CC ARABIC LETTER FARSI YEH */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CD */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CE */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06CF */ + {0xFBE6u, 0xFBE7u, 0xFBE5u, 0xFBE4u}, /* U+06D0 ARABIC LETTER E */ + {0x0000u, 0x0000u, 0x0000u, 0x0000u}, /* U+06D1 */ + {0x0000u, 0x0000u, 0xFBAFu, 0xFBAEu}, /* U+06D2 ARABIC LETTER YEH BARREE */ + {0x0000u, 0x0000u, 0xFBB1u, 0xFBB0u}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */ +}; + +#define SHAPING_TABLE_FIRST 0x0621u +#define SHAPING_TABLE_LAST 0x06D3u + + +static const struct ligature_set_t { + uint16_t first; + struct ligature_pairs_t { + uint16_t components[1]; + uint16_t ligature; + } ligatures[14]; +} ligature_table[] = +{ + { 0xFE91u, { + { {0xFEE2u}, 0xFC08u }, /* ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFC9Fu }, /* ARABIC LIGATURE BEH WITH MEEM INITIAL FORM */ + { {0xFEA0u}, 0xFC9Cu }, /* ARABIC LIGATURE BEH WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFC9Du }, /* ARABIC LIGATURE BEH WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFC9Eu }, /* ARABIC LIGATURE BEH WITH KHAH INITIAL FORM */ + }}, + { 0xFE92u, { + { {0xFEAEu}, 0xFC6Au }, /* ARABIC LIGATURE BEH WITH REH FINAL FORM */ + { {0xFEE6u}, 0xFC6Du }, /* ARABIC LIGATURE BEH WITH NOON FINAL FORM */ + { {0xFEF2u}, 0xFC6Fu }, /* ARABIC LIGATURE BEH WITH YEH FINAL FORM */ + }}, + { 0xFE97u, { + { {0xFEE2u}, 0xFC0Eu }, /* ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFCA4u }, /* ARABIC LIGATURE TEH WITH MEEM INITIAL FORM */ + { {0xFEA0u}, 0xFCA1u }, /* ARABIC LIGATURE TEH WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCA2u }, /* ARABIC LIGATURE TEH WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFCA3u }, /* ARABIC LIGATURE TEH WITH KHAH INITIAL FORM */ + }}, + { 0xFE98u, { + { {0xFEAEu}, 0xFC70u }, /* ARABIC LIGATURE TEH WITH REH FINAL FORM */ + { {0xFEE6u}, 0xFC73u }, /* ARABIC LIGATURE TEH WITH NOON FINAL FORM */ + { {0xFEF2u}, 0xFC75u }, /* ARABIC LIGATURE TEH WITH YEH FINAL FORM */ + }}, + { 0xFE9Bu, { + { {0xFEE2u}, 0xFC12u }, /* ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM */ + }}, + { 0xFE9Fu, { + { {0xFEE4u}, 0xFCA8u }, /* ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM */ + }}, + { 0xFEA3u, { + { {0xFEE4u}, 0xFCAAu }, /* ARABIC LIGATURE HAH WITH MEEM INITIAL FORM */ + }}, + { 0xFEA7u, { + { {0xFEE4u}, 0xFCACu }, /* ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM */ + }}, + { 0xFEB3u, { + { {0xFEE4u}, 0xFCB0u }, /* ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM */ + }}, + { 0xFEB7u, { + { {0xFEE4u}, 0xFD30u }, /* ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM */ + }}, + { 0xFED3u, { + { {0xFEF2u}, 0xFC32u }, /* ARABIC LIGATURE FEH WITH YEH ISOLATED FORM */ + }}, + { 0xFEDFu, { + { {0xFE9Eu}, 0xFC3Fu }, /* ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM */ + { {0xFEA0u}, 0xFCC9u }, /* ARABIC LIGATURE LAM WITH JEEM INITIAL FORM */ + { {0xFEA2u}, 0xFC40u }, /* ARABIC LIGATURE LAM WITH HAH ISOLATED FORM */ + { {0xFEA4u}, 0xFCCAu }, /* ARABIC LIGATURE LAM WITH HAH INITIAL FORM */ + { {0xFEA6u}, 0xFC41u }, /* ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM */ + { {0xFEA8u}, 0xFCCBu }, /* ARABIC LIGATURE LAM WITH KHAH INITIAL FORM */ + { {0xFEE2u}, 0xFC42u }, /* ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFCCCu }, /* ARABIC LIGATURE LAM WITH MEEM INITIAL FORM */ + { {0xFEF2u}, 0xFC44u }, /* ARABIC LIGATURE LAM WITH YEH ISOLATED FORM */ + { {0xFEECu}, 0xFCCDu }, /* ARABIC LIGATURE LAM WITH HEH INITIAL FORM */ + { {0xFE82u}, 0xFEF5u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM */ + { {0xFE84u}, 0xFEF7u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM */ + { {0xFE88u}, 0xFEF9u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM */ + { {0xFE8Eu}, 0xFEFBu }, /* ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM */ + }}, + { 0xFEE0u, { + { {0xFEF0u}, 0xFC86u }, /* ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM */ + { {0xFE82u}, 0xFEF6u }, /* ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM */ + { {0xFE84u}, 0xFEF8u }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM */ + { {0xFE88u}, 0xFEFAu }, /* ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM */ + { {0xFE8Eu}, 0xFEFCu }, /* ARABIC LIGATURE LAM WITH ALEF FINAL FORM */ + }}, + { 0xFEE3u, { + { {0xFEA0u}, 0xFCCEu }, /* ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCCFu }, /* ARABIC LIGATURE MEEM WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFCD0u }, /* ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM */ + { {0xFEE4u}, 0xFCD1u }, /* ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM */ + }}, + { 0xFEE7u, { + { {0xFEE2u}, 0xFC4Eu }, /* ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM */ + { {0xFEE4u}, 0xFCD5u }, /* ARABIC LIGATURE NOON WITH MEEM INITIAL FORM */ + { {0xFEA0u}, 0xFCD2u }, /* ARABIC LIGATURE NOON WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCD3u }, /* ARABIC LIGATURE NOON WITH HAH INITIAL FORM */ + }}, + { 0xFEE8u, { + { {0xFEF2u}, 0xFC8Fu }, /* ARABIC LIGATURE NOON WITH YEH FINAL FORM */ + }}, + { 0xFEF3u, { + { {0xFEA0u}, 0xFCDAu }, /* ARABIC LIGATURE YEH WITH JEEM INITIAL FORM */ + { {0xFEA4u}, 0xFCDBu }, /* ARABIC LIGATURE YEH WITH HAH INITIAL FORM */ + { {0xFEA8u}, 0xFCDCu }, /* ARABIC LIGATURE YEH WITH KHAH INITIAL FORM */ + { {0xFEE4u}, 0xFCDDu }, /* ARABIC LIGATURE YEH WITH MEEM INITIAL FORM */ + }}, + { 0xFEF4u, { + { {0xFEAEu}, 0xFC91u }, /* ARABIC LIGATURE YEH WITH REH FINAL FORM */ + { {0xFEE6u}, 0xFC94u }, /* ARABIC LIGATURE YEH WITH NOON FINAL FORM */ + }}, +}; + + +static const struct ligature_mark_set_t { + uint16_t first; + struct ligature_pairs_t { + uint16_t components[1]; + uint16_t ligature; + } ligatures[5]; +} ligature_mark_table[] = +{ + { 0x0651u, { + { {0x064Cu}, 0xFC5Eu }, /* ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM */ + { {0x064Eu}, 0xFC60u }, /* ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM */ + { {0x064Fu}, 0xFC61u }, /* ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM */ + { {0x0650u}, 0xFC62u }, /* ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM */ + { {0x064Bu}, 0xF2EEu }, /* PUA ARABIC LIGATURE SHADDA WITH FATHATAN ISOLATED FORM */ + }}, +}; + + +static const struct ligature_3_set_t { + uint16_t first; + struct ligature_triplets_t { + uint16_t components[2]; + uint16_t ligature; + } ligatures[3]; +} ligature_3_table[] = +{ + { 0xFEDFu, { + { {0xFEE4u, 0xFEA4u}, 0xFD88u}, /* ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM */ + { {0xFEE0u, 0xFEEAu}, 0xF201u}, /* PUA ARABIC LIGATURE LELLAH ISOLATED FORM */ + { {0xFEE4u, 0xFEA0u}, 0xF211u}, /* PUA ARABIC LIGATURE LAM WITH MEEM WITH JEEM INITIAL FORM */ + }}, +}; + + +#endif /* HB_OT_SHAPER_ARABIC_TABLE_HH */ + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-arabic-win1256.hh b/gfx/harfbuzz/src/hb-ot-shaper-arabic-win1256.hh new file mode 100644 index 0000000000..b8d481c813 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-arabic-win1256.hh @@ -0,0 +1,349 @@ +/* + * Copyright © 2014 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_OT_SHAPER_ARABIC_WIN1256_HH + + +/* + * The macros in the first part of this file are generic macros that can + * be used to define the bytes for OpenType table data in code in a + * readable manner. We can move the macros to reside with their respective + * struct types, but since we only use these to define one data table, the + * Windows-1256 Arabic shaping table in this file, we keep them here. + */ + + +/* First we measure, then we cut. */ +#ifndef OT_MEASURE +#define OT_MEASURE +#define OT_TABLE_START static const struct TABLE_NAME { +#define OT_TABLE_END } +#define OT_LABEL_START(Name) unsigned char Name[ +#define OT_LABEL_END ]; +#define OT_UINT8(u8) +1/*byte*/ +#define OT_UINT16(u16) +2/*bytes*/ +#else +#undef OT_MEASURE +#define OT_TABLE_START TABLE_NAME = { +#define OT_TABLE_END }; +#define OT_LABEL_START(Name) { +#define OT_LABEL_END }, +#define OT_UINT8(u8) (u8), +#define OT_UINT16(u16) (unsigned char)((u16)>>8), (unsigned char)((u16)&0xFFu), +#define OT_COUNT(Name, ItemSize) ((unsigned int) sizeof(((struct TABLE_NAME*)0)->Name) \ + / (unsigned int)(ItemSize) \ + /* OT_ASSERT it's divisible (and positive). */) +#define OT_DISTANCE(From,To) ((unsigned int) \ + ((char*)(&((struct TABLE_NAME*)0)->To) - \ + (char*)(&((struct TABLE_NAME*)0)->From)) \ + /* OT_ASSERT it's positive. */) +#endif + + +#define OT_LABEL(Name) \ + OT_LABEL_END \ + OT_LABEL_START(Name) + +/* Whenever we receive an argument that is a list, it will expand to + * contain commas. That cannot be passed to another macro because the + * commas will throw off the preprocessor. The solution is to wrap + * the passed-in argument in OT_LIST() before passing to the next macro. + * Unfortunately this trick requires vararg macros. */ +#define OT_LIST(...) __VA_ARGS__ + + +/* + * Basic Types + */ + +#define OT_TAG(a,b,c,d) \ + OT_UINT8(a) OT_UINT8(b) OT_UINT8(c) OT_UINT8(d) + +#define OT_OFFSET(From, To) /* Offset from From to To in bytes */ \ + OT_UINT16(OT_DISTANCE(From, To)) + +#define OT_GLYPHID /* GlyphID */ \ + OT_UINT16 +/* Shorthand. */ +#define G OT_GLYPHID + +#define OT_UARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(OT_COUNT(Name##Data, 2)) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + +#define OT_UHEADLESSARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(OT_COUNT(Name##Data, 2) + 1) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + + +/* + * Common Types + */ + +#define OT_LOOKUP_FLAG_IGNORE_MARKS 0x08u + +#define OT_LOOKUP(Name, LookupType, LookupFlag, SubLookupOffsets) \ + OT_LABEL_START(Name) \ + OT_UINT16(LookupType) \ + OT_UINT16(LookupFlag) \ + OT_LABEL_END \ + OT_UARRAY(Name##SubLookupOffsetsArray, OT_LIST(SubLookupOffsets)) + +#define OT_SUBLOOKUP(Name, SubFormat, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(SubFormat) \ + Items + +#define OT_COVERAGE1(Name, Items) \ + OT_LABEL_START(Name) \ + OT_UINT16(1) \ + OT_LABEL_END \ + OT_UARRAY(Name##Glyphs, OT_LIST(Items)) + + +/* + * GSUB + */ + +#define OT_LOOKUP_TYPE_SUBST_SINGLE 1u +#define OT_LOOKUP_TYPE_SUBST_LIGATURE 4u + +#define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \ + OT_SUBLOOKUP(Name, 2, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##Substitute, OT_LIST(ToGlyphs)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FromGlyphs)) \ + /* static_assert_expr (len(FromGlyphs) == len(ToGlyphs)) */ + +#define OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(Name, FirstGlyphs, LigatureSetOffsets) \ + OT_SUBLOOKUP(Name, 1, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##LigatureSetOffsetsArray, OT_LIST(LigatureSetOffsets)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FirstGlyphs)) \ + /* static_assert_expr (len(FirstGlyphs) == len(LigatureSetOffsets)) */ + +#define OT_LIGATURE_SET(Name, LigatureSetOffsets) \ + OT_UARRAY(Name, OT_LIST(LigatureSetOffsets)) + +#define OT_LIGATURE(Name, Components, LigGlyph) \ + OT_LABEL_START(Name) \ + LigGlyph \ + OT_LABEL_END \ + OT_UHEADLESSARRAY(Name##ComponentsArray, OT_LIST(Components)) + +/* + * + * Start of Windows-1256 shaping table. + * + */ + +/* Table name. */ +#define TABLE_NAME arabic_win1256_gsub_lookups + +/* Table manifest. */ +#define MANIFEST(Items) \ + OT_LABEL_START(manifest) \ + OT_UINT16(OT_COUNT(manifestData, 6)) \ + OT_LABEL(manifestData) \ + Items \ + OT_LABEL_END + +#define MANIFEST_LOOKUP(Tag, Name) \ + Tag \ + OT_OFFSET(manifest, Name) + + +/* + * Table Start + */ +OT_TABLE_START + + +/* + * Manifest + */ +MANIFEST( + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligLookup) + MANIFEST_LOOKUP(OT_TAG('i','n','i','t'), initLookup) + MANIFEST_LOOKUP(OT_TAG('m','e','d','i'), mediLookup) + MANIFEST_LOOKUP(OT_TAG('f','i','n','a'), finaLookup) + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligMarksLookup) +) + +/* + * Lookups + */ +OT_LOOKUP(initLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(initLookup, initmediSubLookup) + OT_OFFSET(initLookup, initSubLookup) +) +OT_LOOKUP(mediLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(mediLookup, initmediSubLookup) + OT_OFFSET(mediLookup, mediSubLookup) + OT_OFFSET(mediLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(finaLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(finaLookup, finaSubLookup) + /* We don't need this one currently as the sequence inherits masks + * from the first item. Just in case we change that in the future + * to be smart about Arabic masks when ligating... */ + OT_OFFSET(finaLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(rligLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(rligLookup, lamAlefLigaturesSubLookup) +) +OT_LOOKUP(rligMarksLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, 0, + OT_OFFSET(rligMarksLookup, shaddaLigaturesSubLookup) +) + +/* + * init/medi/fina forms + */ +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initmediSubLookup, + G(198) G(200) G(201) G(202) G(203) G(204) G(205) G(206) G(211) + G(212) G(213) G(214) G(223) G(225) G(227) G(228) G(236) G(237), + G(162) G(4) G(5) G(5) G(6) G(7) G(9) G(11) G(13) + G(14) G(15) G(26) G(140) G(141) G(142) G(143) G(154) G(154) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(27) G(30) G(128) G(131) G(144) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(mediSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(28) G(31) G(129) G(138) G(149) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(finaSubLookup, + G(194) G(195) G(197) G(198) G(199) G(201) G(204) G(205) G(206) + G(218) G(219) G(229) G(236) G(237), + G(2) G(1) G(3) G(181) G(0) G(159) G(8) G(10) G(12) + G(29) G(127) G(152) G(160) G(156) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(medifinaLamAlefSubLookup, + G(165) G(178) G(180) G(252), + G(170) G(179) G(185) G(255) +) + +/* + * Lam+Alef ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(lamAlefLigaturesSubLookup, + G(225), + OT_OFFSET(lamAlefLigaturesSubLookup, lamLigatureSet) +) +OT_LIGATURE_SET(lamLigatureSet, + OT_OFFSET(lamLigatureSet, lamInitLigature1) + OT_OFFSET(lamLigatureSet, lamInitLigature2) + OT_OFFSET(lamLigatureSet, lamInitLigature3) + OT_OFFSET(lamLigatureSet, lamInitLigature4) +) +OT_LIGATURE(lamInitLigature1, G(199), G(165)) +OT_LIGATURE(lamInitLigature2, G(195), G(178)) +OT_LIGATURE(lamInitLigature3, G(194), G(180)) +OT_LIGATURE(lamInitLigature4, G(197), G(252)) + +/* + * Shadda ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(shaddaLigaturesSubLookup, + G(248), + OT_OFFSET(shaddaLigaturesSubLookup, shaddaLigatureSet) +) +OT_LIGATURE_SET(shaddaLigatureSet, + OT_OFFSET(shaddaLigatureSet, shaddaLigature1) + OT_OFFSET(shaddaLigatureSet, shaddaLigature2) + OT_OFFSET(shaddaLigatureSet, shaddaLigature3) +) +OT_LIGATURE(shaddaLigature1, G(243), G(172)) +OT_LIGATURE(shaddaLigature2, G(245), G(173)) +OT_LIGATURE(shaddaLigature3, G(246), G(175)) + +/* + * Table end + */ +OT_TABLE_END + + +/* + * Clean up + */ + +#undef MANIFEST +#undef MANIFEST_LOOKUP + +#undef OT_TABLE_START +#undef OT_TABLE_END +#undef OT_LABEL_START +#undef OT_LABEL_END +#undef OT_UINT8 +#undef OT_UINT16 +#undef OT_COUNT +#undef OT_DISTANCE + +#undef OT_LABEL +#undef OT_LIST + +#undef OT_TAG +#undef OT_OFFSET +#undef OT_GLYPHID +#undef G +#undef OT_UARRAY +#undef OT_UHEADLESSARRAY + +#undef OT_LOOKUP_FLAG_IGNORE_MARKS +#undef OT_LOOKUP +#undef OT_SUBLOOKUP +#undef OT_COVERAGE1 +#undef OT_LOOKUP_TYPE_SUBST_SINGLE +#undef OT_LOOKUP_TYPE_SUBST_LIGATURE +#undef OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2 +#undef OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1 +#undef OT_LIGATURE_SET +#undef OT_LIGATURE + + +/* + * Include a second time to get the table data... + */ +#if 0 +#include "hb.hh" /* Make check-includes.sh happy. */ +#endif +#ifdef OT_MEASURE +#include "hb-ot-shaper-arabic-win1256.hh" +#endif + +#define HB_OT_SHAPER_ARABIC_WIN1256_HH +#endif /* HB_OT_SHAPER_ARABIC_WIN1256_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-arabic.cc b/gfx/harfbuzz/src/hb-ot-shaper-arabic.cc new file mode 100644 index 0000000000..72dcc84df5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-arabic.cc @@ -0,0 +1,774 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-arabic.hh" +#include "hb-ot-shape.hh" + + +/* buffer var allocations */ +#define arabic_shaping_action() ot_shaper_var_u8_auxiliary() /* arabic shaping action */ + +#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_SHAPER0 + +/* See: + * https://github.com/harfbuzz/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */ +#define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ + (FLAG_UNSAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ + /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ + /*FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |*/ \ + /*FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |*/ \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL))) + + +/* + * Joining types: + */ + +/* + * Bits used in the joining tables + */ +enum hb_arabic_joining_type_t { + JOINING_TYPE_U = 0, + JOINING_TYPE_L = 1, + JOINING_TYPE_R = 2, + JOINING_TYPE_D = 3, + JOINING_TYPE_C = JOINING_TYPE_D, + JOINING_GROUP_ALAPH = 4, + JOINING_GROUP_DALATH_RISH = 5, + NUM_STATE_MACHINE_COLS = 6, + + JOINING_TYPE_T = 7, + JOINING_TYPE_X = 8 /* means: use general-category to choose between U or T. */ +}; + +#include "hb-ot-shaper-arabic-table.hh" + +static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat) +{ + unsigned int j_type = joining_type(u); + if (likely (j_type != JOINING_TYPE_X)) + return j_type; + + return (FLAG_UNSAFE(gen_cat) & + (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | + FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | + FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT)) + ) ? JOINING_TYPE_T : JOINING_TYPE_U; +} + +#define FEATURE_IS_SYRIAC(tag) hb_in_range<unsigned char> ((unsigned char) (tag), '2', '3') + +static const hb_tag_t arabic_features[] = +{ + HB_TAG('i','s','o','l'), + HB_TAG('f','i','n','a'), + HB_TAG('f','i','n','2'), + HB_TAG('f','i','n','3'), + HB_TAG('m','e','d','i'), + HB_TAG('m','e','d','2'), + HB_TAG('i','n','i','t'), + HB_TAG_NONE +}; + + +/* Same order as the feature array */ +enum arabic_action_t { + ISOL, + FINA, + FIN2, + FIN3, + MEDI, + MED2, + INIT, + + NONE, + + ARABIC_NUM_FEATURES = NONE, + + /* We abuse the same byte for other things... */ + STCH_FIXED, + STCH_REPEATING, +}; + +static const struct arabic_state_table_entry { + uint8_t prev_action; + uint8_t curr_action; + uint16_t next_state; +} arabic_state_table[][NUM_STATE_MACHINE_COLS] = +{ + /* jt_U, jt_L, jt_R, jt_D, jg_ALAPH, jg_DALATH_RISH */ + + /* State 0: prev was U, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, }, + + /* State 1: prev was R or ISOL/ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, }, + + /* State 2: prev was D/L in ISOL form, willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, }, + + /* State 3: prev was D in FINA form, willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, }, + + /* State 4: prev was FINA ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, }, + + /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, }, + + /* State 6: prev was DALATH/RISH, not willing to join. */ + { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, } +}; + + +static bool +arabic_fallback_shape (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static bool +record_stch (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static bool +deallocate_buffer_var (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); + return false; +} + +static void +collect_features_arabic (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* We apply features according to the Arabic spec, with pauses + * in between most. + * + * The pause between init/medi/... and rlig is required. See eg: + * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 + * + * The pauses between init/medi/... themselves are not necessarily + * needed as only one of those features is applied to any character. + * The only difference it makes is when fonts have contextual + * substitutions. We now follow the order of the spec, which makes + * for better experience if that's what Uniscribe is doing. + * + * At least for Arabic, looks like Uniscribe has a pause between + * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't + * work. However, testing shows that rlig and calt are applied + * together for Mongolian in Uniscribe. As such, we only add a + * pause for Arabic, not other scripts. + */ + + + map->enable_feature (HB_TAG('s','t','c','h')); + map->add_gsub_pause (record_stch); + + map->enable_feature (HB_TAG('c','c','m','p'), F_MANUAL_ZWJ); + map->enable_feature (HB_TAG('l','o','c','l'), F_MANUAL_ZWJ); + + map->add_gsub_pause (nullptr); + + for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) + { + bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]); + map->add_feature (arabic_features[i], F_MANUAL_ZWJ | (has_fallback ? F_HAS_FALLBACK : F_NONE)); + map->add_gsub_pause (nullptr); + } + map->add_gsub_pause (deallocate_buffer_var); + + /* Normally, Unicode says a ZWNJ means "don't ligate". In Arabic script + * however, it says a ZWJ should also mean "don't ligate". So we run + * the main ligating features as MANUAL_ZWJ. */ + + map->enable_feature (HB_TAG('r','l','i','g'), F_MANUAL_ZWJ | F_HAS_FALLBACK); + + if (plan->props.script == HB_SCRIPT_ARABIC) + map->add_gsub_pause (arabic_fallback_shape); + + map->enable_feature (HB_TAG('c','a','l','t'), F_MANUAL_ZWJ); + /* https://github.com/harfbuzz/harfbuzz/issues/1573 */ + if (!map->has_feature (HB_TAG('r','c','l','t'))) + { + map->add_gsub_pause (nullptr); + map->enable_feature (HB_TAG('r','c','l','t'), F_MANUAL_ZWJ); + } + + map->enable_feature (HB_TAG('l','i','g','a'), F_MANUAL_ZWJ); + map->enable_feature (HB_TAG('c','l','i','g'), F_MANUAL_ZWJ); + + /* The spec includes 'cswh'. Earlier versions of Windows + * used to enable this by default, but testing suggests + * that Windows 8 and later do not enable it by default, + * and spec now says 'Off by default'. + * We disabled this in ae23c24c32. + * Note that IranNastaliq uses this feature extensively + * to fixup broken glyph sequences. Oh well... + * Test case: U+0643,U+0640,U+0631. */ + //map->enable_feature (HB_TAG('c','s','w','h'), F_MANUAL_ZWJ); + map->enable_feature (HB_TAG('m','s','e','t'), F_MANUAL_ZWJ); +} + +#include "hb-ot-shaper-arabic-fallback.hh" + +struct arabic_shape_plan_t +{ + /* The "+ 1" in the next array is to accommodate for the "NONE" command, + * which is not an OpenType feature, but this simplifies the code by not + * having to do a "if (... < NONE) ..." and just rely on the fact that + * mask_array[NONE] == 0. */ + hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; + + hb_atomic_ptr_t<arabic_fallback_plan_t> fallback_plan; + + unsigned int do_fallback : 1; + unsigned int has_stch : 1; +}; + +void * +data_create_arabic (const hb_ot_shape_plan_t *plan) +{ + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) hb_calloc (1, sizeof (arabic_shape_plan_t)); + if (unlikely (!arabic_plan)) + return nullptr; + + arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; + arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h')); + for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { + arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); + arabic_plan->do_fallback = arabic_plan->do_fallback && + (FEATURE_IS_SYRIAC (arabic_features[i]) || + plan->map.needs_fallback (arabic_features[i])); + } + + return arabic_plan; +} + +void +data_destroy_arabic (void *data) +{ + arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data; + + arabic_fallback_plan_destroy (arabic_plan->fallback_plan); + + hb_free (data); +} + +static void +arabic_joining (hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + unsigned int prev = UINT_MAX, state = 0; + + /* Check pre-context */ + for (unsigned int i = 0; i < buffer->context_len[0]; i++) + { + unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i])); + + if (unlikely (this_type == JOINING_TYPE_T)) + continue; + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + state = entry->next_state; + break; + } + + for (unsigned int i = 0; i < count; i++) + { + unsigned int this_type = get_joining_type (info[i].codepoint, _hb_glyph_info_get_general_category (&info[i])); + + if (unlikely (this_type == JOINING_TYPE_T)) { + info[i].arabic_shaping_action() = NONE; + continue; + } + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + + if (entry->prev_action != NONE && prev != UINT_MAX) + { + info[prev].arabic_shaping_action() = entry->prev_action; + buffer->safe_to_insert_tatweel (prev, i + 1); + } + else + { + if (prev == UINT_MAX) + { + if (this_type >= JOINING_TYPE_R) + buffer->unsafe_to_concat_from_outbuffer (0, i + 1); + } + else + { + if (this_type >= JOINING_TYPE_R || + (2 <= state && state <= 5) /* States that have a possible prev_action. */) + buffer->unsafe_to_concat (prev, i + 1); + } + } + + info[i].arabic_shaping_action() = entry->curr_action; + + prev = i; + state = entry->next_state; + } + + for (unsigned int i = 0; i < buffer->context_len[1]; i++) + { + unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[1][i])); + + if (unlikely (this_type == JOINING_TYPE_T)) + continue; + + const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; + if (entry->prev_action != NONE && prev != UINT_MAX) + { + info[prev].arabic_shaping_action() = entry->prev_action; + buffer->safe_to_insert_tatweel (prev, buffer->len); + } + else if (2 <= state && state <= 5) /* States that have a possible prev_action. */ + { + buffer->unsafe_to_concat (prev, buffer->len); + } + break; + } +} + +static void +mongolian_variation_selectors (hb_buffer_t *buffer) +{ + /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */ + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 1; i < count; i++) + if (unlikely (hb_in_ranges<hb_codepoint_t> (info[i].codepoint, 0x180Bu, 0x180Du, 0x180Fu, 0x180Fu))) + info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action(); +} + +void +setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan, + hb_buffer_t *buffer, + hb_script_t script) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); + + arabic_joining (buffer); + if (script == HB_SCRIPT_MONGOLIAN) + mongolian_variation_selectors (buffer); + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; +} + +static void +setup_masks_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); +} + +static bool +arabic_fallback_shape (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ +#ifdef HB_NO_OT_SHAPER_ARABIC_FALLBACK + return false; +#endif + + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + + if (!arabic_plan->do_fallback) + return false; + +retry: + arabic_fallback_plan_t *fallback_plan = arabic_plan->fallback_plan; + if (unlikely (!fallback_plan)) + { + /* This sucks. We need a font to build the fallback plan... */ + fallback_plan = arabic_fallback_plan_create (plan, font); + if (unlikely (!arabic_plan->fallback_plan.cmpexch (nullptr, fallback_plan))) + { + arabic_fallback_plan_destroy (fallback_plan); + goto retry; + } + } + + arabic_fallback_plan_shape (fallback_plan, font, buffer); + return true; +} + +/* + * Stretch feature: "stch". + * See example here: + * https://docs.microsoft.com/en-us/typography/script-development/syriac + * We implement this in a generic way, such that the Arabic subtending + * marks can use it as well. + */ + +static bool +record_stch (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; + if (!arabic_plan->has_stch) + return false; + + /* 'stch' feature was just applied. Look for anything that multiplied, + * and record it for stch treatment later. Note that rtlm, frac, etc + * are applied before stch, but we assume that they didn't result in + * anything multiplying into 5 pieces, so it's safe-ish... */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (unlikely (_hb_glyph_info_multiplied (&info[i]))) + { + unsigned int comp = _hb_glyph_info_get_lig_comp (&info[i]); + info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED; + buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH; + } + return false; +} + +static void +apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font) +{ + if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH))) + return; + + bool rtl = buffer->props.direction == HB_DIRECTION_RTL; + + if (!rtl) + buffer->reverse (); + + /* We do a two pass implementation: + * First pass calculates the exact number of extra glyphs we need, + * We then enlarge buffer to have that much room, + * Second pass applies the stretch, copying things to the end of buffer. + */ + + int sign = font->x_scale < 0 ? -1 : +1; + unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT + enum { MEASURE, CUT } /* step_t */; + + for (unsigned int step = MEASURE; step <= CUT; step = step + 1) + { + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + hb_glyph_position_t *pos = buffer->pos; + unsigned int new_len = count + extra_glyphs_needed; // write head during CUT + unsigned int j = new_len; + for (unsigned int i = count; i; i--) + { + if (!hb_in_range<uint8_t> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + { + if (step == CUT) + { + --j; + info[j] = info[i - 1]; + pos[j] = pos[i - 1]; + } + continue; + } + + /* Yay, justification! */ + + hb_position_t w_total = 0; // Total to be filled + hb_position_t w_fixed = 0; // Sum of fixed tiles + hb_position_t w_repeating = 0; // Sum of repeating tiles + int n_fixed = 0; + int n_repeating = 0; + + unsigned int end = i; + while (i && + hb_in_range<uint8_t> (info[i - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING)) + { + i--; + hb_position_t width = font->get_glyph_h_advance (info[i].codepoint); + if (info[i].arabic_shaping_action() == STCH_FIXED) + { + w_fixed += width; + n_fixed++; + } + else + { + w_repeating += width; + n_repeating++; + } + } + unsigned int start = i; + unsigned int context = i; + while (context && + !hb_in_range<uint8_t> (info[context - 1].arabic_shaping_action(), STCH_FIXED, STCH_REPEATING) && + (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || + HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_category (&info[context - 1])))) + { + context--; + w_total += pos[context].x_advance; + } + i++; // Don't touch i again. + + DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%u,%u,%u)", + step == MEASURE ? "measuring" : "cutting", context, start, end); + DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%u width %d", start - context, w_total); + DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); + DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); + + /* Number of additional times to repeat each repeating tile. */ + int n_copies = 0; + + hb_position_t w_remaining = w_total - w_fixed; + if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) + n_copies = (sign * w_remaining) / (sign * w_repeating) - 1; + + /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */ + hb_position_t extra_repeat_overlap = 0; + hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1); + if (shortfall > 0 && n_repeating > 0) + { + ++n_copies; + hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining; + if (excess > 0) + { + extra_repeat_overlap = excess / (n_copies * n_repeating); + w_remaining = 0; + } + } + + if (step == MEASURE) + { + extra_glyphs_needed += n_copies * n_repeating; + DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies); + } + else + { + buffer->unsafe_to_break (context, end); + hb_position_t x_offset = w_remaining / 2; + for (unsigned int k = end; k > start; k--) + { + hb_position_t width = font->get_glyph_h_advance (info[k - 1].codepoint); + + unsigned int repeat = 1; + if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) + repeat += n_copies; + + DEBUG_MSG (ARABIC, nullptr, "appending %u copies of glyph %u; j=%u", + repeat, info[k - 1].codepoint, j); + pos[k - 1].x_advance = 0; + for (unsigned int n = 0; n < repeat; n++) + { + if (rtl) + { + x_offset -= width; + if (n > 0) + x_offset += extra_repeat_overlap; + } + pos[k - 1].x_offset = x_offset; + /* Append copy. */ + --j; + info[j] = info[k - 1]; + pos[j] = pos[k - 1]; + + if (!rtl) + { + x_offset += width; + if (n > 0) + x_offset -= extra_repeat_overlap; + } + } + } + } + } + + if (step == MEASURE) + { + if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) + break; + } + else + { + assert (j == 0); + buffer->len = new_len; + } + } + + if (!rtl) + buffer->reverse (); +} + + +static void +postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + apply_stch (plan, buffer, font); +} + +/* https://www.unicode.org/reports/tr53/ */ + +static hb_codepoint_t +modifier_combining_marks[] = +{ + 0x0654u, /* ARABIC HAMZA ABOVE */ + 0x0655u, /* ARABIC HAMZA BELOW */ + 0x0658u, /* ARABIC MARK NOON GHUNNA */ + 0x06DCu, /* ARABIC SMALL HIGH SEEN */ + 0x06E3u, /* ARABIC SMALL LOW SEEN */ + 0x06E7u, /* ARABIC SMALL HIGH YEH */ + 0x06E8u, /* ARABIC SMALL HIGH NOON */ + 0x08CAu, /* ARABIC SMALL HIGH FARSI YEH */ + 0x08CBu, /* ARABIC SMALL HIGH YEH BARREE WITH TWO DOTS BELOW */ + 0x08CDu, /* ARABIC SMALL HIGH ZAH */ + 0x08CEu, /* ARABIC LARGE ROUND DOT ABOVE */ + 0x08CFu, /* ARABIC LARGE ROUND DOT BELOW */ + 0x08D3u, /* ARABIC SMALL LOW WAW */ + 0x08F3u, /* ARABIC SMALL HIGH WAW */ +}; + +static inline bool +info_is_mcm (const hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + for (unsigned int i = 0; i < ARRAY_LENGTH (modifier_combining_marks); i++) + if (u == modifier_combining_marks[i]) + return true; + return false; +} + +static void +reorder_marks_arabic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + DEBUG_MSG (ARABIC, buffer, "Reordering marks from %u to %u", start, end); + + unsigned int i = start; + for (unsigned int cc = 220; cc <= 230; cc += 10) + { + DEBUG_MSG (ARABIC, buffer, "Looking for %u's starting at %u", cc, i); + while (i < end && info_cc(info[i]) < cc) + i++; + DEBUG_MSG (ARABIC, buffer, "Looking for %u's stopped at %u", cc, i); + + if (i == end) + break; + + if (info_cc(info[i]) > cc) + continue; + + unsigned int j = i; + while (j < end && info_cc(info[j]) == cc && info_is_mcm (info[j])) + j++; + + if (i == j) + continue; + + DEBUG_MSG (ARABIC, buffer, "Found %u's from %u to %u", cc, i, j); + + /* Shift it! */ + DEBUG_MSG (ARABIC, buffer, "Shifting %u's: %u %u", cc, i, j); + hb_glyph_info_t temp[HB_OT_SHAPE_MAX_COMBINING_MARKS]; + assert (j - i <= ARRAY_LENGTH (temp)); + buffer->merge_clusters (start, j); + memmove (temp, &info[i], (j - i) * sizeof (hb_glyph_info_t)); + memmove (&info[start + j - i], &info[start], (i - start) * sizeof (hb_glyph_info_t)); + memmove (&info[start], temp, (j - i) * sizeof (hb_glyph_info_t)); + + /* Renumber CC such that the reordered sequence is still sorted. + * 22 and 26 are chosen because they are smaller than all Arabic categories, + * and are folded back to 220/230 respectively during fallback mark positioning. + * + * We do this because the CGJ-handling logic in the normalizer relies on + * mark sequences having an increasing order even after this reordering. + * https://github.com/harfbuzz/harfbuzz/issues/554 + * This, however, does break some obscure sequences, where the normalizer + * might compose a sequence that it should not. For example, in the seequence + * ALEF, HAMZAH, MADDAH, we should NOT try to compose ALEF+MADDAH, but with this + * renumbering, we will. + */ + unsigned int new_start = start + j - i; + unsigned int new_cc = cc == 220 ? HB_MODIFIED_COMBINING_CLASS_CCC22 : HB_MODIFIED_COMBINING_CLASS_CCC26; + while (start < new_start) + { + _hb_glyph_info_set_modified_combining_class (&info[start], new_cc); + start++; + } + + i = j; + } +} + +const hb_ot_shaper_t _hb_ot_shaper_arabic = +{ + collect_features_arabic, + nullptr, /* override_features */ + data_create_arabic, + data_destroy_arabic, + nullptr, /* preprocess_text */ + postprocess_glyphs_arabic, + nullptr, /* decompose */ + nullptr, /* compose */ + setup_masks_arabic, + reorder_marks_arabic, + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-arabic.hh b/gfx/harfbuzz/src/hb-ot-shaper-arabic.hh new file mode 100644 index 0000000000..a025b1a399 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-arabic.hh @@ -0,0 +1,50 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_ARABIC_HH +#define HB_OT_SHAPER_ARABIC_HH + +#include "hb.hh" + +#include "hb-ot-shaper.hh" + + +struct arabic_shape_plan_t; + +HB_INTERNAL void * +data_create_arabic (const hb_ot_shape_plan_t *plan); + +HB_INTERNAL void +data_destroy_arabic (void *data); + +HB_INTERNAL void +setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan, + hb_buffer_t *buffer, + hb_script_t script); + +#endif /* HB_OT_SHAPER_ARABIC_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-default.cc b/gfx/harfbuzz/src/hb-ot-shaper-default.cc new file mode 100644 index 0000000000..f0404a4d2c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-default.cc @@ -0,0 +1,75 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +const hb_ot_shaper_t _hb_ot_shaper_default = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; + +#ifndef HB_NO_AAT_SHAPE +/* Same as default but no mark advance zeroing / fallback positioning. + * Dumbest shaper ever, basically. */ +const hb_ot_shaper_t _hb_ot_shaper_dumber = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; +#endif + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-hangul.cc b/gfx/harfbuzz/src/hb-ot-shaper-hangul.cc new file mode 100644 index 0000000000..c90476bc46 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-hangul.cc @@ -0,0 +1,436 @@ +/* + * 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +/* Hangul shaper */ + + +/* Same order as the feature array below */ +enum { + _JMO, + + LJMO, + VJMO, + TJMO, + + FIRST_HANGUL_FEATURE = LJMO, + HANGUL_FEATURE_COUNT = TJMO + 1 +}; + +static const hb_tag_t hangul_features[HANGUL_FEATURE_COUNT] = +{ + HB_TAG_NONE, + HB_TAG('l','j','m','o'), + HB_TAG('v','j','m','o'), + HB_TAG('t','j','m','o') +}; + +static void +collect_features_hangul (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + for (unsigned int i = FIRST_HANGUL_FEATURE; i < HANGUL_FEATURE_COUNT; i++) + map->add_feature (hangul_features[i]); +} + +static void +override_features_hangul (hb_ot_shape_planner_t *plan) +{ + /* Uniscribe does not apply 'calt' for Hangul, and certain fonts + * (Noto Sans CJK, Source Sans Han, etc) apply all of jamo lookups + * in calt, which is not desirable. */ + plan->map.disable_feature (HB_TAG('c','a','l','t')); +} + +struct hangul_shape_plan_t +{ + hb_mask_t mask_array[HANGUL_FEATURE_COUNT]; +}; + +static void * +data_create_hangul (const hb_ot_shape_plan_t *plan) +{ + hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) hb_calloc (1, sizeof (hangul_shape_plan_t)); + if (unlikely (!hangul_plan)) + return nullptr; + + for (unsigned int i = 0; i < HANGUL_FEATURE_COUNT; i++) + hangul_plan->mask_array[i] = plan->map.get_1_mask (hangul_features[i]); + + return hangul_plan; +} + +static void +data_destroy_hangul (void *data) +{ + hb_free (data); +} + +/* Constants for algorithmic hangul syllable [de]composition. */ +#define LBase 0x1100u +#define VBase 0x1161u +#define TBase 0x11A7u +#define LCount 19u +#define VCount 21u +#define TCount 28u +#define SBase 0xAC00u +#define NCount (VCount * TCount) +#define SCount (LCount * NCount) + +#define isCombiningL(u) (hb_in_range<hb_codepoint_t> ((u), LBase, LBase+LCount-1)) +#define isCombiningV(u) (hb_in_range<hb_codepoint_t> ((u), VBase, VBase+VCount-1)) +#define isCombiningT(u) (hb_in_range<hb_codepoint_t> ((u), TBase+1, TBase+TCount-1)) +#define isCombinedS(u) (hb_in_range<hb_codepoint_t> ((u), SBase, SBase+SCount-1)) + +#define isL(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu)) +#define isV(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u)) +#define isT(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu)) + +#define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302Eu, 0x302Fu)) + +/* buffer var allocations */ +#define hangul_shaping_feature() ot_shaper_var_u8_auxiliary() /* hangul jamo shaping feature */ + +static bool +is_zero_width_char (hb_font_t *font, + hb_codepoint_t unicode) +{ + hb_codepoint_t glyph; + return hb_font_get_glyph (font, unicode, 0, &glyph) && hb_font_get_glyph_h_advance (font, glyph) == 0; +} + +static void +preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, hangul_shaping_feature); + + /* Hangul syllables come in two shapes: LV, and LVT. Of those: + * + * - LV can be precomposed, or decomposed. Lets call those + * <LV> and <L,V>, + * - LVT can be fully precomposed, partially precomposed, or + * fully decomposed. Ie. <LVT>, <LV,T>, or <L,V,T>. + * + * The composition / decomposition is mechanical. However, not + * all <L,V> sequences compose, and not all <LV,T> sequences + * compose. + * + * Here are the specifics: + * + * - <L>: U+1100..115F, U+A960..A97F + * - <V>: U+1160..11A7, U+D7B0..D7C7 + * - <T>: U+11A8..11FF, U+D7CB..D7FB + * + * - Only the <L,V> sequences for some of the U+11xx ranges combine. + * - Only <LV,T> sequences for some of the Ts in U+11xx range combine. + * + * Here is what we want to accomplish in this shaper: + * + * - If the whole syllable can be precomposed, do that, + * - Otherwise, fully decompose and apply ljmo/vjmo/tjmo features. + * - If a valid syllable is followed by a Hangul tone mark, reorder the tone + * mark to precede the whole syllable - unless it is a zero-width glyph, in + * which case we leave it untouched, assuming it's designed to overstrike. + * + * That is, of the different possible syllables: + * + * <L> + * <L,V> + * <L,V,T> + * <LV> + * <LVT> + * <LV, T> + * + * - <L> needs no work. + * + * - <LV> and <LVT> can stay the way they are if the font supports them, otherwise we + * should fully decompose them if font supports. + * + * - <L,V> and <L,V,T> we should compose if the whole thing can be composed. + * + * - <LV,T> we should compose if the whole thing can be composed, otherwise we should + * decompose. + */ + + buffer->clear_output (); + unsigned int start = 0, end = 0; /* Extent of most recently seen syllable; + * valid only if start < end + */ + unsigned int count = buffer->len; + + for (buffer->idx = 0; buffer->idx < count && buffer->successful;) + { + hb_codepoint_t u = buffer->cur().codepoint; + + if (isHangulTone (u)) + { + /* + * We could cache the width of the tone marks and the existence of dotted-circle, + * but the use of the Hangul tone mark characters seems to be rare enough that + * I didn't bother for now. + */ + if (start < end && end == buffer->out_len) + { + /* Tone mark follows a valid syllable; move it in front, unless it's zero width. */ + buffer->unsafe_to_break_from_outbuffer (start, buffer->idx); + if (unlikely (!buffer->next_glyph ())) break; + if (!is_zero_width_char (font, u)) + { + buffer->merge_out_clusters (start, end + 1); + hb_glyph_info_t *info = buffer->out_info; + hb_glyph_info_t tone = info[end]; + memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t)); + info[start] = tone; + } + } + else + { + /* No valid syllable as base for tone mark; try to insert dotted circle. */ + if (!(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) && + font->has_glyph (0x25CCu)) + { + hb_codepoint_t chars[2]; + if (!is_zero_width_char (font, u)) + { + chars[0] = u; + chars[1] = 0x25CCu; + } else + { + chars[0] = 0x25CCu; + chars[1] = u; + } + (void) buffer->replace_glyphs (1, 2, chars); + } + else + { + /* No dotted circle available in the font; just leave tone mark untouched. */ + (void) buffer->next_glyph (); + } + } + start = end = buffer->out_len; + continue; + } + + start = buffer->out_len; /* Remember current position as a potential syllable start; + * will only be used if we set end to a later position. + */ + + if (isL (u) && buffer->idx + 1 < count) + { + hb_codepoint_t l = u; + hb_codepoint_t v = buffer->cur(+1).codepoint; + if (isV (v)) + { + /* Have <L,V> or <L,V,T>. */ + hb_codepoint_t t = 0; + unsigned int tindex = 0; + if (buffer->idx + 2 < count) + { + t = buffer->cur(+2).codepoint; + if (isT (t)) + tindex = t - TBase; /* Only used if isCombiningT (t); otherwise invalid. */ + else + t = 0; /* The next character was not a trailing jamo. */ + } + buffer->unsafe_to_break (buffer->idx, buffer->idx + (t ? 3 : 2)); + + /* We've got a syllable <L,V,T?>; see if it can potentially be composed. */ + if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t))) + { + /* Try to compose; if this succeeds, end is set to start+1. */ + hb_codepoint_t s = SBase + (l - LBase) * NCount + (v - VBase) * TCount + tindex; + if (font->has_glyph (s)) + { + (void) buffer->replace_glyphs (t ? 3 : 2, 1, &s); + end = start + 1; + continue; + } + } + + /* We didn't compose, either because it's an Old Hangul syllable without a + * precomposed character in Unicode, or because the font didn't support the + * necessary precomposed glyph. + * Set jamo features on the individual glyphs, and advance past them. + */ + buffer->cur().hangul_shaping_feature() = LJMO; + (void) buffer->next_glyph (); + buffer->cur().hangul_shaping_feature() = VJMO; + (void) buffer->next_glyph (); + if (t) + { + buffer->cur().hangul_shaping_feature() = TJMO; + (void) buffer->next_glyph (); + end = start + 3; + } + else + end = start + 2; + if (unlikely (!buffer->successful)) + break; + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start, end); + continue; + } + } + + else if (isCombinedS (u)) + { + /* Have <LV>, <LVT>, or <LV,T> */ + hb_codepoint_t s = u; + bool has_glyph = font->has_glyph (s); + unsigned int lindex = (s - SBase) / NCount; + unsigned int nindex = (s - SBase) % NCount; + unsigned int vindex = nindex / TCount; + unsigned int tindex = nindex % TCount; + + if (!tindex && + buffer->idx + 1 < count && + isCombiningT (buffer->cur(+1).codepoint)) + { + /* <LV,T>, try to combine. */ + unsigned int new_tindex = buffer->cur(+1).codepoint - TBase; + hb_codepoint_t new_s = s + new_tindex; + if (font->has_glyph (new_s)) + { + (void) buffer->replace_glyphs (2, 1, &new_s); + end = start + 1; + continue; + } + else + buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */ + } + + /* Otherwise, decompose if font doesn't support <LV> or <LVT>, + * or if having non-combining <LV,T>. Note that we already handled + * combining <LV,T> above. */ + if (!has_glyph || + (!tindex && + buffer->idx + 1 < count && + isT (buffer->cur(+1).codepoint))) + { + hb_codepoint_t decomposed[3] = {LBase + lindex, + VBase + vindex, + TBase + tindex}; + if (font->has_glyph (decomposed[0]) && + font->has_glyph (decomposed[1]) && + (!tindex || font->has_glyph (decomposed[2]))) + { + unsigned int s_len = tindex ? 3 : 2; + (void) buffer->replace_glyphs (1, s_len, decomposed); + + /* If we decomposed an LV because of a non-combining T following, + * we want to include this T in the syllable. + */ + if (has_glyph && !tindex) + { + (void) buffer->next_glyph (); + s_len++; + } + if (unlikely (!buffer->successful)) + break; + + /* We decomposed S: apply jamo features to the individual glyphs + * that are now in buffer->out_info. + */ + hb_glyph_info_t *info = buffer->out_info; + end = start + s_len; + + unsigned int i = start; + info[i++].hangul_shaping_feature() = LJMO; + info[i++].hangul_shaping_feature() = VJMO; + if (i < end) + info[i++].hangul_shaping_feature() = TJMO; + + if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start, end); + continue; + } + else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint))) + buffer->unsafe_to_break (buffer->idx, buffer->idx + 2); /* Mark unsafe between LV and T. */ + } + + if (has_glyph) + { + /* We didn't decompose the S, so just advance past it and fall through. */ + end = start + 1; + } + } + + /* Didn't find a recognizable syllable, so we leave end <= start; + * this will prevent tone-mark reordering happening. + */ + (void) buffer->next_glyph (); + } + buffer->sync (); +} + +static void +setup_masks_hangul (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const hangul_shape_plan_t *hangul_plan = (const hangul_shape_plan_t *) plan->data; + + if (likely (hangul_plan)) + { + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++, info++) + info->mask |= hangul_plan->mask_array[info->hangul_shaping_feature()]; + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, hangul_shaping_feature); +} + + +const hb_ot_shaper_t _hb_ot_shaper_hangul = +{ + collect_features_hangul, + override_features_hangul, + data_create_hangul, + data_destroy_hangul, + preprocess_text_hangul, + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + setup_masks_hangul, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-hebrew.cc b/gfx/harfbuzz/src/hb-ot-shaper-hebrew.cc new file mode 100644 index 0000000000..e18edd6b3f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-hebrew.cc @@ -0,0 +1,211 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +static bool +compose_hebrew (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Hebrew presentation-form shaping. + * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 + * Hebrew presentation forms with dagesh, for characters U+05D0..05EA; + * Note that some letters do not have a dagesh presForm encoded. + */ + static const hb_codepoint_t sDageshForms[0x05EAu - 0x05D0u + 1] = { + 0xFB30u, /* ALEF */ + 0xFB31u, /* BET */ + 0xFB32u, /* GIMEL */ + 0xFB33u, /* DALET */ + 0xFB34u, /* HE */ + 0xFB35u, /* VAV */ + 0xFB36u, /* ZAYIN */ + 0x0000u, /* HET */ + 0xFB38u, /* TET */ + 0xFB39u, /* YOD */ + 0xFB3Au, /* FINAL KAF */ + 0xFB3Bu, /* KAF */ + 0xFB3Cu, /* LAMED */ + 0x0000u, /* FINAL MEM */ + 0xFB3Eu, /* MEM */ + 0x0000u, /* FINAL NUN */ + 0xFB40u, /* NUN */ + 0xFB41u, /* SAMEKH */ + 0x0000u, /* AYIN */ + 0xFB43u, /* FINAL PE */ + 0xFB44u, /* PE */ + 0x0000u, /* FINAL TSADI */ + 0xFB46u, /* TSADI */ + 0xFB47u, /* QOF */ + 0xFB48u, /* RESH */ + 0xFB49u, /* SHIN */ + 0xFB4Au /* TAV */ + }; + + bool found = (bool) c->unicode->compose (a, b, ab); + +#ifdef HB_NO_OT_SHAPER_HEBREW_FALLBACK + return found; +#endif + + if (!found && !c->plan->has_gpos_mark) + { + /* Special-case Hebrew presentation forms that are excluded from + * standard normalization, but wanted for old fonts. */ + switch (b) { + case 0x05B4u: /* HIRIQ */ + if (a == 0x05D9u) { /* YOD */ + *ab = 0xFB1Du; + found = true; + } + break; + case 0x05B7u: /* PATAH */ + if (a == 0x05F2u) { /* YIDDISH YOD YOD */ + *ab = 0xFB1Fu; + found = true; + } else if (a == 0x05D0u) { /* ALEF */ + *ab = 0xFB2Eu; + found = true; + } + break; + case 0x05B8u: /* QAMATS */ + if (a == 0x05D0u) { /* ALEF */ + *ab = 0xFB2Fu; + found = true; + } + break; + case 0x05B9u: /* HOLAM */ + if (a == 0x05D5u) { /* VAV */ + *ab = 0xFB4Bu; + found = true; + } + break; + case 0x05BCu: /* DAGESH */ + if (a >= 0x05D0u && a <= 0x05EAu) { + *ab = sDageshForms[a - 0x05D0u]; + found = (*ab != 0); + } else if (a == 0xFB2Au) { /* SHIN WITH SHIN DOT */ + *ab = 0xFB2Cu; + found = true; + } else if (a == 0xFB2Bu) { /* SHIN WITH SIN DOT */ + *ab = 0xFB2Du; + found = true; + } + break; + case 0x05BFu: /* RAFE */ + switch (a) { + case 0x05D1u: /* BET */ + *ab = 0xFB4Cu; + found = true; + break; + case 0x05DBu: /* KAF */ + *ab = 0xFB4Du; + found = true; + break; + case 0x05E4u: /* PE */ + *ab = 0xFB4Eu; + found = true; + break; + } + break; + case 0x05C1u: /* SHIN DOT */ + if (a == 0x05E9u) { /* SHIN */ + *ab = 0xFB2Au; + found = true; + } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ + *ab = 0xFB2Cu; + found = true; + } + break; + case 0x05C2u: /* SIN DOT */ + if (a == 0x05E9u) { /* SHIN */ + *ab = 0xFB2Bu; + found = true; + } else if (a == 0xFB49u) { /* SHIN WITH DAGESH */ + *ab = 0xFB2Du; + found = true; + } + break; + } + } + + return found; +} + +static void +reorder_marks_hebrew (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + for (unsigned i = start + 2; i < end; i++) + { + unsigned c0 = info_cc (info[i - 2]); + unsigned c1 = info_cc (info[i - 1]); + unsigned c2 = info_cc (info[i - 0]); + + if ((c0 == HB_MODIFIED_COMBINING_CLASS_CCC17 || c0 == HB_MODIFIED_COMBINING_CLASS_CCC18) /* patach or qamats */ && + (c1 == HB_MODIFIED_COMBINING_CLASS_CCC10 || c1 == HB_MODIFIED_COMBINING_CLASS_CCC14) /* sheva or hiriq */ && + (c2 == HB_MODIFIED_COMBINING_CLASS_CCC22 || c2 == HB_UNICODE_COMBINING_CLASS_BELOW) /* meteg or below */) + { + buffer->merge_clusters (i - 1, i + 1); + hb_swap (info[i - 1], info[i]); + break; + } + } + + +} + +const hb_ot_shaper_t _hb_ot_shaper_hebrew = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + compose_hebrew, + nullptr, /* setup_masks */ + reorder_marks_hebrew, + HB_TAG ('h','e','b','r'), /* gpos_tag. https://github.com/harfbuzz/harfbuzz/issues/347#issuecomment-267838368 */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + true, /* fallback_position */ +}; + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-indic-machine.hh b/gfx/harfbuzz/src/hb-ot-shaper-indic-machine.hh new file mode 100644 index 0000000000..353e32d32c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-indic-machine.hh @@ -0,0 +1,627 @@ + +#line 1 "hb-ot-shaper-indic-machine.rl" +/* + * 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 HB_OT_SHAPER_INDIC_MACHINE_HH +#define HB_OT_SHAPER_INDIC_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define indic_category() ot_shaper_var_u8_category() /* indic_category_t */ +#define indic_position() ot_shaper_var_u8_auxiliary() /* indic_position_t */ + +using indic_category_t = unsigned; +using indic_position_t = ot_position_t; + +#define I_Cat(Cat) indic_syllable_machine_ex_##Cat + +enum indic_syllable_type_t { + indic_consonant_syllable, + indic_vowel_syllable, + indic_standalone_cluster, + indic_symbol_cluster, + indic_broken_cluster, + indic_non_indic_cluster, +}; + + +#line 57 "hb-ot-shaper-indic-machine.hh" +#define indic_syllable_machine_ex_A 9u +#define indic_syllable_machine_ex_C 1u +#define indic_syllable_machine_ex_CM 16u +#define indic_syllable_machine_ex_CS 18u +#define indic_syllable_machine_ex_DOTTEDCIRCLE 11u +#define indic_syllable_machine_ex_H 4u +#define indic_syllable_machine_ex_M 7u +#define indic_syllable_machine_ex_MPst 13u +#define indic_syllable_machine_ex_N 3u +#define indic_syllable_machine_ex_PLACEHOLDER 10u +#define indic_syllable_machine_ex_RS 12u +#define indic_syllable_machine_ex_Ra 15u +#define indic_syllable_machine_ex_Repha 14u +#define indic_syllable_machine_ex_SM 8u +#define indic_syllable_machine_ex_Symbol 17u +#define indic_syllable_machine_ex_V 2u +#define indic_syllable_machine_ex_VD 9u +#define indic_syllable_machine_ex_X 0u +#define indic_syllable_machine_ex_ZWJ 6u +#define indic_syllable_machine_ex_ZWNJ 5u + + +#line 80 "hb-ot-shaper-indic-machine.hh" +static const unsigned char _indic_syllable_machine_trans_keys[] = { + 8u, 8u, 4u, 13u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, + 8u, 8u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, + 8u, 8u, 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 4u, 13u, 8u, 8u, + 5u, 13u, 5u, 13u, 13u, 13u, 4u, 13u, 4u, 13u, 5u, 13u, 8u, 8u, 1u, 18u, + 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 5u, 9u, 5u, 9u, 9u, 9u, 5u, 9u, + 1u, 15u, 1u, 15u, 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 4u, 13u, + 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 3u, 16u, 3u, 16u, 4u, 16u, + 1u, 15u, 3u, 16u, 3u, 16u, 4u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, + 1u, 15u, 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 4u, 13u, 5u, 9u, + 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 3u, 16u, 4u, 13u, 3u, 16u, 3u, 16u, + 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, + 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 3u, 16u, 4u, 13u, 5u, 9u, + 5u, 9u, 3u, 9u, 5u, 9u, 3u, 16u, 4u, 13u, 4u, 13u, 3u, 16u, 3u, 16u, + 4u, 16u, 1u, 15u, 3u, 16u, 1u, 15u, 5u, 9u, 9u, 9u, 5u, 9u, 1u, 15u, + 1u, 15u, 3u, 13u, 4u, 13u, 5u, 13u, 5u, 13u, 3u, 16u, 4u, 13u, 5u, 9u, + 5u, 9u, 3u, 9u, 5u, 9u, 1u, 16u, 3u, 16u, 1u, 16u, 4u, 13u, 5u, 13u, + 5u, 13u, 9u, 9u, 5u, 9u, 1u, 15u, 3u, 9u, 5u, 9u, 5u, 9u, 9u, 9u, + 5u, 9u, 1u, 15u, 0 +}; + +static const char _indic_syllable_machine_key_spans[] = { + 1, 10, 9, 9, 1, 10, 10, 10, + 1, 9, 9, 1, 10, 10, 10, 10, + 1, 9, 9, 1, 10, 10, 10, 1, + 9, 9, 1, 10, 10, 9, 1, 18, + 14, 14, 13, 15, 5, 5, 1, 5, + 15, 15, 15, 11, 10, 9, 9, 10, + 5, 7, 5, 14, 14, 14, 14, 13, + 15, 14, 14, 13, 15, 5, 1, 5, + 15, 15, 11, 10, 9, 9, 10, 5, + 5, 7, 5, 14, 14, 10, 14, 14, + 13, 15, 14, 15, 5, 1, 5, 15, + 15, 11, 10, 9, 9, 14, 10, 5, + 5, 7, 5, 14, 10, 10, 14, 14, + 13, 15, 14, 15, 5, 1, 5, 15, + 15, 11, 10, 9, 9, 14, 10, 5, + 5, 7, 5, 16, 14, 16, 10, 9, + 9, 1, 5, 15, 7, 5, 5, 1, + 5, 15 +}; + +static const short _indic_syllable_machine_index_offsets[] = { + 0, 2, 13, 23, 33, 35, 46, 57, + 68, 70, 80, 90, 92, 103, 114, 125, + 136, 138, 148, 158, 160, 171, 182, 193, + 195, 205, 215, 217, 228, 239, 249, 251, + 270, 285, 300, 314, 330, 336, 342, 344, + 350, 366, 382, 398, 410, 421, 431, 441, + 452, 458, 466, 472, 487, 502, 517, 532, + 546, 562, 577, 592, 606, 622, 628, 630, + 636, 652, 668, 680, 691, 701, 711, 722, + 728, 734, 742, 748, 763, 778, 789, 804, + 819, 833, 849, 864, 880, 886, 888, 894, + 910, 926, 938, 949, 959, 969, 984, 995, + 1001, 1007, 1015, 1021, 1036, 1047, 1058, 1073, + 1088, 1102, 1118, 1133, 1149, 1155, 1157, 1163, + 1179, 1195, 1207, 1218, 1228, 1238, 1253, 1264, + 1270, 1276, 1284, 1290, 1307, 1322, 1339, 1350, + 1360, 1370, 1372, 1378, 1394, 1402, 1408, 1414, + 1416, 1422 +}; + +static const unsigned char _indic_syllable_machine_indicies[] = { + 1, 0, 2, 3, 3, 4, 5, 0, + 0, 0, 0, 4, 0, 3, 3, 4, + 6, 0, 0, 0, 0, 4, 0, 3, + 3, 4, 5, 0, 0, 0, 0, 4, + 0, 4, 0, 7, 3, 3, 4, 5, + 0, 0, 0, 0, 4, 0, 2, 3, + 3, 4, 5, 0, 0, 0, 8, 4, + 0, 10, 11, 11, 12, 13, 9, 9, + 9, 9, 12, 9, 14, 9, 11, 11, + 12, 15, 9, 9, 9, 9, 12, 9, + 11, 11, 12, 13, 9, 9, 9, 9, + 12, 9, 12, 9, 16, 11, 11, 12, + 13, 9, 9, 9, 9, 12, 9, 10, + 11, 11, 12, 13, 9, 9, 9, 17, + 12, 9, 10, 11, 11, 12, 13, 9, + 9, 9, 18, 12, 9, 20, 21, 21, + 22, 23, 19, 19, 19, 24, 22, 19, + 25, 19, 21, 21, 22, 27, 26, 26, + 26, 26, 22, 26, 21, 21, 22, 23, + 19, 19, 19, 19, 22, 19, 22, 26, + 20, 21, 21, 22, 23, 19, 19, 19, + 19, 22, 19, 28, 21, 21, 22, 23, + 19, 19, 19, 19, 22, 19, 30, 31, + 31, 32, 33, 29, 29, 29, 34, 32, + 29, 35, 29, 31, 31, 32, 36, 29, + 29, 29, 29, 32, 29, 31, 31, 32, + 33, 29, 29, 29, 29, 32, 29, 32, + 29, 30, 31, 31, 32, 33, 29, 29, + 29, 29, 32, 29, 37, 31, 31, 32, + 33, 29, 29, 29, 29, 32, 29, 21, + 21, 22, 38, 0, 0, 0, 0, 22, + 0, 40, 39, 42, 43, 44, 45, 46, + 47, 22, 23, 48, 49, 49, 24, 22, + 50, 51, 52, 53, 54, 41, 56, 57, + 58, 59, 4, 5, 60, 55, 55, 8, + 4, 55, 55, 61, 55, 62, 57, 63, + 63, 4, 5, 60, 55, 55, 55, 4, + 55, 55, 61, 55, 57, 63, 63, 4, + 5, 60, 55, 55, 55, 4, 55, 55, + 61, 55, 42, 55, 55, 55, 64, 65, + 55, 1, 60, 55, 55, 55, 55, 55, + 42, 55, 66, 66, 55, 1, 60, 55, + 60, 55, 55, 67, 60, 55, 60, 55, + 60, 55, 55, 55, 60, 55, 42, 55, + 68, 55, 66, 66, 55, 1, 60, 55, + 55, 55, 55, 55, 42, 55, 42, 55, + 55, 55, 66, 66, 55, 1, 60, 55, + 55, 55, 55, 55, 42, 55, 42, 55, + 55, 55, 66, 65, 55, 1, 60, 55, + 55, 55, 55, 55, 42, 55, 69, 70, + 71, 71, 4, 5, 60, 55, 55, 55, + 4, 55, 70, 71, 71, 4, 5, 60, + 55, 55, 55, 4, 55, 71, 71, 4, + 5, 60, 55, 55, 55, 4, 55, 60, + 55, 55, 67, 60, 55, 55, 55, 4, + 55, 72, 73, 73, 4, 5, 60, 55, + 55, 55, 4, 55, 64, 74, 55, 1, + 60, 55, 64, 55, 66, 66, 55, 1, + 60, 55, 66, 74, 55, 1, 60, 55, + 56, 57, 63, 63, 4, 5, 60, 55, + 55, 55, 4, 55, 55, 61, 55, 56, + 57, 58, 63, 4, 5, 60, 55, 55, + 8, 4, 55, 55, 61, 55, 76, 77, + 78, 79, 12, 13, 80, 75, 75, 18, + 12, 75, 75, 81, 75, 82, 77, 83, + 79, 12, 13, 80, 75, 75, 75, 12, + 75, 75, 81, 75, 77, 83, 79, 12, + 13, 80, 75, 75, 75, 12, 75, 75, + 81, 75, 84, 75, 75, 75, 85, 86, + 75, 14, 80, 75, 75, 75, 75, 75, + 84, 75, 87, 77, 88, 89, 12, 13, + 80, 75, 75, 17, 12, 75, 75, 81, + 75, 90, 77, 83, 83, 12, 13, 80, + 75, 75, 75, 12, 75, 75, 81, 75, + 77, 83, 83, 12, 13, 80, 75, 75, + 75, 12, 75, 75, 81, 75, 84, 75, + 75, 75, 91, 86, 75, 14, 80, 75, + 75, 75, 75, 75, 84, 75, 80, 75, + 75, 92, 80, 75, 80, 75, 80, 75, + 75, 75, 80, 75, 84, 75, 93, 75, + 91, 91, 75, 14, 80, 75, 75, 75, + 75, 75, 84, 75, 84, 75, 75, 75, + 91, 91, 75, 14, 80, 75, 75, 75, + 75, 75, 84, 75, 94, 95, 96, 96, + 12, 13, 80, 75, 75, 75, 12, 75, + 95, 96, 96, 12, 13, 80, 75, 75, + 75, 12, 75, 96, 96, 12, 13, 80, + 75, 75, 75, 12, 75, 80, 75, 75, + 92, 80, 75, 75, 75, 12, 75, 97, + 98, 98, 12, 13, 80, 75, 75, 75, + 12, 75, 85, 99, 75, 14, 80, 75, + 91, 91, 75, 14, 80, 75, 85, 75, + 91, 91, 75, 14, 80, 75, 91, 99, + 75, 14, 80, 75, 87, 77, 83, 83, + 12, 13, 80, 75, 75, 75, 12, 75, + 75, 81, 75, 87, 77, 88, 83, 12, + 13, 80, 75, 75, 17, 12, 75, 75, + 81, 75, 10, 11, 11, 12, 13, 75, + 75, 75, 75, 12, 75, 76, 77, 83, + 79, 12, 13, 80, 75, 75, 75, 12, + 75, 75, 81, 75, 101, 45, 102, 102, + 22, 23, 48, 100, 100, 100, 22, 100, + 100, 52, 100, 45, 102, 102, 22, 23, + 48, 100, 100, 100, 22, 100, 100, 52, + 100, 103, 100, 100, 100, 104, 105, 100, + 25, 48, 100, 100, 100, 100, 100, 103, + 100, 44, 45, 106, 107, 22, 23, 48, + 100, 100, 24, 22, 100, 100, 52, 100, + 103, 100, 100, 100, 108, 105, 100, 25, + 48, 100, 100, 100, 100, 100, 103, 100, + 48, 100, 100, 109, 48, 100, 48, 100, + 48, 100, 100, 100, 48, 100, 103, 100, + 110, 100, 108, 108, 100, 25, 48, 100, + 100, 100, 100, 100, 103, 100, 103, 100, + 100, 100, 108, 108, 100, 25, 48, 100, + 100, 100, 100, 100, 103, 100, 111, 112, + 113, 113, 22, 23, 48, 100, 100, 100, + 22, 100, 112, 113, 113, 22, 23, 48, + 100, 100, 100, 22, 100, 113, 113, 22, + 23, 48, 100, 100, 100, 22, 100, 48, + 100, 100, 109, 48, 100, 100, 100, 22, + 100, 44, 45, 102, 102, 22, 23, 48, + 100, 100, 100, 22, 100, 100, 52, 100, + 114, 115, 115, 22, 23, 48, 100, 100, + 100, 22, 100, 104, 116, 100, 25, 48, + 100, 108, 108, 100, 25, 48, 100, 104, + 100, 108, 108, 100, 25, 48, 100, 108, + 116, 100, 25, 48, 100, 44, 45, 106, + 102, 22, 23, 48, 100, 100, 24, 22, + 100, 100, 52, 100, 20, 21, 21, 22, + 23, 117, 117, 117, 24, 22, 117, 20, + 21, 21, 22, 23, 117, 117, 117, 117, + 22, 117, 119, 120, 121, 122, 32, 33, + 123, 118, 118, 34, 32, 118, 118, 124, + 118, 125, 120, 122, 122, 32, 33, 123, + 118, 118, 118, 32, 118, 118, 124, 118, + 120, 122, 122, 32, 33, 123, 118, 118, + 118, 32, 118, 118, 124, 118, 126, 118, + 118, 118, 127, 128, 118, 35, 123, 118, + 118, 118, 118, 118, 126, 118, 119, 120, + 121, 49, 32, 33, 123, 118, 118, 34, + 32, 118, 118, 124, 118, 126, 118, 118, + 118, 129, 128, 118, 35, 123, 118, 118, + 118, 118, 118, 126, 118, 123, 118, 118, + 130, 123, 118, 123, 118, 123, 118, 118, + 118, 123, 118, 126, 118, 131, 118, 129, + 129, 118, 35, 123, 118, 118, 118, 118, + 118, 126, 118, 126, 118, 118, 118, 129, + 129, 118, 35, 123, 118, 118, 118, 118, + 118, 126, 118, 132, 133, 134, 134, 32, + 33, 123, 118, 118, 118, 32, 118, 133, + 134, 134, 32, 33, 123, 118, 118, 118, + 32, 118, 134, 134, 32, 33, 123, 118, + 118, 118, 32, 118, 123, 118, 118, 130, + 123, 118, 118, 118, 32, 118, 119, 120, + 122, 122, 32, 33, 123, 118, 118, 118, + 32, 118, 118, 124, 118, 135, 136, 136, + 32, 33, 123, 118, 118, 118, 32, 118, + 127, 137, 118, 35, 123, 118, 129, 129, + 118, 35, 123, 118, 127, 118, 129, 129, + 118, 35, 123, 118, 129, 137, 118, 35, + 123, 118, 42, 43, 44, 45, 106, 102, + 22, 23, 48, 49, 49, 24, 22, 100, + 42, 52, 100, 56, 138, 58, 59, 4, + 5, 60, 55, 55, 8, 4, 55, 55, + 61, 55, 42, 43, 44, 45, 139, 140, + 22, 141, 142, 55, 49, 24, 22, 55, + 42, 52, 55, 20, 143, 143, 22, 141, + 60, 55, 55, 24, 22, 55, 60, 55, + 55, 67, 60, 55, 55, 55, 22, 55, + 142, 55, 55, 144, 142, 55, 55, 55, + 22, 55, 142, 55, 142, 55, 55, 55, + 142, 55, 42, 55, 68, 20, 143, 143, + 22, 141, 60, 55, 55, 55, 22, 55, + 42, 55, 146, 145, 147, 147, 145, 40, + 148, 145, 147, 147, 145, 40, 148, 145, + 148, 145, 145, 149, 148, 145, 148, 145, + 148, 145, 145, 145, 148, 145, 42, 117, + 117, 117, 117, 117, 117, 117, 117, 49, + 117, 117, 117, 117, 42, 117, 0 +}; + +static const unsigned char _indic_syllable_machine_trans_targs[] = { + 31, 37, 42, 2, 43, 46, 4, 50, + 51, 31, 60, 9, 66, 69, 61, 11, + 74, 75, 78, 31, 83, 17, 89, 92, + 93, 84, 31, 19, 98, 31, 107, 24, + 113, 116, 117, 108, 26, 122, 127, 31, + 134, 31, 32, 53, 79, 81, 100, 101, + 85, 102, 123, 124, 94, 132, 137, 31, + 33, 35, 6, 52, 38, 47, 34, 1, + 36, 40, 0, 39, 41, 44, 45, 3, + 48, 5, 49, 31, 54, 56, 14, 77, + 62, 70, 55, 7, 57, 72, 64, 58, + 13, 76, 59, 8, 63, 65, 67, 68, + 10, 71, 12, 73, 31, 80, 20, 82, + 96, 87, 15, 99, 16, 86, 88, 90, + 91, 18, 95, 21, 97, 31, 31, 103, + 105, 22, 27, 109, 118, 104, 106, 120, + 111, 23, 110, 112, 114, 115, 25, 119, + 28, 121, 125, 126, 131, 128, 129, 29, + 130, 31, 133, 30, 135, 136 +}; + +static const char _indic_syllable_machine_trans_actions[] = { + 1, 0, 2, 0, 2, 0, 0, 2, + 2, 3, 2, 0, 2, 0, 0, 0, + 2, 2, 2, 4, 2, 0, 5, 0, + 5, 0, 6, 0, 2, 7, 2, 0, + 2, 0, 2, 0, 0, 2, 0, 8, + 0, 11, 2, 2, 5, 0, 12, 12, + 0, 2, 5, 2, 5, 2, 0, 13, + 2, 0, 0, 2, 0, 2, 2, 0, + 2, 2, 0, 0, 2, 2, 2, 0, + 0, 0, 2, 14, 2, 0, 0, 2, + 0, 2, 2, 0, 2, 2, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 2, + 0, 0, 0, 2, 15, 5, 0, 5, + 2, 2, 0, 5, 0, 0, 2, 5, + 5, 0, 0, 0, 2, 16, 17, 2, + 0, 0, 0, 0, 2, 2, 2, 2, + 2, 0, 0, 2, 2, 2, 0, 0, + 0, 2, 0, 18, 18, 0, 0, 0, + 0, 19, 2, 0, 0, 0 +}; + +static const char _indic_syllable_machine_to_state_actions[] = { + 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, 9, + 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, 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 +}; + +static const char _indic_syllable_machine_from_state_actions[] = { + 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, 10, + 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, 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 +}; + +static const short _indic_syllable_machine_eof_trans[] = { + 1, 1, 1, 1, 1, 1, 1, 10, + 10, 10, 10, 10, 10, 10, 10, 20, + 20, 27, 20, 27, 20, 20, 30, 30, + 30, 30, 30, 30, 30, 1, 40, 0, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 101, 101, 101, 101, + 101, 101, 101, 101, 118, 118, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 101, 56, 56, 56, 56, + 56, 56, 56, 56, 146, 146, 146, 146, + 146, 118 +}; + +static const int indic_syllable_machine_start = 31; +static const int indic_syllable_machine_first_final = 31; +static const int indic_syllable_machine_error = -1; + +static const int indic_syllable_machine_en_main = 31; + + +#line 58 "hb-ot-shaper-indic-machine.rl" + + + +#line 118 "hb-ot-shaper-indic-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_indic (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 464 "hb-ot-shaper-indic-machine.hh" + { + cs = indic_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 138 "hb-ot-shaper-indic-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + +#line 480 "hb-ot-shaper-indic-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const unsigned char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _indic_syllable_machine_from_state_actions[cs] ) { + case 10: +#line 1 "NONE" + {ts = p;} + break; +#line 494 "hb-ot-shaper-indic-machine.hh" + } + + _keys = _indic_syllable_machine_trans_keys + (cs<<1); + _inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs]; + + _slen = _indic_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) && + ( info[p].indic_category()) <= _keys[1] ? + ( info[p].indic_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _indic_syllable_machine_trans_targs[_trans]; + + if ( _indic_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _indic_syllable_machine_trans_actions[_trans] ) { + case 2: +#line 1 "NONE" + {te = p+1;} + break; + case 11: +#line 114 "hb-ot-shaper-indic-machine.rl" + {te = p+1;{ found_syllable (indic_non_indic_cluster); }} + break; + case 13: +#line 109 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_consonant_syllable); }} + break; + case 14: +#line 110 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_vowel_syllable); }} + break; + case 17: +#line 111 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_standalone_cluster); }} + break; + case 19: +#line 112 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_symbol_cluster); }} + break; + case 15: +#line 113 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 16: +#line 114 "hb-ot-shaper-indic-machine.rl" + {te = p;p--;{ found_syllable (indic_non_indic_cluster); }} + break; + case 1: +#line 109 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_consonant_syllable); }} + break; + case 3: +#line 110 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_vowel_syllable); }} + break; + case 7: +#line 111 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_standalone_cluster); }} + break; + case 8: +#line 112 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_symbol_cluster); }} + break; + case 4: +#line 113 "hb-ot-shaper-indic-machine.rl" + {{p = ((te))-1;}{ found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 6: +#line 1 "NONE" + { switch( act ) { + case 1: + {{p = ((te))-1;} found_syllable (indic_consonant_syllable); } + break; + case 5: + {{p = ((te))-1;} found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + break; + case 6: + {{p = ((te))-1;} found_syllable (indic_non_indic_cluster); } + break; + } + } + break; + case 18: +#line 1 "NONE" + {te = p+1;} +#line 109 "hb-ot-shaper-indic-machine.rl" + {act = 1;} + break; + case 5: +#line 1 "NONE" + {te = p+1;} +#line 113 "hb-ot-shaper-indic-machine.rl" + {act = 5;} + break; + case 12: +#line 1 "NONE" + {te = p+1;} +#line 114 "hb-ot-shaper-indic-machine.rl" + {act = 6;} + break; +#line 597 "hb-ot-shaper-indic-machine.hh" + } + +_again: + switch ( _indic_syllable_machine_to_state_actions[cs] ) { + case 9: +#line 1 "NONE" + {ts = 0;} + break; +#line 606 "hb-ot-shaper-indic-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _indic_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _indic_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 146 "hb-ot-shaper-indic-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_INDIC_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-indic-machine.rl b/gfx/harfbuzz/src/hb-ot-shaper-indic-machine.rl new file mode 100644 index 0000000000..f568a84626 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-indic-machine.rl @@ -0,0 +1,151 @@ +/* + * 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 HB_OT_SHAPER_INDIC_MACHINE_HH +#define HB_OT_SHAPER_INDIC_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define indic_category() ot_shaper_var_u8_category() /* indic_category_t */ +#define indic_position() ot_shaper_var_u8_auxiliary() /* indic_position_t */ + +using indic_category_t = unsigned; +using indic_position_t = ot_position_t; + +#define I_Cat(Cat) indic_syllable_machine_ex_##Cat + +enum indic_syllable_type_t { + indic_consonant_syllable, + indic_vowel_syllable, + indic_standalone_cluster, + indic_symbol_cluster, + indic_broken_cluster, + indic_non_indic_cluster, +}; + +%%{ + machine indic_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + + +export X = 0; +export C = 1; +export V = 2; +export N = 3; +export H = 4; +export ZWNJ = 5; +export ZWJ = 6; +export M = 7; +export SM = 8; +export A = 9; +export VD = 9; +export PLACEHOLDER = 10; +export DOTTEDCIRCLE = 11; +export RS = 12; +export MPst = 13; +export Repha = 14; +export Ra = 15; +export CM = 16; +export Symbol= 17; +export CS = 18; + + +c = (C | Ra); # is_consonant +n = ((ZWNJ?.RS)? (N.N?)?); # is_consonant_modifier +z = ZWJ|ZWNJ; # is_joiner +reph = (Ra H | Repha); # possible reph + +cn = c.ZWJ?.n?; +symbol = Symbol.N?; +matra_group = z*.(M | SM? MPst).N?.H?; +syllable_tail = (z?.SM.SM?.ZWNJ?)? (A | VD)*; +halant_group = (z?.H.(ZWJ.N?)?); +final_halant_group = halant_group | H.ZWNJ; +medial_group = CM?; +halant_or_matra_group = (final_halant_group | matra_group*); + +complex_syllable_tail = (halant_group.cn)* medial_group halant_or_matra_group syllable_tail; + +consonant_syllable = (Repha|CS)? cn complex_syllable_tail; +vowel_syllable = reph? V.n? (ZWJ | complex_syllable_tail); +standalone_cluster = ((Repha|CS)? PLACEHOLDER | reph? DOTTEDCIRCLE).n? complex_syllable_tail; +symbol_cluster = symbol syllable_tail; +broken_cluster = reph? n? complex_syllable_tail; +other = any; + +main := |* + consonant_syllable => { found_syllable (indic_consonant_syllable); }; + vowel_syllable => { found_syllable (indic_vowel_syllable); }; + standalone_cluster => { found_syllable (indic_standalone_cluster); }; + symbol_cluster => { found_syllable (indic_symbol_cluster); }; + broken_cluster => { found_syllable (indic_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (indic_non_indic_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_indic (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].indic_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_INDIC_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-indic-table.cc b/gfx/harfbuzz/src/hb-ot-shaper-indic-table.cc new file mode 100644 index 0000000000..d9899a633c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-indic-table.cc @@ -0,0 +1,561 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt + * + * on files with these headers: + * + * # IndicSyllabicCategory-15.1.0.txt + * # Date: 2023-01-05 + * # IndicPositionalCategory-15.1.0.txt + * # Date: 2023-01-05 + * # Blocks-15.1.0.txt + * # Date: 2023-07-28, 15:47:20 GMT + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-indic.hh" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-macros" + +#include "hb-ot-shaper-indic-machine.hh" +#include "hb-ot-shaper-khmer-machine.hh" +#include "hb-ot-shaper-myanmar-machine.hh" + +/* indic */ +#define OT_X I_Cat(X) +#define OT_C I_Cat(C) +#define OT_V I_Cat(V) +#define OT_N I_Cat(N) +#define OT_H I_Cat(H) +#define OT_ZWNJ I_Cat(ZWNJ) +#define OT_ZWJ I_Cat(ZWJ) +#define OT_M I_Cat(M) +#define OT_SM I_Cat(SM) +#define OT_A I_Cat(A) +#define OT_VD I_Cat(VD) +#define OT_PLACEHOLDER I_Cat(PLACEHOLDER) +#define OT_DOTTEDCIRCLE I_Cat(DOTTEDCIRCLE) +#define OT_RS I_Cat(RS) +#define OT_MPst I_Cat(MPst) +#define OT_Repha I_Cat(Repha) +#define OT_Ra I_Cat(Ra) +#define OT_CM I_Cat(CM) +#define OT_Symbol I_Cat(Symbol) +#define OT_CS I_Cat(CS) +/* khmer */ +#define OT_VAbv K_Cat(VAbv) +#define OT_VBlw K_Cat(VBlw) +#define OT_VPre K_Cat(VPre) +#define OT_VPst K_Cat(VPst) +#define OT_Robatic K_Cat(Robatic) +#define OT_Xgroup K_Cat(Xgroup) +#define OT_Ygroup K_Cat(Ygroup) +/* myanmar */ +static_assert (OT_VAbv == M_Cat(VAbv), ""); +static_assert (OT_VBlw == M_Cat(VBlw), ""); +static_assert (OT_VPre == M_Cat(VPre), ""); +static_assert (OT_VPst == M_Cat(VPst), ""); +#define OT_IV M_Cat(IV) +#define OT_As M_Cat(As) +#define OT_DB M_Cat(DB) +#define OT_GB M_Cat(GB) +#define OT_MH M_Cat(MH) +#define OT_MR M_Cat(MR) +#define OT_MW M_Cat(MW) +#define OT_MY M_Cat(MY) +#define OT_PT M_Cat(PT) +#define OT_VS M_Cat(VS) +#define OT_ML M_Cat(ML) + + +#define _OT_A OT_A /* 53 chars; A */ +#define _OT_As OT_As /* 1 chars; As */ +#define _OT_C OT_C /* 478 chars; C */ +#define _OT_CM OT_CM /* 1 chars; CM */ +#define _OT_CS OT_CS /* 2 chars; CS */ +#define _OT_DC OT_DOTTEDCIRCLE /* 1 chars; DOTTEDCIRCLE */ +#define _OT_H OT_H /* 11 chars; H */ +#define _OT_M OT_M /* 142 chars; M */ +#define _OT_MH OT_MH /* 1 chars; MH */ +#define _OT_ML OT_ML /* 1 chars; ML */ +#define _OT_MP OT_MPst /* 1 chars; MPst */ +#define _OT_MR OT_MR /* 1 chars; MR */ +#define _OT_MW OT_MW /* 2 chars; MW */ +#define _OT_MY OT_MY /* 3 chars; MY */ +#define _OT_N OT_N /* 17 chars; N */ +#define _OT_GB OT_PLACEHOLDER /* 165 chars; PLACEHOLDER */ +#define _OT_PT OT_PT /* 8 chars; PT */ +#define _OT_R OT_Ra /* 14 chars; Ra */ +#define _OT_Rf OT_Repha /* 1 chars; Repha */ +#define _OT_Rt OT_Robatic /* 3 chars; Robatic */ +#define _OT_SM OT_SM /* 56 chars; SM */ +#define _OT_S OT_Symbol /* 22 chars; Symbol */ +#define _OT_V OT_V /* 172 chars; V */ +#define _OT_VA OT_VAbv /* 18 chars; VAbv */ +#define _OT_VB OT_VBlw /* 7 chars; VBlw */ +#define _OT_VL OT_VPre /* 5 chars; VPre */ +#define _OT_VR OT_VPst /* 13 chars; VPst */ +#define _OT_VS OT_VS /* 16 chars; VS */ +#define _OT_X OT_X /* 2 chars; X */ +#define _OT_Xg OT_Xgroup /* 7 chars; Xgroup */ +#define _OT_Yg OT_Ygroup /* 4 chars; Ygroup */ +#define _OT_ZWJ OT_ZWJ /* 1 chars; ZWJ */ +#define _OT_ZWNJ OT_ZWNJ /* 1 chars; ZWNJ */ + +#define _POS_T POS_ABOVE_C /* 22 chars; ABOVE_C */ +#define _POS_A POS_AFTER_MAIN /* 3 chars; AFTER_MAIN */ +#define _POS_AP POS_AFTER_POST /* 50 chars; AFTER_POST */ +#define _POS_AS POS_AFTER_SUB /* 51 chars; AFTER_SUB */ +#define _POS_C POS_BASE_C /* 833 chars; BASE_C */ +#define _POS_BS POS_BEFORE_SUB /* 25 chars; BEFORE_SUB */ +#define _POS_B POS_BELOW_C /* 13 chars; BELOW_C */ +#define _POS_X POS_END /* 71 chars; END */ +#define _POS_R POS_POST_C /* 13 chars; POST_C */ +#define _POS_L POS_PRE_C /* 5 chars; PRE_C */ +#define _POS_LM POS_PRE_M /* 14 chars; PRE_M */ +#define _POS_SM POS_SMVD /* 130 chars; SMVD */ + +#pragma GCC diagnostic pop + +#define INDIC_COMBINE_CATEGORIES(S,M) ((S) | ((M) << 8)) + +#define _(S,M) INDIC_COMBINE_CATEGORIES (_OT_##S, _POS_##M) + + +static const uint16_t indic_table[] = { + + +#define indic_offset_0x0028u 0 + + + /* Basic Latin */ + + /* 0028 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), + /* 0030 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0038 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x00b0u 24 + + + /* Latin-1 Supplement */ + + /* 00B0 */ _(X,X), _(X,X),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00B8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00C0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00C8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 00D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), + +#define indic_offset_0x0900u 64 + + + /* Devanagari */ + + /* 0900 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), _(V,C), + /* 0908 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 0910 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0918 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0920 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0928 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0930 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0938 */ _(C,C), _(C,C), _(M,AS), _(M,AS), _(N,X), _(S,SM), _(M,AS), _(M,LM), + /* 0940 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), + /* 0948 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(H,B), _(M,LM), _(M,AS), + /* 0950 */ _(X,X), _(A,SM), _(A,SM),_(SM,SM),_(SM,SM), _(M,AS), _(M,AS), _(M,AS), + /* 0958 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0960 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0968 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0970 */ _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 0978 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + + /* Bengali */ + + /* 0980 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0988 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C), + /* 0990 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0998 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 09A0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 09A8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 09B0 */ _(R,C), _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), + /* 09B8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM), + /* 09C0 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM), + /* 09C8 */ _(M,LM), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(C,C), _(X,X), + /* 09D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), + /* 09D8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), + /* 09E0 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 09E8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 09F0 */ _(R,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 09F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X),_(SM,SM), _(X,X), + + /* Gurmukhi */ + + /* 0A00 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0A08 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), + /* 0A10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0A18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0A20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0A28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0A30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(X,X), + /* 0A38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(X,X), _(M,AP), _(M,LM), + /* 0A40 */_(MP,AP), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), + /* 0A48 */ _(M,AP), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), + /* 0A50 */ _(X,X), _(M,B), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0A58 */ _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), + /* 0A60 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0A68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0A70 */_(SM,SM),_(SM,SM), _(C,C), _(C,C), _(X,X), _(CM,C), _(X,X), _(X,X), + /* 0A78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Gujarati */ + + /* 0A80 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0A88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), + /* 0A90 */ _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0A98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0AA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0AA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0AB0 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), + /* 0AB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM), + /* 0AC0 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AS), _(X,X), _(M,AS), + /* 0AC8 */ _(M,AS), _(M,AP), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), + /* 0AD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0AD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0AE0 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0AE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0AF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0AF8 */ _(X,X), _(C,C), _(A,SM), _(N,X), _(A,SM), _(N,X), _(N,X), _(N,X), + + /* Oriya */ + + /* 0B00 */ _(X,X),_(SM,BS),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0B08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C), + /* 0B10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0B18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0B20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0B28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0B30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), + /* 0B38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,A), + /* 0B40 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM), + /* 0B48 */ _(M,A), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), + /* 0B50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(N,X), _(M,A), _(M,AP), + /* 0B58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), + /* 0B60 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0B68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0B70 */ _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0B78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Tamil */ + + /* 0B80 */ _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0B88 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(V,C), _(V,C), + /* 0B90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(X,X), _(X,X), + /* 0B98 */ _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), _(C,C), _(C,C), + /* 0BA0 */ _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), + /* 0BA8 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), + /* 0BB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0BB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), _(M,AP), + /* 0BC0 */ _(M,AS), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(M,LM), _(M,LM), + /* 0BC8 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(X,X), _(X,X), + /* 0BD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), + /* 0BD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0BE0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0BE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0BF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0BF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Telugu */ + + /* 0C00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), + /* 0C08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), + /* 0C10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0C18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0C38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS), + /* 0C40 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,BS), + /* 0C48 */ _(M,BS), _(X,X), _(M,BS), _(M,BS), _(M,BS), _(H,T), _(X,X), _(X,X), + /* 0C50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,BS), _(M,BS), _(X,X), + /* 0C58 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(C,C), _(X,X), _(X,X), + /* 0C60 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0C68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0C70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0C78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Kannada */ + + /* 0C80 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), + /* 0C88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), + /* 0C90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0C98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0CA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0CA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0CB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), + /* 0CB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS), + /* 0CC0 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,AS), + /* 0CC8 */ _(M,AS), _(X,X), _(M,AS), _(M,AS), _(M,BS), _(H,T), _(X,X), _(X,X), + /* 0CD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AS), _(M,AS), _(X,X), + /* 0CD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), + /* 0CE0 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0CE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0CF0 */ _(X,X), _(CS,C), _(CS,C),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0CF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + + /* Malayalam */ + + /* 0D00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(GB,C), _(V,C), _(V,C), _(V,C), + /* 0D08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), + /* 0D10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), + /* 0D18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D28 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 0D38 */ _(C,C), _(C,C), _(C,C), _(M,AS), _(M,AS), _(S,SM), _(M,AP), _(M,AP), + /* 0D40 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(X,X), _(M,LM), _(M,LM), + /* 0D48 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(Rf,X), _(X,X), + /* 0D50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(M,AP), + /* 0D58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), + /* 0D60 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C), + /* 0D68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 0D70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 0D78 */ _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + +#define indic_offset_0x1000u 1216 + + + /* Myanmar */ + + /* 1000 */ _(C,C), _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), + /* 1008 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1010 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1018 */ _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1020 */ _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 1028 */ _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), _(VA,T), _(VA,T), _(VB,B), + /* 1030 */ _(VB,B), _(VL,L), _(A,SM), _(VA,T), _(VA,T), _(VA,T), _(A,SM), _(N,X), + /* 1038 */_(SM,SM), _(H,X), _(As,X), _(MY,X), _(MR,X), _(MW,X), _(MH,X), _(C,C), + /* 1040 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 1048 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(C,C), _(X,X), + /* 1050 */ _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), + /* 1058 */ _(VB,B), _(VB,B), _(R,C), _(C,C), _(C,C), _(C,C), _(MY,X), _(MY,X), + /* 1060 */ _(ML,X), _(C,C), _(VR,R), _(PT,X), _(PT,X), _(C,C), _(C,C), _(VR,R), + /* 1068 */ _(VR,R), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(C,C), _(C,C), + /* 1070 */ _(C,C), _(VA,T), _(VA,T), _(VA,T), _(VA,T), _(C,C), _(C,C), _(C,C), + /* 1078 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1080 */ _(C,C), _(C,C), _(MW,X), _(VR,R), _(VL,L), _(VA,T), _(VA,T),_(SM,SM), + /* 1088 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(C,C),_(SM,SM), + /* 1090 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 1098 */ _(GB,C), _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(VA,T), _(X,X), _(X,X), + +#define indic_offset_0x1780u 1376 + + + /* Khmer */ + + /* 1780 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1788 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1790 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 1798 */ _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* 17A0 */ _(C,C), _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 17A8 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), + /* 17B0 */ _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(VR,R), _(VA,T), + /* 17B8 */ _(VA,T), _(VA,T), _(VA,T), _(VB,B), _(VB,B), _(VB,B), _(VA,T), _(VR,R), + /* 17C0 */ _(VR,R), _(VL,L), _(VL,L), _(VL,L), _(VR,R), _(VR,R), _(Xg,X), _(Yg,X), + /* 17C8 */ _(Yg,X), _(Rt,X), _(Rt,X), _(Xg,X), _(Rt,X), _(Xg,X), _(Xg,X), _(Xg,X), + /* 17D0 */ _(Xg,X), _(Xg,X), _(H,X), _(Yg,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 17D8 */ _(X,X), _(GB,C), _(X,X), _(X,X), _(S,SM), _(Yg,X), _(X,X), _(X,X), + /* 17E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* 17E8 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x1cd0u 1488 + + + /* Vedic Extensions */ + + /* 1CD0 */ _(A,SM), _(A,SM), _(A,SM), _(X,X), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* 1CD8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* 1CE0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* 1CE8 */ _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(A,SM), _(S,SM), _(S,SM), + /* 1CF0 */ _(S,SM), _(S,SM), _(C,C), _(C,C), _(A,SM), _(C,C), _(C,C), _(A,SM), + /* 1CF8 */ _(A,SM), _(A,SM), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x2008u 1536 + + + /* General Punctuation */ + + /* 2008 */ _(X,X), _(X,X), _(X,X), _(X,X),_(ZWNJ,X),_(ZWJ,X), _(X,X), _(X,X), + /* 2010 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), + /* 2018 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 2020 */ _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x2070u 1568 + + + /* Superscripts and Subscripts */ + + /* 2070 */ _(X,X), _(X,X), _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(X,X), + /* 2078 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), + /* 2080 */ _(X,X), _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x25f8u 1592 + + + /* Geometric Shapes */ + + /* 25F8 */ _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), + +#define indic_offset_0xa8e0u 1600 + + + /* Devanagari Extended */ + + /* A8E0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* A8E8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), + /* A8F0 */ _(A,SM), _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), + /* A8F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), _(M,AS), + +#define indic_offset_0xa9e0u 1632 + + + /* Myanmar Extended-B */ + + /* A9E0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(VA,T), _(X,X), _(C,C), + /* A9E8 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* A9F0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), + /* A9F8 */ _(GB,C), _(GB,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), + +#define indic_offset_0xaa60u 1664 + + + /* Myanmar Extended-A */ + + /* AA60 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* AA68 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), + /* AA70 */ _(X,X), _(C,C), _(C,C), _(C,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), + /* AA78 */ _(X,X), _(X,X), _(C,C), _(PT,X), _(N,X), _(N,X), _(C,C), _(C,C), + +#define indic_offset_0xfe00u 1696 + + + /* Variation Selectors */ + + /* FE00 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), + /* FE08 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), + +#define indic_offset_0x11300u 1712 + + + /* Grantha */ + + /* 11300 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), + +#define indic_offset_0x11338u 1720 + + /* 11338 */ _(X,X), _(X,X), _(X,X), _(N,X), _(N,X), _(X,X), _(X,X), _(X,X), + +}; /* Table items: 1728; occupancy: 71% */ + +uint16_t +hb_indic_get_categories (hb_codepoint_t u) +{ + switch (u >> 12) + { + case 0x0u: + if (unlikely (u == 0x00A0u)) return _(GB,C); + if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u]; + if (hb_in_range<hb_codepoint_t> (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u]; + if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0D7Fu)) return indic_table[u - 0x0900u + indic_offset_0x0900u]; + break; + + case 0x1u: + if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u]; + if (hb_in_range<hb_codepoint_t> (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u]; + if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u]; + break; + + case 0x2u: + if (unlikely (u == 0x25CCu)) return _(DC,C); + if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2027u)) return indic_table[u - 0x2008u + indic_offset_0x2008u]; + if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u]; + if (hb_in_range<hb_codepoint_t> (u, 0x25F8u, 0x25FFu)) return indic_table[u - 0x25F8u + indic_offset_0x25f8u]; + break; + + case 0xAu: + if (hb_in_range<hb_codepoint_t> (u, 0xA8E0u, 0xA8FFu)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u]; + if (hb_in_range<hb_codepoint_t> (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u]; + if (hb_in_range<hb_codepoint_t> (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u]; + break; + + case 0xFu: + if (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)) return indic_table[u - 0xFE00u + indic_offset_0xfe00u]; + break; + + case 0x11u: + if (hb_in_range<hb_codepoint_t> (u, 0x11300u, 0x11307u)) return indic_table[u - 0x11300u + indic_offset_0x11300u]; + if (hb_in_range<hb_codepoint_t> (u, 0x11338u, 0x1133Fu)) return indic_table[u - 0x11338u + indic_offset_0x11338u]; + break; + + default: + break; + } + return _(X,X); +} + +#undef _ +#undef INDIC_COMBINE_CATEGORIES + +#undef _OT_A +#undef _OT_As +#undef _OT_C +#undef _OT_CM +#undef _OT_CS +#undef _OT_DC +#undef _OT_H +#undef _OT_M +#undef _OT_MH +#undef _OT_ML +#undef _OT_MP +#undef _OT_MR +#undef _OT_MW +#undef _OT_MY +#undef _OT_N +#undef _OT_GB +#undef _OT_PT +#undef _OT_R +#undef _OT_Rf +#undef _OT_Rt +#undef _OT_SM +#undef _OT_S +#undef _OT_V +#undef _OT_VA +#undef _OT_VB +#undef _OT_VL +#undef _OT_VR +#undef _OT_VS +#undef _OT_X +#undef _OT_Xg +#undef _OT_Yg +#undef _OT_ZWJ +#undef _OT_ZWNJ + +#undef _POS_T +#undef _POS_A +#undef _POS_AP +#undef _POS_AS +#undef _POS_C +#undef _POS_BS +#undef _POS_B +#undef _POS_X +#undef _POS_R +#undef _POS_L +#undef _POS_LM +#undef _POS_SM + +#endif + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-indic.cc b/gfx/harfbuzz/src/hb-ot-shaper-indic.cc new file mode 100644 index 0000000000..f8c970fc3e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-indic.cc @@ -0,0 +1,1578 @@ +/* + * 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" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-indic.hh" +#include "hb-ot-shaper-indic-machine.hh" +#include "hb-ot-shaper-vowel-constraints.hh" +#include "hb-ot-layout.hh" + + +/* + * Indic shaper. + */ + + +static inline void +set_indic_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + + info.indic_category() = (indic_category_t) (type & 0xFFu); + info.indic_position() = (indic_position_t) (type >> 8); +} + + +static inline bool +is_one_of (const hb_glyph_info_t &info, unsigned int flags) +{ + /* If it ligated, all bets are off. */ + if (_hb_glyph_info_ligated (&info)) return false; + return !!(FLAG_UNSAFE (info.indic_category()) & flags); +} + +/* Note: + * + * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels + * cannot happen in a consonant syllable. The plus side however is, we can call the + * consonant syllable logic from the vowel syllable function and get it all right! + * + * Keep in sync with consonant_categories in the generator. */ +#define CONSONANT_FLAGS_INDIC (FLAG (I_Cat(C)) | FLAG (I_Cat(CS)) | FLAG (I_Cat(Ra)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(V)) | FLAG (I_Cat(PLACEHOLDER)) | FLAG (I_Cat(DOTTEDCIRCLE))) + +static inline bool +is_consonant (const hb_glyph_info_t &info) +{ + return is_one_of (info, CONSONANT_FLAGS_INDIC); +} + +#define JOINER_FLAGS (FLAG (I_Cat(ZWJ)) | FLAG (I_Cat(ZWNJ))) + +static inline bool +is_joiner (const hb_glyph_info_t &info) +{ + return is_one_of (info, JOINER_FLAGS); +} + +static inline bool +is_halant (const hb_glyph_info_t &info) +{ + return is_one_of (info, FLAG (I_Cat(H))); +} + +struct hb_indic_would_substitute_feature_t +{ + void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) + { + zero_context = zero_context_; + lookups = map->get_stage_lookups (0/*GSUB*/, + map->get_feature_stage (0/*GSUB*/, feature_tag)); + } + + bool would_substitute (const hb_codepoint_t *glyphs, + unsigned int glyphs_count, + hb_face_t *face) const + { + for (const auto &lookup : lookups) + if (hb_ot_layout_lookup_would_substitute (face, lookup.index, glyphs, glyphs_count, zero_context)) + return true; + return false; + } + + private: + hb_array_t<const hb_ot_map_t::lookup_map_t> lookups; + bool zero_context; +}; + + +/* + * Indic configurations. Note that we do not want to keep every single script-specific + * behavior in these tables necessarily. This should mainly be used for per-script + * properties that are cheaper keeping here, than in the code. Ie. if, say, one and + * only one script has an exception, that one script can be if'ed directly in the code, + * instead of adding a new flag in these structs. + */ + +enum reph_position_t { + REPH_POS_AFTER_MAIN = POS_AFTER_MAIN, + REPH_POS_BEFORE_SUB = POS_BEFORE_SUB, + REPH_POS_AFTER_SUB = POS_AFTER_SUB, + REPH_POS_BEFORE_POST = POS_BEFORE_POST, + REPH_POS_AFTER_POST = POS_AFTER_POST +}; +enum reph_mode_t { + REPH_MODE_IMPLICIT, /* Reph formed out of initial Ra,H sequence. */ + REPH_MODE_EXPLICIT, /* Reph formed out of initial Ra,H,ZWJ sequence. */ + REPH_MODE_LOG_REPHA /* Encoded Repha character, needs reordering. */ +}; +enum blwf_mode_t { + BLWF_MODE_PRE_AND_POST, /* Below-forms feature applied to pre-base and post-base. */ + BLWF_MODE_POST_ONLY /* Below-forms feature applied to post-base only. */ +}; +struct indic_config_t +{ + hb_script_t script; + bool has_old_spec; + hb_codepoint_t virama; + reph_position_t reph_pos; + reph_mode_t reph_mode; + blwf_mode_t blwf_mode; +}; + +static const indic_config_t indic_configs[] = +{ + /* Default. Should be first. */ + {HB_SCRIPT_INVALID, false, 0,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_DEVANAGARI,true, 0x094Du,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_BENGALI, true, 0x09CDu,REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_GURMUKHI, true, 0x0A4Du,REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_GUJARATI, true, 0x0ACDu,REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_ORIYA, true, 0x0B4Du,REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_TAMIL, true, 0x0BCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST}, + {HB_SCRIPT_TELUGU, true, 0x0C4Du,REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY}, + {HB_SCRIPT_KANNADA, true, 0x0CCDu,REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY}, + {HB_SCRIPT_MALAYALAM, true, 0x0D4Du,REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST}, +}; + + +static const hb_ot_map_feature_t +indic_features[] = +{ + /* + * Basic features. + * These features are applied in order, one at a time, after initial_reordering, + * constrained to the syllable. + */ + {HB_TAG('n','u','k','t'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','k','h','n'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('r','p','h','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('r','k','r','f'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('h','a','l','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('v','a','t','u'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('c','j','c','t'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + /* + * Other features. + * These features are applied all at once, after final_reordering, constrained + * to the syllable. + * Default Bengali font in Windows for example has intermixed + * lookups for init,pres,abvs,blws features. + */ + {HB_TAG('i','n','i','t'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('h','a','l','n'), F_GLOBAL_MANUAL_JOINERS | F_PER_SYLLABLE}, +}; + +/* + * Must be in the same order as the indic_features array. + */ +enum { + _INDIC_NUKT, + _INDIC_AKHN, + INDIC_RPHF, + _INDIC_RKRF, + INDIC_PREF, + INDIC_BLWF, + INDIC_ABVF, + INDIC_HALF, + INDIC_PSTF, + _INDIC_VATU, + _INDIC_CJCT, + + INDIC_INIT, + _INDIC_PRES, + _INDIC_ABVS, + _INDIC_BLWS, + _INDIC_PSTS, + _INDIC_HALN, + + INDIC_NUM_FEATURES, + INDIC_BASIC_FEATURES = INDIC_INIT, /* Don't forget to update this! */ +}; + +static bool +setup_syllables_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static bool +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static bool +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_indic (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_indic); + + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + /* The Indic specs do not require ccmp, but we apply it here since if + * there is a use of it, it's typically at the beginning. */ + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + + + unsigned int i = 0; + map->add_gsub_pause (initial_reordering_indic); + + for (; i < INDIC_BASIC_FEATURES; i++) { + map->add_feature (indic_features[i]); + map->add_gsub_pause (nullptr); + } + + map->add_gsub_pause (final_reordering_indic); + + for (; i < INDIC_NUM_FEATURES; i++) + map->add_feature (indic_features[i]); +} + +static void +override_features_indic (hb_ot_shape_planner_t *plan) +{ + plan->map.disable_feature (HB_TAG('l','i','g','a')); + plan->map.add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var +} + + +struct indic_shape_plan_t +{ + bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const + { + hb_codepoint_t glyph = virama_glyph; + if (unlikely (glyph == (hb_codepoint_t) -1)) + { + if (!config->virama || !font->get_nominal_glyph (config->virama, &glyph)) + glyph = 0; + /* Technically speaking, the spec says we should apply 'locl' to virama too. + * Maybe one day... */ + + /* Our get_nominal_glyph() function needs a font, so we can't get the virama glyph + * during shape planning... Instead, overwrite it here. */ + virama_glyph = (int) glyph; + } + + *pglyph = glyph; + return glyph != 0; + } + + const indic_config_t *config; + + bool is_old_spec; +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE + bool uniscribe_bug_compatible; +#else + static constexpr bool uniscribe_bug_compatible = false; +#endif + mutable hb_atomic_int_t virama_glyph; + + hb_indic_would_substitute_feature_t rphf; + hb_indic_would_substitute_feature_t pref; + hb_indic_would_substitute_feature_t blwf; + hb_indic_would_substitute_feature_t pstf; + hb_indic_would_substitute_feature_t vatu; + + hb_mask_t mask_array[INDIC_NUM_FEATURES]; +}; + +static void * +data_create_indic (const hb_ot_shape_plan_t *plan) +{ + indic_shape_plan_t *indic_plan = (indic_shape_plan_t *) hb_calloc (1, sizeof (indic_shape_plan_t)); + if (unlikely (!indic_plan)) + return nullptr; + + indic_plan->config = &indic_configs[0]; + for (unsigned int i = 1; i < ARRAY_LENGTH (indic_configs); i++) + if (plan->props.script == indic_configs[i].script) { + indic_plan->config = &indic_configs[i]; + break; + } + + indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); +#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE + indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible; +#endif + indic_plan->virama_glyph = -1; + + /* Use zero-context would_substitute() matching for new-spec of the main + * Indic scripts, and scripts with one spec only, but not for old-specs. + * The new-spec for all dual-spec scripts says zero-context matching happens. + * + * However, testing with Malayalam shows that old and new spec both allow + * context. Testing with Bengali new-spec however shows that it doesn't. + * So, the heuristic here is the way it is. It should *only* be changed, + * as we discover more cases of what Windows does. DON'T TOUCH OTHERWISE. + */ + bool zero_context = !indic_plan->is_old_spec && plan->props.script != HB_SCRIPT_MALAYALAM; + indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'), zero_context); + indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context); + indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context); + indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context); + indic_plan->vatu.init (&plan->map, HB_TAG('v','a','t','u'), zero_context); + + for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) + indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ? + 0 : plan->map.get_1_mask (indic_features[i].tag); + + return indic_plan; +} + +static void +data_destroy_indic (void *data) +{ + hb_free (data); +} + +static indic_position_t +consonant_position_from_face (const indic_shape_plan_t *indic_plan, + const hb_codepoint_t consonant, + const hb_codepoint_t virama, + hb_face_t *face) +{ + /* For old-spec, the order of glyphs is Consonant,Virama, + * whereas for new-spec, it's Virama,Consonant. However, + * some broken fonts (like Free Sans) simply copied lookups + * from old-spec to new-spec without modification. + * And oddly enough, Uniscribe seems to respect those lookups. + * Eg. in the sequence U+0924,U+094D,U+0930, Uniscribe finds + * base at 0. The font however, only has lookups matching + * 930,94D in 'blwf', not the expected 94D,930 (with new-spec + * table). As such, we simply match both sequences. Seems + * to work. + * + * Vatu is done as well, for: + * https://github.com/harfbuzz/harfbuzz/issues/1587 + */ + hb_codepoint_t glyphs[3] = {virama, consonant, virama}; + if (indic_plan->blwf.would_substitute (glyphs , 2, face) || + indic_plan->blwf.would_substitute (glyphs+1, 2, face) || + indic_plan->vatu.would_substitute (glyphs , 2, face) || + indic_plan->vatu.would_substitute (glyphs+1, 2, face)) + return POS_BELOW_C; + if (indic_plan->pstf.would_substitute (glyphs , 2, face) || + indic_plan->pstf.would_substitute (glyphs+1, 2, face)) + return POS_POST_C; + if (indic_plan->pref.would_substitute (glyphs , 2, face) || + indic_plan->pref.would_substitute (glyphs+1, 2, face)) + return POS_POST_C; + return POS_BASE_C; +} + +static void +setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, indic_category); + HB_BUFFER_ALLOCATE_VAR (buffer, indic_position); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_indic_properties (info[i]); +} + +static bool +setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_indic (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); + return false; +} + +static int +compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + int a = pa->indic_position(); + int b = pb->indic_position(); + + return (int) a - (int) b; +} + + + +static void +update_consonant_positions_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + + hb_codepoint_t virama; + if (indic_plan->load_virama_glyph (font, &virama)) + { + hb_face_t *face = font->face; + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + if (info[i].indic_position() == POS_BASE_C) + { + hb_codepoint_t consonant = info[i].codepoint; + info[i].indic_position() = consonant_position_from_face (indic_plan, consonant, virama, face); + } + } +} + + +/* Rules from: + * https://docs.microsqoft.com/en-us/typography/script-development/devanagari */ + +static void +initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + /* https://github.com/harfbuzz/harfbuzz/issues/435#issuecomment-335560167 + * // For compatibility with legacy usage in Kannada, + * // Ra+h+ZWJ must behave like Ra+ZWJ+h... + */ + if (buffer->props.script == HB_SCRIPT_KANNADA && + start + 3 <= end && + is_one_of (info[start ], FLAG (I_Cat(Ra))) && + is_one_of (info[start+1], FLAG (I_Cat(H))) && + is_one_of (info[start+2], FLAG (I_Cat(ZWJ)))) + { + buffer->merge_clusters (start+1, start+3); + hb_swap (info[start+1], info[start+2]); + } + + /* 1. Find base consonant: + * + * The shaping engine finds the base consonant of the syllable, using the + * following algorithm: starting from the end of the syllable, move backwards + * until a consonant is found that does not have a below-base or post-base + * form (post-base forms have to follow below-base forms), or that is not a + * pre-base-reordering Ra, or arrive at the first consonant. The consonant + * stopped at will be the base. + * + * o If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. + */ + + unsigned int base = end; + bool has_reph = false; + + { + /* -> If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. */ + unsigned int limit = start; + if (indic_plan->mask_array[INDIC_RPHF] && + start + 3 <= end && + ( + (indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) || + (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && info[start + 2].indic_category() == I_Cat(ZWJ)) + )) + { + /* See if it matches the 'rphf' feature. */ + hb_codepoint_t glyphs[3] = {info[start].codepoint, + info[start + 1].codepoint, + indic_plan->config->reph_mode == REPH_MODE_EXPLICIT ? + info[start + 2].codepoint : 0}; + if (indic_plan->rphf.would_substitute (glyphs, 2, face) || + (indic_plan->config->reph_mode == REPH_MODE_EXPLICIT && + indic_plan->rphf.would_substitute (glyphs, 3, face))) + { + limit += 2; + while (limit < end && is_joiner (info[limit])) + limit++; + base = start; + has_reph = true; + } + } else if (indic_plan->config->reph_mode == REPH_MODE_LOG_REPHA && info[start].indic_category() == I_Cat(Repha)) + { + limit += 1; + while (limit < end && is_joiner (info[limit])) + limit++; + base = start; + has_reph = true; + } + + { + /* -> starting from the end of the syllable, move backwards */ + unsigned int i = end; + bool seen_below = false; + do { + i--; + /* -> until a consonant is found */ + if (is_consonant (info[i])) + { + /* -> that does not have a below-base or post-base form + * (post-base forms have to follow below-base forms), */ + if (info[i].indic_position() != POS_BELOW_C && + (info[i].indic_position() != POS_POST_C || seen_below)) + { + base = i; + break; + } + if (info[i].indic_position() == POS_BELOW_C) + seen_below = true; + + /* -> or that is not a pre-base-reordering Ra, + * + * IMPLEMENTATION NOTES: + * + * Our pre-base-reordering Ra's are marked POS_POST_C, so will be skipped + * by the logic above already. + */ + + /* -> or arrive at the first consonant. The consonant stopped at will + * be the base. */ + base = i; + } + else + { + /* A ZWJ after a Halant stops the base search, and requests an explicit + * half form. + * A ZWJ before a Halant, requests a subjoined form instead, and hence + * search continues. This is particularly important for Bengali + * sequence Ra,H,Ya that should form Ya-Phalaa by subjoining Ya. */ + if (start < i && + info[i].indic_category() == I_Cat(ZWJ) && + info[i - 1].indic_category() == I_Cat(H)) + break; + } + } while (i > limit); + } + + /* -> If the syllable starts with Ra + Halant (in a script that has Reph) + * and has more than one consonant, Ra is excluded from candidates for + * base consonants. + * + * Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */ + if (has_reph && base == start && limit - base <= 2) { + /* Have no other consonant, so Reph is not formed and Ra becomes base. */ + has_reph = false; + } + } + + + /* 2. Decompose and reorder Matras: + * + * Each matra and any syllable modifier sign in the syllable are moved to the + * appropriate position relative to the consonant(s) in the syllable. The + * shaping engine decomposes two- or three-part matras into their constituent + * parts before any repositioning. Matra characters are classified by which + * consonant in a conjunct they have affinity for and are reordered to the + * following positions: + * + * o Before first half form in the syllable + * o After subjoined consonants + * o After post-form consonant + * o After main consonant (for above marks) + * + * IMPLEMENTATION NOTES: + * + * The normalize() routine has already decomposed matras for us, so we don't + * need to worry about that. + */ + + + /* 3. Reorder marks to canonical order: + * + * Adjacent nukta and halant or nukta and vedic sign are always repositioned + * if necessary, so that the nukta is first. + * + * IMPLEMENTATION NOTES: + * + * We don't need to do this: the normalize() routine already did this for us. + */ + + + /* Reorder characters */ + + for (unsigned int i = start; i < base; i++) + info[i].indic_position() = hb_min (POS_PRE_C, (indic_position_t) info[i].indic_position()); + + if (base < end) + info[base].indic_position() = POS_BASE_C; + + /* Handle beginning Ra */ + if (has_reph) + info[start].indic_position() = POS_RA_TO_BECOME_REPH; + + /* For old-style Indic script tags, move the first post-base Halant after + * last consonant. + * + * Reports suggest that in some scripts Uniscribe does this only if there + * is *not* a Halant after last consonant already. We know that is the + * case for Kannada, while it reorders unconditionally in other scripts, + * eg. Malayalam, Bengali, and Devanagari. We don't currently know about + * other scripts, so we block Kannada. + * + * Kannada test case: + * U+0C9A,U+0CCD,U+0C9A,U+0CCD + * With some versions of Lohit Kannada. + * https://bugs.freedesktop.org/show_bug.cgi?id=59118 + * + * Malayalam test case: + * U+0D38,U+0D4D,U+0D31,U+0D4D,U+0D31,U+0D4D + * With lohit-ttf-20121122/Lohit-Malayalam.ttf + * + * Bengali test case: + * U+0998,U+09CD,U+09AF,U+09CD + * With Windows XP vrinda.ttf + * https://github.com/harfbuzz/harfbuzz/issues/1073 + * + * Devanagari test case: + * U+091F,U+094D,U+0930,U+094D + * With chandas.ttf + * https://github.com/harfbuzz/harfbuzz/issues/1071 + */ + if (indic_plan->is_old_spec) + { + bool disallow_double_halants = buffer->props.script == HB_SCRIPT_KANNADA; + for (unsigned int i = base + 1; i < end; i++) + if (info[i].indic_category() == I_Cat(H)) + { + unsigned int j; + for (j = end - 1; j > i; j--) + if (is_consonant (info[j]) || + (disallow_double_halants && info[j].indic_category() == I_Cat(H))) + break; + if (info[j].indic_category() != I_Cat(H) && j > i) { + /* Move Halant to after last consonant. */ + hb_glyph_info_t t = info[i]; + memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0])); + info[j] = t; + } + break; + } + } + + /* Attach misc marks to previous char to move with them. */ + { + indic_position_t last_pos = POS_START; + for (unsigned int i = start; i < end; i++) + { + if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (I_Cat(N)) | FLAG (I_Cat(RS)) | FLAG (I_Cat(CM)) | FLAG (I_Cat(H))))) + { + info[i].indic_position() = last_pos; + if (unlikely (info[i].indic_category() == I_Cat(H) && + info[i].indic_position() == POS_PRE_M)) + { + /* + * Uniscribe doesn't move the Halant with Left Matra. + * TEST: U+092B,U+093F,U+094D + * We follow. + */ + for (unsigned int j = i; j > start; j--) + if (info[j - 1].indic_position() != POS_PRE_M) { + info[i].indic_position() = info[j - 1].indic_position(); + break; + } + } + } else if (info[i].indic_position() != POS_SMVD) { + if (info[i].indic_category() == I_Cat(MPst) && + i > start && info[i - 1].indic_category() == I_Cat(SM)) + info[i - 1].indic_position() = info[i].indic_position(); + last_pos = (indic_position_t) info[i].indic_position(); + } + } + } + /* For post-base consonants let them own anything before them + * since the last consonant or matra. */ + { + unsigned int last = base; + for (unsigned int i = base + 1; i < end; i++) + if (is_consonant (info[i])) + { + for (unsigned int j = last + 1; j < i; j++) + if (info[j].indic_position() < POS_SMVD) + info[j].indic_position() = info[i].indic_position(); + last = i; + } else if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) + last = i; + } + + + { + /* Use syllable() for sort accounting temporarily. */ + unsigned int syllable = info[start].syllable(); + for (unsigned int i = start; i < end; i++) + info[i].syllable() = i - start; + + /* Sit tight, rock 'n roll! */ + hb_stable_sort (info + start, end - start, compare_indic_order); + + /* Find base again; also flip left-matra sequence. */ + unsigned first_left_matra = end; + unsigned last_left_matra = end; + base = end; + for (unsigned int i = start; i < end; i++) + { + if (info[i].indic_position() == POS_BASE_C) + { + base = i; + break; + } + else if (info[i].indic_position() == POS_PRE_M) + { + if (first_left_matra == end) + first_left_matra = i; + last_left_matra = i; + } + } + /* https://github.com/harfbuzz/harfbuzz/issues/3863 */ + if (first_left_matra < last_left_matra) + { + /* No need to merge clusters, handled later. */ + buffer->reverse_range (first_left_matra, last_left_matra + 1); + /* Reverse back nuktas, etc. */ + unsigned i = first_left_matra; + for (unsigned j = i; j <= last_left_matra; j++) + if (FLAG_UNSAFE (info[j].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) + { + buffer->reverse_range (i, j + 1); + i = j + 1; + } + } + + /* Things are out-of-control for post base positions, they may shuffle + * around like crazy. In old-spec mode, we move halants around, so in + * that case merge all clusters after base. Otherwise, check the sort + * order and merge as needed. + * For pre-base stuff, we handle cluster issues in final reordering. + * + * We could use buffer->sort() for this, if there was no special + * reordering of pre-base stuff happening later... + * We don't want to merge_clusters all of that, which buffer->sort() + * would. Here's a concrete example: + * + * Assume there's a pre-base consonant and explicit Halant before base, + * followed by a prebase-reordering (left) Matra: + * + * C,H,ZWNJ,B,M + * + * At this point in reordering we would have: + * + * M,C,H,ZWNJ,B + * + * whereas in final reordering we will bring the Matra closer to Base: + * + * C,H,ZWNJ,M,B + * + * That's why we don't want to merge-clusters anything before the Base + * at this point. But if something moved from after Base to before it, + * we should merge clusters from base to them. In final-reordering, we + * only move things around before base, and merge-clusters up to base. + * These two merge-clusters from the two sides of base will interlock + * to merge things correctly. See: + * https://github.com/harfbuzz/harfbuzz/issues/2272 + */ + if (indic_plan->is_old_spec || end - start > 127) + buffer->merge_clusters (base, end); + else + { + /* Note! syllable() is a one-byte field. */ + for (unsigned int i = base; i < end; i++) + if (info[i].syllable() != 255) + { + unsigned int min = i; + unsigned int max = i; + unsigned int j = start + info[i].syllable(); + while (j != i) + { + min = hb_min (min, j); + max = hb_max (max, j); + unsigned int next = start + info[j].syllable(); + info[j].syllable() = 255; /* So we don't process j later again. */ + j = next; + } + buffer->merge_clusters (hb_max (base, min), max + 1); + } + } + + /* Put syllable back in. */ + for (unsigned int i = start; i < end; i++) + info[i].syllable() = syllable; + } + + /* Setup masks now */ + + { + hb_mask_t mask; + + /* Reph */ + for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++) + info[i].mask |= indic_plan->mask_array[INDIC_RPHF]; + + /* Pre-base */ + mask = indic_plan->mask_array[INDIC_HALF]; + if (!indic_plan->is_old_spec && + indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST) + mask |= indic_plan->mask_array[INDIC_BLWF]; + for (unsigned int i = start; i < base; i++) + info[i].mask |= mask; + /* Base */ + mask = 0; + if (base < end) + info[base].mask |= mask; + /* Post-base */ + mask = indic_plan->mask_array[INDIC_BLWF] | + indic_plan->mask_array[INDIC_ABVF] | + indic_plan->mask_array[INDIC_PSTF]; + for (unsigned int i = base + 1; i < end; i++) + info[i].mask |= mask; + } + + if (indic_plan->is_old_spec && + buffer->props.script == HB_SCRIPT_DEVANAGARI) + { + /* Old-spec eye-lash Ra needs special handling. From the + * spec: + * + * "The feature 'below-base form' is applied to consonants + * having below-base forms and following the base consonant. + * The exception is vattu, which may appear below half forms + * as well as below the base glyph. The feature 'below-base + * form' will be applied to all such occurrences of Ra as well." + * + * Test case: U+0924,U+094D,U+0930,U+094d,U+0915 + * with Sanskrit 2003 font. + * + * However, note that Ra,Halant,ZWJ is the correct way to + * request eyelash form of Ra, so we wouldbn't inhibit it + * in that sequence. + * + * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915 + */ + for (unsigned int i = start; i + 1 < base; i++) + if (info[i ].indic_category() == I_Cat(Ra) && + info[i+1].indic_category() == I_Cat(H) && + (i + 2 == base || + info[i+2].indic_category() != I_Cat(ZWJ))) + { + info[i ].mask |= indic_plan->mask_array[INDIC_BLWF]; + info[i+1].mask |= indic_plan->mask_array[INDIC_BLWF]; + } + } + + unsigned int pref_len = 2; + if (indic_plan->mask_array[INDIC_PREF] && base + pref_len < end) + { + /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */ + for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) { + hb_codepoint_t glyphs[2]; + for (unsigned int j = 0; j < pref_len; j++) + glyphs[j] = info[i + j].codepoint; + if (indic_plan->pref.would_substitute (glyphs, pref_len, face)) + { + for (unsigned int j = 0; j < pref_len; j++) + info[i++].mask |= indic_plan->mask_array[INDIC_PREF]; + break; + } + } + } + + /* Apply ZWJ/ZWNJ effects */ + for (unsigned int i = start + 1; i < end; i++) + if (is_joiner (info[i])) { + bool non_joiner = info[i].indic_category() == I_Cat(ZWNJ); + unsigned int j = i; + + do { + j--; + + /* ZWJ/ZWNJ should disable CJCT. They do that by simply + * being there, since we don't skip them for the CJCT + * feature (ie. F_MANUAL_ZWJ) */ + + /* A ZWNJ disables HALF. */ + if (non_joiner) + info[j].mask &= ~indic_plan->mask_array[INDIC_HALF]; + + } while (j > start && !is_consonant (info[j])); + } +} + +static void +initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + /* We treat placeholder/dotted-circle as if they are consonants, so we + * should just chain. Only if not in compatibility mode that is... */ + + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + if (indic_plan->uniscribe_bug_compatible) + { + /* For dotted-circle, this is what Uniscribe does: + * If dotted-circle is the last glyph, it just does nothing. + * Ie. It doesn't form Reph. */ + if (buffer->info[end - 1].indic_category() == I_Cat(DOTTEDCIRCLE)) + return; + } + + initial_reordering_consonant_syllable (plan, face, buffer, start, end); +} + +static void +initial_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + indic_syllable_type_t syllable_type = (indic_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) + { + case indic_vowel_syllable: /* We made the vowels look like consonants. So let's call the consonant logic! */ + case indic_consonant_syllable: + initial_reordering_consonant_syllable (plan, face, buffer, start, end); + break; + + case indic_broken_cluster: /* We already inserted dotted-circles, so just call the standalone_cluster. */ + case indic_standalone_cluster: + initial_reordering_standalone_cluster (plan, face, buffer, start, end); + break; + + case indic_symbol_cluster: + case indic_non_indic_cluster: + break; + } +} + +static bool +initial_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + bool ret = false; + if (!buffer->message (font, "start reordering indic initial")) + return ret; + + update_consonant_positions_indic (plan, font, buffer); + if (hb_syllabic_insert_dotted_circles (font, buffer, + indic_broken_cluster, + I_Cat(DOTTEDCIRCLE), + I_Cat(Repha), + POS_END)) + ret = true; + + foreach_syllable (buffer, start, end) + initial_reordering_syllable_indic (plan, font->face, buffer, start, end); + + (void) buffer->message (font, "end reordering indic initial"); + + return ret; +} + +static void +final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + + /* This function relies heavily on halant glyphs. Lots of ligation + * and possibly multiple substitutions happened prior to this + * phase, and that might have messed up our properties. Recover + * from a particular case of that where we're fairly sure that a + * class of I_Cat(H) is desired but has been lost. */ + /* We don't call load_virama_glyph(), since we know it's already + * loaded. */ + hb_codepoint_t virama_glyph = indic_plan->virama_glyph; + if (virama_glyph) + { + for (unsigned int i = start; i < end; i++) + if (info[i].codepoint == virama_glyph && + _hb_glyph_info_ligated (&info[i]) && + _hb_glyph_info_multiplied (&info[i])) + { + /* This will make sure that this glyph passes is_halant() test. */ + info[i].indic_category() = I_Cat(H); + _hb_glyph_info_clear_ligated_and_multiplied (&info[i]); + } + } + + + /* 4. Final reordering: + * + * After the localized forms and basic shaping forms GSUB features have been + * applied (see below), the shaping engine performs some final glyph + * reordering before applying all the remaining font features to the entire + * syllable. + */ + + bool try_pref = !!indic_plan->mask_array[INDIC_PREF]; + + /* Find base again */ + unsigned int base; + for (base = start; base < end; base++) + if (info[base].indic_position() >= POS_BASE_C) + { + if (try_pref && base + 1 < end) + { + for (unsigned int i = base + 1; i < end; i++) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) + { + if (!(_hb_glyph_info_substituted (&info[i]) && + _hb_glyph_info_ligated_and_didnt_multiply (&info[i]))) + { + /* Ok, this was a 'pref' candidate but didn't form any. + * Base is around here... */ + base = i; + while (base < end && is_halant (info[base])) + base++; + if (base < end) + info[base].indic_position() = POS_BASE_C; + + try_pref = false; + } + break; + } + if (base == end) + break; + } + /* For Malayalam, skip over unformed below- (but NOT post-) forms. */ + if (buffer->props.script == HB_SCRIPT_MALAYALAM) + { + for (unsigned int i = base + 1; i < end; i++) + { + while (i < end && is_joiner (info[i])) + i++; + if (i == end || !is_halant (info[i])) + break; + i++; /* Skip halant. */ + while (i < end && is_joiner (info[i])) + i++; + if (i < end && is_consonant (info[i]) && info[i].indic_position() == POS_BELOW_C) + { + base = i; + info[base].indic_position() = POS_BASE_C; + } + } + } + + if (start < base && info[base].indic_position() > POS_BASE_C) + base--; + break; + } + if (base == end && start < base && + is_one_of (info[base - 1], FLAG (I_Cat(ZWJ)))) + base--; + if (base < end) + while (start < base && + is_one_of (info[base], (FLAG (I_Cat(N)) | FLAG (I_Cat(H))))) + base--; + + + /* o Reorder matras: + * + * If a pre-base matra character had been reordered before applying basic + * features, the glyph can be moved closer to the main consonant based on + * whether half-forms had been formed. Actual position for the matra is + * defined as “after last standalone halant glyph, after initial matra + * position and before the main consonant”. If ZWJ or ZWNJ follow this + * halant, position is moved after it. + * + * IMPLEMENTATION NOTES: + * + * It looks like the last sentence is wrong. Testing, with Windows 7 Uniscribe + * and Devanagari shows that the behavior is best described as: + * + * "If ZWJ follows this halant, matra is NOT repositioned after this halant. + * If ZWNJ follows this halant, position is moved after it." + * + * Test case, with Adobe Devanagari or Nirmala UI: + * + * U+091F,U+094D,U+200C,U+092F,U+093F + * (Matra moves to the middle, after ZWNJ.) + * + * U+091F,U+094D,U+200D,U+092F,U+093F + * (Matra does NOT move, stays to the left.) + * + * https://github.com/harfbuzz/harfbuzz/issues/1070 + */ + + if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */ + { + /* If we lost track of base, alas, position before last thingy. */ + unsigned int new_pos = base == end ? base - 2 : base - 1; + + /* Malayalam / Tamil do not have "half" forms or explicit virama forms. + * The glyphs formed by 'half' are Chillus or ligated explicit viramas. + * We want to position matra after them. + */ + if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL) + { + search: + while (new_pos > start && + !(is_one_of (info[new_pos], (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)) | FLAG (I_Cat(H)))))) + new_pos--; + + /* If we found no Halant we are done. + * Otherwise only proceed if the Halant does + * not belong to the Matra itself! */ + if (is_halant (info[new_pos]) && + info[new_pos].indic_position() != POS_PRE_M) + { +#if 0 // See comment above + /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ + if (new_pos + 1 < end && is_joiner (info[new_pos + 1])) + new_pos++; +#endif + if (new_pos + 1 < end) + { + /* -> If ZWJ follows this halant, matra is NOT repositioned after this halant. */ + if (info[new_pos + 1].indic_category() == I_Cat(ZWJ)) + { + /* Keep searching. */ + if (new_pos > start) + { + new_pos--; + goto search; + } + } + /* -> If ZWNJ follows this halant, position is moved after it. + * + * IMPLEMENTATION NOTES: + * + * This is taken care of by the state-machine. A Halant,ZWNJ is a terminating + * sequence for a consonant syllable; any pre-base matras occurring after it + * will belong to the subsequent syllable. + */ + } + } + else + new_pos = start; /* No move. */ + } + + if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M) + { + /* Now go see if there's actually any matras... */ + for (unsigned int i = new_pos; i > start; i--) + if (info[i - 1].indic_position () == POS_PRE_M) + { + unsigned int old_pos = i - 1; + if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */ + base--; + + hb_glyph_info_t tmp = info[old_pos]; + memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0])); + info[new_pos] = tmp; + + /* Note: this merge_clusters() is intentionally *after* the reordering. + * Indic matra reordering is special and tricky... */ + buffer->merge_clusters (new_pos, hb_min (end, base + 1)); + + new_pos--; + } + } else { + for (unsigned int i = start; i < base; i++) + if (info[i].indic_position () == POS_PRE_M) { + buffer->merge_clusters (i, hb_min (end, base + 1)); + break; + } + } + } + + + /* o Reorder reph: + * + * Reph’s original position is always at the beginning of the syllable, + * (i.e. it is not reordered at the character reordering stage). However, + * it will be reordered according to the basic-forms shaping results. + * Possible positions for reph, depending on the script, are; after main, + * before post-base consonant forms, and after post-base consonant forms. + */ + + /* Two cases: + * + * - If repha is encoded as a sequence of characters (Ra,H or Ra,H,ZWJ), then + * we should only move it if the sequence ligated to the repha form. + * + * - If repha is encoded separately and in the logical position, we should only + * move it if it did NOT ligate. If it ligated, it's probably the font trying + * to make it work without the reordering. + */ + if (start + 1 < end && + info[start].indic_position() == POS_RA_TO_BECOME_REPH && + ((info[start].indic_category() == I_Cat(Repha)) ^ + _hb_glyph_info_ligated_and_didnt_multiply (&info[start]))) + { + unsigned int new_reph_pos; + reph_position_t reph_pos = indic_plan->config->reph_pos; + + /* 1. If reph should be positioned after post-base consonant forms, + * proceed to step 5. + */ + if (reph_pos == REPH_POS_AFTER_POST) + { + goto reph_step_5; + } + + /* 2. If the reph repositioning class is not after post-base: target + * position is after the first explicit halant glyph between the + * first post-reph consonant and last main consonant. If ZWJ or ZWNJ + * are following this halant, position is moved after it. If such + * position is found, this is the target position. Otherwise, + * proceed to the next step. + * + * Note: in old-implementation fonts, where classifications were + * fixed in shaping engine, there was no case where reph position + * will be found on this step. + */ + { + new_reph_pos = start + 1; + while (new_reph_pos < base && !is_halant (info[new_reph_pos])) + new_reph_pos++; + + if (new_reph_pos < base && is_halant (info[new_reph_pos])) + { + /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */ + if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) + new_reph_pos++; + goto reph_move; + } + } + + /* 3. If reph should be repositioned after the main consonant: find the + * first consonant not ligated with main, or find the first + * consonant that is not a potential pre-base-reordering Ra. + */ + if (reph_pos == REPH_POS_AFTER_MAIN) + { + new_reph_pos = base; + while (new_reph_pos + 1 < end && info[new_reph_pos + 1].indic_position() <= POS_AFTER_MAIN) + new_reph_pos++; + if (new_reph_pos < end) + goto reph_move; + } + + /* 4. If reph should be positioned before post-base consonant, find + * first post-base classified consonant not ligated with main. If no + * consonant is found, the target position should be before the + * first matra, syllable modifier sign or vedic sign. + */ + /* This is our take on what step 4 is trying to say (and failing, BADLY). */ + if (reph_pos == REPH_POS_AFTER_SUB) + { + new_reph_pos = base; + while (new_reph_pos + 1 < end && + !( FLAG_UNSAFE (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_AFTER_POST) | FLAG (POS_SMVD)))) + new_reph_pos++; + if (new_reph_pos < end) + goto reph_move; + } + + /* 5. If no consonant is found in steps 3 or 4, move reph to a position + * immediately before the first post-base matra, syllable modifier + * sign or vedic sign that has a reordering class after the intended + * reph position. For example, if the reordering position for reph + * is post-main, it will skip above-base matras that also have a + * post-main position. + */ + reph_step_5: + { + /* Copied from step 2. */ + new_reph_pos = start + 1; + while (new_reph_pos < base && !is_halant (info[new_reph_pos])) + new_reph_pos++; + + if (new_reph_pos < base && is_halant (info[new_reph_pos])) + { + /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */ + if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1])) + new_reph_pos++; + goto reph_move; + } + } + /* See https://github.com/harfbuzz/harfbuzz/issues/2298#issuecomment-615318654 */ + + /* 6. Otherwise, reorder reph to the end of the syllable. + */ + { + new_reph_pos = end - 1; + while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD) + new_reph_pos--; + + /* + * If the Reph is to be ending up after a Matra,Halant sequence, + * position it before that Halant so it can interact with the Matra. + * However, if it's a plain Consonant,Halant we shouldn't do that. + * Uniscribe doesn't do this. + * TEST: U+0930,U+094D,U+0915,U+094B,U+094D + */ + if (!indic_plan->uniscribe_bug_compatible && + unlikely (is_halant (info[new_reph_pos]))) + { + for (unsigned int i = base + 1; i < new_reph_pos; i++) + if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) + { + /* Ok, got it. */ + new_reph_pos--; + } + } + + goto reph_move; + } + + reph_move: + { + /* Move */ + buffer->merge_clusters (start, new_reph_pos + 1); + hb_glyph_info_t reph = info[start]; + memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0])); + info[new_reph_pos] = reph; + + if (start < base && base <= new_reph_pos) + base--; + } + } + + + /* o Reorder pre-base-reordering consonants: + * + * If a pre-base-reordering consonant is found, reorder it according to + * the following rules: + */ + + if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */ + { + for (unsigned int i = base + 1; i < end; i++) + if ((info[i].mask & indic_plan->mask_array[INDIC_PREF]) != 0) + { + /* 1. Only reorder a glyph produced by substitution during application + * of the <pref> feature. (Note that a font may shape a Ra consonant with + * the feature generally but block it in certain contexts.) + */ + /* Note: We just check that something got substituted. We don't check that + * the <pref> feature actually did it... + * + * Reorder pref only if it ligated. */ + if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i])) + { + /* + * 2. Try to find a target position the same way as for pre-base matra. + * If it is found, reorder pre-base consonant glyph. + * + * 3. If position is not found, reorder immediately before main + * consonant. + */ + + unsigned int new_pos = base; + /* Malayalam / Tamil do not have "half" forms or explicit virama forms. + * The glyphs formed by 'half' are Chillus or ligated explicit viramas. + * We want to position matra after them. + */ + if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL) + { + while (new_pos > start && + !(is_one_of (info[new_pos - 1], FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)) | FLAG (I_Cat(H))))) + new_pos--; + } + + if (new_pos > start && is_halant (info[new_pos - 1])) + { + /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */ + if (new_pos < end && is_joiner (info[new_pos])) + new_pos++; + } + + { + unsigned int old_pos = i; + + buffer->merge_clusters (new_pos, old_pos + 1); + hb_glyph_info_t tmp = info[old_pos]; + memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0])); + info[new_pos] = tmp; + + if (new_pos <= base && base < old_pos) + base++; + } + } + + break; + } + } + + + /* Apply 'init' to the Left Matra if it's a word start. */ + if (info[start].indic_position () == POS_PRE_M) + { + if (!start || + !(FLAG_UNSAFE (_hb_glyph_info_get_general_category (&info[start - 1])) & + FLAG_RANGE (HB_UNICODE_GENERAL_CATEGORY_FORMAT, HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) + info[start].mask |= indic_plan->mask_array[INDIC_INIT]; + else + buffer->unsafe_to_break (start - 1, start + 1); + } + + + /* + * Finish off the clusters and go home! + */ + if (indic_plan->uniscribe_bug_compatible) + { + switch ((hb_tag_t) plan->props.script) + { + case HB_SCRIPT_TAMIL: + break; + + default: + /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil. + * This means, half forms are submerged into the main consonant's cluster. + * This is unnecessary, and makes cursor positioning harder, but that's what + * Uniscribe does. */ + buffer->merge_clusters (start, end); + break; + } + } +} + + +static bool +final_reordering_indic (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + unsigned int count = buffer->len; + if (unlikely (!count)) return false; + + if (buffer->message (font, "start reordering indic final")) { + foreach_syllable (buffer, start, end) + final_reordering_syllable_indic (plan, buffer, start, end); + (void) buffer->message (font, "end reordering indic final"); + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); + HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); + + return false; +} + + +static void +preprocess_text_indic (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; + if (!indic_plan->uniscribe_bug_compatible) + _hb_preprocess_text_vowel_constraints (plan, buffer, font); +} + +static bool +decompose_indic (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + switch (ab) + { + /* Don't decompose these. */ + case 0x0931u : return false; /* DEVANAGARI LETTER RRA */ + // https://github.com/harfbuzz/harfbuzz/issues/779 + case 0x09DCu : return false; /* BENGALI LETTER RRA */ + case 0x09DDu : return false; /* BENGALI LETTER RHA */ + case 0x0B94u : return false; /* TAMIL LETTER AU */ + + + /* + * Decompose split matras that don't have Unicode decompositions. + */ + +#if 0 + /* Gujarati */ + /* This one has no decomposition in Unicode, but needs no decomposition either. */ + /* case 0x0AC9u : return false; */ + + /* Oriya */ + case 0x0B57u : *a = no decomp, -> RIGHT; return true; +#endif + } + + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_indic (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + /* Composition-exclusion exceptions that we want to recompose. */ + if (a == 0x09AFu && b == 0x09BCu) { *ab = 0x09DFu; return true; } + + return (bool) c->unicode->compose (a, b, ab); +} + + +const hb_ot_shaper_t _hb_ot_shaper_indic = +{ + collect_features_indic, + override_features_indic, + data_create_indic, + data_destroy_indic, + preprocess_text_indic, + nullptr, /* postprocess_glyphs */ + decompose_indic, + compose_indic, + setup_masks_indic, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-indic.hh b/gfx/harfbuzz/src/hb-ot-shaper-indic.hh new file mode 100644 index 0000000000..4f822c26e9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-indic.hh @@ -0,0 +1,66 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_INDIC_HH +#define HB_OT_SHAPER_INDIC_HH + +#include "hb.hh" + +#include "hb-ot-shaper-syllabic.hh" + + +/* Visual positions in a syllable from left to right. */ +enum ot_position_t { + POS_START = 0, + + POS_RA_TO_BECOME_REPH = 1, + POS_PRE_M = 2, + POS_PRE_C = 3, + + POS_BASE_C = 4, + POS_AFTER_MAIN = 5, + + POS_ABOVE_C = 6, + + POS_BEFORE_SUB = 7, + POS_BELOW_C = 8, + POS_AFTER_SUB = 9, + + POS_BEFORE_POST = 10, + POS_POST_C = 11, + POS_AFTER_POST = 12, + + POS_SMVD = 13, + + POS_END = 14 +}; + + +HB_INTERNAL uint16_t +hb_indic_get_categories (hb_codepoint_t u); + + +#endif /* HB_OT_SHAPER_INDIC_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-khmer-machine.hh b/gfx/harfbuzz/src/hb-ot-shaper-khmer-machine.hh new file mode 100644 index 0000000000..f1e7a91f05 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-khmer-machine.hh @@ -0,0 +1,428 @@ + +#line 1 "hb-ot-shaper-khmer-machine.rl" +/* + * 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 HB_OT_SHAPER_KHMER_MACHINE_HH +#define HB_OT_SHAPER_KHMER_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define khmer_category() ot_shaper_var_u8_category() /* khmer_category_t */ + +using khmer_category_t = unsigned; + +#define K_Cat(Cat) khmer_syllable_machine_ex_##Cat + +enum khmer_syllable_type_t { + khmer_consonant_syllable, + khmer_broken_cluster, + khmer_non_khmer_cluster, +}; + + +#line 52 "hb-ot-shaper-khmer-machine.hh" +#define khmer_syllable_machine_ex_C 1u +#define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u +#define khmer_syllable_machine_ex_H 4u +#define khmer_syllable_machine_ex_PLACEHOLDER 10u +#define khmer_syllable_machine_ex_Ra 15u +#define khmer_syllable_machine_ex_Robatic 25u +#define khmer_syllable_machine_ex_V 2u +#define khmer_syllable_machine_ex_VAbv 20u +#define khmer_syllable_machine_ex_VBlw 21u +#define khmer_syllable_machine_ex_VPre 22u +#define khmer_syllable_machine_ex_VPst 23u +#define khmer_syllable_machine_ex_Xgroup 26u +#define khmer_syllable_machine_ex_Ygroup 27u +#define khmer_syllable_machine_ex_ZWJ 6u +#define khmer_syllable_machine_ex_ZWNJ 5u + + +#line 70 "hb-ot-shaper-khmer-machine.hh" +static const unsigned char _khmer_syllable_machine_trans_keys[] = { + 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, + 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, + 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 27u, 4u, 27u, 1u, 15u, + 4u, 27u, 4u, 27u, 27u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, + 4u, 27u, 1u, 15u, 4u, 27u, 4u, 27u, 27u, 27u, 4u, 27u, 4u, 27u, 4u, 27u, + 4u, 27u, 4u, 27u, 5u, 26u, 0 +}; + +static const char _khmer_syllable_machine_key_spans[] = { + 22, 22, 15, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 15, 22, 22, + 22, 22, 22, 22, 22, 27, 24, 15, + 24, 24, 1, 24, 24, 24, 24, 24, + 24, 15, 24, 24, 1, 24, 24, 24, + 24, 24, 22 +}; + +static const short _khmer_syllable_machine_index_offsets[] = { + 0, 23, 46, 62, 85, 108, 131, 154, + 177, 200, 223, 246, 269, 292, 308, 331, + 354, 377, 400, 423, 446, 469, 497, 522, + 538, 563, 588, 590, 615, 640, 665, 690, + 715, 740, 756, 781, 806, 808, 833, 858, + 883, 908, 933 +}; + +static const char _khmer_syllable_machine_indicies[] = { + 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 3, 4, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 4, 0, 6, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 0, 9, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, + 10, 0, 9, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, + 0, 11, 11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 12, 0, + 11, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 13, 4, 0, 15, 15, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 16, 14, 14, + 14, 14, 17, 18, 14, 15, 15, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 18, 19, 20, 20, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 20, 14, 15, 15, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 14, 14, 14, 14, + 14, 18, 14, 21, 21, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 16, 14, 22, 22, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 23, + 14, 24, 24, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 16, 14, 14, 14, 14, 14, 25, 14, + 24, 24, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 25, 14, 26, + 26, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 16, 14, + 14, 14, 14, 14, 27, 14, 26, 26, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 27, 14, 29, 29, 28, + 30, 31, 31, 28, 28, 28, 13, 13, + 28, 28, 28, 29, 28, 28, 28, 28, + 16, 25, 27, 23, 28, 17, 18, 20, + 28, 33, 34, 34, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 2, 10, 12, 8, 32, 13, 4, + 5, 32, 35, 35, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 35, 32, 33, 36, 36, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 2, 10, 12, 8, 32, 3, + 4, 5, 32, 37, 38, 38, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 2, 10, 12, 8, 32, + 32, 4, 5, 32, 5, 32, 37, 6, + 6, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 8, 32, 32, 2, 5, 32, 37, + 7, 7, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 8, 5, 32, + 37, 39, 39, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 2, 32, 32, 8, 32, 32, 10, 5, + 32, 37, 40, 40, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 2, 10, 32, 8, 32, 32, 12, + 5, 32, 33, 38, 38, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 2, 10, 12, 8, 32, 32, + 4, 5, 32, 33, 38, 38, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 2, 10, 12, 8, 32, + 3, 4, 5, 32, 42, 42, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 42, 41, 30, 43, 43, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 16, 25, 27, 23, + 41, 17, 18, 20, 41, 44, 45, 45, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 16, 25, 27, + 23, 41, 41, 18, 20, 41, 20, 41, + 44, 21, 21, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 23, 41, 41, 16, 20, + 41, 44, 22, 22, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 23, + 20, 41, 44, 46, 46, 41, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 16, 41, 41, 23, 41, 41, + 25, 20, 41, 44, 47, 47, 41, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 16, 25, 41, 23, 41, + 41, 27, 20, 41, 30, 45, 45, 41, + 41, 41, 41, 41, 41, 41, 41, 41, + 41, 41, 41, 41, 16, 25, 27, 23, + 41, 41, 18, 20, 41, 15, 15, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 16, 48, 48, 48, + 48, 48, 18, 48, 0 +}; + +static const char _khmer_syllable_machine_trans_targs[] = { + 21, 1, 27, 31, 25, 26, 4, 5, + 28, 7, 29, 9, 30, 32, 21, 12, + 37, 41, 35, 21, 36, 15, 16, 38, + 18, 39, 20, 40, 21, 22, 33, 42, + 21, 23, 10, 24, 0, 2, 3, 6, + 8, 21, 34, 11, 13, 14, 17, 19, + 21 +}; + +static const char _khmer_syllable_machine_trans_actions[] = { + 1, 0, 2, 2, 2, 0, 0, 0, + 2, 0, 2, 0, 2, 2, 3, 0, + 2, 4, 4, 5, 0, 0, 0, 2, + 0, 2, 0, 2, 8, 2, 0, 9, + 10, 0, 0, 2, 0, 0, 0, 0, + 0, 11, 4, 0, 0, 0, 0, 0, + 12 +}; + +static const char _khmer_syllable_machine_to_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const char _khmer_syllable_machine_from_state_actions[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 +}; + +static const short _khmer_syllable_machine_eof_trans[] = { + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 20, 15, 15, 15, + 15, 15, 15, 15, 15, 0, 33, 33, + 33, 33, 33, 33, 33, 33, 33, 33, + 33, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 49 +}; + +static const int khmer_syllable_machine_start = 21; +static const int khmer_syllable_machine_first_final = 21; +static const int khmer_syllable_machine_error = -1; + +static const int khmer_syllable_machine_en_main = 21; + + +#line 53 "hb-ot-shaper-khmer-machine.rl" + + + +#line 102 "hb-ot-shaper-khmer-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_khmer (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 298 "hb-ot-shaper-khmer-machine.hh" + { + cs = khmer_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 122 "hb-ot-shaper-khmer-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + +#line 314 "hb-ot-shaper-khmer-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _khmer_syllable_machine_from_state_actions[cs] ) { + case 7: +#line 1 "NONE" + {ts = p;} + break; +#line 328 "hb-ot-shaper-khmer-machine.hh" + } + + _keys = _khmer_syllable_machine_trans_keys + (cs<<1); + _inds = _khmer_syllable_machine_indicies + _khmer_syllable_machine_index_offsets[cs]; + + _slen = _khmer_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].khmer_category()) && + ( info[p].khmer_category()) <= _keys[1] ? + ( info[p].khmer_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _khmer_syllable_machine_trans_targs[_trans]; + + if ( _khmer_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _khmer_syllable_machine_trans_actions[_trans] ) { + case 2: +#line 1 "NONE" + {te = p+1;} + break; + case 8: +#line 98 "hb-ot-shaper-khmer-machine.rl" + {te = p+1;{ found_syllable (khmer_non_khmer_cluster); }} + break; + case 10: +#line 96 "hb-ot-shaper-khmer-machine.rl" + {te = p;p--;{ found_syllable (khmer_consonant_syllable); }} + break; + case 11: +#line 97 "hb-ot-shaper-khmer-machine.rl" + {te = p;p--;{ found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 12: +#line 98 "hb-ot-shaper-khmer-machine.rl" + {te = p;p--;{ found_syllable (khmer_non_khmer_cluster); }} + break; + case 1: +#line 96 "hb-ot-shaper-khmer-machine.rl" + {{p = ((te))-1;}{ found_syllable (khmer_consonant_syllable); }} + break; + case 3: +#line 97 "hb-ot-shaper-khmer-machine.rl" + {{p = ((te))-1;}{ found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 5: +#line 1 "NONE" + { switch( act ) { + case 2: + {{p = ((te))-1;} found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; } + break; + case 3: + {{p = ((te))-1;} found_syllable (khmer_non_khmer_cluster); } + break; + } + } + break; + case 4: +#line 1 "NONE" + {te = p+1;} +#line 97 "hb-ot-shaper-khmer-machine.rl" + {act = 2;} + break; + case 9: +#line 1 "NONE" + {te = p+1;} +#line 98 "hb-ot-shaper-khmer-machine.rl" + {act = 3;} + break; +#line 398 "hb-ot-shaper-khmer-machine.hh" + } + +_again: + switch ( _khmer_syllable_machine_to_state_actions[cs] ) { + case 6: +#line 1 "NONE" + {ts = 0;} + break; +#line 407 "hb-ot-shaper-khmer-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _khmer_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _khmer_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 130 "hb-ot-shaper-khmer-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_KHMER_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-khmer-machine.rl b/gfx/harfbuzz/src/hb-ot-shaper-khmer-machine.rl new file mode 100644 index 0000000000..c226e7797e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-khmer-machine.rl @@ -0,0 +1,135 @@ +/* + * 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 HB_OT_SHAPER_KHMER_MACHINE_HH +#define HB_OT_SHAPER_KHMER_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define khmer_category() ot_shaper_var_u8_category() /* khmer_category_t */ + +using khmer_category_t = unsigned; + +#define K_Cat(Cat) khmer_syllable_machine_ex_##Cat + +enum khmer_syllable_type_t { + khmer_consonant_syllable, + khmer_broken_cluster, + khmer_non_khmer_cluster, +}; + +%%{ + machine khmer_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + + +# We use category H for spec category Coeng + +export C = 1; +export V = 2; +export H = 4; +export ZWNJ = 5; +export ZWJ = 6; +export PLACEHOLDER = 10; +export DOTTEDCIRCLE = 11; +export Ra = 15; + +export VAbv = 20; +export VBlw = 21; +export VPre = 22; +export VPst = 23; + +export Robatic = 25; +export Xgroup = 26; +export Ygroup = 27; + + +c = (C | Ra | V); +cn = c.((ZWJ|ZWNJ)?.Robatic)?; +joiner = (ZWJ | ZWNJ); +xgroup = (joiner*.Xgroup)*; +ygroup = Ygroup*; + +# This grammar was experimentally extracted from what Uniscribe allows. + +matra_group = VPre? xgroup VBlw? xgroup (joiner?.VAbv)? xgroup VPst?; +syllable_tail = xgroup matra_group xgroup (H.c)? ygroup; + + +broken_cluster = Robatic? (H.cn)* (H | syllable_tail); +consonant_syllable = (cn|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster; +other = any; + +main := |* + consonant_syllable => { found_syllable (khmer_consonant_syllable); }; + broken_cluster => { found_syllable (khmer_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (khmer_non_khmer_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_khmer (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].khmer_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_KHMER_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-khmer.cc b/gfx/harfbuzz/src/hb-ot-shaper-khmer.cc new file mode 100644 index 0000000000..019a285102 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-khmer.cc @@ -0,0 +1,387 @@ +/* + * 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" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-khmer-machine.hh" +#include "hb-ot-shaper-indic.hh" +#include "hb-ot-layout.hh" + + +/* + * Khmer shaper. + */ + + +static const hb_ot_map_feature_t +khmer_features[] = +{ + /* + * Basic features. + * These features are applied all at once, before reordering, constrained + * to the syllable. + */ + {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + {HB_TAG('c','f','a','r'), F_MANUAL_JOINERS | F_PER_SYLLABLE}, + /* + * Other features. + * These features are applied all at once after clearing syllables. + */ + {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS}, + {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS}, +}; + +/* + * Must be in the same order as the khmer_features array. + */ +enum { + KHMER_PREF, + KHMER_BLWF, + KHMER_ABVF, + KHMER_PSTF, + KHMER_CFAR, + + _KHMER_PRES, + _KHMER_ABVS, + _KHMER_BLWS, + _KHMER_PSTS, + + KHMER_NUM_FEATURES, + KHMER_BASIC_FEATURES = _KHMER_PRES, /* Don't forget to update this! */ +}; + +static inline void +set_khmer_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + + info.khmer_category() = (khmer_category_t) (type & 0xFFu); +} + +static bool +setup_syllables_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static bool +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_khmer (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_khmer); + map->add_gsub_pause (reorder_khmer); + + /* Testing suggests that Uniscribe does NOT pause between basic + * features. Test with KhmerUI.ttf and the following three + * sequences: + * + * U+1789,U+17BC + * U+1789,U+17D2,U+1789 + * U+1789,U+17D2,U+1789,U+17BC + * + * https://github.com/harfbuzz/harfbuzz/issues/974 + */ + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + + unsigned int i = 0; + for (; i < KHMER_BASIC_FEATURES; i++) + map->add_feature (khmer_features[i]); + + /* https://github.com/harfbuzz/harfbuzz/issues/3531 */ + map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var + + for (; i < KHMER_NUM_FEATURES; i++) + map->add_feature (khmer_features[i]); +} + +static void +override_features_khmer (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Khmer spec has 'clig' as part of required shaping features: + * "Apply feature 'clig' to form ligatures that are desired for + * typographical correctness.", hence in overrides... */ + map->enable_feature (HB_TAG('c','l','i','g')); + + /* Uniscribe does not apply 'kern' in Khmer. */ + if (hb_options ().uniscribe_bug_compatible) + { + map->disable_feature (HB_TAG('k','e','r','n')); + } + + map->disable_feature (HB_TAG('l','i','g','a')); +} + + +struct khmer_shape_plan_t +{ + hb_mask_t mask_array[KHMER_NUM_FEATURES]; +}; + +static void * +data_create_khmer (const hb_ot_shape_plan_t *plan) +{ + khmer_shape_plan_t *khmer_plan = (khmer_shape_plan_t *) hb_calloc (1, sizeof (khmer_shape_plan_t)); + if (unlikely (!khmer_plan)) + return nullptr; + + for (unsigned int i = 0; i < ARRAY_LENGTH (khmer_plan->mask_array); i++) + khmer_plan->mask_array[i] = (khmer_features[i].flags & F_GLOBAL) ? + 0 : plan->map.get_1_mask (khmer_features[i].tag); + + return khmer_plan; +} + +static void +data_destroy_khmer (void *data) +{ + hb_free (data); +} + +static void +setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, khmer_category); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_khmer_properties (info[i]); +} + +static bool +setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_khmer (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); + return false; +} + + +/* Rules from: + * https://docs.microsoft.com/en-us/typography/script-development/devanagari */ + +static void +reorder_consonant_syllable (const hb_ot_shape_plan_t *plan, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data; + hb_glyph_info_t *info = buffer->info; + + /* Setup masks. */ + { + /* Post-base */ + hb_mask_t mask = khmer_plan->mask_array[KHMER_BLWF] | + khmer_plan->mask_array[KHMER_ABVF] | + khmer_plan->mask_array[KHMER_PSTF]; + for (unsigned int i = start + 1; i < end; i++) + info[i].mask |= mask; + } + + unsigned int num_coengs = 0; + for (unsigned int i = start + 1; i < end; i++) + { + /* """ + * When a COENG + (Cons | IndV) combination are found (and subscript count + * is less than two) the character combination is handled according to the + * subscript type of the character following the COENG. + * + * ... + * + * Subscript Type 2 - The COENG + RO characters are reordered to immediately + * before the base glyph. Then the COENG + RO characters are assigned to have + * the 'pref' OpenType feature applied to them. + * """ + */ + if (info[i].khmer_category() == K_Cat(H) && num_coengs <= 2 && i + 1 < end) + { + num_coengs++; + + if (info[i + 1].khmer_category() == K_Cat(Ra)) + { + for (unsigned int j = 0; j < 2; j++) + info[i + j].mask |= khmer_plan->mask_array[KHMER_PREF]; + + /* Move the Coeng,Ro sequence to the start. */ + buffer->merge_clusters (start, i + 2); + hb_glyph_info_t t0 = info[i]; + hb_glyph_info_t t1 = info[i + 1]; + memmove (&info[start + 2], &info[start], (i - start) * sizeof (info[0])); + info[start] = t0; + info[start + 1] = t1; + + /* Mark the subsequent stuff with 'cfar'. Used in Khmer. + * Read the feature spec. + * This allows distinguishing the following cases with MS Khmer fonts: + * U+1784,U+17D2,U+179A,U+17D2,U+1782 + * U+1784,U+17D2,U+1782,U+17D2,U+179A + */ + if (khmer_plan->mask_array[KHMER_CFAR]) + for (unsigned int j = i + 2; j < end; j++) + info[j].mask |= khmer_plan->mask_array[KHMER_CFAR]; + + num_coengs = 2; /* Done. */ + } + } + + /* Reorder left matra piece. */ + else if (info[i].khmer_category() == K_Cat(VPre)) + { + /* Move to the start. */ + buffer->merge_clusters (start, i + 1); + hb_glyph_info_t t = info[i]; + memmove (&info[start + 1], &info[start], (i - start) * sizeof (info[0])); + info[start] = t; + } + } +} + +static void +reorder_syllable_khmer (const hb_ot_shape_plan_t *plan, + hb_face_t *face, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) + { + case khmer_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case khmer_consonant_syllable: + reorder_consonant_syllable (plan, face, buffer, start, end); + break; + + case khmer_non_khmer_cluster: + break; + } +} + +static bool +reorder_khmer (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + bool ret = false; + if (buffer->message (font, "start reordering khmer")) + { + if (hb_syllabic_insert_dotted_circles (font, buffer, + khmer_broken_cluster, + K_Cat(DOTTEDCIRCLE), + (unsigned) -1)) + ret = true; + + foreach_syllable (buffer, start, end) + reorder_syllable_khmer (plan, font->face, buffer, start, end); + (void) buffer->message (font, "end reordering khmer"); + } + HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category); + + return ret; +} + + +static bool +decompose_khmer (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + switch (ab) + { + /* + * Decompose split matras that don't have Unicode decompositions. + */ + + /* Khmer */ + case 0x17BEu : *a = 0x17C1u; *b= 0x17BEu; return true; + case 0x17BFu : *a = 0x17C1u; *b= 0x17BFu; return true; + case 0x17C0u : *a = 0x17C1u; *b= 0x17C0u; return true; + case 0x17C4u : *a = 0x17C1u; *b= 0x17C4u; return true; + case 0x17C5u : *a = 0x17C1u; *b= 0x17C5u; return true; + } + + return (bool) c->unicode->decompose (ab, a, b); +} + +static bool +compose_khmer (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + return (bool) c->unicode->compose (a, b, ab); +} + + +const hb_ot_shaper_t _hb_ot_shaper_khmer = +{ + collect_features_khmer, + override_features_khmer, + data_create_khmer, + data_destroy_khmer, + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + decompose_khmer, + compose_khmer, + setup_masks_khmer, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh b/gfx/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh new file mode 100644 index 0000000000..f7b456b11f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh @@ -0,0 +1,553 @@ + +#line 1 "hb-ot-shaper-myanmar-machine.rl" +/* + * 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 HB_OT_SHAPER_MYANMAR_MACHINE_HH +#define HB_OT_SHAPER_MYANMAR_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define myanmar_category() ot_shaper_var_u8_category() /* myanmar_category_t */ +#define myanmar_position() ot_shaper_var_u8_auxiliary() /* myanmar_position_t */ + +using myanmar_category_t = unsigned; +using myanmar_position_t = ot_position_t; + +#define M_Cat(Cat) myanmar_syllable_machine_ex_##Cat + +enum myanmar_syllable_type_t { + myanmar_consonant_syllable, + myanmar_broken_cluster, + myanmar_non_myanmar_cluster, +}; + + +#line 54 "hb-ot-shaper-myanmar-machine.hh" +#define myanmar_syllable_machine_ex_A 9u +#define myanmar_syllable_machine_ex_As 32u +#define myanmar_syllable_machine_ex_C 1u +#define myanmar_syllable_machine_ex_CS 18u +#define myanmar_syllable_machine_ex_DB 3u +#define myanmar_syllable_machine_ex_DOTTEDCIRCLE 11u +#define myanmar_syllable_machine_ex_GB 10u +#define myanmar_syllable_machine_ex_H 4u +#define myanmar_syllable_machine_ex_IV 2u +#define myanmar_syllable_machine_ex_MH 35u +#define myanmar_syllable_machine_ex_ML 41u +#define myanmar_syllable_machine_ex_MR 36u +#define myanmar_syllable_machine_ex_MW 37u +#define myanmar_syllable_machine_ex_MY 38u +#define myanmar_syllable_machine_ex_PT 39u +#define myanmar_syllable_machine_ex_Ra 15u +#define myanmar_syllable_machine_ex_SM 8u +#define myanmar_syllable_machine_ex_VAbv 20u +#define myanmar_syllable_machine_ex_VBlw 21u +#define myanmar_syllable_machine_ex_VPre 22u +#define myanmar_syllable_machine_ex_VPst 23u +#define myanmar_syllable_machine_ex_VS 40u +#define myanmar_syllable_machine_ex_ZWJ 6u +#define myanmar_syllable_machine_ex_ZWNJ 5u + + +#line 81 "hb-ot-shaper-myanmar-machine.hh" +static const unsigned char _myanmar_syllable_machine_trans_keys[] = { + 1u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, + 5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 39u, 3u, 39u, + 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 41u, + 3u, 41u, 3u, 41u, 5u, 39u, 5u, 8u, 3u, 41u, 3u, 39u, 3u, 39u, 5u, 39u, + 5u, 39u, 3u, 39u, 3u, 39u, 3u, 41u, 5u, 39u, 1u, 15u, 3u, 41u, 3u, 39u, + 3u, 39u, 3u, 40u, 3u, 39u, 3u, 41u, 3u, 41u, 3u, 39u, 3u, 41u, 3u, 41u, + 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 3u, 41u, 1u, 41u, 1u, 15u, 0 +}; + +static const char _myanmar_syllable_machine_key_spans[] = { + 41, 39, 35, 4, 39, 37, 37, 35, + 35, 37, 37, 39, 35, 15, 37, 37, + 38, 37, 39, 39, 37, 39, 39, 39, + 39, 39, 35, 4, 39, 37, 37, 35, + 35, 37, 37, 39, 35, 15, 39, 37, + 37, 38, 37, 39, 39, 37, 39, 39, + 39, 39, 39, 39, 39, 41, 15 +}; + +static const short _myanmar_syllable_machine_index_offsets[] = { + 0, 42, 82, 118, 123, 163, 201, 239, + 275, 311, 349, 387, 427, 463, 479, 517, + 555, 594, 632, 672, 712, 750, 790, 830, + 870, 910, 950, 986, 991, 1031, 1069, 1107, + 1143, 1179, 1217, 1255, 1295, 1331, 1347, 1387, + 1425, 1463, 1502, 1540, 1580, 1620, 1658, 1698, + 1738, 1778, 1818, 1858, 1898, 1938, 1980 +}; + +static const char _myanmar_syllable_machine_indicies[] = { + 1, 1, 2, 3, 4, 4, 0, 5, + 6, 1, 1, 0, 0, 0, 7, 0, + 0, 8, 0, 9, 10, 11, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 13, + 0, 0, 14, 15, 16, 17, 18, 19, + 20, 0, 22, 23, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 31, + 21, 21, 32, 33, 34, 35, 36, 37, + 38, 21, 24, 24, 21, 25, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 39, 21, 21, + 21, 21, 21, 21, 36, 21, 24, 24, + 21, 25, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 40, 21, 21, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 41, 21, 21, 42, 21, 21, 21, 36, + 21, 41, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 36, + 21, 43, 21, 24, 24, 21, 25, 36, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 44, 21, + 21, 21, 21, 21, 21, 36, 21, 24, + 24, 21, 25, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 44, 21, 21, 21, 21, 21, + 21, 36, 21, 24, 24, 21, 25, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 36, 21, 22, + 21, 24, 24, 21, 25, 26, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 40, 21, 21, 30, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 36, 21, 22, 21, 24, + 24, 21, 25, 26, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 40, 21, + 21, 30, 21, 21, 21, 21, 21, 21, + 21, 21, 41, 21, 21, 21, 21, 21, + 21, 36, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 40, 21, 21, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 41, 21, 21, 21, 21, 21, 21, 36, + 21, 41, 21, 24, 24, 21, 25, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 30, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 36, 21, 1, + 1, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 1, 21, 22, + 21, 24, 24, 21, 25, 26, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 27, 28, 21, 30, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 36, 21, 22, 21, 24, + 24, 21, 25, 26, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 28, + 21, 30, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 36, 21, 22, 21, 24, 24, 21, + 25, 26, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 27, 28, 29, 30, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 36, + 45, 21, 22, 21, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 36, 21, + 22, 21, 24, 24, 21, 25, 26, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 27, 28, 29, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 31, 21, 21, + 32, 33, 34, 35, 36, 21, 38, 21, + 22, 21, 24, 24, 21, 25, 26, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 27, 28, 29, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 45, 21, 21, + 21, 21, 21, 21, 36, 21, 38, 21, + 22, 21, 24, 24, 21, 25, 26, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 27, 28, 29, 30, 21, 21, 21, + 21, 21, 21, 21, 21, 45, 21, 21, + 21, 21, 21, 21, 36, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 32, 21, + 34, 21, 36, 21, 38, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 45, 21, 21, 32, 21, + 21, 21, 36, 21, 38, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 46, 21, 21, 32, 33, + 34, 21, 36, 21, 38, 21, 22, 21, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 32, 33, + 34, 21, 36, 21, 38, 21, 22, 23, + 24, 24, 21, 25, 26, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 27, + 28, 29, 30, 21, 21, 21, 21, 21, + 21, 21, 21, 31, 21, 21, 32, 33, + 34, 35, 36, 21, 38, 21, 48, 48, + 47, 5, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 49, 47, 47, 47, 47, 47, 47, + 18, 47, 48, 48, 47, 5, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 50, 47, 47, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 51, 47, 47, 52, + 47, 47, 47, 18, 47, 51, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 18, 47, 53, 47, 48, + 48, 47, 5, 18, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 54, 47, 47, 47, 47, 47, + 47, 18, 47, 48, 48, 47, 5, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 54, 47, + 47, 47, 47, 47, 47, 18, 47, 48, + 48, 47, 5, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 18, 47, 2, 47, 48, 48, 47, + 5, 6, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 50, 47, 47, 12, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 18, + 47, 2, 47, 48, 48, 47, 5, 6, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 50, 47, 47, 12, 47, 47, + 47, 47, 47, 47, 47, 47, 51, 47, + 47, 47, 47, 47, 47, 18, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 50, 47, 47, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 51, 47, 47, 47, + 47, 47, 47, 18, 47, 51, 47, 48, + 48, 47, 5, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 12, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 18, 47, 55, 55, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 55, 47, 2, 3, 48, 48, 47, + 5, 6, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 9, 10, 11, 12, + 47, 47, 47, 47, 47, 47, 47, 47, + 13, 47, 47, 14, 15, 16, 17, 18, + 19, 20, 47, 2, 47, 48, 48, 47, + 5, 6, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 9, 10, 47, 12, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 18, + 47, 2, 47, 48, 48, 47, 5, 6, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 10, 47, 12, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 18, 47, 2, + 47, 48, 48, 47, 5, 6, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 9, 10, 11, 12, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 18, 56, 47, 2, 47, + 48, 48, 47, 5, 6, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 9, + 10, 11, 12, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 18, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 13, 47, 47, 14, 15, 16, 17, + 18, 47, 20, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 56, 47, 47, 47, 47, 47, 47, + 18, 47, 20, 47, 2, 47, 48, 48, + 47, 5, 6, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 56, 47, 47, 47, 47, 47, 47, + 18, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 14, 47, 16, 47, 18, 47, + 20, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 56, + 47, 47, 14, 47, 47, 47, 18, 47, + 20, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 57, + 47, 47, 14, 15, 16, 47, 18, 47, + 20, 47, 2, 47, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 14, 15, 16, 47, 18, 47, + 20, 47, 2, 3, 48, 48, 47, 5, + 6, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 9, 10, 11, 12, 47, + 47, 47, 47, 47, 47, 47, 47, 13, + 47, 47, 14, 15, 16, 17, 18, 47, + 20, 47, 22, 23, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 58, + 21, 21, 32, 33, 34, 35, 36, 37, + 38, 21, 22, 59, 24, 24, 21, 25, + 26, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 27, 28, 29, 30, 21, + 21, 21, 21, 21, 21, 21, 21, 31, + 21, 21, 32, 33, 34, 35, 36, 21, + 38, 21, 1, 1, 2, 3, 48, 48, + 47, 5, 6, 1, 1, 47, 47, 47, + 1, 47, 47, 47, 47, 9, 10, 11, + 12, 47, 47, 47, 47, 47, 47, 47, + 47, 13, 47, 47, 14, 15, 16, 17, + 18, 19, 20, 47, 1, 1, 60, 60, + 60, 60, 60, 60, 60, 1, 1, 60, + 60, 60, 1, 60, 0 +}; + +static const char _myanmar_syllable_machine_trans_targs[] = { + 0, 1, 26, 37, 0, 27, 29, 51, + 54, 39, 40, 41, 28, 43, 44, 46, + 47, 48, 30, 50, 45, 0, 2, 13, + 0, 3, 5, 14, 15, 16, 4, 18, + 19, 21, 22, 23, 6, 25, 20, 12, + 9, 10, 11, 7, 8, 17, 24, 0, + 0, 36, 33, 34, 35, 31, 32, 38, + 42, 49, 52, 53, 0 +}; + +static const char _myanmar_syllable_machine_trans_actions[] = { + 3, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, + 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 9 +}; + +static const char _myanmar_syllable_machine_to_state_actions[] = { + 1, 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 +}; + +static const char _myanmar_syllable_machine_from_state_actions[] = { + 2, 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 +}; + +static const short _myanmar_syllable_machine_eof_trans[] = { + 0, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 22, 22, 48, 61 +}; + +static const int myanmar_syllable_machine_start = 0; +static const int myanmar_syllable_machine_first_final = 0; +static const int myanmar_syllable_machine_error = -1; + +static const int myanmar_syllable_machine_en_main = 0; + + +#line 55 "hb-ot-shaper-myanmar-machine.rl" + + + +#line 117 "hb-ot-shaper-myanmar-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_myanmar (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + +#line 447 "hb-ot-shaper-myanmar-machine.hh" + { + cs = myanmar_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 137 "hb-ot-shaper-myanmar-machine.rl" + + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + +#line 463 "hb-ot-shaper-myanmar-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _myanmar_syllable_machine_from_state_actions[cs] ) { + case 2: +#line 1 "NONE" + {ts = p;} + break; +#line 477 "hb-ot-shaper-myanmar-machine.hh" + } + + _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); + _inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs]; + + _slen = _myanmar_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) && + ( info[p].myanmar_category()) <= _keys[1] ? + ( info[p].myanmar_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _myanmar_syllable_machine_trans_targs[_trans]; + + if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _myanmar_syllable_machine_trans_actions[_trans] ) { + case 6: +#line 110 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_consonant_syllable); }} + break; + case 4: +#line 111 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }} + break; + case 8: +#line 112 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 3: +#line 113 "hb-ot-shaper-myanmar-machine.rl" + {te = p+1;{ found_syllable (myanmar_non_myanmar_cluster); }} + break; + case 5: +#line 110 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_consonant_syllable); }} + break; + case 7: +#line 112 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 9: +#line 113 "hb-ot-shaper-myanmar-machine.rl" + {te = p;p--;{ found_syllable (myanmar_non_myanmar_cluster); }} + break; +#line 523 "hb-ot-shaper-myanmar-machine.hh" + } + +_again: + switch ( _myanmar_syllable_machine_to_state_actions[cs] ) { + case 1: +#line 1 "NONE" + {ts = 0;} + break; +#line 532 "hb-ot-shaper-myanmar-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _myanmar_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 145 "hb-ot-shaper-myanmar-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_MYANMAR_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-myanmar-machine.rl b/gfx/harfbuzz/src/hb-ot-shaper-myanmar-machine.rl new file mode 100644 index 0000000000..e8d1e788c9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-myanmar-machine.rl @@ -0,0 +1,150 @@ +/* + * 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 HB_OT_SHAPER_MYANMAR_MACHINE_HH +#define HB_OT_SHAPER_MYANMAR_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shaper-indic.hh" + +/* buffer var allocations */ +#define myanmar_category() ot_shaper_var_u8_category() /* myanmar_category_t */ +#define myanmar_position() ot_shaper_var_u8_auxiliary() /* myanmar_position_t */ + +using myanmar_category_t = unsigned; +using myanmar_position_t = ot_position_t; + +#define M_Cat(Cat) myanmar_syllable_machine_ex_##Cat + +enum myanmar_syllable_type_t { + myanmar_consonant_syllable, + myanmar_broken_cluster, + myanmar_non_myanmar_cluster, +}; + +%%{ + machine myanmar_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + + +# Spec category D is folded into GB; D0 is not implemented by Uniscribe and as such folded into D +# Spec category P is folded into GB + +export C = 1; +export IV = 2; +export DB = 3; # Dot below = OT_N +export H = 4; +export ZWNJ = 5; +export ZWJ = 6; +export SM = 8; # Visarga and Shan tones +export GB = 10; # = OT_PLACEHOLDER +export DOTTEDCIRCLE = 11; +export A = 9; +export Ra = 15; +export CS = 18; + +export VAbv = 20; +export VBlw = 21; +export VPre = 22; +export VPst = 23; + +# 32+ are for Myanmar-specific values +export As = 32; # Asat +export MH = 35; # Medial Ha +export MR = 36; # Medial Ra +export MW = 37; # Medial Wa, Shan Wa +export MY = 38; # Medial Ya, Mon Na, Mon Ma +export PT = 39; # Pwo and other tones +export VS = 40; # Variation selectors +export ML = 41; # Medial Mon La + + +j = ZWJ|ZWNJ; # Joiners +k = (Ra As H); # Kinzi + +c = C|Ra; # is_consonant + +medial_group = MY? As? MR? ((MW MH? ML? | MH ML? | ML) As?)?; +main_vowel_group = (VPre.VS?)* VAbv* VBlw* A* (DB As?)?; +post_vowel_group = VPst MH? ML? As* VAbv* A* (DB As?)?; +pwo_tone_group = PT A* DB? As?; + +complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* SM* j?; +syllable_tail = (H (c|IV).VS?)* (H | complex_syllable_tail); + +consonant_syllable = (k|CS)? (c|IV|GB|DOTTEDCIRCLE).VS? syllable_tail; +broken_cluster = k? VS? syllable_tail; +other = any; + +main := |* + consonant_syllable => { found_syllable (myanmar_consonant_syllable); }; + j => { found_syllable (myanmar_non_myanmar_cluster); }; + broken_cluster => { found_syllable (myanmar_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (myanmar_non_myanmar_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", ts, te, #syllable_type); \ + for (unsigned int i = ts; i < te; i++) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + +inline void +find_syllables_myanmar (hb_buffer_t *buffer) +{ + unsigned int p, pe, eof, ts, te, act HB_UNUSED; + int cs; + hb_glyph_info_t *info = buffer->info; + %%{ + write init; + getkey info[p].myanmar_category(); + }%% + + p = 0; + pe = eof = buffer->len; + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_MYANMAR_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-myanmar.cc b/gfx/harfbuzz/src/hb-ot-shaper-myanmar.cc new file mode 100644 index 0000000000..1b2a085a8d --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-myanmar.cc @@ -0,0 +1,390 @@ +/* + * Copyright © 2011,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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-myanmar-machine.hh" +#include "hb-ot-shaper-indic.hh" +#include "hb-ot-layout.hh" + + +/* + * Myanmar shaper. + */ + + +static const hb_tag_t +myanmar_basic_features[] = +{ + /* + * Basic features. + * These features are applied in order, one at a time, after reordering, + * constrained to the syllable. + */ + HB_TAG('r','p','h','f'), + HB_TAG('p','r','e','f'), + HB_TAG('b','l','w','f'), + HB_TAG('p','s','t','f'), +}; +static const hb_tag_t +myanmar_other_features[] = +{ + /* + * Other features. + * These features are applied all at once, after clearing syllables. + */ + HB_TAG('p','r','e','s'), + HB_TAG('a','b','v','s'), + HB_TAG('b','l','w','s'), + HB_TAG('p','s','t','s'), +}; + +static inline void +set_myanmar_properties (hb_glyph_info_t &info) +{ + hb_codepoint_t u = info.codepoint; + unsigned int type = hb_indic_get_categories (u); + + info.myanmar_category() = (myanmar_category_t) (type & 0xFFu); +} + + +static inline bool +is_one_of_myanmar (const hb_glyph_info_t &info, unsigned int flags) +{ + /* If it ligated, all bets are off. */ + if (_hb_glyph_info_ligated (&info)) return false; + return !!(FLAG_UNSAFE (info.myanmar_category()) & flags); +} + +/* Note: + * + * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels + * cannot happen in a consonant syllable. The plus side however is, we can call the + * consonant syllable logic from the vowel syllable function and get it all right! + * + * Keep in sync with consonant_categories in the generator. */ +#define CONSONANT_FLAGS_MYANMAR (FLAG (M_Cat(C)) | FLAG (M_Cat(CS)) | FLAG (M_Cat(Ra)) | /* FLAG (M_Cat(CM)) | */ FLAG (M_Cat(IV)) | FLAG (M_Cat(GB)) | FLAG (M_Cat(DOTTEDCIRCLE))) + +static inline bool +is_consonant_myanmar (const hb_glyph_info_t &info) +{ + return is_one_of_myanmar (info, CONSONANT_FLAGS_MYANMAR); +} + + +static bool +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static bool +reorder_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_myanmar (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_myanmar); + + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + /* The Indic specs do not require ccmp, but we apply it here since if + * there is a use of it, it's typically at the beginning. */ + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + + + map->add_gsub_pause (reorder_myanmar); + + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_basic_features); i++) + { + map->enable_feature (myanmar_basic_features[i], F_MANUAL_ZWJ | F_PER_SYLLABLE); + map->add_gsub_pause (nullptr); + } + map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var + + for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++) + map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ); +} + +static void +setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category); + HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position); + + /* No masks, we just save information about characters. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + set_myanmar_properties (info[i]); +} + +static bool +setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_myanmar (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); + return false; +} + +static int +compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) +{ + int a = pa->myanmar_position(); + int b = pb->myanmar_position(); + + return (int) a - (int) b; +} + + +/* Rules from: + * https://docs.microsoft.com/en-us/typography/script-development/myanmar */ + +static void +initial_reordering_consonant_syllable (hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + hb_glyph_info_t *info = buffer->info; + + unsigned int base = end; + bool has_reph = false; + + { + unsigned int limit = start; + if (start + 3 <= end && + info[start ].myanmar_category() == M_Cat(Ra) && + info[start+1].myanmar_category() == M_Cat(As) && + info[start+2].myanmar_category() == M_Cat(H)) + { + limit += 3; + base = start; + has_reph = true; + } + + { + if (!has_reph) + base = limit; + + for (unsigned int i = limit; i < end; i++) + if (is_consonant_myanmar (info[i])) + { + base = i; + break; + } + } + } + + /* Reorder! */ + { + unsigned int i = start; + for (; i < start + (has_reph ? 3 : 0); i++) + info[i].myanmar_position() = POS_AFTER_MAIN; + for (; i < base; i++) + info[i].myanmar_position() = POS_PRE_C; + if (i < end) + { + info[i].myanmar_position() = POS_BASE_C; + i++; + } + myanmar_position_t pos = POS_AFTER_MAIN; + /* The following loop may be ugly, but it implements all of + * Myanmar reordering! */ + for (; i < end; i++) + { + if (info[i].myanmar_category() == M_Cat(MR)) /* Pre-base reordering */ + { + info[i].myanmar_position() = POS_PRE_C; + continue; + } + if (info[i].myanmar_category() == M_Cat(VPre)) /* Left matra */ + { + info[i].myanmar_position() = POS_PRE_M; + continue; + } + if (info[i].myanmar_category() == M_Cat(VS)) + { + info[i].myanmar_position() = info[i - 1].myanmar_position(); + continue; + } + + if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == M_Cat(VBlw)) + { + pos = POS_BELOW_C; + info[i].myanmar_position() = pos; + continue; + } + + if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(A)) + { + info[i].myanmar_position() = POS_BEFORE_SUB; + continue; + } + if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(VBlw)) + { + info[i].myanmar_position() = pos; + continue; + } + if (pos == POS_BELOW_C && info[i].myanmar_category() != M_Cat(A)) + { + pos = POS_AFTER_SUB; + info[i].myanmar_position() = pos; + continue; + } + info[i].myanmar_position() = pos; + } + } + + /* Sit tight, rock 'n roll! */ + buffer->sort (start, end, compare_myanmar_order); + + /* Flip left-matra sequence. */ + unsigned first_left_matra = end; + unsigned last_left_matra = end; + for (unsigned int i = start; i < end; i++) + { + if (info[i].myanmar_position() == POS_PRE_M) + { + if (first_left_matra == end) + first_left_matra = i; + last_left_matra = i; + } + } + /* https://github.com/harfbuzz/harfbuzz/issues/3863 */ + if (first_left_matra < last_left_matra) + { + /* No need to merge clusters, done already? */ + buffer->reverse_range (first_left_matra, last_left_matra + 1); + /* Reverse back VS, etc. */ + unsigned i = first_left_matra; + for (unsigned j = i; j <= last_left_matra; j++) + if (info[j].myanmar_category() == M_Cat(VPre)) + { + buffer->reverse_range (i, j + 1); + i = j + 1; + } + } +} + +static void +reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_face_t *face HB_UNUSED, + hb_buffer_t *buffer, + unsigned int start, unsigned int end) +{ + myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + switch (syllable_type) { + + case myanmar_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */ + case myanmar_consonant_syllable: + initial_reordering_consonant_syllable (buffer, start, end); + break; + + case myanmar_non_myanmar_cluster: + break; + } +} + +static bool +reorder_myanmar (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + bool ret = false; + if (buffer->message (font, "start reordering myanmar")) + { + if (hb_syllabic_insert_dotted_circles (font, buffer, + myanmar_broken_cluster, + M_Cat(DOTTEDCIRCLE))) + ret = true; + + foreach_syllable (buffer, start, end) + reorder_syllable_myanmar (plan, font->face, buffer, start, end); + (void) buffer->message (font, "end reordering myanmar"); + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category); + HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position); + + return ret; +} + + +const hb_ot_shaper_t _hb_ot_shaper_myanmar = +{ + collect_features_myanmar, + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + setup_masks_myanmar, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + false, /* fallback_position */ +}; + + +#ifndef HB_NO_OT_SHAPER_MYANMAR_ZAWGYI +/* Ugly Zawgyi encoding. + * Disable all auto processing. + * https://github.com/harfbuzz/harfbuzz/issues/1162 */ +const hb_ot_shaper_t _hb_ot_shaper_myanmar_zawgyi = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + nullptr, /* preprocess_text */ + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_NONE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + false, /* fallback_position */ +}; +#endif + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-syllabic.cc b/gfx/harfbuzz/src/hb-ot-shaper-syllabic.cc new file mode 100644 index 0000000000..97f62035c6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-syllabic.cc @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-syllabic.hh" + + +bool +hb_syllabic_insert_dotted_circles (hb_font_t *font, + hb_buffer_t *buffer, + unsigned int broken_syllable_type, + unsigned int dottedcircle_category, + int repha_category, + int dottedcircle_position) +{ + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return false; + if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE))) + { + if (buffer->messaging ()) + (void) buffer->message (font, "skipped inserting dotted-circles because there is no broken syllables"); + return false; + } + + if (buffer->messaging () && + !buffer->message (font, "start inserting dotted-circles")) + return false; + + hb_codepoint_t dottedcircle_glyph; + if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph)) + return false; + + hb_glyph_info_t dottedcircle = {0}; + dottedcircle.codepoint = 0x25CCu; + dottedcircle.ot_shaper_var_u8_category() = dottedcircle_category; + if (dottedcircle_position != -1) + dottedcircle.ot_shaper_var_u8_auxiliary() = dottedcircle_position; + dottedcircle.codepoint = dottedcircle_glyph; + + buffer->clear_output (); + + buffer->idx = 0; + unsigned int last_syllable = 0; + while (buffer->idx < buffer->len && buffer->successful) + { + unsigned int syllable = buffer->cur().syllable(); + if (unlikely (last_syllable != syllable && (syllable & 0x0F) == broken_syllable_type)) + { + last_syllable = syllable; + + hb_glyph_info_t ginfo = dottedcircle; + ginfo.cluster = buffer->cur().cluster; + ginfo.mask = buffer->cur().mask; + ginfo.syllable() = buffer->cur().syllable(); + + /* Insert dottedcircle after possible Repha. */ + if (repha_category != -1) + { + while (buffer->idx < buffer->len && buffer->successful && + last_syllable == buffer->cur().syllable() && + buffer->cur().ot_shaper_var_u8_category() == (unsigned) repha_category) + (void) buffer->next_glyph (); + } + + (void) buffer->output_info (ginfo); + } + else + (void) buffer->next_glyph (); + } + buffer->sync (); + + if (buffer->messaging ()) + (void) buffer->message (font, "end inserting dotted-circles"); + + return true; +} + +HB_INTERNAL bool +hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + HB_BUFFER_DEALLOCATE_VAR (buffer, syllable); + return false; +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-syllabic.hh b/gfx/harfbuzz/src/hb-ot-shaper-syllabic.hh new file mode 100644 index 0000000000..f240ad1c26 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-syllabic.hh @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef HB_OT_SHAPER_SYLLABIC_HH +#define HB_OT_SHAPER_SYLLABIC_HH + +#include "hb.hh" + +#include "hb-ot-shaper.hh" + + +HB_INTERNAL bool +hb_syllabic_insert_dotted_circles (hb_font_t *font, + hb_buffer_t *buffer, + unsigned int broken_syllable_type, + unsigned int dottedcircle_category, + int repha_category = -1, + int dottedcircle_position = -1); + +HB_INTERNAL bool +hb_syllabic_clear_var (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + + +#endif /* HB_OT_SHAPER_SYLLABIC_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-thai.cc b/gfx/harfbuzz/src/hb-ot-shaper-thai.cc new file mode 100644 index 0000000000..6cd67cde35 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-thai.cc @@ -0,0 +1,393 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper.hh" + + +/* Thai / Lao shaper */ + + +/* PUA shaping */ + + +enum thai_consonant_type_t +{ + NC, + AC, + RC, + DC, + NOT_CONSONANT, + NUM_CONSONANT_TYPES = NOT_CONSONANT +}; + +static thai_consonant_type_t +get_consonant_type (hb_codepoint_t u) +{ + if (u == 0x0E1Bu || u == 0x0E1Du || u == 0x0E1Fu/* || u == 0x0E2Cu*/) + return AC; + if (u == 0x0E0Du || u == 0x0E10u) + return RC; + if (u == 0x0E0Eu || u == 0x0E0Fu) + return DC; + if (hb_in_range<hb_codepoint_t> (u, 0x0E01u, 0x0E2Eu)) + return NC; + return NOT_CONSONANT; +} + + +enum thai_mark_type_t +{ + AV, + BV, + T, + NOT_MARK, + NUM_MARK_TYPES = NOT_MARK +}; + +static thai_mark_type_t +get_mark_type (hb_codepoint_t u) +{ + if (u == 0x0E31u || hb_in_range<hb_codepoint_t> (u, 0x0E34u, 0x0E37u) || + u == 0x0E47u || hb_in_range<hb_codepoint_t> (u, 0x0E4Du, 0x0E4Eu)) + return AV; + if (hb_in_range<hb_codepoint_t> (u, 0x0E38u, 0x0E3Au)) + return BV; + if (hb_in_range<hb_codepoint_t> (u, 0x0E48u, 0x0E4Cu)) + return T; + return NOT_MARK; +} + + +enum thai_action_t +{ + NOP, + SD, /* Shift combining-mark down */ + SL, /* Shift combining-mark left */ + SDL, /* Shift combining-mark down-left */ + RD /* Remove descender from base */ +}; + +static hb_codepoint_t +thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font) +{ + struct thai_pua_mapping_t { + uint16_t u; + uint16_t win_pua; + uint16_t mac_pua; + } const *pua_mappings = nullptr; + static const thai_pua_mapping_t SD_mappings[] = { + {0x0E48u, 0xF70Au, 0xF88Bu}, /* MAI EK */ + {0x0E49u, 0xF70Bu, 0xF88Eu}, /* MAI THO */ + {0x0E4Au, 0xF70Cu, 0xF891u}, /* MAI TRI */ + {0x0E4Bu, 0xF70Du, 0xF894u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF70Eu, 0xF897u}, /* THANTHAKHAT */ + {0x0E38u, 0xF718u, 0xF89Bu}, /* SARA U */ + {0x0E39u, 0xF719u, 0xF89Cu}, /* SARA UU */ + {0x0E3Au, 0xF71Au, 0xF89Du}, /* PHINTHU */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t SDL_mappings[] = { + {0x0E48u, 0xF705u, 0xF88Cu}, /* MAI EK */ + {0x0E49u, 0xF706u, 0xF88Fu}, /* MAI THO */ + {0x0E4Au, 0xF707u, 0xF892u}, /* MAI TRI */ + {0x0E4Bu, 0xF708u, 0xF895u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF709u, 0xF898u}, /* THANTHAKHAT */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t SL_mappings[] = { + {0x0E48u, 0xF713u, 0xF88Au}, /* MAI EK */ + {0x0E49u, 0xF714u, 0xF88Du}, /* MAI THO */ + {0x0E4Au, 0xF715u, 0xF890u}, /* MAI TRI */ + {0x0E4Bu, 0xF716u, 0xF893u}, /* MAI CHATTAWA */ + {0x0E4Cu, 0xF717u, 0xF896u}, /* THANTHAKHAT */ + {0x0E31u, 0xF710u, 0xF884u}, /* MAI HAN-AKAT */ + {0x0E34u, 0xF701u, 0xF885u}, /* SARA I */ + {0x0E35u, 0xF702u, 0xF886u}, /* SARA II */ + {0x0E36u, 0xF703u, 0xF887u}, /* SARA UE */ + {0x0E37u, 0xF704u, 0xF888u}, /* SARA UEE */ + {0x0E47u, 0xF712u, 0xF889u}, /* MAITAIKHU */ + {0x0E4Du, 0xF711u, 0xF899u}, /* NIKHAHIT */ + {0x0000u, 0x0000u, 0x0000u} + }; + static const thai_pua_mapping_t RD_mappings[] = { + {0x0E0Du, 0xF70Fu, 0xF89Au}, /* YO YING */ + {0x0E10u, 0xF700u, 0xF89Eu}, /* THO THAN */ + {0x0000u, 0x0000u, 0x0000u} + }; + + switch (action) { + case NOP: return u; + case SD: pua_mappings = SD_mappings; break; + case SDL: pua_mappings = SDL_mappings; break; + case SL: pua_mappings = SL_mappings; break; + case RD: pua_mappings = RD_mappings; break; + } + for (; pua_mappings->u; pua_mappings++) + if (pua_mappings->u == u) + { + hb_codepoint_t glyph; + if (hb_font_get_glyph (font, pua_mappings->win_pua, 0, &glyph)) + return pua_mappings->win_pua; + if (hb_font_get_glyph (font, pua_mappings->mac_pua, 0, &glyph)) + return pua_mappings->mac_pua; + break; + } + return u; +} + + +static enum thai_above_state_t +{ /* Cluster above looks like: */ + T0, /* ⣤ */ + T1, /* ⣼ */ + T2, /* ⣾ */ + T3, /* ⣿ */ + NUM_ABOVE_STATES +} thai_above_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] = +{ + T0, /* NC */ + T1, /* AC */ + T0, /* RC */ + T0, /* DC */ + T3, /* NOT_CONSONANT */ +}; + +static const struct thai_above_state_machine_edge_t { + thai_action_t action; + thai_above_state_t next_state; +} thai_above_state_machine[NUM_ABOVE_STATES][NUM_MARK_TYPES] = +{ /*AV*/ /*BV*/ /*T*/ +/*T0*/ {{NOP,T3}, {NOP,T0}, {SD, T3}}, +/*T1*/ {{SL, T2}, {NOP,T1}, {SDL,T2}}, +/*T2*/ {{NOP,T3}, {NOP,T2}, {SL, T3}}, +/*T3*/ {{NOP,T3}, {NOP,T3}, {NOP,T3}}, +}; + + +static enum thai_below_state_t +{ + B0, /* No descender */ + B1, /* Removable descender */ + B2, /* Strict descender */ + NUM_BELOW_STATES +} thai_below_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] = +{ + B0, /* NC */ + B0, /* AC */ + B1, /* RC */ + B2, /* DC */ + B2, /* NOT_CONSONANT */ +}; + +static const struct thai_below_state_machine_edge_t { + thai_action_t action; + thai_below_state_t next_state; +} thai_below_state_machine[NUM_BELOW_STATES][NUM_MARK_TYPES] = +{ /*AV*/ /*BV*/ /*T*/ +/*B0*/ {{NOP,B0}, {NOP,B2}, {NOP, B0}}, +/*B1*/ {{NOP,B1}, {RD, B2}, {NOP, B1}}, +/*B2*/ {{NOP,B2}, {SD, B2}, {NOP, B2}}, +}; + + +static void +do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font) +{ +#ifdef HB_NO_OT_SHAPER_THAI_FALLBACK + return; +#endif + + thai_above_state_t above_state = thai_above_start_state[NOT_CONSONANT]; + thai_below_state_t below_state = thai_below_start_state[NOT_CONSONANT]; + unsigned int base = 0; + + hb_glyph_info_t *info = buffer->info; + unsigned int count = buffer->len; + for (unsigned int i = 0; i < count; i++) + { + thai_mark_type_t mt = get_mark_type (info[i].codepoint); + + if (mt == NOT_MARK) { + thai_consonant_type_t ct = get_consonant_type (info[i].codepoint); + above_state = thai_above_start_state[ct]; + below_state = thai_below_start_state[ct]; + base = i; + continue; + } + + const thai_above_state_machine_edge_t &above_edge = thai_above_state_machine[above_state][mt]; + const thai_below_state_machine_edge_t &below_edge = thai_below_state_machine[below_state][mt]; + above_state = above_edge.next_state; + below_state = below_edge.next_state; + + /* At least one of the above/below actions is NOP. */ + thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action; + + buffer->unsafe_to_break (base, i); + if (action == RD) + info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font); + else + info[i].codepoint = thai_pua_shape (info[i].codepoint, action, font); + } +} + + +static void +preprocess_text_thai (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + /* This function implements the shaping logic documented here: + * + * https://linux.thai.net/~thep/th-otf/shaping.html + * + * The first shaping rule listed there is needed even if the font has Thai + * OpenType tables. The rest do fallback positioning based on PUA codepoints. + * We implement that only if there exist no Thai GSUB in the font. + */ + + /* The following is NOT specified in the MS OT Thai spec, however, it seems + * to be what Uniscribe and other engines implement. According to Eric Muller: + * + * When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the + * NIKHAHIT backwards over any above-base marks. + * + * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32> + * + * This reordering is legit only when the NIKHAHIT comes from a SARA AM, not + * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably + * not what a user wanted, but the rendering is nevertheless nikhahit above + * chattawa. + * + * Same for Lao. + * + * Note: + * + * Uniscribe also does some below-marks reordering. Namely, it positions U+0E3A + * after U+0E38 and U+0E39. We do that by modifying the ccc for U+0E3A. + * See unicode->modified_combining_class (). Lao does NOT have a U+0E3A + * equivalent. + */ + + + /* + * Here are the characters of significance: + * + * Thai Lao + * SARA AM: U+0E33 U+0EB3 + * SARA AA: U+0E32 U+0EB2 + * Nikhahit: U+0E4D U+0ECD + * + * Testing shows that Uniscribe reorder the following marks: + * Thai: <0E31,0E34..0E37, 0E47..0E4E> + * Lao: <0EB1,0EB4..0EB7,0EBB,0EC8..0ECD> + * + * Note how the Lao versions are the same as Thai + 0x80. + */ + + /* We only get one script at a time, so a script-agnostic implementation + * is adequate here. */ +#define IS_SARA_AM(x) (((x) & ~0x0080u) == 0x0E33u) +#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0x0E33u + 0x0E4Du) +#define SARA_AA_FROM_SARA_AM(x) ((x) - 1) +#define IS_ABOVE_BASE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u, 0x0E3Bu, 0x0E3Bu)) + + buffer->clear_output (); + unsigned int count = buffer->len; + for (buffer->idx = 0; buffer->idx < count /* No need for: && buffer->successful */;) + { + hb_codepoint_t u = buffer->cur().codepoint; + if (likely (!IS_SARA_AM (u))) + { + if (unlikely (!buffer->next_glyph ())) break; + continue; + } + + /* Is SARA AM. Decompose and reorder. */ + (void) buffer->output_glyph (NIKHAHIT_FROM_SARA_AM (u)); + _hb_glyph_info_set_continuation (&buffer->prev()); + if (unlikely (!buffer->replace_glyph (SARA_AA_FROM_SARA_AM (u)))) break; + + /* Make Nikhahit be recognized as a ccc=0 mark when zeroing widths. */ + unsigned int end = buffer->out_len; + _hb_glyph_info_set_general_category (&buffer->out_info[end - 2], HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK); + + /* Ok, let's see... */ + unsigned int start = end - 2; + while (start > 0 && IS_ABOVE_BASE_MARK (buffer->out_info[start - 1].codepoint)) + start--; + + if (start + 2 < end) + { + /* Move Nikhahit (end-2) to the beginning */ + buffer->merge_out_clusters (start, end); + hb_glyph_info_t t = buffer->out_info[end - 2]; + memmove (buffer->out_info + start + 1, + buffer->out_info + start, + sizeof (buffer->out_info[0]) * (end - start - 2)); + buffer->out_info[start] = t; + } + else + { + /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the + * previous cluster. */ + if (start && buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) + buffer->merge_out_clusters (start - 1, end); + } + } + buffer->sync (); + + /* If font has Thai GSUB, we are done. */ + if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0]) + do_thai_pua_shaping (plan, buffer, font); +} + +const hb_ot_shaper_t _hb_ot_shaper_thai = +{ + nullptr, /* collect_features */ + nullptr, /* override_features */ + nullptr, /* data_create */ + nullptr, /* data_destroy */ + preprocess_text_thai, + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + nullptr, /* compose */ + nullptr, /* setup_masks */ + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, + false,/* fallback_position */ +}; + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-use-machine.hh b/gfx/harfbuzz/src/hb-ot-shaper-use-machine.hh new file mode 100644 index 0000000000..be0a2539be --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-use-machine.hh @@ -0,0 +1,1079 @@ + +#line 1 "hb-ot-shaper-use-machine.rl" +/* + * Copyright © 2015 Mozilla Foundation. + * 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_USE_MACHINE_HH +#define HB_OT_SHAPER_USE_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-shaper-syllabic.hh" + +/* buffer var allocations */ +#define use_category() ot_shaper_var_u8_category() + +#define USE(Cat) use_syllable_machine_ex_##Cat + +enum use_syllable_type_t { + use_virama_terminated_cluster, + use_sakot_terminated_cluster, + use_standard_cluster, + use_number_joiner_terminated_cluster, + use_numeral_cluster, + use_symbol_cluster, + use_hieroglyph_cluster, + use_broken_cluster, + use_non_cluster, +}; + + +#line 57 "hb-ot-shaper-use-machine.hh" +#define use_syllable_machine_ex_B 1u +#define use_syllable_machine_ex_CGJ 6u +#define use_syllable_machine_ex_CMAbv 31u +#define use_syllable_machine_ex_CMBlw 32u +#define use_syllable_machine_ex_CS 43u +#define use_syllable_machine_ex_FAbv 24u +#define use_syllable_machine_ex_FBlw 25u +#define use_syllable_machine_ex_FMAbv 45u +#define use_syllable_machine_ex_FMBlw 46u +#define use_syllable_machine_ex_FMPst 47u +#define use_syllable_machine_ex_FPst 26u +#define use_syllable_machine_ex_G 49u +#define use_syllable_machine_ex_GB 5u +#define use_syllable_machine_ex_H 12u +#define use_syllable_machine_ex_HM 54u +#define use_syllable_machine_ex_HN 13u +#define use_syllable_machine_ex_HR 55u +#define use_syllable_machine_ex_HVM 53u +#define use_syllable_machine_ex_IS 44u +#define use_syllable_machine_ex_J 50u +#define use_syllable_machine_ex_MAbv 27u +#define use_syllable_machine_ex_MBlw 28u +#define use_syllable_machine_ex_MPre 30u +#define use_syllable_machine_ex_MPst 29u +#define use_syllable_machine_ex_N 4u +#define use_syllable_machine_ex_O 0u +#define use_syllable_machine_ex_R 18u +#define use_syllable_machine_ex_SB 51u +#define use_syllable_machine_ex_SE 52u +#define use_syllable_machine_ex_SMAbv 41u +#define use_syllable_machine_ex_SMBlw 42u +#define use_syllable_machine_ex_SUB 11u +#define use_syllable_machine_ex_Sk 48u +#define use_syllable_machine_ex_VAbv 33u +#define use_syllable_machine_ex_VBlw 34u +#define use_syllable_machine_ex_VMAbv 37u +#define use_syllable_machine_ex_VMBlw 38u +#define use_syllable_machine_ex_VMPre 23u +#define use_syllable_machine_ex_VMPst 39u +#define use_syllable_machine_ex_VPre 22u +#define use_syllable_machine_ex_VPst 35u +#define use_syllable_machine_ex_WJ 16u +#define use_syllable_machine_ex_ZWNJ 14u + + +#line 103 "hb-ot-shaper-use-machine.hh" +static const unsigned char _use_syllable_machine_trans_keys[] = { + 49u, 51u, 0u, 53u, 11u, 53u, 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, + 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, + 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, + 12u, 53u, 12u, 53u, 11u, 53u, 1u, 14u, 1u, 48u, 14u, 42u, 14u, 42u, 11u, 53u, + 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, + 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, + 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, 1u, 14u, + 1u, 14u, 1u, 48u, 13u, 14u, 4u, 14u, 11u, 53u, 11u, 53u, 1u, 53u, 14u, 48u, + 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, + 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, 14u, 53u, 14u, 53u, 12u, 53u, + 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, 1u, 14u, 1u, 14u, 1u, 48u, + 11u, 53u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, + 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, 14u, 48u, 14u, 53u, 14u, 53u, + 14u, 53u, 14u, 53u, 12u, 53u, 14u, 53u, 12u, 53u, 12u, 53u, 12u, 53u, 11u, 53u, + 1u, 14u, 1u, 48u, 4u, 14u, 13u, 14u, 1u, 53u, 14u, 42u, 14u, 42u, 1u, 5u, + 14u, 55u, 14u, 51u, 14u, 52u, 14u, 54u, 11u, 53u, 0 +}; + +static const char _use_syllable_machine_key_spans[] = { + 3, 54, 43, 43, 53, 35, 34, 34, + 34, 33, 33, 1, 35, 35, 35, 14, + 35, 40, 40, 40, 40, 42, 40, 42, + 42, 42, 43, 14, 48, 29, 29, 43, + 53, 35, 34, 34, 34, 33, 33, 1, + 35, 35, 35, 14, 35, 40, 40, 40, + 40, 42, 40, 42, 42, 42, 43, 14, + 14, 48, 2, 11, 43, 43, 53, 35, + 34, 34, 34, 33, 33, 1, 35, 35, + 35, 14, 35, 40, 40, 40, 40, 42, + 40, 42, 42, 42, 43, 14, 14, 48, + 43, 53, 35, 34, 34, 34, 33, 33, + 1, 35, 35, 35, 14, 35, 40, 40, + 40, 40, 42, 40, 42, 42, 42, 43, + 14, 48, 11, 2, 53, 29, 29, 5, + 42, 38, 39, 41, 43 +}; + +static const short _use_syllable_machine_index_offsets[] = { + 0, 4, 59, 103, 147, 201, 237, 272, + 307, 342, 376, 410, 412, 448, 484, 520, + 535, 571, 612, 653, 694, 735, 778, 819, + 862, 905, 948, 992, 1007, 1056, 1086, 1116, + 1160, 1214, 1250, 1285, 1320, 1355, 1389, 1423, + 1425, 1461, 1497, 1533, 1548, 1584, 1625, 1666, + 1707, 1748, 1791, 1832, 1875, 1918, 1961, 2005, + 2020, 2035, 2084, 2087, 2099, 2143, 2187, 2241, + 2277, 2312, 2347, 2382, 2416, 2450, 2452, 2488, + 2524, 2560, 2575, 2611, 2652, 2693, 2734, 2775, + 2818, 2859, 2902, 2945, 2988, 3032, 3047, 3062, + 3111, 3155, 3209, 3245, 3280, 3315, 3350, 3384, + 3418, 3420, 3456, 3492, 3528, 3543, 3579, 3620, + 3661, 3702, 3743, 3786, 3827, 3870, 3913, 3956, + 4000, 4015, 4064, 4076, 4079, 4133, 4163, 4193, + 4199, 4242, 4281, 4321, 4363 +}; + +static const unsigned char _use_syllable_machine_indicies[] = { + 1, 0, 2, 0, 3, 4, 5, 5, + 6, 7, 5, 5, 5, 5, 5, 8, + 9, 10, 11, 5, 5, 5, 12, 5, + 5, 5, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 8, 22, 23, 24, 25, + 5, 26, 27, 28, 5, 29, 30, 31, + 32, 33, 34, 35, 32, 1, 5, 36, + 5, 37, 5, 39, 40, 38, 41, 38, + 38, 38, 38, 38, 38, 38, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 39, + 51, 52, 53, 54, 38, 55, 56, 57, + 38, 58, 59, 38, 60, 61, 62, 63, + 60, 38, 38, 38, 38, 64, 38, 39, + 40, 38, 41, 38, 38, 38, 38, 38, + 38, 38, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 39, 51, 52, 53, 54, + 38, 55, 56, 57, 38, 38, 38, 38, + 60, 61, 62, 63, 60, 38, 38, 38, + 38, 64, 38, 39, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 41, 38, 38, 38, 38, 38, 38, 38, + 38, 43, 44, 45, 46, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 55, + 56, 57, 38, 38, 38, 38, 38, 61, + 62, 63, 65, 38, 38, 38, 38, 43, + 38, 41, 38, 38, 38, 38, 38, 38, + 38, 38, 43, 44, 45, 46, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 55, 56, 57, 38, 38, 38, 38, 38, + 61, 62, 63, 65, 38, 41, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 44, + 45, 46, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 61, 62, 63, 38, + 41, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 45, 46, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 61, + 62, 63, 38, 41, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 46, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 61, 62, 63, 38, 41, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 61, 62, 38, + 41, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 62, 38, 41, 38, 41, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 44, 45, + 46, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 55, 56, 57, 38, 38, + 38, 38, 38, 61, 62, 63, 65, 38, + 41, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 44, 45, 46, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 56, 57, 38, 38, 38, 38, 38, 61, + 62, 63, 65, 38, 41, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 44, 45, + 46, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 57, 38, 38, + 38, 38, 38, 61, 62, 63, 65, 38, + 66, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 41, 38, 41, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 44, 45, 46, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 61, 62, + 63, 65, 38, 41, 38, 38, 38, 38, + 38, 38, 38, 42, 43, 44, 45, 46, + 38, 38, 38, 38, 38, 38, 52, 53, + 54, 38, 55, 56, 57, 38, 38, 38, + 38, 38, 61, 62, 63, 65, 38, 38, + 38, 38, 43, 38, 41, 38, 38, 38, + 38, 38, 38, 38, 38, 43, 44, 45, + 46, 38, 38, 38, 38, 38, 38, 52, + 53, 54, 38, 55, 56, 57, 38, 38, + 38, 38, 38, 61, 62, 63, 65, 38, + 38, 38, 38, 43, 38, 41, 38, 38, + 38, 38, 38, 38, 38, 38, 43, 44, + 45, 46, 38, 38, 38, 38, 38, 38, + 38, 53, 54, 38, 55, 56, 57, 38, + 38, 38, 38, 38, 61, 62, 63, 65, + 38, 38, 38, 38, 43, 38, 41, 38, + 38, 38, 38, 38, 38, 38, 38, 43, + 44, 45, 46, 38, 38, 38, 38, 38, + 38, 38, 38, 54, 38, 55, 56, 57, + 38, 38, 38, 38, 38, 61, 62, 63, + 65, 38, 38, 38, 38, 43, 38, 67, + 38, 41, 38, 38, 38, 38, 38, 38, + 38, 42, 43, 44, 45, 46, 38, 48, + 49, 38, 38, 38, 52, 53, 54, 38, + 55, 56, 57, 38, 38, 38, 38, 38, + 61, 62, 63, 65, 38, 38, 38, 38, + 43, 38, 41, 38, 38, 38, 38, 38, + 38, 38, 38, 43, 44, 45, 46, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 55, 56, 57, 38, 38, 38, 38, + 38, 61, 62, 63, 65, 38, 38, 38, + 38, 43, 38, 67, 38, 41, 38, 38, + 38, 38, 38, 38, 38, 42, 43, 44, + 45, 46, 38, 38, 49, 38, 38, 38, + 52, 53, 54, 38, 55, 56, 57, 38, + 38, 38, 38, 38, 61, 62, 63, 65, + 38, 38, 38, 38, 43, 38, 67, 38, + 41, 38, 38, 38, 38, 38, 38, 38, + 42, 43, 44, 45, 46, 38, 38, 38, + 38, 38, 38, 52, 53, 54, 38, 55, + 56, 57, 38, 38, 38, 38, 38, 61, + 62, 63, 65, 38, 38, 38, 38, 43, + 38, 67, 38, 41, 38, 38, 38, 38, + 38, 38, 38, 42, 43, 44, 45, 46, + 47, 48, 49, 38, 38, 38, 52, 53, + 54, 38, 55, 56, 57, 38, 38, 38, + 38, 38, 61, 62, 63, 65, 38, 38, + 38, 38, 43, 38, 39, 40, 38, 41, + 38, 38, 38, 38, 38, 38, 38, 42, + 43, 44, 45, 46, 47, 48, 49, 50, + 38, 51, 52, 53, 54, 38, 55, 56, + 57, 38, 38, 38, 38, 60, 61, 62, + 63, 60, 38, 38, 38, 38, 64, 38, + 39, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 41, 38, 39, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 41, 38, 38, 38, + 38, 38, 38, 38, 38, 43, 44, 45, + 46, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 55, 56, 57, 38, 38, + 38, 38, 38, 61, 62, 63, 65, 38, + 41, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 58, 59, 38, 41, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 59, 38, 4, 69, 68, 70, + 68, 68, 68, 68, 68, 68, 68, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 4, 80, 81, 82, 83, 68, 84, 85, + 86, 68, 68, 68, 68, 87, 88, 89, + 90, 91, 68, 68, 68, 68, 92, 68, + 4, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 68, 72, 73, + 74, 75, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 84, 85, 86, 68, + 68, 68, 68, 68, 88, 89, 90, 93, + 68, 68, 68, 68, 72, 68, 70, 68, + 68, 68, 68, 68, 68, 68, 68, 72, + 73, 74, 75, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 84, 85, 86, + 68, 68, 68, 68, 68, 88, 89, 90, + 93, 68, 70, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 73, 74, 75, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 88, 89, 90, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 74, 75, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 88, 89, 90, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 75, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 88, + 89, 90, 68, 70, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 88, 89, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 89, 68, 70, + 68, 70, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 73, 74, 75, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 84, 85, 86, 68, 68, 68, 68, 68, + 88, 89, 90, 93, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 73, + 74, 75, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 85, 86, 68, + 68, 68, 68, 68, 88, 89, 90, 93, + 68, 70, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 73, 74, 75, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 86, 68, 68, 68, 68, 68, + 88, 89, 90, 93, 68, 95, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 96, 94, 70, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 73, 74, + 75, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 88, 89, 90, 93, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 71, 72, 73, 74, 75, 68, 68, 68, + 68, 68, 68, 81, 82, 83, 68, 84, + 85, 86, 68, 68, 68, 68, 68, 88, + 89, 90, 93, 68, 68, 68, 68, 72, + 68, 70, 68, 68, 68, 68, 68, 68, + 68, 68, 72, 73, 74, 75, 68, 68, + 68, 68, 68, 68, 81, 82, 83, 68, + 84, 85, 86, 68, 68, 68, 68, 68, + 88, 89, 90, 93, 68, 68, 68, 68, + 72, 68, 70, 68, 68, 68, 68, 68, + 68, 68, 68, 72, 73, 74, 75, 68, + 68, 68, 68, 68, 68, 68, 82, 83, + 68, 84, 85, 86, 68, 68, 68, 68, + 68, 88, 89, 90, 93, 68, 68, 68, + 68, 72, 68, 70, 68, 68, 68, 68, + 68, 68, 68, 68, 72, 73, 74, 75, + 68, 68, 68, 68, 68, 68, 68, 68, + 83, 68, 84, 85, 86, 68, 68, 68, + 68, 68, 88, 89, 90, 93, 68, 68, + 68, 68, 72, 68, 97, 68, 70, 68, + 68, 68, 68, 68, 68, 68, 71, 72, + 73, 74, 75, 68, 77, 78, 68, 68, + 68, 81, 82, 83, 68, 84, 85, 86, + 68, 68, 68, 68, 68, 88, 89, 90, + 93, 68, 68, 68, 68, 72, 68, 70, + 68, 68, 68, 68, 68, 68, 68, 68, + 72, 73, 74, 75, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 84, 85, + 86, 68, 68, 68, 68, 68, 88, 89, + 90, 93, 68, 68, 68, 68, 72, 68, + 97, 68, 70, 68, 68, 68, 68, 68, + 68, 68, 71, 72, 73, 74, 75, 68, + 68, 78, 68, 68, 68, 81, 82, 83, + 68, 84, 85, 86, 68, 68, 68, 68, + 68, 88, 89, 90, 93, 68, 68, 68, + 68, 72, 68, 97, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 71, 72, 73, + 74, 75, 68, 68, 68, 68, 68, 68, + 81, 82, 83, 68, 84, 85, 86, 68, + 68, 68, 68, 68, 88, 89, 90, 93, + 68, 68, 68, 68, 72, 68, 97, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 71, 72, 73, 74, 75, 76, 77, 78, + 68, 68, 68, 81, 82, 83, 68, 84, + 85, 86, 68, 68, 68, 68, 68, 88, + 89, 90, 93, 68, 68, 68, 68, 72, + 68, 4, 69, 68, 70, 68, 68, 68, + 68, 68, 68, 68, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 68, 80, 81, + 82, 83, 68, 84, 85, 86, 68, 68, + 68, 68, 87, 88, 89, 90, 91, 68, + 68, 68, 68, 92, 68, 4, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 99, 98, 4, 94, 94, 94, + 94, 94, 94, 94, 94, 94, 94, 94, + 94, 96, 94, 4, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 68, 72, 73, 74, 75, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 84, + 85, 86, 68, 68, 68, 68, 68, 88, + 89, 90, 93, 68, 101, 102, 100, 6, + 103, 103, 103, 103, 103, 103, 103, 103, + 103, 104, 103, 105, 106, 68, 70, 68, + 68, 68, 68, 68, 68, 68, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 105, + 116, 117, 118, 119, 68, 120, 121, 122, + 68, 58, 59, 68, 123, 124, 125, 126, + 127, 68, 68, 68, 68, 128, 68, 105, + 106, 68, 70, 68, 68, 68, 68, 68, + 68, 68, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 105, 116, 117, 118, 119, + 68, 120, 121, 122, 68, 68, 68, 68, + 123, 124, 125, 126, 127, 68, 68, 68, + 68, 128, 68, 105, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 68, 108, 109, 110, 111, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 120, + 121, 122, 68, 68, 68, 68, 68, 124, + 125, 126, 129, 68, 68, 68, 68, 108, + 68, 70, 68, 68, 68, 68, 68, 68, + 68, 68, 108, 109, 110, 111, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 120, 121, 122, 68, 68, 68, 68, 68, + 124, 125, 126, 129, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 109, + 110, 111, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 124, 125, 126, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 110, 111, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 124, + 125, 126, 68, 70, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 111, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 124, 125, 126, 68, 70, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 124, 125, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 125, 68, 70, 68, 70, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 109, 110, + 111, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 120, 121, 122, 68, 68, + 68, 68, 68, 124, 125, 126, 129, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 109, 110, 111, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 121, 122, 68, 68, 68, 68, 68, 124, + 125, 126, 129, 68, 70, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 109, 110, + 111, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 122, 68, 68, + 68, 68, 68, 124, 125, 126, 129, 68, + 130, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 94, 96, 94, 70, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 109, 110, 111, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 68, 124, 125, + 126, 129, 68, 70, 68, 68, 68, 68, + 68, 68, 68, 107, 108, 109, 110, 111, + 68, 68, 68, 68, 68, 68, 117, 118, + 119, 68, 120, 121, 122, 68, 68, 68, + 68, 68, 124, 125, 126, 129, 68, 68, + 68, 68, 108, 68, 70, 68, 68, 68, + 68, 68, 68, 68, 68, 108, 109, 110, + 111, 68, 68, 68, 68, 68, 68, 117, + 118, 119, 68, 120, 121, 122, 68, 68, + 68, 68, 68, 124, 125, 126, 129, 68, + 68, 68, 68, 108, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 68, 108, 109, + 110, 111, 68, 68, 68, 68, 68, 68, + 68, 118, 119, 68, 120, 121, 122, 68, + 68, 68, 68, 68, 124, 125, 126, 129, + 68, 68, 68, 68, 108, 68, 70, 68, + 68, 68, 68, 68, 68, 68, 68, 108, + 109, 110, 111, 68, 68, 68, 68, 68, + 68, 68, 68, 119, 68, 120, 121, 122, + 68, 68, 68, 68, 68, 124, 125, 126, + 129, 68, 68, 68, 68, 108, 68, 131, + 68, 70, 68, 68, 68, 68, 68, 68, + 68, 107, 108, 109, 110, 111, 68, 113, + 114, 68, 68, 68, 117, 118, 119, 68, + 120, 121, 122, 68, 68, 68, 68, 68, + 124, 125, 126, 129, 68, 68, 68, 68, + 108, 68, 70, 68, 68, 68, 68, 68, + 68, 68, 68, 108, 109, 110, 111, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 120, 121, 122, 68, 68, 68, 68, + 68, 124, 125, 126, 129, 68, 68, 68, + 68, 108, 68, 131, 68, 70, 68, 68, + 68, 68, 68, 68, 68, 107, 108, 109, + 110, 111, 68, 68, 114, 68, 68, 68, + 117, 118, 119, 68, 120, 121, 122, 68, + 68, 68, 68, 68, 124, 125, 126, 129, + 68, 68, 68, 68, 108, 68, 131, 68, + 70, 68, 68, 68, 68, 68, 68, 68, + 107, 108, 109, 110, 111, 68, 68, 68, + 68, 68, 68, 117, 118, 119, 68, 120, + 121, 122, 68, 68, 68, 68, 68, 124, + 125, 126, 129, 68, 68, 68, 68, 108, + 68, 131, 68, 70, 68, 68, 68, 68, + 68, 68, 68, 107, 108, 109, 110, 111, + 112, 113, 114, 68, 68, 68, 117, 118, + 119, 68, 120, 121, 122, 68, 68, 68, + 68, 68, 124, 125, 126, 129, 68, 68, + 68, 68, 108, 68, 105, 106, 68, 70, + 68, 68, 68, 68, 68, 68, 68, 107, + 108, 109, 110, 111, 112, 113, 114, 115, + 68, 116, 117, 118, 119, 68, 120, 121, + 122, 68, 68, 68, 68, 123, 124, 125, + 126, 127, 68, 68, 68, 68, 128, 68, + 105, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 99, 98, 105, + 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 96, 94, 105, 68, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 70, 68, 68, 68, 68, + 68, 68, 68, 68, 108, 109, 110, 111, + 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 120, 121, 122, 68, 68, 68, + 68, 68, 124, 125, 126, 129, 68, 8, + 9, 132, 11, 132, 132, 132, 132, 132, + 132, 132, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 8, 22, 23, 24, 25, + 132, 26, 27, 28, 132, 132, 132, 132, + 32, 33, 34, 35, 32, 132, 132, 132, + 132, 37, 132, 8, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 11, 132, 132, 132, 132, 132, 132, 132, + 132, 14, 15, 16, 17, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 26, + 27, 28, 132, 132, 132, 132, 132, 33, + 34, 35, 133, 132, 132, 132, 132, 14, + 132, 11, 132, 132, 132, 132, 132, 132, + 132, 132, 14, 15, 16, 17, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 26, 27, 28, 132, 132, 132, 132, 132, + 33, 34, 35, 133, 132, 11, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 15, + 16, 17, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 33, 34, 35, 132, + 11, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 16, 17, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 33, + 34, 35, 132, 11, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 17, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 33, 34, 35, 132, 11, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 33, 34, 132, + 11, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 34, 132, 11, 132, 11, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 15, 16, + 17, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 26, 27, 28, 132, 132, + 132, 132, 132, 33, 34, 35, 133, 132, + 11, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 15, 16, 17, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 27, 28, 132, 132, 132, 132, 132, 33, + 34, 35, 133, 132, 11, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 15, 16, + 17, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 28, 132, 132, + 132, 132, 132, 33, 34, 35, 133, 132, + 134, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 11, 132, 11, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 15, 16, 17, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 33, 34, + 35, 133, 132, 11, 132, 132, 132, 132, + 132, 132, 132, 13, 14, 15, 16, 17, + 132, 132, 132, 132, 132, 132, 23, 24, + 25, 132, 26, 27, 28, 132, 132, 132, + 132, 132, 33, 34, 35, 133, 132, 132, + 132, 132, 14, 132, 11, 132, 132, 132, + 132, 132, 132, 132, 132, 14, 15, 16, + 17, 132, 132, 132, 132, 132, 132, 23, + 24, 25, 132, 26, 27, 28, 132, 132, + 132, 132, 132, 33, 34, 35, 133, 132, + 132, 132, 132, 14, 132, 11, 132, 132, + 132, 132, 132, 132, 132, 132, 14, 15, + 16, 17, 132, 132, 132, 132, 132, 132, + 132, 24, 25, 132, 26, 27, 28, 132, + 132, 132, 132, 132, 33, 34, 35, 133, + 132, 132, 132, 132, 14, 132, 11, 132, + 132, 132, 132, 132, 132, 132, 132, 14, + 15, 16, 17, 132, 132, 132, 132, 132, + 132, 132, 132, 25, 132, 26, 27, 28, + 132, 132, 132, 132, 132, 33, 34, 35, + 133, 132, 132, 132, 132, 14, 132, 135, + 132, 11, 132, 132, 132, 132, 132, 132, + 132, 13, 14, 15, 16, 17, 132, 19, + 20, 132, 132, 132, 23, 24, 25, 132, + 26, 27, 28, 132, 132, 132, 132, 132, + 33, 34, 35, 133, 132, 132, 132, 132, + 14, 132, 11, 132, 132, 132, 132, 132, + 132, 132, 132, 14, 15, 16, 17, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 26, 27, 28, 132, 132, 132, 132, + 132, 33, 34, 35, 133, 132, 132, 132, + 132, 14, 132, 135, 132, 11, 132, 132, + 132, 132, 132, 132, 132, 13, 14, 15, + 16, 17, 132, 132, 20, 132, 132, 132, + 23, 24, 25, 132, 26, 27, 28, 132, + 132, 132, 132, 132, 33, 34, 35, 133, + 132, 132, 132, 132, 14, 132, 135, 132, + 11, 132, 132, 132, 132, 132, 132, 132, + 13, 14, 15, 16, 17, 132, 132, 132, + 132, 132, 132, 23, 24, 25, 132, 26, + 27, 28, 132, 132, 132, 132, 132, 33, + 34, 35, 133, 132, 132, 132, 132, 14, + 132, 135, 132, 11, 132, 132, 132, 132, + 132, 132, 132, 13, 14, 15, 16, 17, + 18, 19, 20, 132, 132, 132, 23, 24, + 25, 132, 26, 27, 28, 132, 132, 132, + 132, 132, 33, 34, 35, 133, 132, 132, + 132, 132, 14, 132, 8, 9, 132, 11, + 132, 132, 132, 132, 132, 132, 132, 13, + 14, 15, 16, 17, 18, 19, 20, 21, + 132, 22, 23, 24, 25, 132, 26, 27, + 28, 132, 132, 132, 132, 32, 33, 34, + 35, 32, 132, 132, 132, 132, 37, 132, + 8, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 11, 132, 8, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 11, 132, 132, 132, + 132, 132, 132, 132, 132, 14, 15, 16, + 17, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 26, 27, 28, 132, 132, + 132, 132, 132, 33, 34, 35, 133, 132, + 136, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 11, 132, 10, 11, 132, 4, + 132, 132, 132, 4, 132, 132, 132, 132, + 132, 8, 9, 10, 11, 132, 132, 132, + 132, 132, 132, 132, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 8, 22, 23, + 24, 25, 132, 26, 27, 28, 132, 29, + 30, 132, 32, 33, 34, 35, 32, 132, + 132, 132, 132, 37, 132, 11, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 29, 30, 132, 11, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 132, 132, 132, 132, 30, + 132, 4, 137, 137, 137, 4, 137, 139, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 140, 138, 141, 138, 141, + 142, 138, 139, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 1, 140, 140, + 138, 139, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 140, 138, 141, + 138, 139, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 138, 138, + 138, 138, 138, 138, 138, 140, 138, 141, + 138, 141, 138, 39, 40, 38, 41, 38, + 38, 38, 38, 38, 38, 38, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 39, + 51, 52, 53, 54, 38, 55, 56, 57, + 38, 58, 59, 38, 60, 61, 62, 63, + 60, 1, 38, 2, 38, 64, 38, 0 +}; + +static const char _use_syllable_machine_trans_targs[] = { + 1, 120, 0, 2, 31, 1, 58, 60, + 88, 89, 114, 1, 116, 102, 90, 91, + 92, 93, 106, 108, 109, 110, 111, 103, + 104, 105, 97, 98, 99, 117, 118, 119, + 112, 94, 95, 96, 124, 113, 1, 3, + 4, 1, 17, 5, 6, 7, 8, 21, + 23, 24, 25, 26, 18, 19, 20, 12, + 13, 14, 29, 30, 27, 9, 10, 11, + 28, 15, 16, 22, 1, 32, 1, 45, + 33, 34, 35, 36, 49, 51, 52, 53, + 54, 46, 47, 48, 40, 41, 42, 55, + 37, 38, 39, 56, 57, 43, 1, 44, + 1, 50, 1, 1, 1, 59, 1, 1, + 1, 61, 62, 75, 63, 64, 65, 66, + 79, 81, 82, 83, 84, 76, 77, 78, + 70, 71, 72, 85, 67, 68, 69, 86, + 87, 73, 74, 80, 1, 100, 101, 107, + 115, 1, 1, 1, 121, 122, 123 +}; + +static const char _use_syllable_machine_trans_actions[] = { + 1, 0, 0, 0, 0, 4, 0, 0, + 0, 0, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 7, 0, + 0, 8, 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, 9, 0, 10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 0, + 12, 0, 13, 14, 15, 0, 16, 17, + 18, 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, 19, 0, 0, 0, + 0, 20, 21, 22, 0, 0, 0 +}; + +static const char _use_syllable_machine_to_state_actions[] = { + 0, 2, 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, + 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 +}; + +static const char _use_syllable_machine_from_state_actions[] = { + 0, 3, 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, + 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 +}; + +static const short _use_syllable_machine_eof_trans[] = { + 1, 0, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 95, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 99, + 95, 69, 101, 104, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, + 69, 95, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 99, 95, 69, + 133, 133, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 133, 133, 133, 133, 133, + 133, 133, 133, 133, 133, 133, 133, 138, + 139, 139, 139, 139, 39 +}; + +static const int use_syllable_machine_start = 1; +static const int use_syllable_machine_first_final = 1; +static const int use_syllable_machine_error = -1; + +static const int use_syllable_machine_en_main = 1; + + +#line 58 "hb-ot-shaper-use-machine.rl" + + + +#line 184 "hb-ot-shaper-use-machine.rl" + + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", (*ts).second.first, (*te).second.first, #syllable_type); \ + for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + + +template <typename Iter> +struct machine_index_t : + hb_iter_with_fallback_t<machine_index_t<Iter>, + typename Iter::item_t> +{ + machine_index_t (const Iter& it) : it (it) {} + machine_index_t (const machine_index_t& o) : hb_iter_with_fallback_t<machine_index_t<Iter>, + typename Iter::item_t> (), + it (o.it), is_null (o.is_null) {} + + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + + typename Iter::item_t __item__ () const { return *it; } + typename Iter::item_t __item_at__ (unsigned i) const { return it[i]; } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + + void operator = (unsigned n) + { + assert (n == 0); + is_null = true; + } + explicit operator bool () { return !is_null; } + + void operator = (const machine_index_t& o) + { + is_null = o.is_null; + unsigned index = (*it).first; + unsigned n = (*o.it).first; + if (index < n) it += n - index; else if (index > n) it -= index - n; + } + bool operator == (const machine_index_t& o) const + { return is_null ? o.is_null : !o.is_null && (*it).first == (*o.it).first; } + bool operator != (const machine_index_t& o) const { return !(*this == o); } + + private: + Iter it; + bool is_null = false; +}; +struct +{ + template <typename Iter, + hb_requires (hb_is_iterable (Iter))> + machine_index_t<hb_iter_type<Iter>> + operator () (Iter&& it) const + { return machine_index_t<hb_iter_type<Iter>> (hb_iter (it)); } +} +HB_FUNCOBJ (machine_index); + + + +static bool +not_ccs_default_ignorable (const hb_glyph_info_t &i) +{ return i.use_category() != USE(CGJ); } + +static inline void +find_syllables_use (hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + auto p = + + hb_iter (info, buffer->len) + | hb_enumerate + | hb_filter ([] (const hb_glyph_info_t &i) { return not_ccs_default_ignorable (i); }, + hb_second) + | hb_filter ([&] (const hb_pair_t<unsigned, const hb_glyph_info_t &> p) + { + if (p.second.use_category() == USE(ZWNJ)) + for (unsigned i = p.first + 1; i < buffer->len; ++i) + if (not_ccs_default_ignorable (info[i])) + return !_hb_glyph_info_is_unicode_mark (&info[i]); + return true; + }) + | hb_enumerate + | machine_index + ; + auto pe = p + p.len (); + auto eof = +pe; + auto ts = +p; + auto te = +p; + unsigned int act HB_UNUSED; + int cs; + +#line 924 "hb-ot-shaper-use-machine.hh" + { + cs = use_syllable_machine_start; + ts = 0; + te = 0; + act = 0; + } + +#line 284 "hb-ot-shaper-use-machine.rl" + + + unsigned int syllable_serial = 1; + +#line 937 "hb-ot-shaper-use-machine.hh" + { + int _slen; + int _trans; + const unsigned char *_keys; + const unsigned char *_inds; + if ( p == pe ) + goto _test_eof; +_resume: + switch ( _use_syllable_machine_from_state_actions[cs] ) { + case 3: +#line 1 "NONE" + {ts = p;} + break; +#line 951 "hb-ot-shaper-use-machine.hh" + } + + _keys = _use_syllable_machine_trans_keys + (cs<<1); + _inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs]; + + _slen = _use_syllable_machine_key_spans[cs]; + _trans = _inds[ _slen > 0 && _keys[0] <=( (*p).second.second.use_category()) && + ( (*p).second.second.use_category()) <= _keys[1] ? + ( (*p).second.second.use_category()) - _keys[0] : _slen ]; + +_eof_trans: + cs = _use_syllable_machine_trans_targs[_trans]; + + if ( _use_syllable_machine_trans_actions[_trans] == 0 ) + goto _again; + + switch ( _use_syllable_machine_trans_actions[_trans] ) { + case 6: +#line 1 "NONE" + {te = p+1;} + break; + case 14: +#line 172 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_virama_terminated_cluster); }} + break; + case 12: +#line 173 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_sakot_terminated_cluster); }} + break; + case 10: +#line 174 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_standard_cluster); }} + break; + case 18: +#line 175 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_number_joiner_terminated_cluster); }} + break; + case 16: +#line 176 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_numeral_cluster); }} + break; + case 8: +#line 177 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_symbol_cluster); }} + break; + case 22: +#line 178 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_hieroglyph_cluster); }} + break; + case 5: +#line 179 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 4: +#line 180 "hb-ot-shaper-use-machine.rl" + {te = p+1;{ found_syllable (use_non_cluster); }} + break; + case 13: +#line 172 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_virama_terminated_cluster); }} + break; + case 11: +#line 173 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_sakot_terminated_cluster); }} + break; + case 9: +#line 174 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_standard_cluster); }} + break; + case 17: +#line 175 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_number_joiner_terminated_cluster); }} + break; + case 15: +#line 176 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_numeral_cluster); }} + break; + case 7: +#line 177 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_symbol_cluster); }} + break; + case 21: +#line 178 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_hieroglyph_cluster); }} + break; + case 19: +#line 179 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }} + break; + case 20: +#line 180 "hb-ot-shaper-use-machine.rl" + {te = p;p--;{ found_syllable (use_non_cluster); }} + break; + case 1: +#line 177 "hb-ot-shaper-use-machine.rl" + {{p = ((te))-1;}{ found_syllable (use_symbol_cluster); }} + break; +#line 1049 "hb-ot-shaper-use-machine.hh" + } + +_again: + switch ( _use_syllable_machine_to_state_actions[cs] ) { + case 2: +#line 1 "NONE" + {ts = 0;} + break; +#line 1058 "hb-ot-shaper-use-machine.hh" + } + + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + if ( _use_syllable_machine_eof_trans[cs] > 0 ) { + _trans = _use_syllable_machine_eof_trans[cs] - 1; + goto _eof_trans; + } + } + + } + +#line 289 "hb-ot-shaper-use-machine.rl" + +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_USE_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-use-machine.rl b/gfx/harfbuzz/src/hb-ot-shaper-use-machine.rl new file mode 100644 index 0000000000..374fcad9f8 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-use-machine.rl @@ -0,0 +1,294 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_USE_MACHINE_HH +#define HB_OT_SHAPER_USE_MACHINE_HH + +#include "hb.hh" + +#include "hb-ot-shaper-syllabic.hh" + +/* buffer var allocations */ +#define use_category() ot_shaper_var_u8_category() + +#define USE(Cat) use_syllable_machine_ex_##Cat + +enum use_syllable_type_t { + use_virama_terminated_cluster, + use_sakot_terminated_cluster, + use_standard_cluster, + use_number_joiner_terminated_cluster, + use_numeral_cluster, + use_symbol_cluster, + use_hieroglyph_cluster, + use_broken_cluster, + use_non_cluster, +}; + +%%{ + machine use_syllable_machine; + alphtype unsigned char; + write exports; + write data; +}%% + +%%{ + +# Categories used in the Universal Shaping Engine spec: +# https://docs.microsoft.com/en-us/typography/script-development/use + +export O = 0; # OTHER + +export B = 1; # BASE +export N = 4; # BASE_NUM +export GB = 5; # BASE_OTHER +export CGJ = 6; # CGJ +export SUB = 11; # CONS_SUB +export H = 12; # HALANT + +export HN = 13; # HALANT_NUM +export ZWNJ = 14; # Zero width non-joiner +export WJ = 16; # Word joiner +export R = 18; # REPHA +export CS = 43; # CONS_WITH_STACKER +export IS = 44; # INVISIBLE_STACKER +export Sk = 48; # SAKOT +export G = 49; # HIEROGLYPH +export J = 50; # HIEROGLYPH_JOINER +export SB = 51; # HIEROGLYPH_SEGMENT_BEGIN +export SE = 52; # HIEROGLYPH_SEGMENT_END +export HVM = 53; # HALANT_OR_VOWEL_MODIFIER +export HM = 54; # HIEROGLYPH_MOD +export HR = 55; # HIEROGLYPH_MIRROR + +export FAbv = 24; # CONS_FINAL_ABOVE +export FBlw = 25; # CONS_FINAL_BELOW +export FPst = 26; # CONS_FINAL_POST +export MAbv = 27; # CONS_MED_ABOVE +export MBlw = 28; # CONS_MED_BELOW +export MPst = 29; # CONS_MED_POST +export MPre = 30; # CONS_MED_PRE +export CMAbv = 31; # CONS_MOD_ABOVE +export CMBlw = 32; # CONS_MOD_BELOW +export VAbv = 33; # VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST +export VBlw = 34; # VOWEL_BELOW / VOWEL_BELOW_POST +export VPst = 35; # VOWEL_POST UIPC = Right +export VPre = 22; # VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST +export VMAbv = 37; # VOWEL_MOD_ABOVE +export VMBlw = 38; # VOWEL_MOD_BELOW +export VMPst = 39; # VOWEL_MOD_POST +export VMPre = 23; # VOWEL_MOD_PRE +export SMAbv = 41; # SYM_MOD_ABOVE +export SMBlw = 42; # SYM_MOD_BELOW +export FMAbv = 45; # CONS_FINAL_MOD UIPC = Top +export FMBlw = 46; # CONS_FINAL_MOD UIPC = Bottom +export FMPst = 47; # CONS_FINAL_MOD UIPC = Not_Applicable + + +h = H | HVM | IS | Sk; + +consonant_modifiers = CMAbv* CMBlw* ((h B | SUB) CMAbv* CMBlw*)*; +medial_consonants = MPre? MAbv? MBlw? MPst?; +dependent_vowels = VPre* VAbv* VBlw* VPst* | H; +vowel_modifiers = HVM? VMPre* VMAbv* VMBlw* VMPst*; +final_consonants = FAbv* FBlw* FPst*; +final_modifiers = FMAbv* FMBlw* | FMPst?; + +complex_syllable_start = (R | CS)? (B | GB); +complex_syllable_middle = + consonant_modifiers + medial_consonants + dependent_vowels + vowel_modifiers + (Sk B)* +; +complex_syllable_tail = + complex_syllable_middle + final_consonants + final_modifiers +; +number_joiner_terminated_cluster_tail = (HN N)* HN; +numeral_cluster_tail = (HN N)+; +symbol_cluster_tail = SMAbv+ SMBlw* | SMBlw+; + +virama_terminated_cluster_tail = + consonant_modifiers + IS +; +virama_terminated_cluster = + complex_syllable_start + virama_terminated_cluster_tail +; +sakot_terminated_cluster_tail = + complex_syllable_middle + Sk +; +sakot_terminated_cluster = + complex_syllable_start + sakot_terminated_cluster_tail +; +standard_cluster = + complex_syllable_start + complex_syllable_tail +; +tail = complex_syllable_tail | sakot_terminated_cluster_tail | symbol_cluster_tail | virama_terminated_cluster_tail; +broken_cluster = + R? + (tail | number_joiner_terminated_cluster_tail | numeral_cluster_tail) +; + +number_joiner_terminated_cluster = N number_joiner_terminated_cluster_tail; +numeral_cluster = N numeral_cluster_tail?; +symbol_cluster = (O | GB | SB) tail?; +hieroglyph_cluster = SB* G HR? HM? SE* (J SB* (G HR? HM? SE*)?)*; +other = any; + +main := |* + virama_terminated_cluster ZWNJ? => { found_syllable (use_virama_terminated_cluster); }; + sakot_terminated_cluster ZWNJ? => { found_syllable (use_sakot_terminated_cluster); }; + standard_cluster ZWNJ? => { found_syllable (use_standard_cluster); }; + number_joiner_terminated_cluster ZWNJ? => { found_syllable (use_number_joiner_terminated_cluster); }; + numeral_cluster ZWNJ? => { found_syllable (use_numeral_cluster); }; + symbol_cluster ZWNJ? => { found_syllable (use_symbol_cluster); }; + hieroglyph_cluster ZWNJ? => { found_syllable (use_hieroglyph_cluster); }; + broken_cluster ZWNJ? => { found_syllable (use_broken_cluster); buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_BROKEN_SYLLABLE; }; + other => { found_syllable (use_non_cluster); }; +*|; + + +}%% + +#define found_syllable(syllable_type) \ + HB_STMT_START { \ + if (0) fprintf (stderr, "syllable %u..%u %s\n", (*ts).second.first, (*te).second.first, #syllable_type); \ + for (unsigned i = (*ts).second.first; i < (*te).second.first; ++i) \ + info[i].syllable() = (syllable_serial << 4) | syllable_type; \ + syllable_serial++; \ + if (syllable_serial == 16) syllable_serial = 1; \ + } HB_STMT_END + + +template <typename Iter> +struct machine_index_t : + hb_iter_with_fallback_t<machine_index_t<Iter>, + typename Iter::item_t> +{ + machine_index_t (const Iter& it) : it (it) {} + machine_index_t (const machine_index_t& o) : hb_iter_with_fallback_t<machine_index_t<Iter>, + typename Iter::item_t> (), + it (o.it), is_null (o.is_null) {} + + static constexpr bool is_random_access_iterator = Iter::is_random_access_iterator; + static constexpr bool is_sorted_iterator = Iter::is_sorted_iterator; + + typename Iter::item_t __item__ () const { return *it; } + typename Iter::item_t __item_at__ (unsigned i) const { return it[i]; } + unsigned __len__ () const { return it.len (); } + void __next__ () { ++it; } + void __forward__ (unsigned n) { it += n; } + void __prev__ () { --it; } + void __rewind__ (unsigned n) { it -= n; } + + void operator = (unsigned n) + { + assert (n == 0); + is_null = true; + } + explicit operator bool () { return !is_null; } + + void operator = (const machine_index_t& o) + { + is_null = o.is_null; + unsigned index = (*it).first; + unsigned n = (*o.it).first; + if (index < n) it += n - index; else if (index > n) it -= index - n; + } + bool operator == (const machine_index_t& o) const + { return is_null ? o.is_null : !o.is_null && (*it).first == (*o.it).first; } + bool operator != (const machine_index_t& o) const { return !(*this == o); } + + private: + Iter it; + bool is_null = false; +}; +struct +{ + template <typename Iter, + hb_requires (hb_is_iterable (Iter))> + machine_index_t<hb_iter_type<Iter>> + operator () (Iter&& it) const + { return machine_index_t<hb_iter_type<Iter>> (hb_iter (it)); } +} +HB_FUNCOBJ (machine_index); + + + +static bool +not_ccs_default_ignorable (const hb_glyph_info_t &i) +{ return i.use_category() != USE(CGJ); } + +static inline void +find_syllables_use (hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + auto p = + + hb_iter (info, buffer->len) + | hb_enumerate + | hb_filter ([] (const hb_glyph_info_t &i) { return not_ccs_default_ignorable (i); }, + hb_second) + | hb_filter ([&] (const hb_pair_t<unsigned, const hb_glyph_info_t &> p) + { + if (p.second.use_category() == USE(ZWNJ)) + for (unsigned i = p.first + 1; i < buffer->len; ++i) + if (not_ccs_default_ignorable (info[i])) + return !_hb_glyph_info_is_unicode_mark (&info[i]); + return true; + }) + | hb_enumerate + | machine_index + ; + auto pe = p + p.len (); + auto eof = +pe; + auto ts = +p; + auto te = +p; + unsigned int act HB_UNUSED; + int cs; + %%{ + write init; + getkey (*p).second.second.use_category(); + }%% + + unsigned int syllable_serial = 1; + %%{ + write exec; + }%% +} + +#undef found_syllable + +#endif /* HB_OT_SHAPER_USE_MACHINE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-use-table.hh b/gfx/harfbuzz/src/hb-ot-shaper-use-table.hh new file mode 100644 index 0000000000..d581b65c07 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-use-table.hh @@ -0,0 +1,690 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt + * + * on files with these headers: + * + * # IndicSyllabicCategory-15.1.0.txt + * # Date: 2023-01-05 + * # IndicPositionalCategory-15.1.0.txt + * # Date: 2023-01-05 + * # ArabicShaping-15.1.0.txt + * # Date: 2023-01-05 + * # DerivedCoreProperties-15.1.0.txt + * # Date: 2023-08-07, 15:21:24 GMT + * # Blocks-15.1.0.txt + * # Date: 2023-07-28, 15:47:20 GMT + * # Scripts-15.1.0.txt + * # Date: 2023-07-28, 16:01:07 GMT + * # Override values For Indic_Syllabic_Category + * # Not derivable + * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 + * # Updated for Unicode 10.0 by Andrew Glass 2017-07-25 + * # Updated for Unicode 12.1 by Andrew Glass 2019-05-24 + * # Updated for Unicode 13.0 by Andrew Glass 2020-07-28 + * # Updated for Unicode 14.0 by Andrew Glass 2021-09-25 + * # Updated for Unicode 15.0 by Andrew Glass 2022-09-16 + * # Updated for Unicode 15.1 by Andrew Glass 2023-09-14 + * # Override values For Indic_Positional_Category + * # Not derivable + * # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 + * # Updated for Unicode 10.0 by Andrew Glass 2017-07-25 + * # Ammended for Unicode 10.0 by Andrew Glass 2018-09-21 + * # Updated for L2/19-083 by Andrew Glass 2019-05-06 + * # Updated for Unicode 12.1 by Andrew Glass 2019-05-30 + * # Updated for Unicode 13.0 by Andrew Glass 2020-07-28 + * # Updated for Unicode 14.0 by Andrew Glass 2021-09-28 + * # Updated for Unicode 15.0 by Andrew Glass 2022-09-16 + * # Updated for Unicode 15.1 by Andrew Glass 2023-09-14 + * UnicodeData.txt does not have a header. + */ + +#ifndef HB_OT_SHAPER_USE_TABLE_HH +#define HB_OT_SHAPER_USE_TABLE_HH + +#include "hb.hh" + +#include "hb-ot-shaper-use-machine.hh" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-macros" +#define B USE(B) /* BASE */ +#define CGJ USE(CGJ) /* CGJ */ +#define CS USE(CS) /* CONS_WITH_STACKER */ +#define G USE(G) /* HIEROGLYPH */ +#define GB USE(GB) /* BASE_OTHER */ +#define H USE(H) /* HALANT */ +#define HM USE(HM) /* HIEROGLYPH_MOD */ +#define HN USE(HN) /* HALANT_NUM */ +#define HR USE(HR) /* HIEROGLYPH_MIRROR */ +#define HVM USE(HVM) /* HALANT_OR_VOWEL_MODIFIER */ +#define IS USE(IS) /* INVISIBLE_STACKER */ +#define J USE(J) /* HIEROGLYPH_JOINER */ +#define N USE(N) /* BASE_NUM */ +#define O USE(O) /* OTHER */ +#define R USE(R) /* REPHA */ +#define SB USE(SB) /* HIEROGLYPH_SEGMENT_BEGIN */ +#define SE USE(SE) /* HIEROGLYPH_SEGMENT_END */ +#define SUB USE(SUB) /* CONS_SUB */ +#define Sk USE(Sk) /* SAKOT */ +#define WJ USE(WJ) /* Word_Joiner */ +#define ZWNJ USE(ZWNJ) /* ZWNJ */ +#define CMAbv USE(CMAbv) +#define CMBlw USE(CMBlw) +#define FAbv USE(FAbv) +#define FBlw USE(FBlw) +#define FPst USE(FPst) +#define FMAbv USE(FMAbv) +#define FMBlw USE(FMBlw) +#define FMPst USE(FMPst) +#define MAbv USE(MAbv) +#define MBlw USE(MBlw) +#define MPst USE(MPst) +#define MPre USE(MPre) +#define SMAbv USE(SMAbv) +#define SMBlw USE(SMBlw) +#define VAbv USE(VAbv) +#define VBlw USE(VBlw) +#define VPst USE(VPst) +#define VPre USE(VPre) +#define VMAbv USE(VMAbv) +#define VMBlw USE(VMBlw) +#define VMPst USE(VMPst) +#define VMPre USE(VMPre) +#pragma GCC diagnostic pop + + +#ifndef HB_OPTIMIZE_SIZE + +static const uint8_t +hb_use_u8[3187] = +{ + 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 14, 0, 1, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 4, 2, 2, + 5, 6, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 2, 17, + 18, 19, 20, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 2, 33, 2, 2, 2, + 2, 34, 35, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 2, 2, 2, + 37, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 38, 2, 39, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 40, 41, 42, 43, 44, 45, 2, 46, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 47, 48, 2, + 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 50, 51, 2, 2, 2, + 2, 2, 2, 2, 2, 52, 53, 2, 54, 2, 2, 55, 2, 2, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 2, 66, 67, 2, 68, 69, 70, 71, + 2, 72, 2, 73, 74, 75, 76, 2, 2, 77, 78, 79, 80, 2, 81, 82, + 2, 83, 83, 83, 83, 83, 83, 83, 83, 84, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 85, 86, 2, 2, 2, 2, 2, 2, 2, 87, + 88, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 89, 89, 89, 90, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 91, 92, 2, 2, 2, 2, 2, + 2, 2, 2, 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 94, 2, 2, 95, 2, 2, 2, 96, 2, 2, 2, 2, 2, + 2, 2, 2, 97, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 98, 98, 99, 100, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, + 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, + 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 11, + 11, 11, 11, 0, 0, 0, 9, 12, 0, 2, 2, 2, 2, 13, 14, 0, + 0, 11, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 16, 17, + 18, 19, 20, 21, 22, 16, 23, 24, 25, 12, 26, 27, 20, 2, 2, 2, + 2, 2, 20, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, + 2, 28, 29, 30, 2, 2, 2, 9, 30, 9, 30, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 9, 0, 2, 2, 0, 17, + 18, 19, 20, 31, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 2, + 30, 2, 0, 0, 0, 0, 0, 9, 36, 12, 15, 30, 2, 2, 9, 0, + 30, 9, 2, 30, 9, 2, 0, 37, 18, 19, 31, 0, 27, 38, 27, 39, + 0, 40, 0, 0, 0, 30, 2, 9, 9, 0, 0, 0, 2, 2, 2, 2, + 2, 41, 42, 43, 0, 0, 0, 0, 0, 12, 15, 30, 2, 2, 2, 2, + 30, 2, 30, 2, 2, 2, 2, 2, 2, 9, 2, 30, 2, 2, 0, 17, + 18, 19, 20, 21, 27, 22, 35, 24, 0, 0, 0, 0, 0, 30, 41, 41, + 44, 12, 29, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 0, 17, + 45, 0, 0, 27, 22, 0, 0, 2, 30, 30, 0, 0, 0, 0, 0, 0, + 0, 0, 46, 30, 2, 2, 9, 0, 2, 9, 2, 2, 0, 30, 9, 9, + 2, 0, 30, 9, 0, 2, 9, 0, 2, 2, 2, 2, 2, 2, 0, 0, + 23, 16, 47, 0, 48, 33, 48, 34, 0, 0, 0, 0, 35, 0, 0, 0, + 0, 15, 29, 49, 2, 2, 2, 9, 2, 9, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 17, 22, 16, 23, 47, 22, 38, 22, 39, + 0, 0, 0, 27, 31, 2, 9, 0, 0, 10, 29, 30, 2, 2, 2, 9, + 2, 2, 2, 30, 2, 2, 0, 17, 45, 0, 0, 35, 47, 0, 0, 0, + 9, 50, 51, 0, 0, 0, 0, 0, 0, 11, 29, 2, 2, 2, 2, 9, + 2, 2, 2, 2, 2, 2, 52, 53, 23, 23, 19, 31, 48, 33, 48, 34, + 54, 0, 0, 0, 35, 0, 0, 0, 30, 12, 29, 30, 2, 2, 2, 2, + 2, 2, 2, 2, 9, 0, 2, 2, 2, 2, 30, 2, 2, 2, 2, 30, + 0, 2, 2, 2, 9, 0, 55, 0, 35, 23, 22, 31, 31, 18, 48, 48, + 25, 0, 23, 0, 0, 0, 0, 0, 0, 2, 0, 2, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 2, 56, 56, 57, 0, 0, + 18, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, + 0, 58, 21, 59, 22, 22, 20, 20, 46, 21, 11, 31, 11, 2, 2, 60, + 61, 61, 61, 61, 61, 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 63, 0, 0, 0, 0, 64, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 65, 45, 59, 66, 22, 22, 67, 68, 69, 70, + 71, 2, 2, 2, 2, 2, 1, 0, 5, 2, 2, 2, 23, 20, 2, 2, + 72, 71, 73, 74, 65, 73, 29, 29, 2, 52, 22, 53, 2, 2, 2, 2, + 2, 2, 75, 76, 77, 29, 29, 78, 79, 2, 2, 2, 2, 2, 29, 45, + 0, 2, 59, 80, 0, 0, 0, 0, 30, 2, 59, 47, 0, 0, 0, 0, + 0, 2, 59, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 9, + 2, 9, 59, 0, 0, 0, 0, 0, 0, 2, 2, 81, 45, 22, 59, 20, + 48, 48, 48, 48, 15, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 88, + 0, 9, 0, 0, 30, 0, 89, 81, 90, 2, 2, 2, 2, 9, 0, 0, + 0, 42, 42, 91, 92, 2, 2, 2, 2, 2, 2, 2, 2, 13, 9, 0, + 0, 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 9, 22, 80, 45, 22, 94, 61, 0, 0, 95, 96, 95, 95, 97, 98, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 29, 0, 0, + 0, 2, 2, 2, 2, 2, 9, 0, 0, 2, 2, 2, 52, 99, 45, 0, + 0, 2, 2, 100, 101, 102, 103, 61, 63, 104, 16, 45, 22, 59, 21, 80, + 48, 48, 76, 11, 11, 11, 105, 46, 40, 11, 106, 74, 2, 2, 2, 2, + 2, 2, 2, 107, 22, 20, 20, 22, 48, 48, 22, 108, 2, 2, 2, 9, + 0, 0, 0, 0, 0, 0, 109, 110, 111, 111, 111, 0, 0, 0, 0, 0, + 0, 106, 74, 2, 2, 2, 2, 2, 2, 60, 61, 59, 25, 22, 112, 61, + 2, 2, 2, 2, 107, 22, 23, 45, 45, 102, 14, 0, 0, 0, 0, 0, + 0, 2, 2, 61, 18, 48, 23, 113, 102, 102, 102, 114, 115, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 30, 2, 11, 46, 116, 116, 116, 11, 116, + 116, 15, 116, 116, 116, 26, 0, 40, 0, 0, 0, 117, 51, 11, 5, 0, + 0, 0, 0, 0, 0, 0, 118, 0, 0, 0, 0, 0, 0, 0, 6, 119, + 120, 42, 42, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 120, + 121, 120, 120, 120, 120, 120, 120, 120, 120, 0, 0, 122, 0, 0, 0, 0, + 0, 0, 7, 122, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 123, 123, 0, 0, + 0, 2, 2, 2, 2, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, + 124, 0, 123, 123, 0, 0, 0, 0, 0, 2, 53, 2, 108, 2, 10, 2, + 2, 2, 65, 19, 16, 0, 0, 31, 0, 2, 2, 0, 0, 0, 0, 0, + 0, 29, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 23, 23, 23, 23, + 23, 23, 23, 126, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 2, 0, 0, 0, 0, 0, 52, 2, 2, 2, 22, 22, 127, 116, + 0, 2, 2, 2, 128, 20, 59, 20, 113, 102, 129, 0, 0, 0, 0, 0, + 0, 11, 130, 2, 2, 2, 2, 2, 2, 2, 131, 23, 22, 20, 48, 132, + 133, 134, 0, 0, 0, 0, 0, 0, 0, 2, 2, 52, 30, 2, 2, 2, + 2, 2, 2, 2, 2, 10, 22, 59, 99, 76, 135, 136, 137, 0, 0, 0, + 0, 2, 138, 2, 2, 2, 2, 139, 0, 30, 2, 42, 5, 0, 79, 15, + 2, 53, 22, 140, 52, 53, 2, 2, 105, 10, 9, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 141, 21, 25, 0, 0, 142, 143, 0, 0, 0, + 0, 2, 65, 45, 23, 80, 47, 144, 0, 81, 81, 81, 81, 81, 81, 81, + 81, 0, 0, 0, 0, 0, 0, 0, 6, 120, 120, 120, 120, 121, 0, 0, + 0, 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 2, 30, 2, 2, 2, + 2, 2, 30, 2, 2, 2, 30, 9, 0, 128, 20, 27, 31, 0, 0, 145, + 146, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, 2, 0, 14, 37, 0, + 147, 2, 2, 13, 37, 0, 30, 2, 2, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 30, 2, 2, 9, 2, 2, 11, 41, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 38, 0, 2, 2, 2, 116, 116, 116, 116, + 116, 148, 2, 9, 0, 0, 0, 0, 0, 2, 14, 14, 0, 0, 0, 0, + 0, 9, 2, 2, 9, 2, 2, 2, 2, 30, 2, 9, 0, 30, 2, 0, + 0, 149, 150, 151, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 20, + 20, 20, 22, 22, 134, 0, 0, 0, 0, 0, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 2, 2, 2, 2, 2, 53, 52, 53, 0, 0, 0, 0, + 153, 11, 74, 2, 2, 2, 2, 2, 2, 18, 19, 21, 16, 24, 37, 0, + 0, 0, 31, 0, 0, 0, 0, 0, 0, 11, 49, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 128, 20, 22, 154, 22, 21, 155, 156, 2, 2, 2, 2, + 2, 0, 0, 65, 157, 0, 0, 0, 0, 2, 13, 0, 0, 0, 0, 0, + 0, 2, 65, 25, 20, 20, 20, 22, 22, 108, 158, 0, 0, 56, 159, 31, + 160, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, + 19, 22, 22, 161, 44, 0, 0, 0, 49, 128, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 9, 9, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, + 30, 2, 2, 2, 2, 2, 2, 2, 10, 18, 19, 21, 22, 162, 31, 0, + 0, 11, 11, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 58, 17, + 23, 16, 23, 47, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 0, + 2, 2, 23, 0, 11, 11, 11, 46, 0, 11, 11, 46, 0, 0, 0, 0, + 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 126, 15, 17, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 0, 163, 164, 0, 0, 0, 0, 0, 0, + 0, 18, 19, 20, 20, 66, 99, 25, 160, 11, 165, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, + 166, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, + 0, 23, 19, 20, 20, 21, 16, 82, 166, 38, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 10, 167, 25, 20, 22, 22, 165, 9, 0, 0, + 0, 2, 2, 2, 2, 2, 9, 43, 136, 23, 22, 20, 76, 21, 22, 0, + 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, + 19, 20, 21, 22, 105, 166, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, + 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 168, + 169, 170, 171, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, + 2, 65, 25, 20, 20, 0, 22, 23, 29, 108, 0, 33, 0, 0, 0, 0, + 0, 52, 20, 22, 22, 22, 140, 2, 2, 2, 172, 173, 11, 15, 174, 72, + 175, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, + 2, 2, 2, 158, 158, 158, 176, 176, 176, 176, 176, 176, 15, 177, 0, 30, + 0, 22, 20, 20, 31, 22, 22, 11, 166, 0, 61, 61, 61, 61, 61, 61, + 61, 66, 21, 82, 46, 0, 0, 0, 0, 2, 2, 2, 9, 2, 30, 2, + 2, 52, 22, 22, 31, 0, 38, 22, 27, 11, 159, 178, 174, 0, 0, 0, + 0, 2, 2, 2, 30, 9, 2, 2, 2, 2, 2, 2, 2, 2, 23, 23, + 47, 22, 35, 82, 68, 0, 0, 0, 0, 2, 179, 66, 47, 0, 0, 0, + 0, 11, 180, 2, 2, 2, 2, 2, 2, 2, 2, 23, 22, 20, 31, 0, + 48, 16, 143, 0, 0, 0, 0, 0, 0, 181, 181, 181, 181, 181, 181, 181, + 181, 182, 182, 182, 183, 184, 182, 181, 181, 185, 181, 181, 186, 187, 187, 187, + 187, 187, 187, 187, 0, 0, 0, 0, 0, 11, 11, 11, 46, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 9, 0, 58, 188, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, + 40, 116, 26, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 58, + 37, 0, 6, 120, 120, 120, 121, 0, 0, 11, 11, 11, 49, 2, 2, 2, + 0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 46, 2, 2, 2, 2, 2, 2, 11, 11, 2, 2, 2, 2, 2, 2, 22, + 22, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, + SB, O, SE, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv, + VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, + VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, + VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, + VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, + MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS, + VMPst, B, VAbv, VAbv, B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, + VBlw, B, SUB, SUB, SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv, + VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, + VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv, + FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, + CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, + VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, + VPst, H, B, O,SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv, + CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, + SE, O, H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B, + CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, + FPst, VBlw, B, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, + IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, + IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, + B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, VPst, IS, R, MPst, R, MPst, + CMBlw, B,FMBlw, VBlw,VMAbv, R, MBlw, MBlw, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, + VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, G, G, HM, HM, + HM, O, VBlw, +}; +static const uint16_t +hb_use_u16[808] = +{ + 0, 0, 1, 2, 0, 3, 0, 3, 0, 0, 4, 5, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 12, + 0, 0, 0, 0, 10, 13, 0, 0, 14, 10, 10, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 18, 26, 27, 21, 22, 28, 29, 30, 31, 32, + 33, 34, 22, 35, 36, 0, 18, 37, 38, 21, 22, 39, 24, 40, 18, 41, + 42, 43, 44, 45, 46, 47, 31, 0, 48, 49, 22, 50, 51, 52, 18, 0, + 53, 49, 22, 54, 51, 55, 18, 56, 57, 49, 10, 58, 59, 60, 18, 0, + 61, 62, 10, 63, 64, 65, 31, 66, 67, 68, 10, 69, 70, 10, 71, 72, + 73, 74, 75, 76, 77, 0, 0, 0, 10, 10, 78, 79, 80, 81, 82, 83, + 84, 85, 0, 0, 0, 0, 0, 0, 10, 86, 10, 87, 10, 88, 89, 90, + 10, 10, 10, 91, 92, 93, 2, 0, 94, 0, 10, 10, 10, 10, 10, 95, + 96, 10, 97, 0, 0, 0, 0, 0, 98, 99,100,101, 31, 10,102,103, + 10, 10,104, 10,105,106, 0, 0, 10,107, 10, 10, 10,108,109,110, + 2, 2, 0, 0, 0, 0, 0, 0,111, 10, 10,112,113, 2,114,115, + 116, 10,117, 10, 10, 10,118,119, 10, 10,120,121,122, 0, 0, 0, + 0, 0, 0, 0, 0,123,124,125, 0, 0, 0, 0, 0, 0, 0,126, + 127,128,129, 0, 0, 0,130,131,132, 0, 0, 0, 0, 0, 0,133, + 0, 0, 0, 0,134, 0, 0, 0, 0, 0, 0, 0, 0, 0,135, 0, + 0, 0, 0, 10, 10, 10,136,137, 0, 0,138, 0, 0, 0, 0, 0, + 139, 10,140, 0, 10, 10, 10,141,142, 10, 10,143,144, 2,145,146, + 10, 10,147, 10,148,149, 0, 0,150, 10, 10,151,152, 2,153, 99, + 10, 10,154,155,156, 2, 10,157, 10, 10, 10,158,159, 0,160,161, + 0, 0, 0, 0, 10, 10,162, 2,163, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,164, 0, 0, 0, 0, 0, 0, 0,165, + 0, 0, 0, 0, 0, 0, 0,166,166,167, 34,168, 0, 0, 0, 0, + 169,170, 10,171, 95, 0, 0, 0, 0, 0, 0, 0, 70, 10,172, 0, + 10,173,174, 0, 0, 0, 0, 0, 10, 10,175, 2, 0, 0, 0, 0, + 10, 10,176,173, 0, 0, 0, 0, 0, 0, 0, 10,177,178, 0, 10, + 179, 0, 0,180,181, 0, 0, 0,182, 10, 10,183,184,185,186,187, + 188, 10, 10,189,190, 0, 0, 0,191, 10,192,193,194, 10, 10,195, + 188, 10, 10,196,197,106,198,103, 10, 34,199,200,201, 0, 0, 0, + 202,203, 95, 10, 10,204,205, 2,206, 21, 22,207,208,209,210,211, + 10, 10, 10,212,213,214,215, 0,198, 10, 10,216,217, 2, 0, 0, + 10, 10,218,219,220,221, 0, 0, 10, 10, 10,222,223, 2, 0, 0, + 10, 10,224,225, 2, 0, 0, 0, 10,226,227,104,228, 0, 0, 0, + 10, 10,229,230, 0, 0, 0, 0,231,232, 10,233,234, 2, 0, 0, + 0, 0,235, 10, 10,236,237, 0,238, 10, 10,239,240,241, 10, 10, + 242,243, 0, 0, 0, 0, 0, 0, 22, 10,218,244, 8, 10, 71, 19, + 10,245, 74,246, 0, 0, 0, 0,247, 10, 10,248,249, 2,250, 10, + 251,252, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,253, + 254, 49, 10,255,256, 2, 0, 0,257,257,257,257,257,257,257,257, + 257,257,257,258,259,260, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 10, 10, 10,261, 0, 0, 0, 0, 10, 10, 10, 10,262,263,264,264, + 265,266, 0, 0, 0, 0,267, 0, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10,268, 0, 0, 10, 10, 10, 10, 10, 10,106, 71, + 95,269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,270, + 10, 10, 71,271,272, 0, 0, 0, 0, 10,273, 0, 10, 10,274, 2, + 0, 0, 0, 0, 0, 10,275, 2, 10, 10, 10, 10,276, 2, 0, 0, + 130,130,130,130,130,130,130,130,163,163,163,163,163,163,163,163, + 163,163,163,163,163,163,163,130, +}; + +static inline unsigned +hb_use_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline uint_fast8_t +hb_use_get_category (unsigned u) +{ + return u<921600u?hb_use_u8[2809+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; +} + + +#else + +static const uint8_t +hb_use_u8[3483] = +{ + 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 14, 0, 1, 1, 2, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 1, + 11, 12, 1, 1, 1, 1, 1, 1, 13, 14, 15, 16, 17, 18, 19, 1, + 1, 20, 1, 1, 1, 1, 21, 1, 22, 1, 1, 1, 1, 1, 23, 24, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 25, 26, 27, 28, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, + 30, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 32, 33, 1, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 1, 48, 49, 50, + 51, 52, 52, 52, 52, 53, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 54, 55, 1, 1, 1, + 56, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 57, 58, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 59, 1, 1, + 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 61, 62, 1, 63, 1, 1, 1, 1, 64, 1, 1, 1, 1, 1, + 1, 65, 66, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, + 65, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 9, 0, 0, 0, 0, + 0, 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, 37, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 0, 56, 57, 58, 59, 60, 0, 0, 0, 61, 62, 63, 64, 56, 65, 66, + 67, 68, 56, 56, 69, 70, 71, 0, 0, 72, 73, 74, 75, 56, 76, 77, + 0, 78, 56, 79, 80, 81, 0, 0, 0, 82, 83, 84, 85, 86, 87, 56, + 88, 56, 89, 90, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 0, + 93, 94, 95, 0, 96, 97, 0, 0, 98, 0, 0, 0, 0, 0, 0, 99, + 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 101, 56, 102, 0, 0, 0, + 0, 0, 103, 0, 0, 0, 0, 0, 0, 104, 105, 56, 106, 107, 108, 109, + 110, 56, 111, 112, 0, 113, 114, 115, 116, 56, 117, 118, 119, 56, 120, 121, + 122, 0, 0, 0, 0, 0, 0, 56, 123, 124, 0, 0, 0, 0, 0, 0, + 125, 0, 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 127, 128, 129, 0, + 0, 130, 131, 132, 0, 0, 0, 51, 133, 0, 0, 0, 0, 134, 135, 0, + 0, 56, 136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 137, 0, + 0, 0, 101, 138, 101, 139, 140, 141, 0, 142, 143, 144, 145, 146, 147, 148, + 0, 149, 150, 151, 152, 146, 153, 154, 155, 156, 157, 158, 0, 159, 160, 161, + 162, 163, 164, 165, 166, 0, 0, 0, 0, 56, 167, 168, 169, 170, 171, 172, + 0, 0, 0, 0, 0, 56, 173, 174, 0, 56, 175, 176, 0, 56, 177, 67, + 0, 178, 179, 180, 0, 0, 0, 0, 0, 56, 181, 0, 0, 0, 0, 0, + 0, 182, 183, 184, 0, 0, 185, 186, 187, 188, 189, 190, 56, 191, 0, 0, + 0, 192, 193, 194, 195, 196, 197, 0, 0, 198, 199, 200, 201, 202, 67, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 203, 204, 205, 206, 0, 0, 0, 0, + 0, 207, 207, 207, 207, 207, 207, 207, 207, 207, 208, 209, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 67, 0, 56, 210, 0, 0, 0, 0, 0, + 0, 56, 56, 211, 212, 213, 0, 0, 214, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 215, 0, 56, 56, 56, 216, 217, 0, 0, + 0, 0, 0, 0, 218, 0, 0, 0, 0, 56, 219, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 101, 221, 56, 222, 0, 0, 0, 0, 0, 0, 101, + 223, 56, 56, 224, 0, 0, 0, 0, 0, 225, 225, 225, 225, 225, 225, 225, + 225, 226, 226, 226, 226, 226, 226, 226, 227, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 6, + 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 10, 11, 11, 11, 11, 0, 0, 0, 9, 12, + 0, 2, 2, 2, 2, 13, 14, 0, 0, 11, 15, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 21, 22, 16, 23, 24, + 25, 12, 26, 27, 20, 2, 2, 2, 2, 2, 20, 0, 2, 2, 2, 2, + 2, 0, 2, 2, 2, 2, 2, 2, 2, 28, 29, 30, 2, 2, 2, 9, + 30, 9, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, 2, 2, + 2, 9, 9, 0, 2, 2, 0, 17, 18, 19, 20, 31, 32, 33, 32, 34, + 0, 0, 0, 0, 35, 0, 0, 2, 30, 2, 0, 0, 0, 0, 0, 9, + 36, 12, 15, 30, 2, 2, 9, 0, 30, 9, 2, 30, 9, 2, 0, 37, + 18, 19, 31, 0, 27, 38, 27, 39, 0, 40, 0, 0, 0, 30, 2, 9, + 9, 0, 0, 0, 2, 2, 2, 2, 2, 41, 42, 43, 0, 0, 0, 0, + 0, 12, 15, 30, 2, 2, 2, 2, 30, 2, 30, 2, 2, 2, 2, 2, + 2, 9, 2, 30, 2, 2, 0, 17, 18, 19, 20, 21, 27, 22, 35, 24, + 0, 0, 0, 0, 0, 30, 41, 41, 44, 12, 29, 30, 2, 2, 2, 9, + 30, 9, 2, 30, 2, 2, 0, 17, 45, 0, 0, 27, 22, 0, 0, 2, + 30, 30, 0, 0, 0, 0, 0, 0, 0, 0, 46, 30, 2, 2, 9, 0, + 2, 9, 2, 2, 0, 30, 9, 9, 2, 0, 30, 9, 0, 2, 9, 0, + 2, 2, 2, 2, 2, 2, 0, 0, 23, 16, 47, 0, 48, 33, 48, 34, + 0, 0, 0, 0, 35, 0, 0, 0, 0, 15, 29, 49, 2, 2, 2, 9, + 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 17, + 22, 16, 23, 47, 22, 38, 22, 39, 0, 0, 0, 27, 31, 2, 9, 0, + 0, 10, 29, 30, 2, 2, 2, 9, 2, 2, 2, 30, 2, 2, 0, 17, + 45, 0, 0, 35, 47, 0, 0, 0, 9, 50, 51, 0, 0, 0, 0, 0, + 0, 11, 29, 2, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 52, 53, + 23, 23, 19, 31, 48, 33, 48, 34, 54, 0, 0, 0, 35, 0, 0, 0, + 30, 12, 29, 30, 2, 2, 2, 2, 2, 2, 2, 2, 9, 0, 2, 2, + 2, 2, 30, 2, 2, 2, 2, 30, 0, 2, 2, 2, 9, 0, 55, 0, + 35, 23, 22, 31, 31, 18, 48, 48, 25, 0, 23, 0, 0, 0, 0, 0, + 0, 2, 0, 2, 9, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, + 0, 2, 2, 56, 56, 57, 0, 0, 18, 2, 2, 2, 2, 30, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 9, 0, 58, 21, 59, 22, 22, 20, 20, + 46, 21, 11, 31, 11, 2, 2, 60, 61, 61, 61, 61, 61, 62, 61, 61, + 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 63, + 0, 0, 0, 0, 64, 0, 0, 0, 0, 2, 2, 2, 2, 2, 65, 45, + 59, 66, 22, 22, 67, 68, 69, 70, 71, 2, 2, 2, 2, 2, 1, 0, + 5, 2, 2, 2, 23, 20, 2, 2, 72, 71, 73, 74, 65, 73, 29, 29, + 2, 52, 22, 53, 2, 2, 2, 2, 2, 2, 75, 76, 77, 29, 29, 78, + 79, 2, 2, 2, 2, 2, 29, 45, 0, 2, 59, 80, 0, 0, 0, 0, + 30, 2, 59, 47, 0, 0, 0, 0, 0, 2, 59, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 9, 2, 9, 59, 0, 0, 0, 0, 0, + 0, 2, 2, 81, 45, 22, 59, 20, 48, 48, 48, 48, 15, 82, 83, 84, + 85, 86, 87, 0, 0, 0, 0, 88, 0, 9, 0, 0, 30, 0, 89, 81, + 90, 2, 2, 2, 2, 9, 0, 0, 0, 42, 42, 91, 92, 2, 2, 2, + 2, 2, 2, 2, 2, 13, 9, 0, 0, 93, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 94, 61, 0, + 0, 95, 96, 95, 95, 97, 98, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, + 0, 2, 2, 2, 2, 29, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, + 0, 2, 2, 2, 52, 99, 45, 0, 0, 2, 2, 100, 101, 102, 103, 61, + 63, 104, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 105, 46, + 40, 11, 106, 74, 2, 2, 2, 2, 2, 2, 2, 107, 22, 20, 20, 22, + 48, 48, 22, 108, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 109, 110, + 111, 111, 111, 0, 0, 0, 0, 0, 0, 106, 74, 2, 2, 2, 2, 2, + 2, 60, 61, 59, 25, 22, 112, 61, 2, 2, 2, 2, 107, 22, 23, 45, + 45, 102, 14, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 113, + 102, 102, 102, 114, 115, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, + 2, 11, 46, 116, 116, 116, 11, 116, 116, 15, 116, 116, 116, 26, 0, 40, + 0, 0, 0, 117, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 118, 0, + 0, 0, 0, 0, 0, 0, 6, 119, 120, 42, 42, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 120, 120, 121, 120, 120, 120, 120, 120, 120, 120, + 120, 0, 0, 122, 0, 0, 0, 0, 0, 0, 7, 122, 0, 0, 0, 0, + 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 0, 0, 0, 0, 123, 123, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 30, 0, 0, 0, 0, 0, 0, 0, 124, 0, 123, 123, 0, 0, 0, 0, + 0, 2, 53, 2, 108, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 29, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 125, 23, 23, 23, 23, 23, 23, 23, 126, 0, 0, 0, 0, + 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 0, 0, 0, 0, 0, + 52, 2, 2, 2, 22, 22, 127, 116, 0, 2, 2, 2, 128, 20, 59, 20, + 113, 102, 129, 0, 0, 0, 0, 0, 0, 11, 130, 2, 2, 2, 2, 2, + 2, 2, 131, 23, 22, 20, 48, 132, 133, 134, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 52, 30, 2, 2, 2, 2, 2, 2, 2, 2, 10, 22, 59, + 99, 76, 135, 136, 137, 0, 0, 0, 0, 2, 138, 2, 2, 2, 2, 139, + 0, 30, 2, 42, 5, 0, 79, 15, 2, 53, 22, 140, 52, 53, 2, 2, + 105, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, + 25, 0, 0, 142, 143, 0, 0, 0, 0, 2, 65, 45, 23, 80, 47, 144, + 0, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, + 6, 120, 120, 120, 120, 121, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, + 2, 2, 9, 2, 30, 2, 2, 2, 2, 2, 30, 2, 2, 2, 30, 9, + 0, 128, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, + 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, + 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, + 0, 2, 2, 2, 116, 116, 116, 116, 116, 148, 2, 9, 0, 0, 0, 0, + 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, + 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 134, 0, 0, 0, + 0, 0, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 2, 2, 2, 2, + 2, 53, 52, 53, 0, 0, 0, 0, 153, 11, 74, 2, 2, 2, 2, 2, + 2, 18, 19, 21, 16, 24, 37, 0, 0, 0, 31, 0, 0, 0, 0, 0, + 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 128, 20, 22, 154, + 22, 21, 155, 156, 2, 2, 2, 2, 2, 0, 0, 65, 157, 0, 0, 0, + 0, 2, 13, 0, 0, 0, 0, 0, 0, 2, 65, 25, 20, 20, 20, 22, + 22, 108, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 23, 19, 22, 22, 161, 44, 0, 0, 0, + 49, 128, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, + 30, 2, 2, 2, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, + 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, + 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, + 0, 0, 0, 0, 35, 0, 0, 0, 2, 2, 23, 0, 11, 11, 11, 46, + 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 65, 25, 20, 20, 20, + 22, 23, 126, 15, 17, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, + 163, 164, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 20, 66, 99, 25, + 160, 11, 165, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 65, 25, 20, 20, 0, 48, 48, 11, 166, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 20, 0, 23, 19, 20, 20, 21, 16, 82, + 166, 38, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 10, 167, + 25, 20, 22, 22, 165, 9, 0, 0, 0, 2, 2, 2, 2, 2, 9, 43, + 136, 23, 22, 20, 76, 21, 22, 0, 0, 2, 2, 2, 9, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 2, 18, 19, 20, 21, 22, 105, 166, 37, 0, + 0, 2, 2, 2, 9, 30, 0, 2, 2, 2, 2, 30, 9, 2, 2, 2, + 2, 23, 23, 18, 32, 33, 12, 168, 169, 170, 171, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 0, 2, 2, 2, 65, 25, 20, 20, 0, 22, 23, + 29, 108, 0, 33, 0, 0, 0, 0, 0, 52, 20, 22, 22, 22, 140, 2, + 2, 2, 172, 173, 11, 15, 174, 72, 175, 0, 0, 1, 147, 0, 0, 0, + 0, 52, 20, 22, 16, 19, 20, 2, 2, 2, 2, 158, 158, 158, 176, 176, + 176, 176, 176, 176, 15, 177, 0, 30, 0, 22, 20, 20, 31, 22, 22, 11, + 166, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, + 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, + 27, 11, 159, 178, 174, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, + 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, + 0, 2, 179, 66, 47, 0, 0, 0, 0, 11, 180, 2, 2, 2, 2, 2, + 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, + 0, 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, 182, 183, 184, 182, 181, + 181, 185, 181, 181, 186, 187, 187, 187, 187, 187, 187, 187, 0, 0, 0, 0, + 0, 11, 11, 11, 46, 0, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, + 58, 188, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 0, 0, 0, 40, 116, 26, 0, 0, 0, 0, 0, + 0, 0, 0, 9, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 120, 120, 120, 121, 0, + 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, + 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 44, 44, 44, 92, 0, + 0, O, O, O, GB, B, B, O, SB, O, SE, GB, O, O, WJ,FMPst, + FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv, + VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, + VPst, VPst, H, VPre, VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, + O, O, VPre, VPre, O, VPre, H, O, VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, + H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, + O, VPre, VPre,VMAbv, B, O, CS, CS,VMPst, B, VAbv, VAbv, B, R, O, HVM, + O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB, SUB, O, SUB, SUB, + O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, + B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B, + VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS, + FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, + FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, + SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,SMAbv,SMBlw,SMAbv,SMAbv, + SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, + CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, H, MPst, VPst, H,VMAbv, VAbv, + VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, + MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O,VMPst, IS, + O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, + B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R,CMBlw, + VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, + VAbv,VMAbv, VPst, IS, R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R, MBlw, MBlw, + GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, VAbv, R,VMPst, G, G, J, J, J, + SB, SE, J, HR, G, G, HM, HM, HM, O, VBlw, +}; +static const uint16_t +hb_use_u16[456] = +{ + 0, 0, 1, 2, 0, 3, 4, 5, 0, 6, 7, 0, 8, 0, 9, 10, + 11, 12, 10, 13, 14, 10, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 18, 26, 27, 21, 22, 28, 29, 30, 31, 32, 33, 34, 22, 35, + 36, 0, 18, 37, 38, 21, 22, 39, 24, 40, 18, 41, 42, 43, 44, 45, + 46, 47, 31, 0, 48, 49, 22, 50, 51, 52, 18, 0, 53, 49, 22, 54, + 51, 55, 18, 56, 57, 49, 10, 58, 59, 60, 61, 62, 10, 63, 64, 65, + 31, 66, 67, 68, 10, 69, 70, 10, 71, 72, 73, 74, 75, 76, 77, 0, + 10, 10, 78, 79, 80, 81, 82, 83, 84, 85, 10, 86, 10, 87, 10, 88, + 89, 90, 10, 91, 92, 93, 2, 0, 94, 0, 10, 95, 96, 10, 97, 0, + 98, 99,100,101, 31, 10,102,103,104, 10,105,106, 10,107, 10,108, + 109,110, 2, 2,111, 10, 10,112,113, 2,114,115,116, 10,117, 10, + 118,119,120,121,122, 0, 0,123,124,125, 0,126,127,128,129, 0, + 130,131,132, 0, 0,133,134, 0,135, 0, 0, 10,136,137,138, 0, + 139, 10,140, 0, 10,141,142, 10, 10,143,144, 2,145,146,147, 10, + 148,149,150, 10, 10,151,152, 2,153, 99,154,155,156, 2, 10,157, + 10,158,159, 0,160,161,162, 2,163, 0, 0,164, 0,165, 0,166, + 166,167, 34,168,169,170, 10,171, 95, 0,172, 0, 10,173,174, 0, + 175, 2,176,173,177,178,179, 0, 0,180,181, 0,182, 10, 10,183, + 184,185,186,187,188, 10, 10,189,190, 0,191, 10,192,193,194, 10, + 10,195, 10,196,197,106,198,103, 10, 34,199,200,201, 0,202,203, + 95, 10, 10,204,205, 2,206, 21, 22,207,208,209,210,211, 10,212, + 213,214,215, 0,198, 10, 10,216,217, 2,218,219,220,221, 10,222, + 223, 2,224,225, 10,226,227,104,228, 0,229,230,231,232, 10,233, + 234, 2,235, 10, 10,236,237, 0,238, 10, 10,239,240,241,242,243, + 22, 10,218,244, 8, 10, 71, 19, 10,245, 74,246,247, 10, 10,248, + 249, 2,250, 10,251,252, 10,253,254, 49, 10,255,256, 2,257,257, + 257,258,259,260, 10,261,262,263,264,264,265,266,267, 0, 10,268, + 106, 71, 95,269, 0,270, 71,271,272, 0,273, 0,274, 2,275, 2, + 276, 2,130,130,163,163,163,130, +}; + +static inline unsigned +hb_use_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline uint_fast8_t +hb_use_get_category (unsigned u) +{ + return u<921600u?hb_use_u8[3105+(((hb_use_u8[889+(((hb_use_u16[((hb_use_u8[353+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O; +} + +#endif + +#undef B +#undef CGJ +#undef CS +#undef G +#undef GB +#undef H +#undef HM +#undef HN +#undef HR +#undef HVM +#undef IS +#undef J +#undef N +#undef O +#undef R +#undef SB +#undef SE +#undef SUB +#undef Sk +#undef WJ +#undef ZWNJ +#undef CMAbv +#undef CMBlw +#undef FAbv +#undef FBlw +#undef FPst +#undef FMAbv +#undef FMBlw +#undef FMPst +#undef MAbv +#undef MBlw +#undef MPst +#undef MPre +#undef SMAbv +#undef SMBlw +#undef VAbv +#undef VBlw +#undef VPst +#undef VPre +#undef VMAbv +#undef VMBlw +#undef VMPst +#undef VMPre + + +#endif /* HB_OT_SHAPER_USE_TABLE_HH */ +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-use.cc b/gfx/harfbuzz/src/hb-ot-shaper-use.cc new file mode 100644 index 0000000000..c35765af95 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-use.cc @@ -0,0 +1,514 @@ +/* + * Copyright © 2015 Mozilla Foundation. + * 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. + * + * Mozilla Author(s): Jonathan Kew + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-use-machine.hh" +#include "hb-ot-shaper-use-table.hh" +#include "hb-ot-shaper-arabic.hh" +#include "hb-ot-shaper-arabic-joining-list.hh" +#include "hb-ot-shaper-vowel-constraints.hh" + + +/* + * Universal Shaping Engine. + * https://docs.microsoft.com/en-us/typography/script-development/use + */ + +static const hb_tag_t +use_basic_features[] = +{ + /* + * Basic features. + * These features are applied all at once, before reordering, constrained + * to the syllable. + */ + HB_TAG('r','k','r','f'), + HB_TAG('a','b','v','f'), + HB_TAG('b','l','w','f'), + HB_TAG('h','a','l','f'), + HB_TAG('p','s','t','f'), + HB_TAG('v','a','t','u'), + HB_TAG('c','j','c','t'), +}; +static const hb_tag_t +use_topographical_features[] = +{ + HB_TAG('i','s','o','l'), + HB_TAG('i','n','i','t'), + HB_TAG('m','e','d','i'), + HB_TAG('f','i','n','a'), +}; +/* Same order as use_topographical_features. */ +enum joining_form_t { + JOINING_FORM_ISOL, + JOINING_FORM_INIT, + JOINING_FORM_MEDI, + JOINING_FORM_FINA, + _JOINING_FORM_NONE +}; +static const hb_tag_t +use_other_features[] = +{ + /* + * Other features. + * These features are applied all at once, after reordering and + * clearing syllables. + */ + HB_TAG('a','b','v','s'), + HB_TAG('b','l','w','s'), + HB_TAG('h','a','l','n'), + HB_TAG('p','r','e','s'), + HB_TAG('p','s','t','s'), +}; + +static bool +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static bool +record_rphf_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static bool +record_pref_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); +static bool +reorder_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer); + +static void +collect_features_use (hb_ot_shape_planner_t *plan) +{ + hb_ot_map_builder_t *map = &plan->map; + + /* Do this before any lookups have been applied. */ + map->add_gsub_pause (setup_syllables_use); + + /* "Default glyph pre-processing group" */ + map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('n','u','k','t'), F_PER_SYLLABLE); + map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ | F_PER_SYLLABLE); + + /* "Reordering group" */ + map->add_gsub_pause (_hb_clear_substitution_flags); + map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ | F_PER_SYLLABLE); + map->add_gsub_pause (record_rphf_use); + map->add_gsub_pause (_hb_clear_substitution_flags); + map->enable_feature (HB_TAG('p','r','e','f'), F_MANUAL_ZWJ | F_PER_SYLLABLE); + map->add_gsub_pause (record_pref_use); + + /* "Orthographic unit shaping group" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (use_basic_features); i++) + map->enable_feature (use_basic_features[i], F_MANUAL_ZWJ | F_PER_SYLLABLE); + + map->add_gsub_pause (reorder_use); + map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var + + /* "Topographical features" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (use_topographical_features); i++) + map->add_feature (use_topographical_features[i]); + map->add_gsub_pause (nullptr); + + /* "Standard typographic presentation" */ + for (unsigned int i = 0; i < ARRAY_LENGTH (use_other_features); i++) + map->enable_feature (use_other_features[i], F_MANUAL_ZWJ); +} + +struct use_shape_plan_t +{ + hb_mask_t rphf_mask; + + arabic_shape_plan_t *arabic_plan; +}; + +static void * +data_create_use (const hb_ot_shape_plan_t *plan) +{ + use_shape_plan_t *use_plan = (use_shape_plan_t *) hb_calloc (1, sizeof (use_shape_plan_t)); + if (unlikely (!use_plan)) + return nullptr; + + use_plan->rphf_mask = plan->map.get_1_mask (HB_TAG('r','p','h','f')); + + if (has_arabic_joining (plan->props.script)) + { + use_plan->arabic_plan = (arabic_shape_plan_t *) data_create_arabic (plan); + if (unlikely (!use_plan->arabic_plan)) + { + hb_free (use_plan); + return nullptr; + } + } + + return use_plan; +} + +static void +data_destroy_use (void *data) +{ + use_shape_plan_t *use_plan = (use_shape_plan_t *) data; + + if (use_plan->arabic_plan) + data_destroy_arabic (use_plan->arabic_plan); + + hb_free (data); +} + +static void +setup_masks_use (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + /* Do this before allocating use_category(). */ + if (use_plan->arabic_plan) + { + setup_masks_arabic_plan (use_plan->arabic_plan, buffer, plan->props.script); + } + + HB_BUFFER_ALLOCATE_VAR (buffer, use_category); + + /* We cannot setup masks here. We save information about characters + * and setup masks later on in a pause-callback. */ + + unsigned int count = buffer->len; + hb_glyph_info_t *info = buffer->info; + for (unsigned int i = 0; i < count; i++) + info[i].use_category() = hb_use_get_category (info[i].codepoint); +} + +static void +setup_rphf_mask (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + hb_mask_t mask = use_plan->rphf_mask; + if (!mask) return; + + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + unsigned int limit = info[start].use_category() == USE(R) ? 1 : hb_min (3u, end - start); + for (unsigned int i = start; i < start + limit; i++) + info[i].mask |= mask; + } +} + +static void +setup_topographical_masks (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + if (use_plan->arabic_plan) + return; + + static_assert ((JOINING_FORM_INIT < 4 && JOINING_FORM_ISOL < 4 && JOINING_FORM_MEDI < 4 && JOINING_FORM_FINA < 4), ""); + hb_mask_t masks[4], all_masks = 0; + for (unsigned int i = 0; i < 4; i++) + { + masks[i] = plan->map.get_1_mask (use_topographical_features[i]); + if (masks[i] == plan->map.get_global_mask ()) + masks[i] = 0; + all_masks |= masks[i]; + } + if (!all_masks) + return; + hb_mask_t other_masks = ~all_masks; + + unsigned int last_start = 0; + joining_form_t last_form = _JOINING_FORM_NONE; + hb_glyph_info_t *info = buffer->info; + foreach_syllable (buffer, start, end) + { + use_syllable_type_t syllable_type = (use_syllable_type_t) (info[start].syllable() & 0x0F); + switch (syllable_type) + { + case use_hieroglyph_cluster: + case use_non_cluster: + /* These don't join. Nothing to do. */ + last_form = _JOINING_FORM_NONE; + break; + + case use_virama_terminated_cluster: + case use_sakot_terminated_cluster: + case use_standard_cluster: + case use_number_joiner_terminated_cluster: + case use_numeral_cluster: + case use_symbol_cluster: + case use_broken_cluster: + + bool join = last_form == JOINING_FORM_FINA || last_form == JOINING_FORM_ISOL; + + if (join) + { + /* Fixup previous syllable's form. */ + last_form = last_form == JOINING_FORM_FINA ? JOINING_FORM_MEDI : JOINING_FORM_INIT; + for (unsigned int i = last_start; i < start; i++) + info[i].mask = (info[i].mask & other_masks) | masks[last_form]; + } + + /* Form for this syllable. */ + last_form = join ? JOINING_FORM_FINA : JOINING_FORM_ISOL; + for (unsigned int i = start; i < end; i++) + info[i].mask = (info[i].mask & other_masks) | masks[last_form]; + + break; + } + + last_start = start; + } +} + +static bool +setup_syllables_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + HB_BUFFER_ALLOCATE_VAR (buffer, syllable); + find_syllables_use (buffer); + foreach_syllable (buffer, start, end) + buffer->unsafe_to_break (start, end); + setup_rphf_mask (plan, buffer); + setup_topographical_masks (plan, buffer); + return false; +} + +static bool +record_rphf_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + const use_shape_plan_t *use_plan = (const use_shape_plan_t *) plan->data; + + hb_mask_t mask = use_plan->rphf_mask; + if (!mask) return false; + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + /* Mark a substituted repha as USE(R). */ + for (unsigned int i = start; i < end && (info[i].mask & mask); i++) + if (_hb_glyph_info_substituted (&info[i])) + { + info[i].use_category() = USE(R); + break; + } + } + return false; +} + +static bool +record_pref_use (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_font_t *font HB_UNUSED, + hb_buffer_t *buffer) +{ + hb_glyph_info_t *info = buffer->info; + + foreach_syllable (buffer, start, end) + { + /* Mark a substituted pref as VPre, as they behave the same way. */ + for (unsigned int i = start; i < end; i++) + if (_hb_glyph_info_substituted (&info[i])) + { + info[i].use_category() = USE(VPre); + break; + } + } + return false; +} + +static inline bool +is_halant_use (const hb_glyph_info_t &info) +{ + return (info.use_category() == USE(H) || info.use_category() == USE(HVM) || info.use_category() == USE(IS)) && + !_hb_glyph_info_ligated (&info); +} + +static void +reorder_syllable_use (hb_buffer_t *buffer, unsigned int start, unsigned int end) +{ + use_syllable_type_t syllable_type = (use_syllable_type_t) (buffer->info[start].syllable() & 0x0F); + /* Only a few syllable types need reordering. */ + if (unlikely (!(FLAG_UNSAFE (syllable_type) & + (FLAG (use_virama_terminated_cluster) | + FLAG (use_sakot_terminated_cluster) | + FLAG (use_standard_cluster) | + FLAG (use_symbol_cluster) | + FLAG (use_broken_cluster) | + 0)))) + return; + + hb_glyph_info_t *info = buffer->info; + +#define POST_BASE_FLAGS64 (FLAG64 (USE(FAbv)) | \ + FLAG64 (USE(FBlw)) | \ + FLAG64 (USE(FPst)) | \ + FLAG64 (USE(FMAbv)) | \ + FLAG64 (USE(FMBlw)) | \ + FLAG64 (USE(FMPst)) | \ + FLAG64 (USE(MAbv)) | \ + FLAG64 (USE(MBlw)) | \ + FLAG64 (USE(MPst)) | \ + FLAG64 (USE(MPre)) | \ + FLAG64 (USE(VAbv)) | \ + FLAG64 (USE(VBlw)) | \ + FLAG64 (USE(VPst)) | \ + FLAG64 (USE(VPre)) | \ + FLAG64 (USE(VMAbv)) | \ + FLAG64 (USE(VMBlw)) | \ + FLAG64 (USE(VMPst)) | \ + FLAG64 (USE(VMPre))) + + /* Move things forward. */ + if (info[start].use_category() == USE(R) && end - start > 1) + { + /* Got a repha. Reorder it towards the end, but before the first post-base + * glyph. */ + for (unsigned int i = start + 1; i < end; i++) + { + bool is_post_base_glyph = (FLAG64_UNSAFE (info[i].use_category()) & POST_BASE_FLAGS64) || + is_halant_use (info[i]); + if (is_post_base_glyph || i == end - 1) + { + /* If we hit a post-base glyph, move before it; otherwise move to the + * end. Shift things in between backward. */ + + if (is_post_base_glyph) + i--; + + buffer->merge_clusters (start, i + 1); + hb_glyph_info_t t = info[start]; + memmove (&info[start], &info[start + 1], (i - start) * sizeof (info[0])); + info[i] = t; + + break; + } + } + } + + /* Move things back. */ + unsigned int j = start; + for (unsigned int i = start; i < end; i++) + { + uint32_t flag = FLAG_UNSAFE (info[i].use_category()); + if (is_halant_use (info[i])) + { + /* If we hit a halant, move after it; otherwise move to the beginning, and + * shift things in between forward. */ + j = i + 1; + } + else if (((flag) & (FLAG (USE(VPre)) | FLAG (USE(VMPre)))) && + /* Only move the first component of a MultipleSubst. */ + 0 == _hb_glyph_info_get_lig_comp (&info[i]) && + j < i) + { + buffer->merge_clusters (j, i + 1); + hb_glyph_info_t t = info[i]; + memmove (&info[j + 1], &info[j], (i - j) * sizeof (info[0])); + info[j] = t; + } + } +} + +static bool +reorder_use (const hb_ot_shape_plan_t *plan, + hb_font_t *font, + hb_buffer_t *buffer) +{ + bool ret = false; + if (buffer->message (font, "start reordering USE")) + { + if (hb_syllabic_insert_dotted_circles (font, buffer, + use_broken_cluster, + USE(B), + USE(R))) + ret = true; + + foreach_syllable (buffer, start, end) + reorder_syllable_use (buffer, start, end); + + (void) buffer->message (font, "end reordering USE"); + } + + HB_BUFFER_DEALLOCATE_VAR (buffer, use_category); + + return ret; +} + + +static void +preprocess_text_use (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font) +{ + _hb_preprocess_text_vowel_constraints (plan, buffer, font); +} + +static bool +compose_use (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + /* Avoid recomposing split matras. */ + if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (c->unicode->general_category (a))) + return false; + + return (bool)c->unicode->compose (a, b, ab); +} + + +const hb_ot_shaper_t _hb_ot_shaper_use = +{ + collect_features_use, + nullptr, /* override_features */ + data_create_use, + data_destroy_use, + preprocess_text_use, + nullptr, /* postprocess_glyphs */ + nullptr, /* decompose */ + compose_use, + setup_masks_use, + nullptr, /* reorder_marks */ + HB_TAG_NONE, /* gpos_tag */ + HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + false, /* fallback_position */ +}; + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc b/gfx/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc new file mode 100644 index 0000000000..d1ed894596 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc @@ -0,0 +1,477 @@ +/* == Start of generated functions == */ +/* + * The following functions are generated by running: + * + * ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt + * + * on files with these headers: + * + * # IndicShapingInvalidCluster.txt + * # Date: 2015-03-12, 21:17:00 GMT [AG] + * # Date: 2019-11-08, 23:22:00 GMT [AG] + * + * # Scripts-15.1.0.txt + * # Date: 2023-07-28, 16:01:07 GMT + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_SHAPE + +#include "hb-ot-shaper-vowel-constraints.hh" + +static void +_output_dotted_circle (hb_buffer_t *buffer) +{ + (void) buffer->output_glyph (0x25CCu); + _hb_glyph_info_reset_continuation (&buffer->prev()); +} + +static void +_output_with_dotted_circle (hb_buffer_t *buffer) +{ + _output_dotted_circle (buffer); + (void) buffer->next_glyph (); +} + +void +_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, + hb_buffer_t *buffer, + hb_font_t *font HB_UNUSED) +{ +#ifdef HB_NO_OT_SHAPER_VOWEL_CONSTRAINTS + return; +#endif + if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) + return; + + /* UGLY UGLY UGLY business of adding dotted-circle in the middle of + * vowel-sequences that look like another vowel. Data for each script + * collected from the USE script development spec. + * + * https://github.com/harfbuzz/harfbuzz/issues/1019 + */ + buffer->clear_output (); + unsigned int count = buffer->len; + switch ((unsigned) buffer->props.script) + { + case HB_SCRIPT_DEVANAGARI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0905u: + switch (buffer->cur (1).codepoint) + { + case 0x093Au: case 0x093Bu: case 0x093Eu: case 0x0945u: + case 0x0946u: case 0x0949u: case 0x094Au: case 0x094Bu: + case 0x094Cu: case 0x094Fu: case 0x0956u: case 0x0957u: + matched = true; + break; + } + break; + case 0x0906u: + switch (buffer->cur (1).codepoint) + { + case 0x093Au: case 0x0945u: case 0x0946u: case 0x0947u: + case 0x0948u: + matched = true; + break; + } + break; + case 0x0909u: + matched = 0x0941u == buffer->cur (1).codepoint; + break; + case 0x090Fu: + switch (buffer->cur (1).codepoint) + { + case 0x0945u: case 0x0946u: case 0x0947u: + matched = true; + break; + } + break; + case 0x0930u: + if (0x094Du == buffer->cur (1).codepoint && + buffer->idx + 2 < count && + 0x0907u == buffer->cur (2).codepoint) + { + (void) buffer->next_glyph (); + matched = true; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_BENGALI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0985u: + matched = 0x09BEu == buffer->cur (1).codepoint; + break; + case 0x098Bu: + matched = 0x09C3u == buffer->cur (1).codepoint; + break; + case 0x098Cu: + matched = 0x09E2u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_GURMUKHI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0A05u: + switch (buffer->cur (1).codepoint) + { + case 0x0A3Eu: case 0x0A48u: case 0x0A4Cu: + matched = true; + break; + } + break; + case 0x0A72u: + switch (buffer->cur (1).codepoint) + { + case 0x0A3Fu: case 0x0A40u: case 0x0A47u: + matched = true; + break; + } + break; + case 0x0A73u: + switch (buffer->cur (1).codepoint) + { + case 0x0A41u: case 0x0A42u: case 0x0A4Bu: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_GUJARATI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0A85u: + switch (buffer->cur (1).codepoint) + { + case 0x0ABEu: case 0x0AC5u: case 0x0AC7u: case 0x0AC8u: + case 0x0AC9u: case 0x0ACBu: case 0x0ACCu: + matched = true; + break; + } + break; + case 0x0AC5u: + matched = 0x0ABEu == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_ORIYA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0B05u: + matched = 0x0B3Eu == buffer->cur (1).codepoint; + break; + case 0x0B0Fu: case 0x0B13u: + matched = 0x0B57u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TAMIL: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + if (0x0B85u == buffer->cur ().codepoint && + 0x0BC2u == buffer->cur (1).codepoint) + { + matched = true; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TELUGU: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0C12u: + switch (buffer->cur (1).codepoint) + { + case 0x0C4Cu: case 0x0C55u: + matched = true; + break; + } + break; + case 0x0C3Fu: case 0x0C46u: case 0x0C4Au: + matched = 0x0C55u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_KANNADA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0C89u: case 0x0C8Bu: + matched = 0x0CBEu == buffer->cur (1).codepoint; + break; + case 0x0C92u: + matched = 0x0CCCu == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_MALAYALAM: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0D07u: case 0x0D09u: + matched = 0x0D57u == buffer->cur (1).codepoint; + break; + case 0x0D0Eu: + matched = 0x0D46u == buffer->cur (1).codepoint; + break; + case 0x0D12u: + switch (buffer->cur (1).codepoint) + { + case 0x0D3Eu: case 0x0D57u: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_SINHALA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x0D85u: + switch (buffer->cur (1).codepoint) + { + case 0x0DCFu: case 0x0DD0u: case 0x0DD1u: + matched = true; + break; + } + break; + case 0x0D8Bu: case 0x0D8Fu: case 0x0D94u: + matched = 0x0DDFu == buffer->cur (1).codepoint; + break; + case 0x0D8Du: + matched = 0x0DD8u == buffer->cur (1).codepoint; + break; + case 0x0D91u: + switch (buffer->cur (1).codepoint) + { + case 0x0DCAu: case 0x0DD9u: case 0x0DDAu: case 0x0DDCu: + case 0x0DDDu: case 0x0DDEu: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_BRAHMI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11005u: + matched = 0x11038u == buffer->cur (1).codepoint; + break; + case 0x1100Bu: + matched = 0x1103Eu == buffer->cur (1).codepoint; + break; + case 0x1100Fu: + matched = 0x11042u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_KHOJKI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11200u: + switch (buffer->cur (1).codepoint) + { + case 0x1122Cu: case 0x11231u: case 0x11233u: + matched = true; + break; + } + break; + case 0x11206u: + matched = 0x1122Cu == buffer->cur (1).codepoint; + break; + case 0x1122Cu: + switch (buffer->cur (1).codepoint) + { + case 0x11230u: case 0x11231u: + matched = true; + break; + } + break; + case 0x11240u: + matched = 0x1122Eu == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_KHUDAWADI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x112B0u: + switch (buffer->cur (1).codepoint) + { + case 0x112E0u: case 0x112E5u: case 0x112E6u: case 0x112E7u: + case 0x112E8u: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TIRHUTA: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11481u: + matched = 0x114B0u == buffer->cur (1).codepoint; + break; + case 0x1148Bu: case 0x1148Du: + matched = 0x114BAu == buffer->cur (1).codepoint; + break; + case 0x114AAu: + switch (buffer->cur (1).codepoint) + { + case 0x114B5u: case 0x114B6u: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_MODI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11600u: case 0x11601u: + switch (buffer->cur (1).codepoint) + { + case 0x11639u: case 0x1163Au: + matched = true; + break; + } + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + case HB_SCRIPT_TAKRI: + for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;) + { + bool matched = false; + switch (buffer->cur ().codepoint) + { + case 0x11680u: + switch (buffer->cur (1).codepoint) + { + case 0x116ADu: case 0x116B4u: case 0x116B5u: + matched = true; + break; + } + break; + case 0x11686u: + matched = 0x116B2u == buffer->cur (1).codepoint; + break; + } + (void) buffer->next_glyph (); + if (matched) _output_with_dotted_circle (buffer); + } + break; + + default: + break; + } + buffer->sync (); +} + + +#endif +/* == End of generated functions == */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper-vowel-constraints.hh b/gfx/harfbuzz/src/hb-ot-shaper-vowel-constraints.hh new file mode 100644 index 0000000000..5a7ee1b0f2 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper-vowel-constraints.hh @@ -0,0 +1,39 @@ +/* + * 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_OT_SHAPER_VOWEL_CONSTRAINTS_HH +#define HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH + +#include "hb.hh" + +#include "hb-ot-shaper.hh" + +HB_INTERNAL void +_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + +#endif /* HB_OT_SHAPER_VOWEL_CONSTRAINTS_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-shaper.hh b/gfx/harfbuzz/src/hb-ot-shaper.hh new file mode 100644 index 0000000000..0207b2bbe3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-shaper.hh @@ -0,0 +1,403 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPER_HH +#define HB_OT_SHAPER_HH + +#include "hb.hh" + +#include "hb-ot-layout.hh" +#include "hb-ot-shape.hh" +#include "hb-ot-shape-normalize.hh" + + +/* buffer var allocations, used by all OT shapers */ +#define ot_shaper_var_u8_category() var2.u8[2] +#define ot_shaper_var_u8_auxiliary() var2.u8[3] + + +#define HB_OT_SHAPE_MAX_COMBINING_MARKS 32 + +enum hb_ot_shape_zero_width_marks_type_t { + HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY, + HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE +}; + + +/* Master OT shaper list */ +#define HB_OT_SHAPERS_IMPLEMENT_SHAPERS \ + HB_OT_SHAPER_IMPLEMENT (arabic) \ + HB_OT_SHAPER_IMPLEMENT (default) \ + HB_OT_SHAPER_IMPLEMENT (dumber) \ + HB_OT_SHAPER_IMPLEMENT (hangul) \ + HB_OT_SHAPER_IMPLEMENT (hebrew) \ + HB_OT_SHAPER_IMPLEMENT (indic) \ + HB_OT_SHAPER_IMPLEMENT (khmer) \ + HB_OT_SHAPER_IMPLEMENT (myanmar) \ + HB_OT_SHAPER_IMPLEMENT (myanmar_zawgyi) \ + HB_OT_SHAPER_IMPLEMENT (thai) \ + HB_OT_SHAPER_IMPLEMENT (use) \ + /* ^--- Add new shapers here; keep sorted. */ + + +struct hb_ot_shaper_t +{ + /* collect_features() + * Called during shape_plan(). + * Shapers should use plan->map to add their features and callbacks. + * May be NULL. + */ + void (*collect_features) (hb_ot_shape_planner_t *plan); + + /* override_features() + * Called during shape_plan(). + * Shapers should use plan->map to override features and add callbacks after + * common features are added. + * May be NULL. + */ + void (*override_features) (hb_ot_shape_planner_t *plan); + + + /* data_create() + * Called at the end of shape_plan(). + * Whatever shapers return will be accessible through plan->data later. + * If nullptr is returned, means a plan failure. + */ + void *(*data_create) (const hb_ot_shape_plan_t *plan); + + /* data_destroy() + * Called when the shape_plan is being destroyed. + * plan->data is passed here for destruction. + * If nullptr is returned, means a plan failure. + * May be NULL. + */ + void (*data_destroy) (void *data); + + + /* preprocess_text() + * Called during shape(). + * Shapers can use to modify text before shaping starts. + * May be NULL. + */ + void (*preprocess_text) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + /* postprocess_glyphs() + * Called during shape(). + * Shapers can use to modify glyphs after shaping ends. + * May be NULL. + */ + void (*postprocess_glyphs) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + + /* decompose() + * Called during shape()'s normalization. + * May be NULL. + */ + bool (*decompose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + + /* compose() + * Called during shape()'s normalization. + * May be NULL. + */ + bool (*compose) (const hb_ot_shape_normalize_context_t *c, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); + + /* setup_masks() + * Called during shape(). + * Shapers should use map to get feature masks and set on buffer. + * Shapers may NOT modify characters. + * May be NULL. + */ + void (*setup_masks) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + hb_font_t *font); + + /* reorder_marks() + * Called during shape(). + * Shapers can use to modify ordering of combining marks. + * May be NULL. + */ + void (*reorder_marks) (const hb_ot_shape_plan_t *plan, + hb_buffer_t *buffer, + unsigned int start, + unsigned int end); + + /* gpos_tag() + * If not HB_TAG_NONE, then must match found GPOS script tag for + * GPOS to be applied. Otherwise, fallback positioning will be used. + */ + hb_tag_t gpos_tag; + + hb_ot_shape_normalization_mode_t normalization_preference; + + hb_ot_shape_zero_width_marks_type_t zero_width_marks; + + bool fallback_position; +}; + +#define HB_OT_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_shaper_t _hb_ot_shaper_##name; +HB_OT_SHAPERS_IMPLEMENT_SHAPERS +#undef HB_OT_SHAPER_IMPLEMENT + + +static inline const hb_ot_shaper_t * +hb_ot_shaper_categorize (const hb_ot_shape_planner_t *planner) +{ + switch ((hb_tag_t) planner->props.script) + { + default: + return &_hb_ot_shaper_default; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_ARABIC: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_SYRIAC: + + /* For Arabic script, use the Arabic shaper even if no OT script tag was found. + * This is because we do fallback shaping for Arabic script (and not others). + * But note that Arabic shaping is applicable only to horizontal layout; for + * vertical text, just use the generic shaper instead. */ + if ((planner->map.chosen_script[0] != HB_OT_TAG_DEFAULT_SCRIPT || + planner->props.script == HB_SCRIPT_ARABIC) && + HB_DIRECTION_IS_HORIZONTAL(planner->props.direction)) + return &_hb_ot_shaper_arabic; + else + return &_hb_ot_shaper_default; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_THAI: + case HB_SCRIPT_LAO: + + return &_hb_ot_shaper_thai; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HANGUL: + + return &_hb_ot_shaper_hangul; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_HEBREW: + + return &_hb_ot_shaper_hebrew; + + + /* Unicode-1.1 additions */ + case HB_SCRIPT_BENGALI: + case HB_SCRIPT_DEVANAGARI: + case HB_SCRIPT_GUJARATI: + case HB_SCRIPT_GURMUKHI: + case HB_SCRIPT_KANNADA: + case HB_SCRIPT_MALAYALAM: + case HB_SCRIPT_ORIYA: + case HB_SCRIPT_TAMIL: + case HB_SCRIPT_TELUGU: + + /* If the designer designed the font for the 'DFLT' script, + * (or we ended up arbitrarily pick 'latn'), use the default shaper. + * Otherwise, use the specific shaper. + * + * If it's indy3 tag, send to USE. */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || + planner->map.chosen_script[0] == HB_TAG ('l','a','t','n')) + return &_hb_ot_shaper_default; + else if ((planner->map.chosen_script[0] & 0x000000FF) == '3') + return &_hb_ot_shaper_use; + else + return &_hb_ot_shaper_indic; + + case HB_SCRIPT_KHMER: + return &_hb_ot_shaper_khmer; + + case HB_SCRIPT_MYANMAR: + /* If the designer designed the font for the 'DFLT' script, + * (or we ended up arbitrarily pick 'latn'), use the default shaper. + * Otherwise, use the specific shaper. + * + * If designer designed for 'mymr' tag, also send to default + * shaper. That's tag used from before Myanmar shaping spec + * was developed. The shaping spec uses 'mym2' tag. */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || + planner->map.chosen_script[0] == HB_TAG ('l','a','t','n') || + planner->map.chosen_script[0] == HB_TAG ('m','y','m','r')) + return &_hb_ot_shaper_default; + else + return &_hb_ot_shaper_myanmar; + + +#ifndef HB_NO_OT_SHAPER_MYANMAR_ZAWGYI +#define HB_SCRIPT_MYANMAR_ZAWGYI ((hb_script_t) HB_TAG ('Q','a','a','g')) + case HB_SCRIPT_MYANMAR_ZAWGYI: + /* https://github.com/harfbuzz/harfbuzz/issues/1162 */ + + return &_hb_ot_shaper_myanmar_zawgyi; +#endif + + + /* Unicode-2.0 additions */ + case HB_SCRIPT_TIBETAN: + + /* Unicode-3.0 additions */ + case HB_SCRIPT_MONGOLIAN: + case HB_SCRIPT_SINHALA: + + /* Unicode-3.2 additions */ + case HB_SCRIPT_BUHID: + case HB_SCRIPT_HANUNOO: + case HB_SCRIPT_TAGALOG: + case HB_SCRIPT_TAGBANWA: + + /* Unicode-4.0 additions */ + case HB_SCRIPT_LIMBU: + case HB_SCRIPT_TAI_LE: + + /* Unicode-4.1 additions */ + case HB_SCRIPT_BUGINESE: + case HB_SCRIPT_KHAROSHTHI: + case HB_SCRIPT_SYLOTI_NAGRI: + case HB_SCRIPT_TIFINAGH: + + /* Unicode-5.0 additions */ + case HB_SCRIPT_BALINESE: + case HB_SCRIPT_NKO: + case HB_SCRIPT_PHAGS_PA: + + /* Unicode-5.1 additions */ + case HB_SCRIPT_CHAM: + case HB_SCRIPT_KAYAH_LI: + case HB_SCRIPT_LEPCHA: + case HB_SCRIPT_REJANG: + case HB_SCRIPT_SAURASHTRA: + case HB_SCRIPT_SUNDANESE: + + /* Unicode-5.2 additions */ + case HB_SCRIPT_EGYPTIAN_HIEROGLYPHS: + case HB_SCRIPT_JAVANESE: + case HB_SCRIPT_KAITHI: + case HB_SCRIPT_MEETEI_MAYEK: + case HB_SCRIPT_TAI_THAM: + case HB_SCRIPT_TAI_VIET: + + /* Unicode-6.0 additions */ + case HB_SCRIPT_BATAK: + case HB_SCRIPT_BRAHMI: + case HB_SCRIPT_MANDAIC: + + /* Unicode-6.1 additions */ + case HB_SCRIPT_CHAKMA: + case HB_SCRIPT_MIAO: + case HB_SCRIPT_SHARADA: + case HB_SCRIPT_TAKRI: + + /* Unicode-7.0 additions */ + case HB_SCRIPT_DUPLOYAN: + case HB_SCRIPT_GRANTHA: + case HB_SCRIPT_KHOJKI: + case HB_SCRIPT_KHUDAWADI: + case HB_SCRIPT_MAHAJANI: + case HB_SCRIPT_MANICHAEAN: + case HB_SCRIPT_MODI: + case HB_SCRIPT_PAHAWH_HMONG: + case HB_SCRIPT_PSALTER_PAHLAVI: + case HB_SCRIPT_SIDDHAM: + case HB_SCRIPT_TIRHUTA: + + /* Unicode-8.0 additions */ + case HB_SCRIPT_AHOM: + case HB_SCRIPT_MULTANI: + + /* Unicode-9.0 additions */ + case HB_SCRIPT_ADLAM: + case HB_SCRIPT_BHAIKSUKI: + case HB_SCRIPT_MARCHEN: + case HB_SCRIPT_NEWA: + + /* Unicode-10.0 additions */ + case HB_SCRIPT_MASARAM_GONDI: + case HB_SCRIPT_SOYOMBO: + case HB_SCRIPT_ZANABAZAR_SQUARE: + + /* Unicode-11.0 additions */ + case HB_SCRIPT_DOGRA: + case HB_SCRIPT_GUNJALA_GONDI: + case HB_SCRIPT_HANIFI_ROHINGYA: + case HB_SCRIPT_MAKASAR: + case HB_SCRIPT_MEDEFAIDRIN: + case HB_SCRIPT_OLD_SOGDIAN: + case HB_SCRIPT_SOGDIAN: + + /* Unicode-12.0 additions */ + case HB_SCRIPT_ELYMAIC: + case HB_SCRIPT_NANDINAGARI: + case HB_SCRIPT_NYIAKENG_PUACHUE_HMONG: + case HB_SCRIPT_WANCHO: + + /* Unicode-13.0 additions */ + case HB_SCRIPT_CHORASMIAN: + case HB_SCRIPT_DIVES_AKURU: + case HB_SCRIPT_KHITAN_SMALL_SCRIPT: + case HB_SCRIPT_YEZIDI: + + /* Unicode-14.0 additions */ + case HB_SCRIPT_CYPRO_MINOAN: + case HB_SCRIPT_OLD_UYGHUR: + case HB_SCRIPT_TANGSA: + case HB_SCRIPT_TOTO: + case HB_SCRIPT_VITHKUQI: + + /* Unicode-15.0 additions */ + case HB_SCRIPT_KAWI: + case HB_SCRIPT_NAG_MUNDARI: + + /* If the designer designed the font for the 'DFLT' script, + * (or we ended up arbitrarily pick 'latn'), use the default shaper. + * Otherwise, use the specific shaper. + * Note that for some simple scripts, there may not be *any* + * GSUB/GPOS needed, so there may be no scripts found! */ + if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T') || + planner->map.chosen_script[0] == HB_TAG ('l','a','t','n')) + return &_hb_ot_shaper_default; + else + return &_hb_ot_shaper_use; + } +} + + +#endif /* HB_OT_SHAPER_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-stat-table.hh b/gfx/harfbuzz/src/hb-ot-stat-table.hh new file mode 100644 index 0000000000..58b3cd74df --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-stat-table.hh @@ -0,0 +1,618 @@ +/* + * 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_STAT_TABLE_HH +#define HB_OT_STAT_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-layout-common.hh" + +/* + * STAT -- Style Attributes + * https://docs.microsoft.com/en-us/typography/opentype/spec/stat + */ +#define HB_OT_TAG_STAT HB_TAG('S','T','A','T') + + +namespace OT { + +enum +{ + OLDER_SIBLING_FONT_ATTRIBUTE = 0x0001, /* If set, this axis value table + * provides axis value information + * that is applicable to other fonts + * within the same font family. This + * is used if the other fonts were + * released earlier and did not include + * information about values for some axis. + * If newer versions of the other + * fonts include the information + * themselves and are present, + * then this record is ignored. */ + ELIDABLE_AXIS_VALUE_NAME = 0x0002 /* If set, it indicates that the axis + * value represents the “normal” value + * for the axis and may be omitted when + * composing name strings. */ + // Reserved = 0xFFFC /* Reserved for future use — set to zero. */ +}; + +static bool axis_value_is_outside_axis_range (hb_tag_t axis_tag, float axis_value, + const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) +{ + if (!user_axes_location->has (axis_tag)) + return false; + + Triple axis_range = user_axes_location->get (axis_tag); + return (axis_value < axis_range.minimum || axis_value > axis_range.maximum); +} + +struct StatAxisRecord +{ + int cmp (hb_tag_t key) const { return tag.cmp (key); } + + hb_ot_name_id_t get_name_id () const { return nameID; } + + hb_tag_t get_axis_tag () const { return tag; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this))); + } + + protected: + Tag tag; /* A tag identifying the axis of design variation. */ + NameID nameID; /* The name ID for entries in the 'name' table that + * provide a display string for this axis. */ + HBUINT16 ordering; /* A value that applications can use to determine + * primary sorting of face names, or for ordering + * of descriptors when composing family or face names. */ + public: + DEFINE_SIZE_STATIC (8); +}; + +struct AxisValueFormat1 +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const + { + unsigned axis_idx = get_axis_index (); + return axis_records[axis_idx].get_axis_tag (); + } + + bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, + const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const + { + hb_tag_t axis_tag = get_axis_tag (axis_records); + float axis_value = get_value (); + + return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location); + } + + bool subset (hb_subset_context_t *c, + const hb_array_t<const StatAxisRecord> axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location; + + if (keep_axis_value (axis_records, user_axes_location)) + return_trace (c->serializer->embed (this)); + + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier — set to 1. */ + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis of design variation + * to which the axis value record applies. + * Must be less than designAxisCount. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + F16DOT16 value; /* A numeric value for this attribute value. */ + public: + DEFINE_SIZE_STATIC (12); +}; + +struct AxisValueFormat2 +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return nominalValue.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const + { + unsigned axis_idx = get_axis_index (); + return axis_records[axis_idx].get_axis_tag (); + } + + bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, + const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const + { + hb_tag_t axis_tag = get_axis_tag (axis_records); + float axis_value = get_value (); + + return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location); + } + + bool subset (hb_subset_context_t *c, + const hb_array_t<const StatAxisRecord> axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location; + + if (keep_axis_value (axis_records, user_axes_location)) + return_trace (c->serializer->embed (this)); + + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier — set to 2. */ + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis of design variation + * to which the axis value record applies. + * Must be less than designAxisCount. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + F16DOT16 nominalValue; /* A numeric value for this attribute value. */ + F16DOT16 rangeMinValue; /* The minimum value for a range associated + * with the specified name ID. */ + F16DOT16 rangeMaxValue; /* The maximum value for a range associated + * with the specified name ID. */ + public: + DEFINE_SIZE_STATIC (20); +}; + +struct AxisValueFormat3 +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + hb_tag_t get_axis_tag (const hb_array_t<const StatAxisRecord> axis_records) const + { + unsigned axis_idx = get_axis_index (); + return axis_records[axis_idx].get_axis_tag (); + } + + bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, + const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const + { + hb_tag_t axis_tag = get_axis_tag (axis_records); + float axis_value = get_value (); + + return !axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location); + } + + bool subset (hb_subset_context_t *c, + const hb_array_t<const StatAxisRecord> axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t<hb_tag_t, Triple>* user_axes_location = &c->plan->user_axes_location; + + if (keep_axis_value (axis_records, user_axes_location)) + return_trace (c->serializer->embed (this)); + + return_trace (false); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 format; /* Format identifier — set to 3. */ + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis of design variation + * to which the axis value record applies. + * Must be less than designAxisCount. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + F16DOT16 value; /* A numeric value for this attribute value. */ + F16DOT16 linkedValue; /* The numeric value for a style-linked mapping + * from this value. */ + public: + DEFINE_SIZE_STATIC (16); +}; + +struct AxisValueRecord +{ + unsigned int get_axis_index () const { return axisIndex; } + float get_value () const { return value.to_float (); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + protected: + HBUINT16 axisIndex; /* Zero-base index into the axis record array + * identifying the axis to which this value + * applies. Must be less than designAxisCount. */ + F16DOT16 value; /* A numeric value for this attribute value. */ + public: + DEFINE_SIZE_STATIC (6); +}; + +struct AxisValueFormat4 +{ + const AxisValueRecord &get_axis_record (unsigned int axis_index) const + { return axisValues.as_array (axisCount)[axis_index]; } + + bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, + const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const + { + hb_array_t<const AxisValueRecord> axis_value_records = axisValues.as_array (axisCount); + + for (const auto& rec : axis_value_records) + { + unsigned axis_idx = rec.get_axis_index (); + float axis_value = rec.get_value (); + hb_tag_t axis_tag = axis_records[axis_idx].get_axis_tag (); + + if (axis_value_is_outside_axis_range (axis_tag, axis_value, user_axes_location)) + return false; + } + + return true; + } + + bool subset (hb_subset_context_t *c, + const hb_array_t<const StatAxisRecord> axis_records) const + { + TRACE_SUBSET (this); + const hb_hashmap_t<hb_tag_t, Triple> *user_axes_location = &c->plan->user_axes_location; + if (!keep_axis_value (axis_records, user_axes_location)) + return_trace (false); + + unsigned total_size = min_size + axisCount * AxisValueRecord::static_size; + auto *out = c->serializer->allocate_size<AxisValueFormat4> (total_size); + if (unlikely (!out)) return_trace (false); + hb_memcpy (out, this, total_size); + return_trace (true); + } + + hb_ot_name_id_t get_value_name_id () const { return valueNameID; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + hb_barrier () && + axisValues.sanitize (c, axisCount))); + } + + protected: + HBUINT16 format; /* Format identifier — set to 4. */ + HBUINT16 axisCount; /* The total number of axes contributing to + * this axis-values combination. */ + HBUINT16 flags; /* Flags — see below for details. */ + NameID valueNameID; /* The name ID for entries in the 'name' table + * that provide a display string for this + * attribute value. */ + UnsizedArrayOf<AxisValueRecord> + axisValues; /* Array of AxisValue records that provide the + * combination of axis values, one for each + * contributing axis. */ + public: + DEFINE_SIZE_ARRAY (8, axisValues); +}; + +struct AxisValue +{ + bool get_value (unsigned int axis_index) const + { + switch (u.format) + { + case 1: return u.format1.get_value (); + case 2: return u.format2.get_value (); + case 3: return u.format3.get_value (); + case 4: return u.format4.get_axis_record (axis_index).get_value (); + default:return 0; + } + } + + unsigned int get_axis_index () const + { + switch (u.format) + { + case 1: return u.format1.get_axis_index (); + case 2: return u.format2.get_axis_index (); + case 3: return u.format3.get_axis_index (); + /* case 4: Makes more sense for variable fonts which are handled by fvar in hb-style */ + default:return -1; + } + } + + hb_ot_name_id_t get_value_name_id () const + { + switch (u.format) + { + case 1: return u.format1.get_value_name_id (); + case 2: return u.format2.get_value_name_id (); + case 3: return u.format3.get_value_name_id (); + case 4: return u.format4.get_value_name_id (); + default:return HB_OT_NAME_ID_INVALID; + } + } + + template <typename context_t, typename ...Ts> + 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<Ts> (ds)...)); + case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); + case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); + case 4: return_trace (c->dispatch (u.format4, std::forward<Ts> (ds)...)); + default:return_trace (c->default_return_value ()); + } + } + + bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, + hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const + { + switch (u.format) + { + case 1: return u.format1.keep_axis_value (axis_records, user_axes_location); + case 2: return u.format2.keep_axis_value (axis_records, user_axes_location); + case 3: return u.format3.keep_axis_value (axis_records, user_axes_location); + case 4: return u.format4.keep_axis_value (axis_records, user_axes_location); + default:return false; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (unlikely (!c->check_struct (this))) + return_trace (false); + hb_barrier (); + + 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)); + case 4: return_trace (u.format4.sanitize (c)); + default:return_trace (true); + } + } + + protected: + union + { + HBUINT16 format; + AxisValueFormat1 format1; + AxisValueFormat2 format2; + AxisValueFormat3 format3; + AxisValueFormat4 format4; + } u; + public: + DEFINE_SIZE_UNION (2, format); +}; + +struct AxisValueOffsetArray: UnsizedArrayOf<Offset16To<AxisValue>> +{ + bool subset (hb_subset_context_t *c, + unsigned axisValueCount, + unsigned& count, + const hb_array_t<const StatAxisRecord> axis_records) const + { + TRACE_SUBSET (this); + + auto axisValueOffsets = as_array (axisValueCount); + count = 0; + for (const auto& offset : axisValueOffsets) + { + if (!offset) continue; + auto o_snap = c->serializer->snapshot (); + auto *o = c->serializer->embed (offset); + if (!o) return_trace (false); + if (!o->serialize_subset (c, offset, this, axis_records)) + { + c->serializer->revert (o_snap); + continue; + } + count++; + } + + return_trace (count); + } +}; + +struct STAT +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_STAT; + + bool has_data () const { return version.to_int (); } + + bool get_value (hb_tag_t tag, float *value) const + { + unsigned int axis_index; + if (!get_design_axes ().lfind (tag, &axis_index)) return false; + + hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets (); + for (unsigned int i = 0; i < axis_values.length; i++) + { + const AxisValue& axis_value = this+axis_values[i]; + if (axis_value.get_axis_index () == axis_index) + { + if (value) + *value = axis_value.get_value (axis_index); + return true; + } + } + return false; + } + + unsigned get_design_axis_count () const { return designAxisCount; } + + hb_ot_name_id_t get_axis_record_name_id (unsigned axis_record_index) const + { + if (unlikely (axis_record_index >= designAxisCount)) return HB_OT_NAME_ID_INVALID; + const StatAxisRecord &axis_record = get_design_axes ()[axis_record_index]; + return axis_record.get_name_id (); + } + + unsigned get_axis_value_count () const { return axisValueCount; } + + hb_ot_name_id_t get_axis_value_name_id (unsigned axis_value_index) const + { + if (unlikely (axis_value_index >= axisValueCount)) return HB_OT_NAME_ID_INVALID; + const AxisValue &axis_value = (this + get_axis_value_offsets ()[axis_value_index]); + return axis_value.get_value_name_id (); + } + + void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location, + hb_set_t *nameids_to_retain /* OUT */) const + { + if (!has_data ()) return; + + + get_design_axes () + | hb_map (&StatAxisRecord::get_name_id) + | hb_sink (nameids_to_retain) + ; + + auto designAxes = get_design_axes (); + + + get_axis_value_offsets () + | hb_map (hb_add (&(this + offsetToAxisValueOffsets))) + | hb_filter ([&] (const AxisValue& _) + { return _.keep_axis_value (designAxes, user_axes_location); }) + | hb_map (&AxisValue::get_value_name_id) + | hb_sink (nameids_to_retain) + ; + + nameids_to_retain->add (elidedFallbackNameID); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + STAT *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + auto designAxes = get_design_axes (); + for (unsigned i = 0; i < (unsigned)designAxisCount; i++) + if (unlikely (!c->serializer->embed (designAxes[i]))) + return_trace (false); + + if (designAxisCount) + c->serializer->check_assign (out->designAxesOffset, this->get_size (), + HB_SERIALIZE_ERROR_INT_OVERFLOW); + + unsigned count = 0; + out->offsetToAxisValueOffsets.serialize_subset (c, offsetToAxisValueOffsets, this, + axisValueCount, count, designAxes); + return_trace (c->serializer->check_assign (out->axisValueCount, count, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (likely (c->check_struct (this) && + hb_barrier () && + version.major == 1 && + version.minor > 0 && + designAxesOffset.sanitize (c, this, designAxisCount) && + offsetToAxisValueOffsets.sanitize (c, this, axisValueCount, &(this+offsetToAxisValueOffsets)))); + } + + protected: + hb_array_t<const StatAxisRecord> const get_design_axes () const + { return (this+designAxesOffset).as_array (designAxisCount); } + + hb_array_t<const Offset16To<AxisValue>> const get_axis_value_offsets () const + { return (this+offsetToAxisValueOffsets).as_array (axisValueCount); } + + + protected: + FixedVersion<>version; /* Version of the stat table + * initially set to 0x00010002u */ + HBUINT16 designAxisSize; /* The size in bytes of each axis record. */ + HBUINT16 designAxisCount;/* The number of design axis records. In a + * font with an 'fvar' table, this value must be + * greater than or equal to the axisCount value + * in the 'fvar' table. In all fonts, must + * be greater than zero if axisValueCount + * is greater than zero. */ + NNOffset32To<UnsizedArrayOf<StatAxisRecord>> + designAxesOffset; + /* Offset in bytes from the beginning of + * the STAT table to the start of the design + * axes array. If designAxisCount is zero, + * set to zero; if designAxisCount is greater + * than zero, must be greater than zero. */ + HBUINT16 axisValueCount; /* The number of axis value tables. */ + NNOffset32To<AxisValueOffsetArray> + offsetToAxisValueOffsets; + /* Offset in bytes from the beginning of + * the STAT table to the start of the design + * axes value offsets array. If axisValueCount + * is zero, set to zero; if axisValueCount is + * greater than zero, must be greater than zero. */ + NameID elidedFallbackNameID; + /* Name ID used as fallback when projection of + * names into a particular font model produces + * a subfamily name containing only elidable + * elements. */ + public: + DEFINE_SIZE_STATIC (20); +}; + + +} /* namespace OT */ + + +#endif /* HB_OT_STAT_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-tag-table.hh b/gfx/harfbuzz/src/hb-ot-tag-table.hh new file mode 100644 index 0000000000..032a7c866c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-tag-table.hh @@ -0,0 +1,2996 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-tag-table.py languagetags language-subtag-registry + * + * on files with these headers: + * + * <meta name="updated_at" content="2022-09-30 11:47 PM" /> + * File-Date: 2023-08-02 + */ + +#ifndef HB_OT_TAG_TABLE_HH +#define HB_OT_TAG_TABLE_HH + +static const LangTag ot_languages2[] = { + {HB_TAG('a','a',' ',' '), HB_TAG('A','F','R',' ')}, /* Afar */ + {HB_TAG('a','b',' ',' '), HB_TAG('A','B','K',' ')}, /* Abkhazian */ + {HB_TAG('a','f',' ',' '), HB_TAG('A','F','K',' ')}, /* Afrikaans */ + {HB_TAG('a','k',' ',' '), HB_TAG('A','K','A',' ')}, /* Akan [macrolanguage] */ + {HB_TAG('a','m',' ',' '), HB_TAG('A','M','H',' ')}, /* Amharic */ + {HB_TAG('a','n',' ',' '), HB_TAG('A','R','G',' ')}, /* Aragonese */ + {HB_TAG('a','r',' ',' '), HB_TAG('A','R','A',' ')}, /* Arabic [macrolanguage] */ + {HB_TAG('a','s',' ',' '), HB_TAG('A','S','M',' ')}, /* Assamese */ + {HB_TAG('a','v',' ',' '), HB_TAG('A','V','R',' ')}, /* Avaric -> Avar */ + {HB_TAG('a','y',' ',' '), HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */ + {HB_TAG('a','z',' ',' '), HB_TAG('A','Z','E',' ')}, /* Azerbaijani [macrolanguage] */ + {HB_TAG('b','a',' ',' '), HB_TAG('B','S','H',' ')}, /* Bashkir */ + {HB_TAG('b','e',' ',' '), HB_TAG('B','E','L',' ')}, /* Belarusian -> Belarussian */ + {HB_TAG('b','g',' ',' '), HB_TAG('B','G','R',' ')}, /* Bulgarian */ + {HB_TAG('b','i',' ',' '), HB_TAG('B','I','S',' ')}, /* Bislama */ + {HB_TAG('b','i',' ',' '), HB_TAG('C','P','P',' ')}, /* Bislama -> Creoles */ + {HB_TAG('b','m',' ',' '), HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */ + {HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bengali */ + {HB_TAG('b','o',' ',' '), HB_TAG('T','I','B',' ')}, /* Tibetan */ + {HB_TAG('b','r',' ',' '), HB_TAG('B','R','E',' ')}, /* Breton */ + {HB_TAG('b','s',' ',' '), HB_TAG('B','O','S',' ')}, /* Bosnian */ + {HB_TAG('c','a',' ',' '), HB_TAG('C','A','T',' ')}, /* Catalan */ + {HB_TAG('c','e',' ',' '), HB_TAG('C','H','E',' ')}, /* Chechen */ + {HB_TAG('c','h',' ',' '), HB_TAG('C','H','A',' ')}, /* Chamorro */ + {HB_TAG('c','o',' ',' '), HB_TAG('C','O','S',' ')}, /* Corsican */ + {HB_TAG('c','r',' ',' '), HB_TAG('C','R','E',' ')}, /* Cree [macrolanguage] */ + {HB_TAG('c','s',' ',' '), HB_TAG('C','S','Y',' ')}, /* Czech */ + {HB_TAG('c','u',' ',' '), HB_TAG('C','S','L',' ')}, /* Church Slavonic */ + {HB_TAG('c','v',' ',' '), HB_TAG('C','H','U',' ')}, /* Chuvash */ + {HB_TAG('c','y',' ',' '), HB_TAG('W','E','L',' ')}, /* Welsh */ + {HB_TAG('d','a',' ',' '), HB_TAG('D','A','N',' ')}, /* Danish */ + {HB_TAG('d','e',' ',' '), HB_TAG('D','E','U',' ')}, /* German */ + {HB_TAG('d','v',' ',' '), HB_TAG('D','I','V',' ')}, /* Divehi (Dhivehi, Maldivian) */ + {HB_TAG('d','v',' ',' '), HB_TAG('D','H','V',' ')}, /* Divehi (Dhivehi, Maldivian) (deprecated) */ + {HB_TAG('d','z',' ',' '), HB_TAG('D','Z','N',' ')}, /* Dzongkha */ + {HB_TAG('e','e',' ',' '), HB_TAG('E','W','E',' ')}, /* Ewe */ + {HB_TAG('e','l',' ',' '), HB_TAG('E','L','L',' ')}, /* Modern Greek (1453-) -> Greek */ + {HB_TAG('e','n',' ',' '), HB_TAG('E','N','G',' ')}, /* English */ + {HB_TAG('e','o',' ',' '), HB_TAG('N','T','O',' ')}, /* Esperanto */ + {HB_TAG('e','s',' ',' '), HB_TAG('E','S','P',' ')}, /* Spanish */ + {HB_TAG('e','t',' ',' '), HB_TAG('E','T','I',' ')}, /* Estonian [macrolanguage] */ + {HB_TAG('e','u',' ',' '), HB_TAG('E','U','Q',' ')}, /* Basque */ + {HB_TAG('f','a',' ',' '), HB_TAG('F','A','R',' ')}, /* Persian [macrolanguage] */ + {HB_TAG('f','f',' ',' '), HB_TAG('F','U','L',' ')}, /* Fulah [macrolanguage] */ + {HB_TAG('f','i',' ',' '), HB_TAG('F','I','N',' ')}, /* Finnish */ + {HB_TAG('f','j',' ',' '), HB_TAG('F','J','I',' ')}, /* Fijian */ + {HB_TAG('f','o',' ',' '), HB_TAG('F','O','S',' ')}, /* Faroese */ + {HB_TAG('f','r',' ',' '), HB_TAG('F','R','A',' ')}, /* French */ + {HB_TAG('f','y',' ',' '), HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */ + {HB_TAG('g','a',' ',' '), HB_TAG('I','R','I',' ')}, /* Irish */ + {HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */ + {HB_TAG('g','l',' ',' '), HB_TAG('G','A','L',' ')}, /* Galician */ + {HB_TAG('g','n',' ',' '), HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ + {HB_TAG('g','u',' ',' '), HB_TAG('G','U','J',' ')}, /* Gujarati */ + {HB_TAG('g','v',' ',' '), HB_TAG('M','N','X',' ')}, /* Manx */ + {HB_TAG('h','a',' ',' '), HB_TAG('H','A','U',' ')}, /* Hausa */ + {HB_TAG('h','e',' ',' '), HB_TAG('I','W','R',' ')}, /* Hebrew */ + {HB_TAG('h','i',' ',' '), HB_TAG('H','I','N',' ')}, /* Hindi */ + {HB_TAG('h','o',' ',' '), HB_TAG('H','M','O',' ')}, /* Hiri Motu */ + {HB_TAG('h','o',' ',' '), HB_TAG('C','P','P',' ')}, /* Hiri Motu -> Creoles */ + {HB_TAG('h','r',' ',' '), HB_TAG('H','R','V',' ')}, /* Croatian */ + {HB_TAG('h','t',' ',' '), HB_TAG('H','A','I',' ')}, /* Haitian (Haitian Creole) */ + {HB_TAG('h','t',' ',' '), HB_TAG('C','P','P',' ')}, /* Haitian -> Creoles */ + {HB_TAG('h','u',' ',' '), HB_TAG('H','U','N',' ')}, /* Hungarian */ + {HB_TAG('h','y',' ',' '), HB_TAG('H','Y','E','0')}, /* Armenian -> Armenian East */ + {HB_TAG('h','y',' ',' '), HB_TAG('H','Y','E',' ')}, /* Armenian */ + {HB_TAG('h','z',' ',' '), HB_TAG('H','E','R',' ')}, /* Herero */ + {HB_TAG('i','a',' ',' '), HB_TAG('I','N','A',' ')}, /* Interlingua (International Auxiliary Language Association) */ + {HB_TAG('i','d',' ',' '), HB_TAG('I','N','D',' ')}, /* Indonesian */ + {HB_TAG('i','d',' ',' '), HB_TAG('M','L','Y',' ')}, /* Indonesian -> Malay */ + {HB_TAG('i','e',' ',' '), HB_TAG('I','L','E',' ')}, /* Interlingue */ + {HB_TAG('i','g',' ',' '), HB_TAG('I','B','O',' ')}, /* Igbo */ + {HB_TAG('i','i',' ',' '), HB_TAG('Y','I','M',' ')}, /* Sichuan Yi -> Yi Modern */ + {HB_TAG('i','k',' ',' '), HB_TAG('I','P','K',' ')}, /* Inupiaq [macrolanguage] -> Inupiat */ + {HB_TAG('i','n',' ',' '), HB_TAG('I','N','D',' ')}, /* Indonesian (retired code) */ + {HB_TAG('i','n',' ',' '), HB_TAG('M','L','Y',' ')}, /* Indonesian (retired code) -> Malay */ + {HB_TAG('i','o',' ',' '), HB_TAG('I','D','O',' ')}, /* Ido */ + {HB_TAG('i','s',' ',' '), HB_TAG('I','S','L',' ')}, /* Icelandic */ + {HB_TAG('i','t',' ',' '), HB_TAG('I','T','A',' ')}, /* Italian */ + {HB_TAG('i','u',' ',' '), HB_TAG('I','N','U',' ')}, /* Inuktitut [macrolanguage] */ + {HB_TAG('i','u',' ',' '), HB_TAG('I','N','U','K')}, /* Inuktitut [macrolanguage] -> Nunavik Inuktitut */ + {HB_TAG('i','w',' ',' '), HB_TAG('I','W','R',' ')}, /* Hebrew (retired code) */ + {HB_TAG('j','a',' ',' '), HB_TAG('J','A','N',' ')}, /* Japanese */ + {HB_TAG('j','i',' ',' '), HB_TAG('J','I','I',' ')}, /* Yiddish (retired code) */ + {HB_TAG('j','v',' ',' '), HB_TAG('J','A','V',' ')}, /* Javanese */ + {HB_TAG('j','w',' ',' '), HB_TAG('J','A','V',' ')}, /* Javanese (retired code) */ + {HB_TAG('k','a',' ',' '), HB_TAG('K','A','T',' ')}, /* Georgian */ + {HB_TAG('k','g',' ',' '), HB_TAG('K','O','N','0')}, /* Kongo [macrolanguage] */ + {HB_TAG('k','i',' ',' '), HB_TAG('K','I','K',' ')}, /* Kikuyu (Gikuyu) */ + {HB_TAG('k','j',' ',' '), HB_TAG('K','U','A',' ')}, /* Kuanyama */ + {HB_TAG('k','k',' ',' '), HB_TAG('K','A','Z',' ')}, /* Kazakh */ + {HB_TAG('k','l',' ',' '), HB_TAG('G','R','N',' ')}, /* Greenlandic */ + {HB_TAG('k','m',' ',' '), HB_TAG('K','H','M',' ')}, /* Khmer */ + {HB_TAG('k','n',' ',' '), HB_TAG('K','A','N',' ')}, /* Kannada */ + {HB_TAG('k','o',' ',' '), HB_TAG('K','O','R',' ')}, /* Korean */ + {HB_TAG('k','o',' ',' '), HB_TAG('K','O','H',' ')}, /* Korean -> Korean Old Hangul */ + {HB_TAG('k','r',' ',' '), HB_TAG('K','N','R',' ')}, /* Kanuri [macrolanguage] */ + {HB_TAG('k','s',' ',' '), HB_TAG('K','S','H',' ')}, /* Kashmiri */ + {HB_TAG('k','u',' ',' '), HB_TAG('K','U','R',' ')}, /* Kurdish [macrolanguage] */ + {HB_TAG('k','v',' ',' '), HB_TAG('K','O','M',' ')}, /* Komi [macrolanguage] */ + {HB_TAG('k','w',' ',' '), HB_TAG('C','O','R',' ')}, /* Cornish */ + {HB_TAG('k','y',' ',' '), HB_TAG('K','I','R',' ')}, /* Kirghiz (Kyrgyz) */ + {HB_TAG('l','a',' ',' '), HB_TAG('L','A','T',' ')}, /* Latin */ + {HB_TAG('l','b',' ',' '), HB_TAG('L','T','Z',' ')}, /* Luxembourgish */ + {HB_TAG('l','g',' ',' '), HB_TAG('L','U','G',' ')}, /* Ganda */ + {HB_TAG('l','i',' ',' '), HB_TAG('L','I','M',' ')}, /* Limburgish */ + {HB_TAG('l','n',' ',' '), HB_TAG('L','I','N',' ')}, /* Lingala */ + {HB_TAG('l','o',' ',' '), HB_TAG('L','A','O',' ')}, /* Lao */ + {HB_TAG('l','t',' ',' '), HB_TAG('L','T','H',' ')}, /* Lithuanian */ + {HB_TAG('l','u',' ',' '), HB_TAG('L','U','B',' ')}, /* Luba-Katanga */ + {HB_TAG('l','v',' ',' '), HB_TAG('L','V','I',' ')}, /* Latvian [macrolanguage] */ + {HB_TAG('m','g',' ',' '), HB_TAG('M','L','G',' ')}, /* Malagasy [macrolanguage] */ + {HB_TAG('m','h',' ',' '), HB_TAG('M','A','H',' ')}, /* Marshallese */ + {HB_TAG('m','i',' ',' '), HB_TAG('M','R','I',' ')}, /* Maori */ + {HB_TAG('m','k',' ',' '), HB_TAG('M','K','D',' ')}, /* Macedonian */ + {HB_TAG('m','l',' ',' '), HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */ + {HB_TAG('m','l',' ',' '), HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */ + {HB_TAG('m','n',' ',' '), HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */ + {HB_TAG('m','o',' ',' '), HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */ + {HB_TAG('m','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Moldavian (retired code) -> Romanian */ + {HB_TAG('m','r',' ',' '), HB_TAG('M','A','R',' ')}, /* Marathi */ + {HB_TAG('m','s',' ',' '), HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */ + {HB_TAG('m','t',' ',' '), HB_TAG('M','T','S',' ')}, /* Maltese */ + {HB_TAG('m','y',' ',' '), HB_TAG('B','R','M',' ')}, /* Burmese */ + {HB_TAG('n','a',' ',' '), HB_TAG('N','A','U',' ')}, /* Nauru -> Nauruan */ + {HB_TAG('n','b',' ',' '), HB_TAG('N','O','R',' ')}, /* Norwegian Bokmål -> Norwegian */ + {HB_TAG('n','d',' ',' '), HB_TAG('N','D','B',' ')}, /* North Ndebele -> Ndebele */ + {HB_TAG('n','e',' ',' '), HB_TAG('N','E','P',' ')}, /* Nepali [macrolanguage] */ + {HB_TAG('n','g',' ',' '), HB_TAG('N','D','G',' ')}, /* Ndonga */ + {HB_TAG('n','l',' ',' '), HB_TAG('N','L','D',' ')}, /* Dutch */ + {HB_TAG('n','n',' ',' '), HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */ + {HB_TAG('n','o',' ',' '), HB_TAG('N','O','R',' ')}, /* Norwegian [macrolanguage] */ + {HB_TAG('n','r',' ',' '), HB_TAG('N','D','B',' ')}, /* South Ndebele -> Ndebele */ + {HB_TAG('n','v',' ',' '), HB_TAG('N','A','V',' ')}, /* Navajo */ + {HB_TAG('n','v',' ',' '), HB_TAG('A','T','H',' ')}, /* Navajo -> Athapaskan */ + {HB_TAG('n','y',' ',' '), HB_TAG('C','H','I',' ')}, /* Chichewa (Chewa, Nyanja) */ + {HB_TAG('o','c',' ',' '), HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ + {HB_TAG('o','j',' ',' '), HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */ + {HB_TAG('o','m',' ',' '), HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ + {HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */ + {HB_TAG('o','s',' ',' '), HB_TAG('O','S','S',' ')}, /* Ossetian */ + {HB_TAG('p','a',' ',' '), HB_TAG('P','A','N',' ')}, /* Punjabi */ + {HB_TAG('p','i',' ',' '), HB_TAG('P','A','L',' ')}, /* Pali */ + {HB_TAG('p','l',' ',' '), HB_TAG('P','L','K',' ')}, /* Polish */ + {HB_TAG('p','s',' ',' '), HB_TAG('P','A','S',' ')}, /* Pashto [macrolanguage] */ + {HB_TAG('p','t',' ',' '), HB_TAG('P','T','G',' ')}, /* Portuguese */ + {HB_TAG('q','u',' ',' '), HB_TAG('Q','U','Z',' ')}, /* Quechua [macrolanguage] */ + {HB_TAG('r','m',' ',' '), HB_TAG('R','M','S',' ')}, /* Romansh */ + {HB_TAG('r','n',' ',' '), HB_TAG('R','U','N',' ')}, /* Rundi */ + {HB_TAG('r','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Romanian */ + {HB_TAG('r','u',' ',' '), HB_TAG('R','U','S',' ')}, /* Russian */ + {HB_TAG('r','w',' ',' '), HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ + {HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit */ + {HB_TAG('s','c',' ',' '), HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ + {HB_TAG('s','d',' ',' '), HB_TAG('S','N','D',' ')}, /* Sindhi */ + {HB_TAG('s','e',' ',' '), HB_TAG('N','S','M',' ')}, /* Northern Sami */ + {HB_TAG('s','g',' ',' '), HB_TAG('S','G','O',' ')}, /* Sango */ + {HB_TAG('s','h',' ',' '), HB_TAG('B','O','S',' ')}, /* Serbo-Croatian [macrolanguage] -> Bosnian */ + {HB_TAG('s','h',' ',' '), HB_TAG('H','R','V',' ')}, /* Serbo-Croatian [macrolanguage] -> Croatian */ + {HB_TAG('s','h',' ',' '), HB_TAG('S','R','B',' ')}, /* Serbo-Croatian [macrolanguage] -> Serbian */ + {HB_TAG('s','i',' ',' '), HB_TAG('S','N','H',' ')}, /* Sinhala (Sinhalese) */ + {HB_TAG('s','k',' ',' '), HB_TAG('S','K','Y',' ')}, /* Slovak */ + {HB_TAG('s','l',' ',' '), HB_TAG('S','L','V',' ')}, /* Slovenian */ + {HB_TAG('s','m',' ',' '), HB_TAG('S','M','O',' ')}, /* Samoan */ + {HB_TAG('s','n',' ',' '), HB_TAG('S','N','A','0')}, /* Shona */ + {HB_TAG('s','o',' ',' '), HB_TAG('S','M','L',' ')}, /* Somali */ + {HB_TAG('s','q',' ',' '), HB_TAG('S','Q','I',' ')}, /* Albanian [macrolanguage] */ + {HB_TAG('s','r',' ',' '), HB_TAG('S','R','B',' ')}, /* Serbian */ + {HB_TAG('s','s',' ',' '), HB_TAG('S','W','Z',' ')}, /* Swati */ + {HB_TAG('s','t',' ',' '), HB_TAG('S','O','T',' ')}, /* Southern Sotho */ + {HB_TAG('s','u',' ',' '), HB_TAG('S','U','N',' ')}, /* Sundanese */ + {HB_TAG('s','v',' ',' '), HB_TAG('S','V','E',' ')}, /* Swedish */ + {HB_TAG('s','w',' ',' '), HB_TAG('S','W','K',' ')}, /* Swahili [macrolanguage] */ + {HB_TAG('t','a',' ',' '), HB_TAG('T','A','M',' ')}, /* Tamil */ + {HB_TAG('t','e',' ',' '), HB_TAG('T','E','L',' ')}, /* Telugu */ + {HB_TAG('t','g',' ',' '), HB_TAG('T','A','J',' ')}, /* Tajik -> Tajiki */ + {HB_TAG('t','h',' ',' '), HB_TAG('T','H','A',' ')}, /* Thai */ + {HB_TAG('t','i',' ',' '), HB_TAG('T','G','Y',' ')}, /* Tigrinya */ + {HB_TAG('t','k',' ',' '), HB_TAG('T','K','M',' ')}, /* Turkmen */ + {HB_TAG('t','l',' ',' '), HB_TAG('T','G','L',' ')}, /* Tagalog */ + {HB_TAG('t','n',' ',' '), HB_TAG('T','N','A',' ')}, /* Tswana */ + {HB_TAG('t','o',' ',' '), HB_TAG('T','G','N',' ')}, /* Tonga (Tonga Islands) -> Tongan */ + {HB_TAG('t','r',' ',' '), HB_TAG('T','R','K',' ')}, /* Turkish */ + {HB_TAG('t','s',' ',' '), HB_TAG('T','S','G',' ')}, /* Tsonga */ + {HB_TAG('t','t',' ',' '), HB_TAG('T','A','T',' ')}, /* Tatar */ + {HB_TAG('t','w',' ',' '), HB_TAG('T','W','I',' ')}, /* Twi */ + {HB_TAG('t','w',' ',' '), HB_TAG('A','K','A',' ')}, /* Twi -> Akan */ + {HB_TAG('t','y',' ',' '), HB_TAG('T','H','T',' ')}, /* Tahitian */ + {HB_TAG('u','g',' ',' '), HB_TAG('U','Y','G',' ')}, /* Uyghur */ + {HB_TAG('u','k',' ',' '), HB_TAG('U','K','R',' ')}, /* Ukrainian */ + {HB_TAG('u','r',' ',' '), HB_TAG('U','R','D',' ')}, /* Urdu */ + {HB_TAG('u','z',' ',' '), HB_TAG('U','Z','B',' ')}, /* Uzbek [macrolanguage] */ + {HB_TAG('v','e',' ',' '), HB_TAG('V','E','N',' ')}, /* Venda */ + {HB_TAG('v','i',' ',' '), HB_TAG('V','I','T',' ')}, /* Vietnamese */ + {HB_TAG('v','o',' ',' '), HB_TAG('V','O','L',' ')}, /* Volapük */ + {HB_TAG('w','a',' ',' '), HB_TAG('W','L','N',' ')}, /* Walloon */ + {HB_TAG('w','o',' ',' '), HB_TAG('W','L','F',' ')}, /* Wolof */ + {HB_TAG('x','h',' ',' '), HB_TAG('X','H','S',' ')}, /* Xhosa */ + {HB_TAG('y','i',' ',' '), HB_TAG('J','I','I',' ')}, /* Yiddish [macrolanguage] */ + {HB_TAG('y','o',' ',' '), HB_TAG('Y','B','A',' ')}, /* Yoruba */ + {HB_TAG('z','a',' ',' '), HB_TAG('Z','H','A',' ')}, /* Zhuang [macrolanguage] */ + {HB_TAG('z','h',' ',' '), HB_TAG('Z','H','S',' ')}, /* Chinese, Simplified [macrolanguage] */ + {HB_TAG('z','u',' ',' '), HB_TAG('Z','U','L',' ')}, /* Zulu */ +}; + +#ifndef HB_NO_LANGUAGE_LONG +static const LangTag ot_languages3[] = { + {HB_TAG('a','a','e',' '), HB_TAG('S','Q','I',' ')}, /* Arbëreshë Albanian -> Albanian */ + {HB_TAG('a','a','o',' '), HB_TAG('A','R','A',' ')}, /* Algerian Saharan Arabic -> Arabic */ + {HB_TAG('a','a','t',' '), HB_TAG('S','Q','I',' ')}, /* Arvanitika Albanian -> Albanian */ + {HB_TAG('a','b','a',' '), HB_TAG_NONE }, /* Abé != Abaza */ + {HB_TAG('a','b','h',' '), HB_TAG('A','R','A',' ')}, /* Tajiki Arabic -> Arabic */ + {HB_TAG('a','b','q',' '), HB_TAG('A','B','A',' ')}, /* Abaza */ + {HB_TAG('a','b','s',' '), HB_TAG('C','P','P',' ')}, /* Ambonese Malay -> Creoles */ + {HB_TAG('a','b','v',' '), HB_TAG('A','R','A',' ')}, /* Baharna Arabic -> Arabic */ + {HB_TAG('a','c','f',' '), HB_TAG('F','A','N',' ')}, /* Saint Lucian Creole French -> French Antillean */ + {HB_TAG('a','c','f',' '), HB_TAG('C','P','P',' ')}, /* Saint Lucian Creole French -> Creoles */ +/*{HB_TAG('a','c','h',' '), HB_TAG('A','C','H',' ')},*/ /* Acoli -> Acholi */ + {HB_TAG('a','c','m',' '), HB_TAG('A','R','A',' ')}, /* Mesopotamian Arabic -> Arabic */ + {HB_TAG('a','c','q',' '), HB_TAG('A','R','A',' ')}, /* Ta'izzi-Adeni Arabic -> Arabic */ + {HB_TAG('a','c','r',' '), HB_TAG('A','C','R',' ')}, /* Achi */ + {HB_TAG('a','c','r',' '), HB_TAG('M','Y','N',' ')}, /* Achi -> Mayan */ + {HB_TAG('a','c','w',' '), HB_TAG('A','R','A',' ')}, /* Hijazi Arabic -> Arabic */ + {HB_TAG('a','c','x',' '), HB_TAG('A','R','A',' ')}, /* Omani Arabic -> Arabic */ + {HB_TAG('a','c','y',' '), HB_TAG('A','R','A',' ')}, /* Cypriot Arabic -> Arabic */ + {HB_TAG('a','d','a',' '), HB_TAG('D','N','G',' ')}, /* Adangme -> Dangme */ + {HB_TAG('a','d','f',' '), HB_TAG('A','R','A',' ')}, /* Dhofari Arabic -> Arabic */ + {HB_TAG('a','d','p',' '), HB_TAG('D','Z','N',' ')}, /* Adap (retired code) -> Dzongkha */ +/*{HB_TAG('a','d','y',' '), HB_TAG('A','D','Y',' ')},*/ /* Adyghe */ + {HB_TAG('a','e','b',' '), HB_TAG('A','R','A',' ')}, /* Tunisian Arabic -> Arabic */ + {HB_TAG('a','e','c',' '), HB_TAG('A','R','A',' ')}, /* Saidi Arabic -> Arabic */ + {HB_TAG('a','f','b',' '), HB_TAG('A','R','A',' ')}, /* Gulf Arabic -> Arabic */ + {HB_TAG('a','f','k',' '), HB_TAG_NONE }, /* Nanubae != Afrikaans */ + {HB_TAG('a','f','s',' '), HB_TAG('C','P','P',' ')}, /* Afro-Seminole Creole -> Creoles */ + {HB_TAG('a','g','u',' '), HB_TAG('M','Y','N',' ')}, /* Aguacateco -> Mayan */ + {HB_TAG('a','g','w',' '), HB_TAG_NONE }, /* Kahua != Agaw */ + {HB_TAG('a','h','g',' '), HB_TAG('A','G','W',' ')}, /* Qimant -> Agaw */ + {HB_TAG('a','h','t',' '), HB_TAG('A','T','H',' ')}, /* Ahtena -> Athapaskan */ + {HB_TAG('a','i','g',' '), HB_TAG('C','P','P',' ')}, /* Antigua and Barbuda Creole English -> Creoles */ + {HB_TAG('a','i','i',' '), HB_TAG('S','W','A',' ')}, /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ + {HB_TAG('a','i','i',' '), HB_TAG('S','Y','R',' ')}, /* Assyrian Neo-Aramaic -> Syriac */ +/*{HB_TAG('a','i','o',' '), HB_TAG('A','I','O',' ')},*/ /* Aiton */ + {HB_TAG('a','i','w',' '), HB_TAG('A','R','I',' ')}, /* Aari */ + {HB_TAG('a','j','p',' '), HB_TAG('A','R','A',' ')}, /* South Levantine Arabic (retired code) -> Arabic */ + {HB_TAG('a','j','t',' '), HB_TAG('A','R','A',' ')}, /* Judeo-Tunisian Arabic (retired code) -> Arabic */ + {HB_TAG('a','k','b',' '), HB_TAG('A','K','B',' ')}, /* Batak Angkola */ + {HB_TAG('a','k','b',' '), HB_TAG('B','T','K',' ')}, /* Batak Angkola -> Batak */ + {HB_TAG('a','l','n',' '), HB_TAG('S','Q','I',' ')}, /* Gheg Albanian -> Albanian */ + {HB_TAG('a','l','s',' '), HB_TAG('S','Q','I',' ')}, /* Tosk Albanian -> Albanian */ +/*{HB_TAG('a','l','t',' '), HB_TAG('A','L','T',' ')},*/ /* Southern Altai -> Altai */ + {HB_TAG('a','m','f',' '), HB_TAG('H','B','N',' ')}, /* Hamer-Banna -> Hammer-Banna */ + {HB_TAG('a','m','w',' '), HB_TAG('S','Y','R',' ')}, /* Western Neo-Aramaic -> Syriac */ +/*{HB_TAG('a','n','g',' '), HB_TAG('A','N','G',' ')},*/ /* Old English (ca. 450-1100) -> Anglo-Saxon */ + {HB_TAG('a','o','a',' '), HB_TAG('C','P','P',' ')}, /* Angolar -> Creoles */ + {HB_TAG('a','p','a',' '), HB_TAG('A','T','H',' ')}, /* Apache [collection] -> Athapaskan */ + {HB_TAG('a','p','c',' '), HB_TAG('A','R','A',' ')}, /* Levantine Arabic -> Arabic */ + {HB_TAG('a','p','d',' '), HB_TAG('A','R','A',' ')}, /* Sudanese Arabic -> Arabic */ + {HB_TAG('a','p','j',' '), HB_TAG('A','T','H',' ')}, /* Jicarilla Apache -> Athapaskan */ + {HB_TAG('a','p','k',' '), HB_TAG('A','T','H',' ')}, /* Kiowa Apache -> Athapaskan */ + {HB_TAG('a','p','l',' '), HB_TAG('A','T','H',' ')}, /* Lipan Apache -> Athapaskan */ + {HB_TAG('a','p','m',' '), HB_TAG('A','T','H',' ')}, /* Mescalero-Chiricahua Apache -> Athapaskan */ + {HB_TAG('a','p','w',' '), HB_TAG('A','T','H',' ')}, /* Western Apache -> Athapaskan */ + {HB_TAG('a','r','b',' '), HB_TAG('A','R','A',' ')}, /* Standard Arabic -> Arabic */ + {HB_TAG('a','r','i',' '), HB_TAG_NONE }, /* Arikara != Aari */ + {HB_TAG('a','r','k',' '), HB_TAG_NONE }, /* Arikapú != Rakhine */ + {HB_TAG('a','r','n',' '), HB_TAG('M','A','P',' ')}, /* Mapudungun */ + {HB_TAG('a','r','q',' '), HB_TAG('A','R','A',' ')}, /* Algerian Arabic -> Arabic */ + {HB_TAG('a','r','s',' '), HB_TAG('A','R','A',' ')}, /* Najdi Arabic -> Arabic */ + {HB_TAG('a','r','y',' '), HB_TAG('M','O','R',' ')}, /* Moroccan Arabic -> Moroccan */ + {HB_TAG('a','r','y',' '), HB_TAG('A','R','A',' ')}, /* Moroccan Arabic -> Arabic */ + {HB_TAG('a','r','z',' '), HB_TAG('A','R','A',' ')}, /* Egyptian Arabic -> Arabic */ +/*{HB_TAG('a','s','t',' '), HB_TAG('A','S','T',' ')},*/ /* Asturian */ +/*{HB_TAG('a','t','h',' '), HB_TAG('A','T','H',' ')},*/ /* Athapascan [collection] -> Athapaskan */ + {HB_TAG('a','t','j',' '), HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */ + {HB_TAG('a','t','v',' '), HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */ + {HB_TAG('a','u','j',' '), HB_TAG('B','B','R',' ')}, /* Awjilah -> Berber */ + {HB_TAG('a','u','z',' '), HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */ + {HB_TAG('a','v','l',' '), HB_TAG('A','R','A',' ')}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ +/*{HB_TAG('a','v','n',' '), HB_TAG('A','V','N',' ')},*/ /* Avatime */ +/*{HB_TAG('a','w','a',' '), HB_TAG('A','W','A',' ')},*/ /* Awadhi */ + {HB_TAG('a','y','c',' '), HB_TAG('A','Y','M',' ')}, /* Southern Aymara -> Aymara */ + {HB_TAG('a','y','h',' '), HB_TAG('A','R','A',' ')}, /* Hadrami Arabic -> Arabic */ + {HB_TAG('a','y','l',' '), HB_TAG('A','R','A',' ')}, /* Libyan Arabic -> Arabic */ + {HB_TAG('a','y','n',' '), HB_TAG('A','R','A',' ')}, /* Sanaani Arabic -> Arabic */ + {HB_TAG('a','y','p',' '), HB_TAG('A','R','A',' ')}, /* North Mesopotamian Arabic -> Arabic */ + {HB_TAG('a','y','r',' '), HB_TAG('A','Y','M',' ')}, /* Central Aymara -> Aymara */ + {HB_TAG('a','z','b',' '), HB_TAG('A','Z','B',' ')}, /* South Azerbaijani -> Torki */ + {HB_TAG('a','z','b',' '), HB_TAG('A','Z','E',' ')}, /* South Azerbaijani -> Azerbaijani */ + {HB_TAG('a','z','d',' '), HB_TAG('N','A','H',' ')}, /* Eastern Durango Nahuatl -> Nahuatl */ + {HB_TAG('a','z','j',' '), HB_TAG('A','Z','E',' ')}, /* North Azerbaijani -> Azerbaijani */ + {HB_TAG('a','z','n',' '), HB_TAG('N','A','H',' ')}, /* Western Durango Nahuatl -> Nahuatl */ + {HB_TAG('a','z','z',' '), HB_TAG('N','A','H',' ')}, /* Highland Puebla Nahuatl -> Nahuatl */ + {HB_TAG('b','a','d',' '), HB_TAG('B','A','D','0')}, /* Banda [collection] */ + {HB_TAG('b','a','g',' '), HB_TAG_NONE }, /* Tuki != Baghelkhandi */ + {HB_TAG('b','a','h',' '), HB_TAG('C','P','P',' ')}, /* Bahamas Creole English -> Creoles */ + {HB_TAG('b','a','i',' '), HB_TAG('B','M','L',' ')}, /* Bamileke [collection] */ + {HB_TAG('b','a','l',' '), HB_TAG('B','L','I',' ')}, /* Baluchi [macrolanguage] */ +/*{HB_TAG('b','a','n',' '), HB_TAG('B','A','N',' ')},*/ /* Balinese */ +/*{HB_TAG('b','a','r',' '), HB_TAG('B','A','R',' ')},*/ /* Bavarian */ + {HB_TAG('b','a','u',' '), HB_TAG_NONE }, /* Bada (Nigeria) != Baulé */ + {HB_TAG('b','b','c',' '), HB_TAG('B','B','C',' ')}, /* Batak Toba */ + {HB_TAG('b','b','c',' '), HB_TAG('B','T','K',' ')}, /* Batak Toba -> Batak */ + {HB_TAG('b','b','j',' '), HB_TAG('B','M','L',' ')}, /* Ghomálá' -> Bamileke */ + {HB_TAG('b','b','p',' '), HB_TAG('B','A','D','0')}, /* West Central Banda -> Banda */ + {HB_TAG('b','b','r',' '), HB_TAG_NONE }, /* Girawa != Berber */ + {HB_TAG('b','b','z',' '), HB_TAG('A','R','A',' ')}, /* Babalia Creole Arabic (retired code) -> Arabic */ + {HB_TAG('b','c','c',' '), HB_TAG('B','L','I',' ')}, /* Southern Balochi -> Baluchi */ + {HB_TAG('b','c','h',' '), HB_TAG_NONE }, /* Bariai != Bench */ + {HB_TAG('b','c','i',' '), HB_TAG('B','A','U',' ')}, /* Baoulé -> Baulé */ + {HB_TAG('b','c','l',' '), HB_TAG('B','I','K',' ')}, /* Central Bikol -> Bikol */ + {HB_TAG('b','c','q',' '), HB_TAG('B','C','H',' ')}, /* Bench */ + {HB_TAG('b','c','r',' '), HB_TAG('A','T','H',' ')}, /* Babine -> Athapaskan */ +/*{HB_TAG('b','d','y',' '), HB_TAG('B','D','Y',' ')},*/ /* Bandjalang */ + {HB_TAG('b','e','a',' '), HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */ + {HB_TAG('b','e','b',' '), HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */ +/*{HB_TAG('b','e','m',' '), HB_TAG('B','E','M',' ')},*/ /* Bemba (Zambia) */ + {HB_TAG('b','e','r',' '), HB_TAG('B','B','R',' ')}, /* Berber [collection] */ + {HB_TAG('b','e','w',' '), HB_TAG('C','P','P',' ')}, /* Betawi -> Creoles */ + {HB_TAG('b','f','l',' '), HB_TAG('B','A','D','0')}, /* Banda-Ndélé -> Banda */ + {HB_TAG('b','f','q',' '), HB_TAG('B','A','D',' ')}, /* Badaga */ + {HB_TAG('b','f','t',' '), HB_TAG('B','L','T',' ')}, /* Balti */ + {HB_TAG('b','f','u',' '), HB_TAG('L','A','H',' ')}, /* Gahri -> Lahuli */ + {HB_TAG('b','f','y',' '), HB_TAG('B','A','G',' ')}, /* Bagheli -> Baghelkhandi */ +/*{HB_TAG('b','g','c',' '), HB_TAG('B','G','C',' ')},*/ /* Haryanvi */ + {HB_TAG('b','g','n',' '), HB_TAG('B','L','I',' ')}, /* Western Balochi -> Baluchi */ + {HB_TAG('b','g','p',' '), HB_TAG('B','L','I',' ')}, /* Eastern Balochi -> Baluchi */ + {HB_TAG('b','g','q',' '), HB_TAG('B','G','Q',' ')}, /* Bagri */ + {HB_TAG('b','g','q',' '), HB_TAG('R','A','J',' ')}, /* Bagri -> Rajasthani */ + {HB_TAG('b','g','r',' '), HB_TAG('Q','I','N',' ')}, /* Bawm Chin -> Chin */ + {HB_TAG('b','h','b',' '), HB_TAG('B','H','I',' ')}, /* Bhili */ +/*{HB_TAG('b','h','i',' '), HB_TAG('B','H','I',' ')},*/ /* Bhilali -> Bhili */ + {HB_TAG('b','h','k',' '), HB_TAG('B','I','K',' ')}, /* Albay Bicolano (retired code) -> Bikol */ +/*{HB_TAG('b','h','o',' '), HB_TAG('B','H','O',' ')},*/ /* Bhojpuri */ + {HB_TAG('b','h','r',' '), HB_TAG('M','L','G',' ')}, /* Bara Malagasy -> Malagasy */ +/*{HB_TAG('b','i','k',' '), HB_TAG('B','I','K',' ')},*/ /* Bikol [macrolanguage] */ + {HB_TAG('b','i','l',' '), HB_TAG_NONE }, /* Bile != Bilen */ + {HB_TAG('b','i','n',' '), HB_TAG('E','D','O',' ')}, /* Edo */ + {HB_TAG('b','i','u',' '), HB_TAG('Q','I','N',' ')}, /* Biete -> Chin */ +/*{HB_TAG('b','j','j',' '), HB_TAG('B','J','J',' ')},*/ /* Kanauji */ + {HB_TAG('b','j','n',' '), HB_TAG('M','L','Y',' ')}, /* Banjar -> Malay */ + {HB_TAG('b','j','o',' '), HB_TAG('B','A','D','0')}, /* Mid-Southern Banda -> Banda */ + {HB_TAG('b','j','q',' '), HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */ + {HB_TAG('b','j','s',' '), HB_TAG('C','P','P',' ')}, /* Bajan -> Creoles */ + {HB_TAG('b','j','t',' '), HB_TAG('B','L','N',' ')}, /* Balanta-Ganja -> Balante */ + {HB_TAG('b','k','f',' '), HB_TAG_NONE }, /* Beeke != Blackfoot */ + {HB_TAG('b','k','o',' '), HB_TAG('B','M','L',' ')}, /* Kwa' -> Bamileke */ + {HB_TAG('b','l','a',' '), HB_TAG('B','K','F',' ')}, /* Siksika -> Blackfoot */ + {HB_TAG('b','l','e',' '), HB_TAG('B','L','N',' ')}, /* Balanta-Kentohe -> Balante */ + {HB_TAG('b','l','g',' '), HB_TAG('I','B','A',' ')}, /* Balau (retired code) -> Iban */ + {HB_TAG('b','l','i',' '), HB_TAG_NONE }, /* Bolia != Baluchi */ + {HB_TAG('b','l','k',' '), HB_TAG('B','L','K',' ')}, /* Pa’o Karen */ + {HB_TAG('b','l','k',' '), HB_TAG('K','R','N',' ')}, /* Pa'o Karen -> Karen */ + {HB_TAG('b','l','n',' '), HB_TAG('B','I','K',' ')}, /* Southern Catanduanes Bikol -> Bikol */ + {HB_TAG('b','l','t',' '), HB_TAG_NONE }, /* Tai Dam != Balti */ + {HB_TAG('b','m','b',' '), HB_TAG_NONE }, /* Bembe != Bambara (Bamanankan) */ + {HB_TAG('b','m','l',' '), HB_TAG_NONE }, /* Bomboli != Bamileke */ + {HB_TAG('b','m','m',' '), HB_TAG('M','L','G',' ')}, /* Northern Betsimisaraka Malagasy -> Malagasy */ + {HB_TAG('b','p','d',' '), HB_TAG('B','A','D','0')}, /* Banda-Banda -> Banda */ + {HB_TAG('b','p','l',' '), HB_TAG('C','P','P',' ')}, /* Broome Pearling Lugger Pidgin -> Creoles */ + {HB_TAG('b','p','q',' '), HB_TAG('C','P','P',' ')}, /* Banda Malay -> Creoles */ +/*{HB_TAG('b','p','y',' '), HB_TAG('B','P','Y',' ')},*/ /* Bishnupriya -> Bishnupriya Manipuri */ + {HB_TAG('b','q','i',' '), HB_TAG('L','R','C',' ')}, /* Bakhtiari -> Luri */ + {HB_TAG('b','q','k',' '), HB_TAG('B','A','D','0')}, /* Banda-Mbrès -> Banda */ + {HB_TAG('b','r','a',' '), HB_TAG('B','R','I',' ')}, /* Braj -> Braj Bhasha */ + {HB_TAG('b','r','c',' '), HB_TAG('C','P','P',' ')}, /* Berbice Creole Dutch -> Creoles */ +/*{HB_TAG('b','r','h',' '), HB_TAG('B','R','H',' ')},*/ /* Brahui */ + {HB_TAG('b','r','i',' '), HB_TAG_NONE }, /* Mokpwe != Braj Bhasha */ + {HB_TAG('b','r','m',' '), HB_TAG_NONE }, /* Barambu != Burmese */ +/*{HB_TAG('b','r','x',' '), HB_TAG('B','R','X',' ')},*/ /* Bodo (India) */ + {HB_TAG('b','s','h',' '), HB_TAG_NONE }, /* Kati != Bashkir */ +/*{HB_TAG('b','s','k',' '), HB_TAG('B','S','K',' ')},*/ /* Burushaski */ + {HB_TAG('b','t','b',' '), HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) (retired code) */ + {HB_TAG('b','t','d',' '), HB_TAG('B','T','D',' ')}, /* Batak Dairi (Pakpak) */ + {HB_TAG('b','t','d',' '), HB_TAG('B','T','K',' ')}, /* Batak Dairi -> Batak */ + {HB_TAG('b','t','i',' '), HB_TAG_NONE }, /* Burate != Beti */ + {HB_TAG('b','t','j',' '), HB_TAG('M','L','Y',' ')}, /* Bacanese Malay -> Malay */ +/*{HB_TAG('b','t','k',' '), HB_TAG('B','T','K',' ')},*/ /* Batak [collection] */ + {HB_TAG('b','t','m',' '), HB_TAG('B','T','M',' ')}, /* Batak Mandailing */ + {HB_TAG('b','t','m',' '), HB_TAG('B','T','K',' ')}, /* Batak Mandailing -> Batak */ + {HB_TAG('b','t','o',' '), HB_TAG('B','I','K',' ')}, /* Rinconada Bikol -> Bikol */ + {HB_TAG('b','t','s',' '), HB_TAG('B','T','S',' ')}, /* Batak Simalungun */ + {HB_TAG('b','t','s',' '), HB_TAG('B','T','K',' ')}, /* Batak Simalungun -> Batak */ + {HB_TAG('b','t','x',' '), HB_TAG('B','T','X',' ')}, /* Batak Karo */ + {HB_TAG('b','t','x',' '), HB_TAG('B','T','K',' ')}, /* Batak Karo -> Batak */ + {HB_TAG('b','t','z',' '), HB_TAG('B','T','Z',' ')}, /* Batak Alas-Kluet */ + {HB_TAG('b','t','z',' '), HB_TAG('B','T','K',' ')}, /* Batak Alas-Kluet -> Batak */ +/*{HB_TAG('b','u','g',' '), HB_TAG('B','U','G',' ')},*/ /* Buginese -> Bugis */ + {HB_TAG('b','u','m',' '), HB_TAG('B','T','I',' ')}, /* Bulu (Cameroon) -> Beti */ + {HB_TAG('b','v','e',' '), HB_TAG('M','L','Y',' ')}, /* Berau Malay -> Malay */ + {HB_TAG('b','v','u',' '), HB_TAG('M','L','Y',' ')}, /* Bukit Malay -> Malay */ + {HB_TAG('b','w','e',' '), HB_TAG('K','R','N',' ')}, /* Bwe Karen -> Karen */ + {HB_TAG('b','x','k',' '), HB_TAG('L','U','H',' ')}, /* Bukusu -> Luyia */ + {HB_TAG('b','x','o',' '), HB_TAG('C','P','P',' ')}, /* Barikanchi -> Creoles */ + {HB_TAG('b','x','p',' '), HB_TAG('B','T','I',' ')}, /* Bebil -> Beti */ + {HB_TAG('b','x','r',' '), HB_TAG('R','B','U',' ')}, /* Russia Buriat -> Russian Buriat */ + {HB_TAG('b','y','n',' '), HB_TAG('B','I','L',' ')}, /* Bilin -> Bilen */ + {HB_TAG('b','y','v',' '), HB_TAG('B','Y','V',' ')}, /* Medumba */ + {HB_TAG('b','y','v',' '), HB_TAG('B','M','L',' ')}, /* Medumba -> Bamileke */ + {HB_TAG('b','z','c',' '), HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy -> Malagasy */ + {HB_TAG('b','z','j',' '), HB_TAG('C','P','P',' ')}, /* Belize Kriol English -> Creoles */ + {HB_TAG('b','z','k',' '), HB_TAG('C','P','P',' ')}, /* Nicaragua Creole English -> Creoles */ + {HB_TAG('c','a','a',' '), HB_TAG('M','Y','N',' ')}, /* Chortí -> Mayan */ + {HB_TAG('c','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Chuj -> Mayan */ + {HB_TAG('c','a','f',' '), HB_TAG('C','R','R',' ')}, /* Southern Carrier -> Carrier */ + {HB_TAG('c','a','f',' '), HB_TAG('A','T','H',' ')}, /* Southern Carrier -> Athapaskan */ + {HB_TAG('c','a','k',' '), HB_TAG('C','A','K',' ')}, /* Kaqchikel */ + {HB_TAG('c','a','k',' '), HB_TAG('M','Y','N',' ')}, /* Kaqchikel -> Mayan */ + {HB_TAG('c','b','k',' '), HB_TAG('C','B','K',' ')}, /* Chavacano -> Zamboanga Chavacano */ + {HB_TAG('c','b','k',' '), HB_TAG('C','P','P',' ')}, /* Chavacano -> Creoles */ + {HB_TAG('c','b','l',' '), HB_TAG('Q','I','N',' ')}, /* Bualkhaw Chin -> Chin */ + {HB_TAG('c','c','l',' '), HB_TAG('C','P','P',' ')}, /* Cutchi-Swahili -> Creoles */ + {HB_TAG('c','c','m',' '), HB_TAG('C','P','P',' ')}, /* Malaccan Creole Malay -> Creoles */ + {HB_TAG('c','c','o',' '), HB_TAG('C','C','H','N')}, /* Comaltepec Chinantec -> Chinantec */ + {HB_TAG('c','c','q',' '), HB_TAG('A','R','K',' ')}, /* Chaungtha (retired code) -> Rakhine */ + {HB_TAG('c','d','o',' '), HB_TAG('Z','H','S',' ')}, /* Min Dong Chinese -> Chinese, Simplified */ +/*{HB_TAG('c','e','b',' '), HB_TAG('C','E','B',' ')},*/ /* Cebuano */ + {HB_TAG('c','e','k',' '), HB_TAG('Q','I','N',' ')}, /* Eastern Khumi Chin -> Chin */ + {HB_TAG('c','e','y',' '), HB_TAG('Q','I','N',' ')}, /* Ekai Chin -> Chin */ + {HB_TAG('c','f','m',' '), HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) */ + {HB_TAG('c','f','m',' '), HB_TAG('Q','I','N',' ')}, /* Falam Chin -> Chin */ +/*{HB_TAG('c','g','g',' '), HB_TAG('C','G','G',' ')},*/ /* Chiga */ + {HB_TAG('c','h','f',' '), HB_TAG('M','Y','N',' ')}, /* Tabasco Chontal -> Mayan */ + {HB_TAG('c','h','g',' '), HB_TAG_NONE }, /* Chagatai != Chaha Gurage */ + {HB_TAG('c','h','h',' '), HB_TAG_NONE }, /* Chinook != Chattisgarhi */ + {HB_TAG('c','h','j',' '), HB_TAG('C','C','H','N')}, /* Ojitlán Chinantec -> Chinantec */ + {HB_TAG('c','h','k',' '), HB_TAG('C','H','K','0')}, /* Chuukese */ + {HB_TAG('c','h','m',' '), HB_TAG('H','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> High Mari */ + {HB_TAG('c','h','m',' '), HB_TAG('L','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> Low Mari */ + {HB_TAG('c','h','n',' '), HB_TAG('C','P','P',' ')}, /* Chinook jargon -> Creoles */ +/*{HB_TAG('c','h','o',' '), HB_TAG('C','H','O',' ')},*/ /* Choctaw */ + {HB_TAG('c','h','p',' '), HB_TAG('C','H','P',' ')}, /* Chipewyan */ + {HB_TAG('c','h','p',' '), HB_TAG('S','A','Y',' ')}, /* Chipewyan -> Sayisi */ + {HB_TAG('c','h','p',' '), HB_TAG('A','T','H',' ')}, /* Chipewyan -> Athapaskan */ + {HB_TAG('c','h','q',' '), HB_TAG('C','C','H','N')}, /* Quiotepec Chinantec -> Chinantec */ +/*{HB_TAG('c','h','r',' '), HB_TAG('C','H','R',' ')},*/ /* Cherokee */ +/*{HB_TAG('c','h','y',' '), HB_TAG('C','H','Y',' ')},*/ /* Cheyenne */ + {HB_TAG('c','h','z',' '), HB_TAG('C','C','H','N')}, /* Ozumacín Chinantec -> Chinantec */ + {HB_TAG('c','i','w',' '), HB_TAG('O','J','B',' ')}, /* Chippewa -> Ojibway */ +/*{HB_TAG('c','j','a',' '), HB_TAG('C','J','A',' ')},*/ /* Western Cham */ +/*{HB_TAG('c','j','m',' '), HB_TAG('C','J','M',' ')},*/ /* Eastern Cham */ + {HB_TAG('c','j','y',' '), HB_TAG('Z','H','S',' ')}, /* Jinyu Chinese -> Chinese, Simplified */ + {HB_TAG('c','k','a',' '), HB_TAG('Q','I','N',' ')}, /* Khumi Awa Chin (retired code) -> Chin */ + {HB_TAG('c','k','b',' '), HB_TAG('K','U','R',' ')}, /* Central Kurdish -> Kurdish */ + {HB_TAG('c','k','n',' '), HB_TAG('Q','I','N',' ')}, /* Kaang Chin -> Chin */ + {HB_TAG('c','k','s',' '), HB_TAG('C','P','P',' ')}, /* Tayo -> Creoles */ + {HB_TAG('c','k','t',' '), HB_TAG('C','H','K',' ')}, /* Chukot -> Chukchi */ + {HB_TAG('c','k','z',' '), HB_TAG('M','Y','N',' ')}, /* Cakchiquel-Quiché Mixed Language -> Mayan */ + {HB_TAG('c','l','c',' '), HB_TAG('A','T','H',' ')}, /* Chilcotin -> Athapaskan */ + {HB_TAG('c','l','d',' '), HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */ + {HB_TAG('c','l','e',' '), HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */ + {HB_TAG('c','l','j',' '), HB_TAG('Q','I','N',' ')}, /* Laitu Chin -> Chin */ + {HB_TAG('c','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Lautu Chin -> Chin */ + {HB_TAG('c','m','n',' '), HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese, Simplified */ + {HB_TAG('c','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ + {HB_TAG('c','n','b',' '), HB_TAG('Q','I','N',' ')}, /* Chinbon Chin -> Chin */ + {HB_TAG('c','n','h',' '), HB_TAG('Q','I','N',' ')}, /* Hakha Chin -> Chin */ + {HB_TAG('c','n','k',' '), HB_TAG('Q','I','N',' ')}, /* Khumi Chin -> Chin */ + {HB_TAG('c','n','l',' '), HB_TAG('C','C','H','N')}, /* Lalana Chinantec -> Chinantec */ + {HB_TAG('c','n','p',' '), HB_TAG('Z','H','S',' ')}, /* Northern Ping Chinese -> Chinese, Simplified */ + {HB_TAG('c','n','r',' '), HB_TAG('S','R','B',' ')}, /* Montenegrin -> Serbian */ + {HB_TAG('c','n','t',' '), HB_TAG('C','C','H','N')}, /* Tepetotutla Chinantec -> Chinantec */ + {HB_TAG('c','n','u',' '), HB_TAG('B','B','R',' ')}, /* Chenoua -> Berber */ + {HB_TAG('c','n','w',' '), HB_TAG('Q','I','N',' ')}, /* Ngawn Chin -> Chin */ + {HB_TAG('c','o','a',' '), HB_TAG('M','L','Y',' ')}, /* Cocos Islands Malay -> Malay */ + {HB_TAG('c','o','b',' '), HB_TAG('M','Y','N',' ')}, /* Chicomuceltec -> Mayan */ +/*{HB_TAG('c','o','p',' '), HB_TAG('C','O','P',' ')},*/ /* Coptic */ + {HB_TAG('c','o','q',' '), HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */ + {HB_TAG('c','p','a',' '), HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */ + {HB_TAG('c','p','e',' '), HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','p','f',' '), HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','p','i',' '), HB_TAG('C','P','P',' ')}, /* Chinese Pidgin English -> Creoles */ +/*{HB_TAG('c','p','p',' '), HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','p','x',' '), HB_TAG('Z','H','S',' ')}, /* Pu-Xian Chinese -> Chinese, Simplified */ + {HB_TAG('c','q','d',' '), HB_TAG('H','M','N',' ')}, /* Chuanqiandian Cluster Miao -> Hmong */ + {HB_TAG('c','q','u',' '), HB_TAG('Q','U','H',' ')}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ + {HB_TAG('c','q','u',' '), HB_TAG('Q','U','Z',' ')}, /* Chilean Quechua (retired code) -> Quechua */ + {HB_TAG('c','r','h',' '), HB_TAG('C','R','T',' ')}, /* Crimean Tatar */ + {HB_TAG('c','r','i',' '), HB_TAG('C','P','P',' ')}, /* Sãotomense -> Creoles */ + {HB_TAG('c','r','j',' '), HB_TAG('E','C','R',' ')}, /* Southern East Cree -> Eastern Cree */ + {HB_TAG('c','r','j',' '), HB_TAG('Y','C','R',' ')}, /* Southern East Cree -> Y-Cree */ + {HB_TAG('c','r','j',' '), HB_TAG('C','R','E',' ')}, /* Southern East Cree -> Cree */ + {HB_TAG('c','r','k',' '), HB_TAG('W','C','R',' ')}, /* Plains Cree -> West-Cree */ + {HB_TAG('c','r','k',' '), HB_TAG('Y','C','R',' ')}, /* Plains Cree -> Y-Cree */ + {HB_TAG('c','r','k',' '), HB_TAG('C','R','E',' ')}, /* Plains Cree -> Cree */ + {HB_TAG('c','r','l',' '), HB_TAG('E','C','R',' ')}, /* Northern East Cree -> Eastern Cree */ + {HB_TAG('c','r','l',' '), HB_TAG('Y','C','R',' ')}, /* Northern East Cree -> Y-Cree */ + {HB_TAG('c','r','l',' '), HB_TAG('C','R','E',' ')}, /* Northern East Cree -> Cree */ + {HB_TAG('c','r','m',' '), HB_TAG('M','C','R',' ')}, /* Moose Cree */ + {HB_TAG('c','r','m',' '), HB_TAG('L','C','R',' ')}, /* Moose Cree -> L-Cree */ + {HB_TAG('c','r','m',' '), HB_TAG('C','R','E',' ')}, /* Moose Cree -> Cree */ + {HB_TAG('c','r','p',' '), HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [collection] -> Creoles */ + {HB_TAG('c','r','r',' '), HB_TAG_NONE }, /* Carolina Algonquian != Carrier */ + {HB_TAG('c','r','s',' '), HB_TAG('C','P','P',' ')}, /* Seselwa Creole French -> Creoles */ + {HB_TAG('c','r','t',' '), HB_TAG_NONE }, /* Iyojwa'ja Chorote != Crimean Tatar */ + {HB_TAG('c','r','x',' '), HB_TAG('C','R','R',' ')}, /* Carrier */ + {HB_TAG('c','r','x',' '), HB_TAG('A','T','H',' ')}, /* Carrier -> Athapaskan */ + {HB_TAG('c','s','a',' '), HB_TAG('C','C','H','N')}, /* Chiltepec Chinantec -> Chinantec */ +/*{HB_TAG('c','s','b',' '), HB_TAG('C','S','B',' ')},*/ /* Kashubian */ + {HB_TAG('c','s','h',' '), HB_TAG('Q','I','N',' ')}, /* Asho Chin -> Chin */ + {HB_TAG('c','s','j',' '), HB_TAG('Q','I','N',' ')}, /* Songlai Chin -> Chin */ + {HB_TAG('c','s','l',' '), HB_TAG_NONE }, /* Chinese Sign Language != Church Slavonic */ + {HB_TAG('c','s','o',' '), HB_TAG('C','C','H','N')}, /* Sochiapam Chinantec -> Chinantec */ + {HB_TAG('c','s','p',' '), HB_TAG('Z','H','S',' ')}, /* Southern Ping Chinese -> Chinese, Simplified */ + {HB_TAG('c','s','v',' '), HB_TAG('Q','I','N',' ')}, /* Sumtu Chin -> Chin */ + {HB_TAG('c','s','w',' '), HB_TAG('N','C','R',' ')}, /* Swampy Cree -> N-Cree */ + {HB_TAG('c','s','w',' '), HB_TAG('N','H','C',' ')}, /* Swampy Cree -> Norway House Cree */ + {HB_TAG('c','s','w',' '), HB_TAG('C','R','E',' ')}, /* Swampy Cree -> Cree */ + {HB_TAG('c','s','y',' '), HB_TAG('Q','I','N',' ')}, /* Siyin Chin -> Chin */ + {HB_TAG('c','t','c',' '), HB_TAG('A','T','H',' ')}, /* Chetco -> Athapaskan */ + {HB_TAG('c','t','d',' '), HB_TAG('Q','I','N',' ')}, /* Tedim Chin -> Chin */ + {HB_TAG('c','t','e',' '), HB_TAG('C','C','H','N')}, /* Tepinapa Chinantec -> Chinantec */ +/*{HB_TAG('c','t','g',' '), HB_TAG('C','T','G',' ')},*/ /* Chittagonian */ + {HB_TAG('c','t','h',' '), HB_TAG('Q','I','N',' ')}, /* Thaiphum Chin -> Chin */ + {HB_TAG('c','t','l',' '), HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */ + {HB_TAG('c','t','s',' '), HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */ +/*{HB_TAG('c','t','t',' '), HB_TAG('C','T','T',' ')},*/ /* Wayanad Chetti */ + {HB_TAG('c','t','u',' '), HB_TAG('M','Y','N',' ')}, /* Chol -> Mayan */ + {HB_TAG('c','u','c',' '), HB_TAG('C','C','H','N')}, /* Usila Chinantec -> Chinantec */ +/*{HB_TAG('c','u','k',' '), HB_TAG('C','U','K',' ')},*/ /* San Blas Kuna */ + {HB_TAG('c','v','n',' '), HB_TAG('C','C','H','N')}, /* Valle Nacional Chinantec -> Chinantec */ + {HB_TAG('c','w','d',' '), HB_TAG('D','C','R',' ')}, /* Woods Cree */ + {HB_TAG('c','w','d',' '), HB_TAG('T','C','R',' ')}, /* Woods Cree -> TH-Cree */ + {HB_TAG('c','w','d',' '), HB_TAG('C','R','E',' ')}, /* Woods Cree -> Cree */ + {HB_TAG('c','z','h',' '), HB_TAG('Z','H','S',' ')}, /* Huizhou Chinese -> Chinese, Simplified */ + {HB_TAG('c','z','o',' '), HB_TAG('Z','H','S',' ')}, /* Min Zhong Chinese -> Chinese, Simplified */ + {HB_TAG('c','z','t',' '), HB_TAG('Q','I','N',' ')}, /* Zotung Chin -> Chin */ +/*{HB_TAG('d','a','g',' '), HB_TAG('D','A','G',' ')},*/ /* Dagbani */ + {HB_TAG('d','a','o',' '), HB_TAG('Q','I','N',' ')}, /* Daai Chin -> Chin */ + {HB_TAG('d','a','p',' '), HB_TAG('N','I','S',' ')}, /* Nisi (India) (retired code) */ +/*{HB_TAG('d','a','r',' '), HB_TAG('D','A','R',' ')},*/ /* Dargwa */ +/*{HB_TAG('d','a','x',' '), HB_TAG('D','A','X',' ')},*/ /* Dayi */ + {HB_TAG('d','c','r',' '), HB_TAG('C','P','P',' ')}, /* Negerhollands -> Creoles */ + {HB_TAG('d','e','n',' '), HB_TAG('S','L','A',' ')}, /* Slave (Athapascan) [macrolanguage] -> Slavey */ + {HB_TAG('d','e','n',' '), HB_TAG('A','T','H',' ')}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ + {HB_TAG('d','e','p',' '), HB_TAG('C','P','P',' ')}, /* Pidgin Delaware -> Creoles */ + {HB_TAG('d','g','o',' '), HB_TAG('D','G','O',' ')}, /* Dogri (individual language) */ + {HB_TAG('d','g','o',' '), HB_TAG('D','G','R',' ')}, /* Dogri (macrolanguage) */ + {HB_TAG('d','g','r',' '), HB_TAG('A','T','H',' ')}, /* Dogrib -> Athapaskan */ + {HB_TAG('d','h','d',' '), HB_TAG('M','A','W',' ')}, /* Dhundari -> Marwari */ +/*{HB_TAG('d','h','g',' '), HB_TAG('D','H','G',' ')},*/ /* Dhangu */ + {HB_TAG('d','h','v',' '), HB_TAG_NONE }, /* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */ + {HB_TAG('d','i','b',' '), HB_TAG('D','N','K',' ')}, /* South Central Dinka -> Dinka */ + {HB_TAG('d','i','k',' '), HB_TAG('D','N','K',' ')}, /* Southwestern Dinka -> Dinka */ + {HB_TAG('d','i','n',' '), HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */ + {HB_TAG('d','i','p',' '), HB_TAG('D','N','K',' ')}, /* Northeastern Dinka -> Dinka */ + {HB_TAG('d','i','q',' '), HB_TAG('D','I','Q',' ')}, /* Dimli */ + {HB_TAG('d','i','q',' '), HB_TAG('Z','Z','A',' ')}, /* Dimli -> Zazaki */ + {HB_TAG('d','i','w',' '), HB_TAG('D','N','K',' ')}, /* Northwestern Dinka -> Dinka */ + {HB_TAG('d','j','e',' '), HB_TAG('D','J','R',' ')}, /* Zarma */ + {HB_TAG('d','j','k',' '), HB_TAG('C','P','P',' ')}, /* Eastern Maroon Creole -> Creoles */ + {HB_TAG('d','j','r',' '), HB_TAG('D','J','R','0')}, /* Djambarrpuyngu */ + {HB_TAG('d','k','s',' '), HB_TAG('D','N','K',' ')}, /* Southeastern Dinka -> Dinka */ + {HB_TAG('d','n','g',' '), HB_TAG('D','U','N',' ')}, /* Dungan */ +/*{HB_TAG('d','n','j',' '), HB_TAG('D','N','J',' ')},*/ /* Dan */ + {HB_TAG('d','n','k',' '), HB_TAG_NONE }, /* Dengka != Dinka */ + {HB_TAG('d','o','i',' '), HB_TAG('D','G','R',' ')}, /* Dogri (macrolanguage) [macrolanguage] */ + {HB_TAG('d','r','h',' '), HB_TAG('M','N','G',' ')}, /* Darkhat (retired code) -> Mongolian */ + {HB_TAG('d','r','i',' '), HB_TAG_NONE }, /* C'Lela != Dari */ + {HB_TAG('d','r','w',' '), HB_TAG('D','R','I',' ')}, /* Darwazi (retired code) -> Dari */ + {HB_TAG('d','r','w',' '), HB_TAG('F','A','R',' ')}, /* Darwazi (retired code) -> Persian */ + {HB_TAG('d','s','b',' '), HB_TAG('L','S','B',' ')}, /* Lower Sorbian */ + {HB_TAG('d','t','y',' '), HB_TAG('N','E','P',' ')}, /* Dotyali -> Nepali */ +/*{HB_TAG('d','u','j',' '), HB_TAG('D','U','J',' ')},*/ /* Dhuwal (retired code) */ + {HB_TAG('d','u','n',' '), HB_TAG_NONE }, /* Dusun Deyah != Dungan */ + {HB_TAG('d','u','p',' '), HB_TAG('M','L','Y',' ')}, /* Duano -> Malay */ + {HB_TAG('d','w','k',' '), HB_TAG('K','U','I',' ')}, /* Dawik Kui -> Kui */ + {HB_TAG('d','w','u',' '), HB_TAG('D','U','J',' ')}, /* Dhuwal */ + {HB_TAG('d','w','y',' '), HB_TAG('D','U','J',' ')}, /* Dhuwaya -> Dhuwal */ + {HB_TAG('d','y','u',' '), HB_TAG('J','U','L',' ')}, /* Dyula -> Jula */ + {HB_TAG('d','z','n',' '), HB_TAG_NONE }, /* Dzando != Dzongkha */ + {HB_TAG('e','c','r',' '), HB_TAG_NONE }, /* Eteocretan != Eastern Cree */ +/*{HB_TAG('e','f','i',' '), HB_TAG('E','F','I',' ')},*/ /* Efik */ + {HB_TAG('e','k','k',' '), HB_TAG('E','T','I',' ')}, /* Standard Estonian -> Estonian */ + {HB_TAG('e','k','y',' '), HB_TAG('K','R','N',' ')}, /* Eastern Kayah -> Karen */ + {HB_TAG('e','m','k',' '), HB_TAG('E','M','K',' ')}, /* Eastern Maninkakan */ + {HB_TAG('e','m','k',' '), HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan -> Maninka */ + {HB_TAG('e','m','y',' '), HB_TAG('M','Y','N',' ')}, /* Epigraphic Mayan -> Mayan */ + {HB_TAG('e','n','b',' '), HB_TAG('K','A','L',' ')}, /* Markweeta -> Kalenjin */ + {HB_TAG('e','n','f',' '), HB_TAG('F','N','E',' ')}, /* Forest Enets */ + {HB_TAG('e','n','h',' '), HB_TAG('T','N','E',' ')}, /* Tundra Enets */ + {HB_TAG('e','s','g',' '), HB_TAG('G','O','N',' ')}, /* Aheri Gondi -> Gondi */ + {HB_TAG('e','s','i',' '), HB_TAG('I','P','K',' ')}, /* North Alaskan Inupiatun -> Inupiat */ + {HB_TAG('e','s','k',' '), HB_TAG('I','P','K',' ')}, /* Northwest Alaska Inupiatun -> Inupiat */ +/*{HB_TAG('e','s','u',' '), HB_TAG('E','S','U',' ')},*/ /* Central Yupik */ + {HB_TAG('e','t','o',' '), HB_TAG('B','T','I',' ')}, /* Eton (Cameroon) -> Beti */ + {HB_TAG('e','u','q',' '), HB_TAG_NONE }, /* Basque [collection] != Basque */ + {HB_TAG('e','v','e',' '), HB_TAG('E','V','N',' ')}, /* Even */ + {HB_TAG('e','v','n',' '), HB_TAG('E','V','K',' ')}, /* Evenki */ + {HB_TAG('e','w','o',' '), HB_TAG('B','T','I',' ')}, /* Ewondo -> Beti */ + {HB_TAG('e','y','o',' '), HB_TAG('K','A','L',' ')}, /* Keiyo -> Kalenjin */ + {HB_TAG('f','a','b',' '), HB_TAG('C','P','P',' ')}, /* Fa d'Ambu -> Creoles */ + {HB_TAG('f','a','n',' '), HB_TAG('F','A','N','0')}, /* Fang (Equatorial Guinea) */ + {HB_TAG('f','a','n',' '), HB_TAG('B','T','I',' ')}, /* Fang (Equatorial Guinea) -> Beti */ + {HB_TAG('f','a','r',' '), HB_TAG_NONE }, /* Fataleka != Persian */ + {HB_TAG('f','a','t',' '), HB_TAG('F','A','T',' ')}, /* Fanti */ + {HB_TAG('f','a','t',' '), HB_TAG('A','K','A',' ')}, /* Fanti -> Akan */ + {HB_TAG('f','b','l',' '), HB_TAG('B','I','K',' ')}, /* West Albay Bikol -> Bikol */ + {HB_TAG('f','f','m',' '), HB_TAG('F','U','L',' ')}, /* Maasina Fulfulde -> Fulah */ + {HB_TAG('f','i','l',' '), HB_TAG('P','I','L',' ')}, /* Filipino */ + {HB_TAG('f','l','m',' '), HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) (retired code) */ + {HB_TAG('f','l','m',' '), HB_TAG('Q','I','N',' ')}, /* Falam Chin (retired code) -> Chin */ + {HB_TAG('f','m','p',' '), HB_TAG('F','M','P',' ')}, /* Fe’fe’ */ + {HB_TAG('f','m','p',' '), HB_TAG('B','M','L',' ')}, /* Fe'fe' -> Bamileke */ + {HB_TAG('f','n','g',' '), HB_TAG('C','P','P',' ')}, /* Fanagalo -> Creoles */ +/*{HB_TAG('f','o','n',' '), HB_TAG('F','O','N',' ')},*/ /* Fon */ + {HB_TAG('f','o','s',' '), HB_TAG_NONE }, /* Siraya != Faroese */ + {HB_TAG('f','p','e',' '), HB_TAG('C','P','P',' ')}, /* Fernando Po Creole English -> Creoles */ +/*{HB_TAG('f','r','c',' '), HB_TAG('F','R','C',' ')},*/ /* Cajun French */ +/*{HB_TAG('f','r','p',' '), HB_TAG('F','R','P',' ')},*/ /* Arpitan */ + {HB_TAG('f','u','b',' '), HB_TAG('F','U','L',' ')}, /* Adamawa Fulfulde -> Fulah */ + {HB_TAG('f','u','c',' '), HB_TAG('F','U','L',' ')}, /* Pulaar -> Fulah */ + {HB_TAG('f','u','e',' '), HB_TAG('F','U','L',' ')}, /* Borgu Fulfulde -> Fulah */ + {HB_TAG('f','u','f',' '), HB_TAG('F','T','A',' ')}, /* Pular -> Futa */ + {HB_TAG('f','u','f',' '), HB_TAG('F','U','L',' ')}, /* Pular -> Fulah */ + {HB_TAG('f','u','h',' '), HB_TAG('F','U','L',' ')}, /* Western Niger Fulfulde -> Fulah */ + {HB_TAG('f','u','i',' '), HB_TAG('F','U','L',' ')}, /* Bagirmi Fulfulde -> Fulah */ + {HB_TAG('f','u','q',' '), HB_TAG('F','U','L',' ')}, /* Central-Eastern Niger Fulfulde -> Fulah */ + {HB_TAG('f','u','r',' '), HB_TAG('F','R','L',' ')}, /* Friulian */ + {HB_TAG('f','u','v',' '), HB_TAG('F','U','V',' ')}, /* Nigerian Fulfulde */ + {HB_TAG('f','u','v',' '), HB_TAG('F','U','L',' ')}, /* Nigerian Fulfulde -> Fulah */ + {HB_TAG('g','a','a',' '), HB_TAG('G','A','D',' ')}, /* Ga */ + {HB_TAG('g','a','c',' '), HB_TAG('C','P','P',' ')}, /* Mixed Great Andamanese -> Creoles */ + {HB_TAG('g','a','d',' '), HB_TAG_NONE }, /* Gaddang != Ga */ + {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic (Gaelic) */ +/*{HB_TAG('g','a','g',' '), HB_TAG('G','A','G',' ')},*/ /* Gagauz */ + {HB_TAG('g','a','l',' '), HB_TAG_NONE }, /* Galolen != Galician */ + {HB_TAG('g','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese, Simplified */ + {HB_TAG('g','a','r',' '), HB_TAG_NONE }, /* Galeya != Garshuni */ + {HB_TAG('g','a','w',' '), HB_TAG_NONE }, /* Nobonob != Garhwali */ + {HB_TAG('g','a','x',' '), HB_TAG('O','R','O',' ')}, /* Borana-Arsi-Guji Oromo -> Oromo */ + {HB_TAG('g','a','z',' '), HB_TAG('O','R','O',' ')}, /* West Central Oromo -> Oromo */ + {HB_TAG('g','b','m',' '), HB_TAG('G','A','W',' ')}, /* Garhwali */ + {HB_TAG('g','c','e',' '), HB_TAG('A','T','H',' ')}, /* Galice -> Athapaskan */ + {HB_TAG('g','c','f',' '), HB_TAG('C','P','P',' ')}, /* Guadeloupean Creole French -> Creoles */ + {HB_TAG('g','c','l',' '), HB_TAG('C','P','P',' ')}, /* Grenadian Creole English -> Creoles */ + {HB_TAG('g','c','r',' '), HB_TAG('C','P','P',' ')}, /* Guianese Creole French -> Creoles */ + {HB_TAG('g','d','a',' '), HB_TAG('R','A','J',' ')}, /* Gade Lohar -> Rajasthani */ +/*{HB_TAG('g','e','z',' '), HB_TAG('G','E','Z',' ')},*/ /* Geez */ + {HB_TAG('g','g','o',' '), HB_TAG('G','O','N',' ')}, /* Southern Gondi (retired code) -> Gondi */ + {HB_TAG('g','h','a',' '), HB_TAG('B','B','R',' ')}, /* Ghadamès -> Berber */ + {HB_TAG('g','h','k',' '), HB_TAG('K','R','N',' ')}, /* Geko Karen -> Karen */ + {HB_TAG('g','h','o',' '), HB_TAG('B','B','R',' ')}, /* Ghomara -> Berber */ + {HB_TAG('g','i','b',' '), HB_TAG('C','P','P',' ')}, /* Gibanawa -> Creoles */ +/*{HB_TAG('g','i','h',' '), HB_TAG('G','I','H',' ')},*/ /* Githabul */ + {HB_TAG('g','i','l',' '), HB_TAG('G','I','L','0')}, /* Kiribati (Gilbertese) */ + {HB_TAG('g','j','u',' '), HB_TAG('R','A','J',' ')}, /* Gujari -> Rajasthani */ + {HB_TAG('g','k','p',' '), HB_TAG('G','K','P',' ')}, /* Guinea Kpelle -> Kpelle (Guinea) */ + {HB_TAG('g','k','p',' '), HB_TAG('K','P','L',' ')}, /* Guinea Kpelle -> Kpelle */ + {HB_TAG('g','l','d',' '), HB_TAG('N','A','N',' ')}, /* Nanai */ +/*{HB_TAG('g','l','k',' '), HB_TAG('G','L','K',' ')},*/ /* Gilaki */ + {HB_TAG('g','m','z',' '), HB_TAG_NONE }, /* Mgbolizhia != Gumuz */ + {HB_TAG('g','n','b',' '), HB_TAG('Q','I','N',' ')}, /* Gangte -> Chin */ +/*{HB_TAG('g','n','n',' '), HB_TAG('G','N','N',' ')},*/ /* Gumatj */ + {HB_TAG('g','n','o',' '), HB_TAG('G','O','N',' ')}, /* Northern Gondi -> Gondi */ + {HB_TAG('g','n','w',' '), HB_TAG('G','U','A',' ')}, /* Western Bolivian Guaraní -> Guarani */ +/*{HB_TAG('g','o','g',' '), HB_TAG('G','O','G',' ')},*/ /* Gogo */ + {HB_TAG('g','o','m',' '), HB_TAG('K','O','K',' ')}, /* Goan Konkani -> Konkani */ +/*{HB_TAG('g','o','n',' '), HB_TAG('G','O','N',' ')},*/ /* Gondi [macrolanguage] */ + {HB_TAG('g','o','q',' '), HB_TAG('C','P','P',' ')}, /* Gorap -> Creoles */ + {HB_TAG('g','o','x',' '), HB_TAG('B','A','D','0')}, /* Gobu -> Banda */ + {HB_TAG('g','p','e',' '), HB_TAG('C','P','P',' ')}, /* Ghanaian Pidgin English -> Creoles */ + {HB_TAG('g','r','o',' '), HB_TAG_NONE }, /* Groma != Garo */ + {HB_TAG('g','r','r',' '), HB_TAG('B','B','R',' ')}, /* Taznatit -> Berber */ + {HB_TAG('g','r','t',' '), HB_TAG('G','R','O',' ')}, /* Garo */ + {HB_TAG('g','r','u',' '), HB_TAG('S','O','G',' ')}, /* Kistane -> Sodo Gurage */ + {HB_TAG('g','s','w',' '), HB_TAG('A','L','S',' ')}, /* Alsatian */ + {HB_TAG('g','u','a',' '), HB_TAG_NONE }, /* Shiki != Guarani */ +/*{HB_TAG('g','u','c',' '), HB_TAG('G','U','C',' ')},*/ /* Wayuu */ +/*{HB_TAG('g','u','f',' '), HB_TAG('G','U','F',' ')},*/ /* Gupapuyngu */ + {HB_TAG('g','u','g',' '), HB_TAG('G','U','A',' ')}, /* Paraguayan Guaraní -> Guarani */ + {HB_TAG('g','u','i',' '), HB_TAG('G','U','A',' ')}, /* Eastern Bolivian Guaraní -> Guarani */ + {HB_TAG('g','u','k',' '), HB_TAG('G','M','Z',' ')}, /* Gumuz */ + {HB_TAG('g','u','l',' '), HB_TAG('C','P','P',' ')}, /* Sea Island Creole English -> Creoles */ + {HB_TAG('g','u','n',' '), HB_TAG('G','U','A',' ')}, /* Mbyá Guaraní -> Guarani */ +/*{HB_TAG('g','u','z',' '), HB_TAG('G','U','Z',' ')},*/ /* Gusii */ + {HB_TAG('g','w','i',' '), HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */ + {HB_TAG('g','y','n',' '), HB_TAG('C','P','P',' ')}, /* Guyanese Creole English -> Creoles */ + {HB_TAG('h','a','a',' '), HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */ + {HB_TAG('h','a','e',' '), HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ + {HB_TAG('h','a','i',' '), HB_TAG('H','A','I','0')}, /* Haida [macrolanguage] */ + {HB_TAG('h','a','k',' '), HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese, Simplified */ + {HB_TAG('h','a','l',' '), HB_TAG_NONE }, /* Halang != Halam (Falam Chin) */ + {HB_TAG('h','a','r',' '), HB_TAG('H','R','I',' ')}, /* Harari */ +/*{HB_TAG('h','a','w',' '), HB_TAG('H','A','W',' ')},*/ /* Hawaiian */ + {HB_TAG('h','a','x',' '), HB_TAG('H','A','I','0')}, /* Southern Haida -> Haida */ +/*{HB_TAG('h','a','y',' '), HB_TAG('H','A','Y',' ')},*/ /* Haya */ +/*{HB_TAG('h','a','z',' '), HB_TAG('H','A','Z',' ')},*/ /* Hazaragi */ + {HB_TAG('h','b','n',' '), HB_TAG_NONE }, /* Heiban != Hammer-Banna */ + {HB_TAG('h','c','a',' '), HB_TAG('C','P','P',' ')}, /* Andaman Creole Hindi -> Creoles */ + {HB_TAG('h','d','n',' '), HB_TAG('H','A','I','0')}, /* Northern Haida -> Haida */ + {HB_TAG('h','e','a',' '), HB_TAG('H','M','N',' ')}, /* Northern Qiandong Miao -> Hmong */ +/*{HB_TAG('h','e','i',' '), HB_TAG('H','E','I',' ')},*/ /* Heiltsuk */ +/*{HB_TAG('h','i','l',' '), HB_TAG('H','I','L',' ')},*/ /* Hiligaynon */ + {HB_TAG('h','j','i',' '), HB_TAG('M','L','Y',' ')}, /* Haji -> Malay */ + {HB_TAG('h','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Matu Chin -> Chin */ + {HB_TAG('h','m','a',' '), HB_TAG('H','M','N',' ')}, /* Southern Mashan Hmong -> Hmong */ + {HB_TAG('h','m','c',' '), HB_TAG('H','M','N',' ')}, /* Central Huishui Hmong -> Hmong */ + {HB_TAG('h','m','d',' '), HB_TAG('H','M','D',' ')}, /* Large Flowery Miao -> A-Hmao */ + {HB_TAG('h','m','d',' '), HB_TAG('H','M','N',' ')}, /* Large Flowery Miao -> Hmong */ + {HB_TAG('h','m','e',' '), HB_TAG('H','M','N',' ')}, /* Eastern Huishui Hmong -> Hmong */ + {HB_TAG('h','m','g',' '), HB_TAG('H','M','N',' ')}, /* Southwestern Guiyang Hmong -> Hmong */ + {HB_TAG('h','m','h',' '), HB_TAG('H','M','N',' ')}, /* Southwestern Huishui Hmong -> Hmong */ + {HB_TAG('h','m','i',' '), HB_TAG('H','M','N',' ')}, /* Northern Huishui Hmong -> Hmong */ + {HB_TAG('h','m','j',' '), HB_TAG('H','M','N',' ')}, /* Ge -> Hmong */ + {HB_TAG('h','m','l',' '), HB_TAG('H','M','N',' ')}, /* Luopohe Hmong -> Hmong */ + {HB_TAG('h','m','m',' '), HB_TAG('H','M','N',' ')}, /* Central Mashan Hmong -> Hmong */ +/*{HB_TAG('h','m','n',' '), HB_TAG('H','M','N',' ')},*/ /* Hmong [macrolanguage] */ + {HB_TAG('h','m','p',' '), HB_TAG('H','M','N',' ')}, /* Northern Mashan Hmong -> Hmong */ + {HB_TAG('h','m','q',' '), HB_TAG('H','M','N',' ')}, /* Eastern Qiandong Miao -> Hmong */ + {HB_TAG('h','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Hmar -> Chin */ + {HB_TAG('h','m','s',' '), HB_TAG('H','M','N',' ')}, /* Southern Qiandong Miao -> Hmong */ + {HB_TAG('h','m','w',' '), HB_TAG('H','M','N',' ')}, /* Western Mashan Hmong -> Hmong */ + {HB_TAG('h','m','y',' '), HB_TAG('H','M','N',' ')}, /* Southern Guiyang Hmong -> Hmong */ + {HB_TAG('h','m','z',' '), HB_TAG('H','M','Z',' ')}, /* Hmong Shua -> Hmong Shuat */ + {HB_TAG('h','m','z',' '), HB_TAG('H','M','N',' ')}, /* Hmong Shua -> Hmong */ +/*{HB_TAG('h','n','d',' '), HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ + {HB_TAG('h','n','e',' '), HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ + {HB_TAG('h','n','j',' '), HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */ + {HB_TAG('h','n','o',' '), HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */ + {HB_TAG('h','o','c',' '), HB_TAG('H','O',' ',' ')}, /* Ho */ + {HB_TAG('h','o','i',' '), HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */ + {HB_TAG('h','o','j',' '), HB_TAG('H','A','R',' ')}, /* Hadothi -> Harauti */ + {HB_TAG('h','o','j',' '), HB_TAG('R','A','J',' ')}, /* Hadothi -> Rajasthani */ + {HB_TAG('h','r','a',' '), HB_TAG('Q','I','N',' ')}, /* Hrangkhol -> Chin */ + {HB_TAG('h','r','m',' '), HB_TAG('H','M','N',' ')}, /* Horned Miao -> Hmong */ + {HB_TAG('h','s','b',' '), HB_TAG('U','S','B',' ')}, /* Upper Sorbian */ + {HB_TAG('h','s','n',' '), HB_TAG('Z','H','S',' ')}, /* Xiang Chinese -> Chinese, Simplified */ + {HB_TAG('h','u','j',' '), HB_TAG('H','M','N',' ')}, /* Northern Guiyang Hmong -> Hmong */ + {HB_TAG('h','u','p',' '), HB_TAG('A','T','H',' ')}, /* Hupa -> Athapaskan */ + {HB_TAG('h','u','s',' '), HB_TAG('M','Y','N',' ')}, /* Huastec -> Mayan */ + {HB_TAG('h','w','c',' '), HB_TAG('C','P','P',' ')}, /* Hawai'i Creole English -> Creoles */ + {HB_TAG('h','y','w',' '), HB_TAG('H','Y','E',' ')}, /* Western Armenian -> Armenian */ +/*{HB_TAG('i','b','a',' '), HB_TAG('I','B','A',' ')},*/ /* Iban */ +/*{HB_TAG('i','b','b',' '), HB_TAG('I','B','B',' ')},*/ /* Ibibio */ + {HB_TAG('i','b','y',' '), HB_TAG('I','J','O',' ')}, /* Ibani -> Ijo */ + {HB_TAG('i','c','r',' '), HB_TAG('C','P','P',' ')}, /* Islander Creole English -> Creoles */ + {HB_TAG('i','d','a',' '), HB_TAG('L','U','H',' ')}, /* Idakho-Isukha-Tiriki -> Luyia */ + {HB_TAG('i','d','b',' '), HB_TAG('C','P','P',' ')}, /* Indo-Portuguese -> Creoles */ + {HB_TAG('i','g','b',' '), HB_TAG('E','B','I',' ')}, /* Ebira */ + {HB_TAG('i','h','b',' '), HB_TAG('C','P','P',' ')}, /* Iha Based Pidgin -> Creoles */ + {HB_TAG('i','j','c',' '), HB_TAG('I','J','O',' ')}, /* Izon -> Ijo */ + {HB_TAG('i','j','e',' '), HB_TAG('I','J','O',' ')}, /* Biseni -> Ijo */ + {HB_TAG('i','j','n',' '), HB_TAG('I','J','O',' ')}, /* Kalabari -> Ijo */ +/*{HB_TAG('i','j','o',' '), HB_TAG('I','J','O',' ')},*/ /* Ijo [collection] */ + {HB_TAG('i','j','s',' '), HB_TAG('I','J','O',' ')}, /* Southeast Ijo -> Ijo */ + {HB_TAG('i','k','e',' '), HB_TAG('I','N','U',' ')}, /* Eastern Canadian Inuktitut -> Inuktitut */ + {HB_TAG('i','k','e',' '), HB_TAG('I','N','U','K')}, /* Eastern Canadian Inuktitut -> Nunavik Inuktitut */ + {HB_TAG('i','k','t',' '), HB_TAG('I','N','U',' ')}, /* Inuinnaqtun -> Inuktitut */ +/*{HB_TAG('i','l','o',' '), HB_TAG('I','L','O',' ')},*/ /* Iloko -> Ilokano */ + {HB_TAG('i','n','g',' '), HB_TAG('A','T','H',' ')}, /* Degexit'an -> Athapaskan */ + {HB_TAG('i','n','h',' '), HB_TAG('I','N','G',' ')}, /* Ingush */ + {HB_TAG('i','r','i',' '), HB_TAG_NONE }, /* Rigwe != Irish */ +/*{HB_TAG('i','r','u',' '), HB_TAG('I','R','U',' ')},*/ /* Irula */ + {HB_TAG('i','s','m',' '), HB_TAG_NONE }, /* Masimasi != Inari Sami */ + {HB_TAG('i','t','z',' '), HB_TAG('M','Y','N',' ')}, /* Itzá -> Mayan */ + {HB_TAG('i','x','l',' '), HB_TAG('M','Y','N',' ')}, /* Ixil -> Mayan */ + {HB_TAG('j','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Popti' -> Mayan */ + {HB_TAG('j','a','k',' '), HB_TAG('M','L','Y',' ')}, /* Jakun -> Malay */ + {HB_TAG('j','a','m',' '), HB_TAG('J','A','M',' ')}, /* Jamaican Creole English -> Jamaican Creole */ + {HB_TAG('j','a','m',' '), HB_TAG('C','P','P',' ')}, /* Jamaican Creole English -> Creoles */ + {HB_TAG('j','a','n',' '), HB_TAG_NONE }, /* Jandai != Japanese */ + {HB_TAG('j','a','x',' '), HB_TAG('M','L','Y',' ')}, /* Jambi Malay -> Malay */ + {HB_TAG('j','b','e',' '), HB_TAG('B','B','R',' ')}, /* Judeo-Berber -> Berber */ + {HB_TAG('j','b','n',' '), HB_TAG('B','B','R',' ')}, /* Nafusi -> Berber */ +/*{HB_TAG('j','b','o',' '), HB_TAG('J','B','O',' ')},*/ /* Lojban */ +/*{HB_TAG('j','c','t',' '), HB_TAG('J','C','T',' ')},*/ /* Krymchak */ + {HB_TAG('j','g','o',' '), HB_TAG('B','M','L',' ')}, /* Ngomba -> Bamileke */ + {HB_TAG('j','i','i',' '), HB_TAG_NONE }, /* Jiiddu != Yiddish */ + {HB_TAG('j','k','m',' '), HB_TAG('K','R','N',' ')}, /* Mobwa Karen -> Karen */ + {HB_TAG('j','k','p',' '), HB_TAG('K','R','N',' ')}, /* Paku Karen -> Karen */ + {HB_TAG('j','u','d',' '), HB_TAG_NONE }, /* Worodougou != Ladino */ + {HB_TAG('j','u','l',' '), HB_TAG_NONE }, /* Jirel != Jula */ + {HB_TAG('j','v','d',' '), HB_TAG('C','P','P',' ')}, /* Javindo -> Creoles */ + {HB_TAG('k','a','a',' '), HB_TAG('K','R','K',' ')}, /* Karakalpak */ + {HB_TAG('k','a','b',' '), HB_TAG('K','A','B','0')}, /* Kabyle */ + {HB_TAG('k','a','b',' '), HB_TAG('B','B','R',' ')}, /* Kabyle -> Berber */ + {HB_TAG('k','a','c',' '), HB_TAG_NONE }, /* Kachin != Kachchi */ + {HB_TAG('k','a','m',' '), HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ + {HB_TAG('k','a','r',' '), HB_TAG('K','R','N',' ')}, /* Karen [collection] */ +/*{HB_TAG('k','a','w',' '), HB_TAG('K','A','W',' ')},*/ /* Kawi (Old Javanese) */ + {HB_TAG('k','b','d',' '), HB_TAG('K','A','B',' ')}, /* Kabardian */ + {HB_TAG('k','b','y',' '), HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */ + {HB_TAG('k','c','a',' '), HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */ + {HB_TAG('k','c','a',' '), HB_TAG('K','H','S',' ')}, /* Khanty -> Khanty-Shurishkar */ + {HB_TAG('k','c','a',' '), HB_TAG('K','H','V',' ')}, /* Khanty -> Khanty-Vakhi */ + {HB_TAG('k','c','n',' '), HB_TAG('C','P','P',' ')}, /* Nubi -> Creoles */ +/*{HB_TAG('k','d','e',' '), HB_TAG('K','D','E',' ')},*/ /* Makonde */ + {HB_TAG('k','d','r',' '), HB_TAG('K','R','M',' ')}, /* Karaim */ + {HB_TAG('k','d','t',' '), HB_TAG('K','U','Y',' ')}, /* Kuy */ + {HB_TAG('k','e','a',' '), HB_TAG('K','E','A',' ')}, /* Kabuverdianu (Crioulo) */ + {HB_TAG('k','e','a',' '), HB_TAG('C','P','P',' ')}, /* Kabuverdianu -> Creoles */ + {HB_TAG('k','e','b',' '), HB_TAG_NONE }, /* Kélé != Kebena */ + {HB_TAG('k','e','k',' '), HB_TAG('K','E','K',' ')}, /* Kekchi */ + {HB_TAG('k','e','k',' '), HB_TAG('M','Y','N',' ')}, /* Kekchí -> Mayan */ + {HB_TAG('k','e','x',' '), HB_TAG('K','K','N',' ')}, /* Kukna -> Kokni */ + {HB_TAG('k','f','a',' '), HB_TAG('K','O','D',' ')}, /* Kodava -> Kodagu */ + {HB_TAG('k','f','r',' '), HB_TAG('K','A','C',' ')}, /* Kachhi -> Kachchi */ + {HB_TAG('k','f','x',' '), HB_TAG('K','U','L',' ')}, /* Kullu Pahari -> Kulvi */ + {HB_TAG('k','f','y',' '), HB_TAG('K','M','N',' ')}, /* Kumaoni */ + {HB_TAG('k','g','e',' '), HB_TAG_NONE }, /* Komering != Khutsuri Georgian */ + {HB_TAG('k','h','a',' '), HB_TAG('K','S','I',' ')}, /* Khasi */ + {HB_TAG('k','h','b',' '), HB_TAG('X','B','D',' ')}, /* Lü */ + {HB_TAG('k','h','k',' '), HB_TAG('M','N','G',' ')}, /* Halh Mongolian -> Mongolian */ + {HB_TAG('k','h','n',' '), HB_TAG_NONE }, /* Khandesi != Khamti Shan (Microsoft fonts) */ + {HB_TAG('k','h','s',' '), HB_TAG_NONE }, /* Kasua != Khanty-Shurishkar */ + {HB_TAG('k','h','t',' '), HB_TAG('K','H','T',' ')}, /* Khamti -> Khamti Shan */ + {HB_TAG('k','h','t',' '), HB_TAG('K','H','N',' ')}, /* Khamti -> Khamti Shan (Microsoft fonts) */ + {HB_TAG('k','h','v',' '), HB_TAG_NONE }, /* Khvarshi != Khanty-Vakhi */ +/*{HB_TAG('k','h','w',' '), HB_TAG('K','H','W',' ')},*/ /* Khowar */ + {HB_TAG('k','i','s',' '), HB_TAG_NONE }, /* Kis != Kisii */ + {HB_TAG('k','i','u',' '), HB_TAG('K','I','U',' ')}, /* Kirmanjki */ + {HB_TAG('k','i','u',' '), HB_TAG('Z','Z','A',' ')}, /* Kirmanjki -> Zazaki */ + {HB_TAG('k','j','b',' '), HB_TAG('M','Y','N',' ')}, /* Q'anjob'al -> Mayan */ +/*{HB_TAG('k','j','d',' '), HB_TAG('K','J','D',' ')},*/ /* Southern Kiwai */ + {HB_TAG('k','j','h',' '), HB_TAG('K','H','A',' ')}, /* Khakas -> Khakass */ + {HB_TAG('k','j','p',' '), HB_TAG('K','J','P',' ')}, /* Pwo Eastern Karen -> Eastern Pwo Karen */ + {HB_TAG('k','j','p',' '), HB_TAG('K','R','N',' ')}, /* Pwo Eastern Karen -> Karen */ + {HB_TAG('k','j','t',' '), HB_TAG('K','R','N',' ')}, /* Phrae Pwo Karen -> Karen */ +/*{HB_TAG('k','j','z',' '), HB_TAG('K','J','Z',' ')},*/ /* Bumthangkha */ + {HB_TAG('k','k','n',' '), HB_TAG_NONE }, /* Kon Keu != Kokni */ + {HB_TAG('k','k','z',' '), HB_TAG('A','T','H',' ')}, /* Kaska -> Athapaskan */ + {HB_TAG('k','l','m',' '), HB_TAG_NONE }, /* Migum != Kalmyk */ + {HB_TAG('k','l','n',' '), HB_TAG('K','A','L',' ')}, /* Kalenjin [macrolanguage] */ + {HB_TAG('k','m','b',' '), HB_TAG('M','B','N',' ')}, /* Kimbundu -> Mbundu */ + {HB_TAG('k','m','n',' '), HB_TAG_NONE }, /* Awtuw != Kumaoni */ + {HB_TAG('k','m','o',' '), HB_TAG_NONE }, /* Kwoma != Komo */ + {HB_TAG('k','m','r',' '), HB_TAG('K','U','R',' ')}, /* Northern Kurdish -> Kurdish */ + {HB_TAG('k','m','s',' '), HB_TAG_NONE }, /* Kamasau != Komso */ + {HB_TAG('k','m','v',' '), HB_TAG('C','P','P',' ')}, /* Karipúna Creole French -> Creoles */ + {HB_TAG('k','m','w',' '), HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */ +/*{HB_TAG('k','m','z',' '), HB_TAG('K','M','Z',' ')},*/ /* Khorasani Turkish -> Khorasani Turkic */ + {HB_TAG('k','n','c',' '), HB_TAG('K','N','R',' ')}, /* Central Kanuri -> Kanuri */ + {HB_TAG('k','n','g',' '), HB_TAG('K','O','N','0')}, /* Koongo -> Kongo */ + {HB_TAG('k','n','j',' '), HB_TAG('M','Y','N',' ')}, /* Western Kanjobal -> Mayan */ + {HB_TAG('k','n','n',' '), HB_TAG('K','O','K',' ')}, /* Konkani */ + {HB_TAG('k','n','r',' '), HB_TAG_NONE }, /* Kaningra != Kanuri */ + {HB_TAG('k','o','d',' '), HB_TAG_NONE }, /* Kodi != Kodagu */ + {HB_TAG('k','o','h',' '), HB_TAG_NONE }, /* Koyo != Korean Old Hangul */ + {HB_TAG('k','o','i',' '), HB_TAG('K','O','P',' ')}, /* Komi-Permyak */ + {HB_TAG('k','o','i',' '), HB_TAG('K','O','M',' ')}, /* Komi-Permyak -> Komi */ +/*{HB_TAG('k','o','k',' '), HB_TAG('K','O','K',' ')},*/ /* Konkani [macrolanguage] */ + {HB_TAG('k','o','p',' '), HB_TAG_NONE }, /* Waube != Komi-Permyak */ +/*{HB_TAG('k','o','s',' '), HB_TAG('K','O','S',' ')},*/ /* Kosraean */ + {HB_TAG('k','o','y',' '), HB_TAG('A','T','H',' ')}, /* Koyukon -> Athapaskan */ + {HB_TAG('k','o','z',' '), HB_TAG_NONE }, /* Korak != Komi-Zyrian */ + {HB_TAG('k','p','e',' '), HB_TAG('K','P','L',' ')}, /* Kpelle [macrolanguage] */ + {HB_TAG('k','p','l',' '), HB_TAG_NONE }, /* Kpala != Kpelle */ + {HB_TAG('k','p','p',' '), HB_TAG('K','R','N',' ')}, /* Paku Karen (retired code) -> Karen */ + {HB_TAG('k','p','v',' '), HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */ + {HB_TAG('k','p','v',' '), HB_TAG('K','O','M',' ')}, /* Komi-Zyrian -> Komi */ + {HB_TAG('k','p','y',' '), HB_TAG('K','Y','K',' ')}, /* Koryak */ + {HB_TAG('k','q','s',' '), HB_TAG('K','I','S',' ')}, /* Northern Kissi -> Kisii */ + {HB_TAG('k','q','y',' '), HB_TAG('K','R','T',' ')}, /* Koorete */ + {HB_TAG('k','r','c',' '), HB_TAG('K','A','R',' ')}, /* Karachay-Balkar -> Karachay */ + {HB_TAG('k','r','c',' '), HB_TAG('B','A','L',' ')}, /* Karachay-Balkar -> Balkar */ + {HB_TAG('k','r','i',' '), HB_TAG('K','R','I',' ')}, /* Krio */ + {HB_TAG('k','r','i',' '), HB_TAG('C','P','P',' ')}, /* Krio -> Creoles */ + {HB_TAG('k','r','k',' '), HB_TAG_NONE }, /* Kerek != Karakalpak */ +/*{HB_TAG('k','r','l',' '), HB_TAG('K','R','L',' ')},*/ /* Karelian */ + {HB_TAG('k','r','m',' '), HB_TAG_NONE }, /* Krim (retired code) != Karaim */ + {HB_TAG('k','r','n',' '), HB_TAG_NONE }, /* Sapo != Karen */ + {HB_TAG('k','r','t',' '), HB_TAG('K','N','R',' ')}, /* Tumari Kanuri -> Kanuri */ + {HB_TAG('k','r','u',' '), HB_TAG('K','U','U',' ')}, /* Kurukh */ + {HB_TAG('k','s','h',' '), HB_TAG('K','S','H','0')}, /* Kölsch -> Ripuarian */ + {HB_TAG('k','s','i',' '), HB_TAG_NONE }, /* Krisa != Khasi */ + {HB_TAG('k','s','m',' '), HB_TAG_NONE }, /* Kumba != Kildin Sami */ + {HB_TAG('k','s','s',' '), HB_TAG('K','I','S',' ')}, /* Southern Kisi -> Kisii */ + {HB_TAG('k','s','w',' '), HB_TAG('K','S','W',' ')}, /* S’gaw Karen */ + {HB_TAG('k','s','w',' '), HB_TAG('K','R','N',' ')}, /* S'gaw Karen -> Karen */ + {HB_TAG('k','t','b',' '), HB_TAG('K','E','B',' ')}, /* Kambaata -> Kebena */ + {HB_TAG('k','t','u',' '), HB_TAG('K','O','N',' ')}, /* Kituba (Democratic Republic of Congo) -> Kikongo */ + {HB_TAG('k','t','w',' '), HB_TAG('A','T','H',' ')}, /* Kato -> Athapaskan */ + {HB_TAG('k','u','i',' '), HB_TAG_NONE }, /* Kuikúro-Kalapálo != Kui */ + {HB_TAG('k','u','l',' '), HB_TAG_NONE }, /* Kulere != Kulvi */ +/*{HB_TAG('k','u','m',' '), HB_TAG('K','U','M',' ')},*/ /* Kumyk */ + {HB_TAG('k','u','u',' '), HB_TAG('A','T','H',' ')}, /* Upper Kuskokwim -> Athapaskan */ + {HB_TAG('k','u','w',' '), HB_TAG('B','A','D','0')}, /* Kpagua -> Banda */ + {HB_TAG('k','u','y',' '), HB_TAG_NONE }, /* Kuuku-Ya'u != Kuy */ + {HB_TAG('k','v','b',' '), HB_TAG('M','L','Y',' ')}, /* Kubu -> Malay */ + {HB_TAG('k','v','l',' '), HB_TAG('K','R','N',' ')}, /* Kayaw -> Karen */ + {HB_TAG('k','v','q',' '), HB_TAG('K','R','N',' ')}, /* Geba Karen -> Karen */ + {HB_TAG('k','v','r',' '), HB_TAG('M','L','Y',' ')}, /* Kerinci -> Malay */ + {HB_TAG('k','v','t',' '), HB_TAG('K','R','N',' ')}, /* Lahta Karen -> Karen */ + {HB_TAG('k','v','u',' '), HB_TAG('K','R','N',' ')}, /* Yinbaw Karen -> Karen */ + {HB_TAG('k','v','y',' '), HB_TAG('K','R','N',' ')}, /* Yintale Karen -> Karen */ +/*{HB_TAG('k','w','k',' '), HB_TAG('K','W','K',' ')},*/ /* Kwakiutl -> Kwakʼwala */ + {HB_TAG('k','w','w',' '), HB_TAG('C','P','P',' ')}, /* Kwinti -> Creoles */ + {HB_TAG('k','w','y',' '), HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ + {HB_TAG('k','x','c',' '), HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ + {HB_TAG('k','x','d',' '), HB_TAG('M','L','Y',' ')}, /* Brunei -> Malay */ + {HB_TAG('k','x','f',' '), HB_TAG('K','R','N',' ')}, /* Manumanaw Karen -> Karen */ + {HB_TAG('k','x','k',' '), HB_TAG('K','R','N',' ')}, /* Zayein Karen -> Karen */ + {HB_TAG('k','x','l',' '), HB_TAG('K','U','U',' ')}, /* Nepali Kurux (retired code) -> Kurukh */ + {HB_TAG('k','x','u',' '), HB_TAG('K','U','I',' ')}, /* Kui (India) (retired code) */ + {HB_TAG('k','y','k',' '), HB_TAG_NONE }, /* Kamayo != Koryak */ + {HB_TAG('k','y','u',' '), HB_TAG('K','Y','U',' ')}, /* Western Kayah */ + {HB_TAG('k','y','u',' '), HB_TAG('K','R','N',' ')}, /* Western Kayah -> Karen */ + {HB_TAG('l','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Lacandon -> Mayan */ + {HB_TAG('l','a','d',' '), HB_TAG('J','U','D',' ')}, /* Ladino */ + {HB_TAG('l','a','h',' '), HB_TAG_NONE }, /* Lahnda [macrolanguage] != Lahuli */ + {HB_TAG('l','a','k',' '), HB_TAG_NONE }, /* Laka (Nigeria) (retired code) != Lak */ + {HB_TAG('l','a','m',' '), HB_TAG_NONE }, /* Lamba != Lambani */ + {HB_TAG('l','a','z',' '), HB_TAG_NONE }, /* Aribwatsa != Laz */ + {HB_TAG('l','b','e',' '), HB_TAG('L','A','K',' ')}, /* Lak */ + {HB_TAG('l','b','j',' '), HB_TAG('L','D','K',' ')}, /* Ladakhi */ + {HB_TAG('l','b','l',' '), HB_TAG('B','I','K',' ')}, /* Libon Bikol -> Bikol */ + {HB_TAG('l','c','e',' '), HB_TAG('M','L','Y',' ')}, /* Loncong -> Malay */ + {HB_TAG('l','c','f',' '), HB_TAG('M','L','Y',' ')}, /* Lubu -> Malay */ + {HB_TAG('l','d','i',' '), HB_TAG('K','O','N','0')}, /* Laari -> Kongo */ + {HB_TAG('l','d','k',' '), HB_TAG_NONE }, /* Leelau != Ladakhi */ +/*{HB_TAG('l','e','f',' '), HB_TAG('L','E','F',' ')},*/ /* Lelemi */ +/*{HB_TAG('l','e','z',' '), HB_TAG('L','E','Z',' ')},*/ /* Lezghian -> Lezgi */ + {HB_TAG('l','i','f',' '), HB_TAG('L','M','B',' ')}, /* Limbu */ +/*{HB_TAG('l','i','j',' '), HB_TAG('L','I','J',' ')},*/ /* Ligurian */ + {HB_TAG('l','i','r',' '), HB_TAG('C','P','P',' ')}, /* Liberian English -> Creoles */ +/*{HB_TAG('l','i','s',' '), HB_TAG('L','I','S',' ')},*/ /* Lisu */ + {HB_TAG('l','i','w',' '), HB_TAG('M','L','Y',' ')}, /* Col -> Malay */ + {HB_TAG('l','i','y',' '), HB_TAG('B','A','D','0')}, /* Banda-Bambari -> Banda */ +/*{HB_TAG('l','j','p',' '), HB_TAG('L','J','P',' ')},*/ /* Lampung Api -> Lampung */ + {HB_TAG('l','k','b',' '), HB_TAG('L','U','H',' ')}, /* Kabras -> Luyia */ +/*{HB_TAG('l','k','i',' '), HB_TAG('L','K','I',' ')},*/ /* Laki */ + {HB_TAG('l','k','o',' '), HB_TAG('L','U','H',' ')}, /* Khayo -> Luyia */ + {HB_TAG('l','k','s',' '), HB_TAG('L','U','H',' ')}, /* Kisa -> Luyia */ + {HB_TAG('l','l','d',' '), HB_TAG('L','A','D',' ')}, /* Ladin */ + {HB_TAG('l','m','a',' '), HB_TAG_NONE }, /* East Limba != Low Mari */ + {HB_TAG('l','m','b',' '), HB_TAG_NONE }, /* Merei != Limbu */ + {HB_TAG('l','m','n',' '), HB_TAG('L','A','M',' ')}, /* Lambadi -> Lambani */ +/*{HB_TAG('l','m','o',' '), HB_TAG('L','M','O',' ')},*/ /* Lombard */ + {HB_TAG('l','m','w',' '), HB_TAG_NONE }, /* Lake Miwok != Lomwe */ + {HB_TAG('l','n','a',' '), HB_TAG('B','A','D','0')}, /* Langbashe -> Banda */ + {HB_TAG('l','n','l',' '), HB_TAG('B','A','D','0')}, /* South Central Banda -> Banda */ +/*{HB_TAG('l','o','m',' '), HB_TAG('L','O','M',' ')},*/ /* Loma (Liberia) */ + {HB_TAG('l','o','u',' '), HB_TAG('C','P','P',' ')}, /* Louisiana Creole -> Creoles */ +/*{HB_TAG('l','p','o',' '), HB_TAG('L','P','O',' ')},*/ /* Lipo */ +/*{HB_TAG('l','r','c',' '), HB_TAG('L','R','C',' ')},*/ /* Northern Luri -> Luri */ + {HB_TAG('l','r','i',' '), HB_TAG('L','U','H',' ')}, /* Marachi -> Luyia */ + {HB_TAG('l','r','m',' '), HB_TAG('L','U','H',' ')}, /* Marama -> Luyia */ + {HB_TAG('l','r','t',' '), HB_TAG('C','P','P',' ')}, /* Larantuka Malay -> Creoles */ + {HB_TAG('l','s','b',' '), HB_TAG_NONE }, /* Burundian Sign Language != Lower Sorbian */ + {HB_TAG('l','s','m',' '), HB_TAG('L','U','H',' ')}, /* Saamia -> Luyia */ + {HB_TAG('l','t','g',' '), HB_TAG('L','V','I',' ')}, /* Latgalian -> Latvian */ + {HB_TAG('l','t','h',' '), HB_TAG_NONE }, /* Thur != Lithuanian */ + {HB_TAG('l','t','o',' '), HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */ + {HB_TAG('l','t','s',' '), HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */ +/*{HB_TAG('l','u','a',' '), HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ +/*{HB_TAG('l','u','o',' '), HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */ + {HB_TAG('l','u','s',' '), HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */ + {HB_TAG('l','u','s',' '), HB_TAG('Q','I','N',' ')}, /* Lushai -> Chin */ + {HB_TAG('l','u','y',' '), HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */ + {HB_TAG('l','u','z',' '), HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */ + {HB_TAG('l','v','i',' '), HB_TAG_NONE }, /* Lavi != Latvian */ + {HB_TAG('l','v','s',' '), HB_TAG('L','V','I',' ')}, /* Standard Latvian -> Latvian */ + {HB_TAG('l','w','g',' '), HB_TAG('L','U','H',' ')}, /* Wanga -> Luyia */ + {HB_TAG('l','z','h',' '), HB_TAG('Z','H','T',' ')}, /* Literary Chinese -> Chinese, Traditional */ + {HB_TAG('l','z','z',' '), HB_TAG('L','A','Z',' ')}, /* Laz */ +/*{HB_TAG('m','a','d',' '), HB_TAG('M','A','D',' ')},*/ /* Madurese -> Madura */ +/*{HB_TAG('m','a','g',' '), HB_TAG('M','A','G',' ')},*/ /* Magahi */ + {HB_TAG('m','a','i',' '), HB_TAG('M','T','H',' ')}, /* Maithili */ + {HB_TAG('m','a','j',' '), HB_TAG_NONE }, /* Jalapa De Díaz Mazatec != Majang */ + {HB_TAG('m','a','k',' '), HB_TAG('M','K','R',' ')}, /* Makasar */ + {HB_TAG('m','a','m',' '), HB_TAG('M','A','M',' ')}, /* Mam */ + {HB_TAG('m','a','m',' '), HB_TAG('M','Y','N',' ')}, /* Mam -> Mayan */ + {HB_TAG('m','a','n',' '), HB_TAG('M','N','K',' ')}, /* Mandingo [macrolanguage] -> Maninka */ + {HB_TAG('m','a','p',' '), HB_TAG_NONE }, /* Austronesian [collection] != Mapudungun */ + {HB_TAG('m','a','w',' '), HB_TAG_NONE }, /* Mampruli != Marwari */ + {HB_TAG('m','a','x',' '), HB_TAG('M','L','Y',' ')}, /* North Moluccan Malay -> Malay */ + {HB_TAG('m','a','x',' '), HB_TAG('C','P','P',' ')}, /* North Moluccan Malay -> Creoles */ + {HB_TAG('m','b','f',' '), HB_TAG('C','P','P',' ')}, /* Baba Malay -> Creoles */ + {HB_TAG('m','b','n',' '), HB_TAG_NONE }, /* Macaguán != Mbundu */ +/*{HB_TAG('m','b','o',' '), HB_TAG('M','B','O',' ')},*/ /* Mbo (Cameroon) */ + {HB_TAG('m','c','h',' '), HB_TAG_NONE }, /* Maquiritari != Manchu */ + {HB_TAG('m','c','m',' '), HB_TAG('C','P','P',' ')}, /* Malaccan Creole Portuguese -> Creoles */ + {HB_TAG('m','c','r',' '), HB_TAG_NONE }, /* Menya != Moose Cree */ + {HB_TAG('m','c','t',' '), HB_TAG('B','T','I',' ')}, /* Mengisa -> Beti */ + {HB_TAG('m','d','e',' '), HB_TAG_NONE }, /* Maba (Chad) != Mende */ + {HB_TAG('m','d','f',' '), HB_TAG('M','O','K',' ')}, /* Moksha */ +/*{HB_TAG('m','d','r',' '), HB_TAG('M','D','R',' ')},*/ /* Mandar */ + {HB_TAG('m','d','y',' '), HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */ + {HB_TAG('m','e','n',' '), HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */ + {HB_TAG('m','e','o',' '), HB_TAG('M','L','Y',' ')}, /* Kedah Malay -> Malay */ +/*{HB_TAG('m','e','r',' '), HB_TAG('M','E','R',' ')},*/ /* Meru */ + {HB_TAG('m','f','a',' '), HB_TAG('M','F','A',' ')}, /* Pattani Malay */ + {HB_TAG('m','f','a',' '), HB_TAG('M','L','Y',' ')}, /* Pattani Malay -> Malay */ + {HB_TAG('m','f','b',' '), HB_TAG('M','L','Y',' ')}, /* Bangka -> Malay */ + {HB_TAG('m','f','e',' '), HB_TAG('M','F','E',' ')}, /* Morisyen */ + {HB_TAG('m','f','e',' '), HB_TAG('C','P','P',' ')}, /* Morisyen -> Creoles */ + {HB_TAG('m','f','p',' '), HB_TAG('C','P','P',' ')}, /* Makassar Malay -> Creoles */ + {HB_TAG('m','h','c',' '), HB_TAG('M','Y','N',' ')}, /* Mocho -> Mayan */ + {HB_TAG('m','h','r',' '), HB_TAG('L','M','A',' ')}, /* Eastern Mari -> Low Mari */ + {HB_TAG('m','h','v',' '), HB_TAG('A','R','K',' ')}, /* Arakanese (retired code) -> Rakhine */ + {HB_TAG('m','i','n',' '), HB_TAG('M','I','N',' ')}, /* Minangkabau */ + {HB_TAG('m','i','n',' '), HB_TAG('M','L','Y',' ')}, /* Minangkabau -> Malay */ + {HB_TAG('m','i','z',' '), HB_TAG_NONE }, /* Coatzospan Mixtec != Mizo */ + {HB_TAG('m','k','n',' '), HB_TAG('C','P','P',' ')}, /* Kupang Malay -> Creoles */ + {HB_TAG('m','k','r',' '), HB_TAG_NONE }, /* Malas != Makasar */ + {HB_TAG('m','k','u',' '), HB_TAG('M','N','K',' ')}, /* Konyanka Maninka -> Maninka */ +/*{HB_TAG('m','k','w',' '), HB_TAG('M','K','W',' ')},*/ /* Kituba (Congo) */ + {HB_TAG('m','l','e',' '), HB_TAG_NONE }, /* Manambu != Male */ + {HB_TAG('m','l','n',' '), HB_TAG_NONE }, /* Malango != Malinke */ + {HB_TAG('m','l','q',' '), HB_TAG('M','L','N',' ')}, /* Western Maninkakan -> Malinke */ + {HB_TAG('m','l','q',' '), HB_TAG('M','N','K',' ')}, /* Western Maninkakan -> Maninka */ + {HB_TAG('m','l','r',' '), HB_TAG_NONE }, /* Vame != Malayalam Reformed */ + {HB_TAG('m','m','r',' '), HB_TAG('H','M','N',' ')}, /* Western Xiangxi Miao -> Hmong */ + {HB_TAG('m','n','c',' '), HB_TAG('M','C','H',' ')}, /* Manchu */ + {HB_TAG('m','n','d',' '), HB_TAG_NONE }, /* Mondé != Mandinka */ + {HB_TAG('m','n','g',' '), HB_TAG_NONE }, /* Eastern Mnong != Mongolian */ + {HB_TAG('m','n','h',' '), HB_TAG('B','A','D','0')}, /* Mono (Democratic Republic of Congo) -> Banda */ +/*{HB_TAG('m','n','i',' '), HB_TAG('M','N','I',' ')},*/ /* Manipuri */ + {HB_TAG('m','n','k',' '), HB_TAG('M','N','D',' ')}, /* Mandinka */ + {HB_TAG('m','n','k',' '), HB_TAG('M','N','K',' ')}, /* Mandinka -> Maninka */ + {HB_TAG('m','n','p',' '), HB_TAG('Z','H','S',' ')}, /* Min Bei Chinese -> Chinese, Simplified */ + {HB_TAG('m','n','s',' '), HB_TAG('M','A','N',' ')}, /* Mansi */ + {HB_TAG('m','n','w',' '), HB_TAG('M','O','N',' ')}, /* Mon */ + {HB_TAG('m','n','w',' '), HB_TAG('M','O','N','T')}, /* Mon -> Thailand Mon */ + {HB_TAG('m','n','x',' '), HB_TAG_NONE }, /* Manikion != Manx */ + {HB_TAG('m','o','d',' '), HB_TAG('C','P','P',' ')}, /* Mobilian -> Creoles */ +/*{HB_TAG('m','o','h',' '), HB_TAG('M','O','H',' ')},*/ /* Mohawk */ + {HB_TAG('m','o','k',' '), HB_TAG_NONE }, /* Morori != Moksha */ + {HB_TAG('m','o','p',' '), HB_TAG('M','Y','N',' ')}, /* Mopán Maya -> Mayan */ + {HB_TAG('m','o','r',' '), HB_TAG_NONE }, /* Moro != Moroccan */ +/*{HB_TAG('m','o','s',' '), HB_TAG('M','O','S',' ')},*/ /* Mossi */ + {HB_TAG('m','p','e',' '), HB_TAG('M','A','J',' ')}, /* Majang */ + {HB_TAG('m','q','g',' '), HB_TAG('M','L','Y',' ')}, /* Kota Bangun Kutai Malay -> Malay */ + {HB_TAG('m','r','h',' '), HB_TAG('Q','I','N',' ')}, /* Mara Chin -> Chin */ + {HB_TAG('m','r','j',' '), HB_TAG('H','M','A',' ')}, /* Western Mari -> High Mari */ + {HB_TAG('m','s','c',' '), HB_TAG('M','N','K',' ')}, /* Sankaran Maninka -> Maninka */ + {HB_TAG('m','s','h',' '), HB_TAG('M','L','G',' ')}, /* Masikoro Malagasy -> Malagasy */ + {HB_TAG('m','s','i',' '), HB_TAG('M','L','Y',' ')}, /* Sabah Malay -> Malay */ + {HB_TAG('m','s','i',' '), HB_TAG('C','P','P',' ')}, /* Sabah Malay -> Creoles */ + {HB_TAG('m','t','h',' '), HB_TAG_NONE }, /* Munggui != Maithili */ + {HB_TAG('m','t','r',' '), HB_TAG('M','A','W',' ')}, /* Mewari -> Marwari */ + {HB_TAG('m','t','s',' '), HB_TAG_NONE }, /* Yora != Maltese */ + {HB_TAG('m','u','d',' '), HB_TAG('C','P','P',' ')}, /* Mednyj Aleut -> Creoles */ + {HB_TAG('m','u','i',' '), HB_TAG('M','L','Y',' ')}, /* Musi -> Malay */ + {HB_TAG('m','u','n',' '), HB_TAG_NONE }, /* Munda [collection] != Mundari */ + {HB_TAG('m','u','p',' '), HB_TAG('R','A','J',' ')}, /* Malvi -> Rajasthani */ + {HB_TAG('m','u','q',' '), HB_TAG('H','M','N',' ')}, /* Eastern Xiangxi Miao -> Hmong */ +/*{HB_TAG('m','u','s',' '), HB_TAG('M','U','S',' ')},*/ /* Creek -> Muscogee */ + {HB_TAG('m','v','b',' '), HB_TAG('A','T','H',' ')}, /* Mattole -> Athapaskan */ + {HB_TAG('m','v','e',' '), HB_TAG('M','A','W',' ')}, /* Marwari (Pakistan) */ + {HB_TAG('m','v','f',' '), HB_TAG('M','N','G',' ')}, /* Peripheral Mongolian -> Mongolian */ + {HB_TAG('m','w','k',' '), HB_TAG('M','N','K',' ')}, /* Kita Maninkakan -> Maninka */ +/*{HB_TAG('m','w','l',' '), HB_TAG('M','W','L',' ')},*/ /* Mirandese */ + {HB_TAG('m','w','q',' '), HB_TAG('Q','I','N',' ')}, /* Mün Chin -> Chin */ + {HB_TAG('m','w','r',' '), HB_TAG('M','A','W',' ')}, /* Marwari [macrolanguage] */ + {HB_TAG('m','w','w',' '), HB_TAG('M','W','W',' ')}, /* Hmong Daw */ + {HB_TAG('m','w','w',' '), HB_TAG('H','M','N',' ')}, /* Hmong Daw -> Hmong */ + {HB_TAG('m','y','m',' '), HB_TAG('M','E','N',' ')}, /* Me’en */ +/*{HB_TAG('m','y','n',' '), HB_TAG('M','Y','N',' ')},*/ /* Mayan [collection] */ + {HB_TAG('m','y','q',' '), HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) -> Maninka */ + {HB_TAG('m','y','v',' '), HB_TAG('E','R','Z',' ')}, /* Erzya */ + {HB_TAG('m','z','b',' '), HB_TAG('B','B','R',' ')}, /* Tumzabt -> Berber */ +/*{HB_TAG('m','z','n',' '), HB_TAG('M','Z','N',' ')},*/ /* Mazanderani */ + {HB_TAG('m','z','s',' '), HB_TAG('C','P','P',' ')}, /* Macanese -> Creoles */ + {HB_TAG('n','a','g',' '), HB_TAG('N','A','G',' ')}, /* Naga Pidgin -> Naga-Assamese */ + {HB_TAG('n','a','g',' '), HB_TAG('C','P','P',' ')}, /* Naga Pidgin -> Creoles */ +/*{HB_TAG('n','a','h',' '), HB_TAG('N','A','H',' ')},*/ /* Nahuatl [collection] */ + {HB_TAG('n','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Min Nan Chinese -> Chinese, Simplified */ +/*{HB_TAG('n','a','p',' '), HB_TAG('N','A','P',' ')},*/ /* Neapolitan */ + {HB_TAG('n','a','s',' '), HB_TAG_NONE }, /* Naasioi != Naskapi */ + {HB_TAG('n','a','z',' '), HB_TAG('N','A','H',' ')}, /* Coatepec Nahuatl -> Nahuatl */ + {HB_TAG('n','c','h',' '), HB_TAG('N','A','H',' ')}, /* Central Huasteca Nahuatl -> Nahuatl */ + {HB_TAG('n','c','i',' '), HB_TAG('N','A','H',' ')}, /* Classical Nahuatl -> Nahuatl */ + {HB_TAG('n','c','j',' '), HB_TAG('N','A','H',' ')}, /* Northern Puebla Nahuatl -> Nahuatl */ + {HB_TAG('n','c','l',' '), HB_TAG('N','A','H',' ')}, /* Michoacán Nahuatl -> Nahuatl */ + {HB_TAG('n','c','r',' '), HB_TAG_NONE }, /* Ncane != N-Cree */ + {HB_TAG('n','c','x',' '), HB_TAG('N','A','H',' ')}, /* Central Puebla Nahuatl -> Nahuatl */ + {HB_TAG('n','d','b',' '), HB_TAG_NONE }, /* Kenswei Nsei != Ndebele */ +/*{HB_TAG('n','d','c',' '), HB_TAG('N','D','C',' ')},*/ /* Ndau */ + {HB_TAG('n','d','g',' '), HB_TAG_NONE }, /* Ndengereko != Ndonga */ +/*{HB_TAG('n','d','s',' '), HB_TAG('N','D','S',' ')},*/ /* Low Saxon */ + {HB_TAG('n','e','f',' '), HB_TAG('C','P','P',' ')}, /* Nefamese -> Creoles */ +/*{HB_TAG('n','e','w',' '), HB_TAG('N','E','W',' ')},*/ /* Newari */ +/*{HB_TAG('n','g','a',' '), HB_TAG('N','G','A',' ')},*/ /* Ngbaka */ + {HB_TAG('n','g','l',' '), HB_TAG('L','M','W',' ')}, /* Lomwe */ + {HB_TAG('n','g','m',' '), HB_TAG('C','P','P',' ')}, /* Ngatik Men's Creole -> Creoles */ + {HB_TAG('n','g','o',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (retired code) -> Sutu */ + {HB_TAG('n','g','r',' '), HB_TAG_NONE }, /* Engdewu != Nagari */ + {HB_TAG('n','g','u',' '), HB_TAG('N','A','H',' ')}, /* Guerrero Nahuatl -> Nahuatl */ + {HB_TAG('n','h','c',' '), HB_TAG('N','A','H',' ')}, /* Tabasco Nahuatl -> Nahuatl */ + {HB_TAG('n','h','d',' '), HB_TAG('G','U','A',' ')}, /* Chiripá -> Guarani */ + {HB_TAG('n','h','e',' '), HB_TAG('N','A','H',' ')}, /* Eastern Huasteca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','g',' '), HB_TAG('N','A','H',' ')}, /* Tetelcingo Nahuatl -> Nahuatl */ + {HB_TAG('n','h','i',' '), HB_TAG('N','A','H',' ')}, /* Zacatlán-Ahuacatlán-Tepetzintla Nahuatl -> Nahuatl */ + {HB_TAG('n','h','k',' '), HB_TAG('N','A','H',' ')}, /* Isthmus-Cosoleacaque Nahuatl -> Nahuatl */ + {HB_TAG('n','h','m',' '), HB_TAG('N','A','H',' ')}, /* Morelos Nahuatl -> Nahuatl */ + {HB_TAG('n','h','n',' '), HB_TAG('N','A','H',' ')}, /* Central Nahuatl -> Nahuatl */ + {HB_TAG('n','h','p',' '), HB_TAG('N','A','H',' ')}, /* Isthmus-Pajapan Nahuatl -> Nahuatl */ + {HB_TAG('n','h','q',' '), HB_TAG('N','A','H',' ')}, /* Huaxcaleca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','t',' '), HB_TAG('N','A','H',' ')}, /* Ometepec Nahuatl -> Nahuatl */ + {HB_TAG('n','h','v',' '), HB_TAG('N','A','H',' ')}, /* Temascaltepec Nahuatl -> Nahuatl */ + {HB_TAG('n','h','w',' '), HB_TAG('N','A','H',' ')}, /* Western Huasteca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','x',' '), HB_TAG('N','A','H',' ')}, /* Isthmus-Mecayapan Nahuatl -> Nahuatl */ + {HB_TAG('n','h','y',' '), HB_TAG('N','A','H',' ')}, /* Northern Oaxaca Nahuatl -> Nahuatl */ + {HB_TAG('n','h','z',' '), HB_TAG('N','A','H',' ')}, /* Santa María La Alta Nahuatl -> Nahuatl */ + {HB_TAG('n','i','q',' '), HB_TAG('K','A','L',' ')}, /* Nandi -> Kalenjin */ + {HB_TAG('n','i','s',' '), HB_TAG_NONE }, /* Nimi != Nisi */ +/*{HB_TAG('n','i','u',' '), HB_TAG('N','I','U',' ')},*/ /* Niuean */ + {HB_TAG('n','i','v',' '), HB_TAG('G','I','L',' ')}, /* Gilyak */ + {HB_TAG('n','j','t',' '), HB_TAG('C','P','P',' ')}, /* Ndyuka-Trio Pidgin -> Creoles */ + {HB_TAG('n','j','z',' '), HB_TAG('N','I','S',' ')}, /* Nyishi -> Nisi */ + {HB_TAG('n','k','o',' '), HB_TAG_NONE }, /* Nkonya != N’Ko */ + {HB_TAG('n','k','x',' '), HB_TAG('I','J','O',' ')}, /* Nkoroo -> Ijo */ + {HB_TAG('n','l','a',' '), HB_TAG('B','M','L',' ')}, /* Ngombale -> Bamileke */ + {HB_TAG('n','l','e',' '), HB_TAG('L','U','H',' ')}, /* East Nyala -> Luyia */ + {HB_TAG('n','l','n',' '), HB_TAG('N','A','H',' ')}, /* Durango Nahuatl (retired code) -> Nahuatl */ + {HB_TAG('n','l','v',' '), HB_TAG('N','A','H',' ')}, /* Orizaba Nahuatl -> Nahuatl */ + {HB_TAG('n','n','h',' '), HB_TAG('B','M','L',' ')}, /* Ngiemboon -> Bamileke */ + {HB_TAG('n','n','z',' '), HB_TAG('B','M','L',' ')}, /* Nda'nda' -> Bamileke */ + {HB_TAG('n','o','d',' '), HB_TAG('N','T','A',' ')}, /* Northern Thai -> Northern Tai */ +/*{HB_TAG('n','o','e',' '), HB_TAG('N','O','E',' ')},*/ /* Nimadi */ +/*{HB_TAG('n','o','g',' '), HB_TAG('N','O','G',' ')},*/ /* Nogai */ +/*{HB_TAG('n','o','v',' '), HB_TAG('N','O','V',' ')},*/ /* Novial */ + {HB_TAG('n','p','i',' '), HB_TAG('N','E','P',' ')}, /* Nepali */ + {HB_TAG('n','p','l',' '), HB_TAG('N','A','H',' ')}, /* Southeastern Puebla Nahuatl -> Nahuatl */ + {HB_TAG('n','q','o',' '), HB_TAG('N','K','O',' ')}, /* N’Ko */ + {HB_TAG('n','s','k',' '), HB_TAG('N','A','S',' ')}, /* Naskapi */ + {HB_TAG('n','s','m',' '), HB_TAG_NONE }, /* Sumi Naga != Northern Sami */ +/*{HB_TAG('n','s','o',' '), HB_TAG('N','S','O',' ')},*/ /* Northern Sotho */ + {HB_TAG('n','s','u',' '), HB_TAG('N','A','H',' ')}, /* Sierra Negra Nahuatl -> Nahuatl */ + {HB_TAG('n','t','o',' '), HB_TAG_NONE }, /* Ntomba != Esperanto */ + {HB_TAG('n','u','e',' '), HB_TAG('B','A','D','0')}, /* Ngundu -> Banda */ + {HB_TAG('n','u','u',' '), HB_TAG('B','A','D','0')}, /* Ngbundu -> Banda */ + {HB_TAG('n','u','z',' '), HB_TAG('N','A','H',' ')}, /* Tlamacazapa Nahuatl -> Nahuatl */ + {HB_TAG('n','w','e',' '), HB_TAG('B','M','L',' ')}, /* Ngwe -> Bamileke */ + {HB_TAG('n','y','d',' '), HB_TAG('L','U','H',' ')}, /* Nyore -> Luyia */ +/*{HB_TAG('n','y','m',' '), HB_TAG('N','Y','M',' ')},*/ /* Nyamwezi */ + {HB_TAG('n','y','n',' '), HB_TAG('N','K','L',' ')}, /* Nyankole */ +/*{HB_TAG('n','z','a',' '), HB_TAG('N','Z','A',' ')},*/ /* Tigon Mbembe -> Mbembe Tigon */ +/*{HB_TAG('o','j','b',' '), HB_TAG('O','J','B',' ')},*/ /* Northwestern Ojibwa -> Ojibway */ + {HB_TAG('o','j','c',' '), HB_TAG('O','J','B',' ')}, /* Central Ojibwa -> Ojibway */ + {HB_TAG('o','j','g',' '), HB_TAG('O','J','B',' ')}, /* Eastern Ojibwa -> Ojibway */ + {HB_TAG('o','j','s',' '), HB_TAG('O','C','R',' ')}, /* Severn Ojibwa -> Oji-Cree */ + {HB_TAG('o','j','s',' '), HB_TAG('O','J','B',' ')}, /* Severn Ojibwa -> Ojibway */ + {HB_TAG('o','j','w',' '), HB_TAG('O','J','B',' ')}, /* Western Ojibwa -> Ojibway */ + {HB_TAG('o','k','d',' '), HB_TAG('I','J','O',' ')}, /* Okodia -> Ijo */ + {HB_TAG('o','k','i',' '), HB_TAG('K','A','L',' ')}, /* Okiek -> Kalenjin */ + {HB_TAG('o','k','m',' '), HB_TAG('K','O','H',' ')}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ + {HB_TAG('o','k','r',' '), HB_TAG('I','J','O',' ')}, /* Kirike -> Ijo */ + {HB_TAG('o','n','x',' '), HB_TAG('C','P','P',' ')}, /* Onin Based Pidgin -> Creoles */ + {HB_TAG('o','o','r',' '), HB_TAG('C','P','P',' ')}, /* Oorlams -> Creoles */ + {HB_TAG('o','r','c',' '), HB_TAG('O','R','O',' ')}, /* Orma -> Oromo */ + {HB_TAG('o','r','n',' '), HB_TAG('M','L','Y',' ')}, /* Orang Kanaq -> Malay */ + {HB_TAG('o','r','o',' '), HB_TAG_NONE }, /* Orokolo != Oromo */ + {HB_TAG('o','r','r',' '), HB_TAG('I','J','O',' ')}, /* Oruma -> Ijo */ + {HB_TAG('o','r','s',' '), HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ + {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */ + {HB_TAG('o','t','w',' '), HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ + {HB_TAG('o','u','a',' '), HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */ + {HB_TAG('p','a','a',' '), HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */ +/*{HB_TAG('p','a','g',' '), HB_TAG('P','A','G',' ')},*/ /* Pangasinan */ + {HB_TAG('p','a','l',' '), HB_TAG_NONE }, /* Pahlavi != Pali */ +/*{HB_TAG('p','a','m',' '), HB_TAG('P','A','M',' ')},*/ /* Pampanga -> Pampangan */ + {HB_TAG('p','a','p',' '), HB_TAG('P','A','P','0')}, /* Papiamento -> Papiamentu */ + {HB_TAG('p','a','p',' '), HB_TAG('C','P','P',' ')}, /* Papiamento -> Creoles */ + {HB_TAG('p','a','s',' '), HB_TAG_NONE }, /* Papasena != Pashto */ +/*{HB_TAG('p','a','u',' '), HB_TAG('P','A','U',' ')},*/ /* Palauan */ + {HB_TAG('p','b','t',' '), HB_TAG('P','A','S',' ')}, /* Southern Pashto -> Pashto */ + {HB_TAG('p','b','u',' '), HB_TAG('P','A','S',' ')}, /* Northern Pashto -> Pashto */ +/*{HB_TAG('p','c','c',' '), HB_TAG('P','C','C',' ')},*/ /* Bouyei */ +/*{HB_TAG('p','c','d',' '), HB_TAG('P','C','D',' ')},*/ /* Picard */ + {HB_TAG('p','c','e',' '), HB_TAG('P','L','G',' ')}, /* Ruching Palaung -> Palaung */ + {HB_TAG('p','c','k',' '), HB_TAG('Q','I','N',' ')}, /* Paite Chin -> Chin */ + {HB_TAG('p','c','m',' '), HB_TAG('C','P','P',' ')}, /* Nigerian Pidgin -> Creoles */ +/*{HB_TAG('p','d','c',' '), HB_TAG('P','D','C',' ')},*/ /* Pennsylvania German */ + {HB_TAG('p','d','u',' '), HB_TAG('K','R','N',' ')}, /* Kayan -> Karen */ + {HB_TAG('p','e','a',' '), HB_TAG('C','P','P',' ')}, /* Peranakan Indonesian -> Creoles */ + {HB_TAG('p','e','l',' '), HB_TAG('M','L','Y',' ')}, /* Pekal -> Malay */ + {HB_TAG('p','e','s',' '), HB_TAG('F','A','R',' ')}, /* Iranian Persian -> Persian */ + {HB_TAG('p','e','y',' '), HB_TAG('C','P','P',' ')}, /* Petjo -> Creoles */ + {HB_TAG('p','g','a',' '), HB_TAG('A','R','A',' ')}, /* Sudanese Creole Arabic -> Arabic */ + {HB_TAG('p','g','a',' '), HB_TAG('C','P','P',' ')}, /* Sudanese Creole Arabic -> Creoles */ +/*{HB_TAG('p','h','k',' '), HB_TAG('P','H','K',' ')},*/ /* Phake */ + {HB_TAG('p','i','h',' '), HB_TAG('P','I','H',' ')}, /* Pitcairn-Norfolk -> Norfolk */ + {HB_TAG('p','i','h',' '), HB_TAG('C','P','P',' ')}, /* Pitcairn-Norfolk -> Creoles */ + {HB_TAG('p','i','l',' '), HB_TAG_NONE }, /* Yom != Filipino */ + {HB_TAG('p','i','s',' '), HB_TAG('C','P','P',' ')}, /* Pijin -> Creoles */ + {HB_TAG('p','k','h',' '), HB_TAG('Q','I','N',' ')}, /* Pankhu -> Chin */ + {HB_TAG('p','k','o',' '), HB_TAG('K','A','L',' ')}, /* Pökoot -> Kalenjin */ + {HB_TAG('p','l','g',' '), HB_TAG_NONE }, /* Pilagá != Palaung */ + {HB_TAG('p','l','k',' '), HB_TAG_NONE }, /* Kohistani Shina != Polish */ + {HB_TAG('p','l','l',' '), HB_TAG('P','L','G',' ')}, /* Shwe Palaung -> Palaung */ + {HB_TAG('p','l','n',' '), HB_TAG('C','P','P',' ')}, /* Palenquero -> Creoles */ + {HB_TAG('p','l','p',' '), HB_TAG('P','A','P',' ')}, /* Palpa (retired code) */ + {HB_TAG('p','l','t',' '), HB_TAG('M','L','G',' ')}, /* Plateau Malagasy -> Malagasy */ + {HB_TAG('p','m','l',' '), HB_TAG('C','P','P',' ')}, /* Lingua Franca -> Creoles */ +/*{HB_TAG('p','m','s',' '), HB_TAG('P','M','S',' ')},*/ /* Piemontese */ + {HB_TAG('p','m','y',' '), HB_TAG('C','P','P',' ')}, /* Papuan Malay -> Creoles */ +/*{HB_TAG('p','n','b',' '), HB_TAG('P','N','B',' ')},*/ /* Western Panjabi */ + {HB_TAG('p','o','c',' '), HB_TAG('M','Y','N',' ')}, /* Poqomam -> Mayan */ + {HB_TAG('p','o','h',' '), HB_TAG('P','O','H',' ')}, /* Poqomchi' -> Pocomchi */ + {HB_TAG('p','o','h',' '), HB_TAG('M','Y','N',' ')}, /* Poqomchi' -> Mayan */ +/*{HB_TAG('p','o','n',' '), HB_TAG('P','O','N',' ')},*/ /* Pohnpeian */ + {HB_TAG('p','o','v',' '), HB_TAG('C','P','P',' ')}, /* Upper Guinea Crioulo -> Creoles */ + {HB_TAG('p','p','a',' '), HB_TAG('B','A','G',' ')}, /* Pao (retired code) -> Baghelkhandi */ + {HB_TAG('p','r','e',' '), HB_TAG('C','P','P',' ')}, /* Principense -> Creoles */ +/*{HB_TAG('p','r','o',' '), HB_TAG('P','R','O',' ')},*/ /* Old Provençal (to 1500) -> Provençal / Old Provençal */ + {HB_TAG('p','r','p',' '), HB_TAG('G','U','J',' ')}, /* Parsi (retired code) -> Gujarati */ + {HB_TAG('p','r','s',' '), HB_TAG('D','R','I',' ')}, /* Dari */ + {HB_TAG('p','r','s',' '), HB_TAG('F','A','R',' ')}, /* Dari -> Persian */ + {HB_TAG('p','s','e',' '), HB_TAG('M','L','Y',' ')}, /* Central Malay -> Malay */ + {HB_TAG('p','s','t',' '), HB_TAG('P','A','S',' ')}, /* Central Pashto -> Pashto */ + {HB_TAG('p','u','b',' '), HB_TAG('Q','I','N',' ')}, /* Purum -> Chin */ + {HB_TAG('p','u','z',' '), HB_TAG('Q','I','N',' ')}, /* Purum Naga (retired code) -> Chin */ + {HB_TAG('p','w','o',' '), HB_TAG('P','W','O',' ')}, /* Pwo Western Karen -> Western Pwo Karen */ + {HB_TAG('p','w','o',' '), HB_TAG('K','R','N',' ')}, /* Pwo Western Karen -> Karen */ + {HB_TAG('p','w','w',' '), HB_TAG('K','R','N',' ')}, /* Pwo Northern Karen -> Karen */ + {HB_TAG('q','u','b',' '), HB_TAG('Q','W','H',' ')}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','b',' '), HB_TAG('Q','U','Z',' ')}, /* Huallaga Huánuco Quechua -> Quechua */ + {HB_TAG('q','u','c',' '), HB_TAG('Q','U','C',' ')}, /* K’iche’ */ + {HB_TAG('q','u','c',' '), HB_TAG('M','Y','N',' ')}, /* K'iche' -> Mayan */ + {HB_TAG('q','u','d',' '), HB_TAG('Q','V','I',' ')}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','d',' '), HB_TAG('Q','U','Z',' ')}, /* Calderón Highland Quichua -> Quechua */ + {HB_TAG('q','u','f',' '), HB_TAG('Q','U','Z',' ')}, /* Lambayeque Quechua -> Quechua */ + {HB_TAG('q','u','g',' '), HB_TAG('Q','V','I',' ')}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','g',' '), HB_TAG('Q','U','Z',' ')}, /* Chimborazo Highland Quichua -> Quechua */ + {HB_TAG('q','u','h',' '), HB_TAG('Q','U','H',' ')}, /* South Bolivian Quechua -> Quechua (Bolivia) */ + {HB_TAG('q','u','h',' '), HB_TAG('Q','U','Z',' ')}, /* South Bolivian Quechua -> Quechua */ + {HB_TAG('q','u','k',' '), HB_TAG('Q','U','Z',' ')}, /* Chachapoyas Quechua -> Quechua */ + {HB_TAG('q','u','l',' '), HB_TAG('Q','U','H',' ')}, /* North Bolivian Quechua -> Quechua (Bolivia) */ + {HB_TAG('q','u','l',' '), HB_TAG('Q','U','Z',' ')}, /* North Bolivian Quechua -> Quechua */ + {HB_TAG('q','u','m',' '), HB_TAG('M','Y','N',' ')}, /* Sipacapense -> Mayan */ + {HB_TAG('q','u','p',' '), HB_TAG('Q','V','I',' ')}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ + {HB_TAG('q','u','p',' '), HB_TAG('Q','U','Z',' ')}, /* Southern Pastaza Quechua -> Quechua */ + {HB_TAG('q','u','r',' '), HB_TAG('Q','W','H',' ')}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','r',' '), HB_TAG('Q','U','Z',' ')}, /* Yanahuanca Pasco Quechua -> Quechua */ + {HB_TAG('q','u','s',' '), HB_TAG('Q','U','H',' ')}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ + {HB_TAG('q','u','s',' '), HB_TAG('Q','U','Z',' ')}, /* Santiago del Estero Quichua -> Quechua */ + {HB_TAG('q','u','v',' '), HB_TAG('M','Y','N',' ')}, /* Sacapulteco -> Mayan */ + {HB_TAG('q','u','w',' '), HB_TAG('Q','V','I',' ')}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','w',' '), HB_TAG('Q','U','Z',' ')}, /* Tena Lowland Quichua -> Quechua */ + {HB_TAG('q','u','x',' '), HB_TAG('Q','W','H',' ')}, /* Yauyos Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','x',' '), HB_TAG('Q','U','Z',' ')}, /* Yauyos Quechua -> Quechua */ + {HB_TAG('q','u','y',' '), HB_TAG('Q','U','Z',' ')}, /* Ayacucho Quechua -> Quechua */ +/*{HB_TAG('q','u','z',' '), HB_TAG('Q','U','Z',' ')},*/ /* Cusco Quechua -> Quechua */ + {HB_TAG('q','v','a',' '), HB_TAG('Q','W','H',' ')}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','a',' '), HB_TAG('Q','U','Z',' ')}, /* Ambo-Pasco Quechua -> Quechua */ + {HB_TAG('q','v','c',' '), HB_TAG('Q','U','Z',' ')}, /* Cajamarca Quechua -> Quechua */ + {HB_TAG('q','v','e',' '), HB_TAG('Q','U','Z',' ')}, /* Eastern Apurímac Quechua -> Quechua */ + {HB_TAG('q','v','h',' '), HB_TAG('Q','W','H',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','h',' '), HB_TAG('Q','U','Z',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */ + {HB_TAG('q','v','i',' '), HB_TAG('Q','V','I',' ')}, /* Imbabura Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','i',' '), HB_TAG('Q','U','Z',' ')}, /* Imbabura Highland Quichua -> Quechua */ + {HB_TAG('q','v','j',' '), HB_TAG('Q','V','I',' ')}, /* Loja Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','j',' '), HB_TAG('Q','U','Z',' ')}, /* Loja Highland Quichua -> Quechua */ + {HB_TAG('q','v','l',' '), HB_TAG('Q','W','H',' ')}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','l',' '), HB_TAG('Q','U','Z',' ')}, /* Cajatambo North Lima Quechua -> Quechua */ + {HB_TAG('q','v','m',' '), HB_TAG('Q','W','H',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','m',' '), HB_TAG('Q','U','Z',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua */ + {HB_TAG('q','v','n',' '), HB_TAG('Q','W','H',' ')}, /* North Junín Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','n',' '), HB_TAG('Q','U','Z',' ')}, /* North Junín Quechua -> Quechua */ + {HB_TAG('q','v','o',' '), HB_TAG('Q','V','I',' ')}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ + {HB_TAG('q','v','o',' '), HB_TAG('Q','U','Z',' ')}, /* Napo Lowland Quechua -> Quechua */ + {HB_TAG('q','v','p',' '), HB_TAG('Q','W','H',' ')}, /* Pacaraos Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','p',' '), HB_TAG('Q','U','Z',' ')}, /* Pacaraos Quechua -> Quechua */ + {HB_TAG('q','v','s',' '), HB_TAG('Q','U','Z',' ')}, /* San Martín Quechua -> Quechua */ + {HB_TAG('q','v','w',' '), HB_TAG('Q','W','H',' ')}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','w',' '), HB_TAG('Q','U','Z',' ')}, /* Huaylla Wanca Quechua -> Quechua */ + {HB_TAG('q','v','z',' '), HB_TAG('Q','V','I',' ')}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','z',' '), HB_TAG('Q','U','Z',' ')}, /* Northern Pastaza Quichua -> Quechua */ + {HB_TAG('q','w','a',' '), HB_TAG('Q','W','H',' ')}, /* Corongo Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','a',' '), HB_TAG('Q','U','Z',' ')}, /* Corongo Ancash Quechua -> Quechua */ + {HB_TAG('q','w','c',' '), HB_TAG('Q','U','Z',' ')}, /* Classical Quechua -> Quechua */ + {HB_TAG('q','w','h',' '), HB_TAG('Q','W','H',' ')}, /* Huaylas Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','h',' '), HB_TAG('Q','U','Z',' ')}, /* Huaylas Ancash Quechua -> Quechua */ + {HB_TAG('q','w','s',' '), HB_TAG('Q','W','H',' ')}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','s',' '), HB_TAG('Q','U','Z',' ')}, /* Sihuas Ancash Quechua -> Quechua */ + {HB_TAG('q','w','t',' '), HB_TAG('A','T','H',' ')}, /* Kwalhioqua-Tlatskanai -> Athapaskan */ + {HB_TAG('q','x','a',' '), HB_TAG('Q','W','H',' ')}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','a',' '), HB_TAG('Q','U','Z',' ')}, /* Chiquián Ancash Quechua -> Quechua */ + {HB_TAG('q','x','c',' '), HB_TAG('Q','W','H',' ')}, /* Chincha Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','c',' '), HB_TAG('Q','U','Z',' ')}, /* Chincha Quechua -> Quechua */ + {HB_TAG('q','x','h',' '), HB_TAG('Q','W','H',' ')}, /* Panao Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','h',' '), HB_TAG('Q','U','Z',' ')}, /* Panao Huánuco Quechua -> Quechua */ + {HB_TAG('q','x','l',' '), HB_TAG('Q','V','I',' ')}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','x','l',' '), HB_TAG('Q','U','Z',' ')}, /* Salasaca Highland Quichua -> Quechua */ + {HB_TAG('q','x','n',' '), HB_TAG('Q','W','H',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','n',' '), HB_TAG('Q','U','Z',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua */ + {HB_TAG('q','x','o',' '), HB_TAG('Q','W','H',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','o',' '), HB_TAG('Q','U','Z',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua */ + {HB_TAG('q','x','p',' '), HB_TAG('Q','U','Z',' ')}, /* Puno Quechua -> Quechua */ + {HB_TAG('q','x','r',' '), HB_TAG('Q','V','I',' ')}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','x','r',' '), HB_TAG('Q','U','Z',' ')}, /* Cañar Highland Quichua -> Quechua */ + {HB_TAG('q','x','t',' '), HB_TAG('Q','W','H',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','t',' '), HB_TAG('Q','U','Z',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua */ + {HB_TAG('q','x','u',' '), HB_TAG('Q','U','Z',' ')}, /* Arequipa-La Unión Quechua -> Quechua */ + {HB_TAG('q','x','w',' '), HB_TAG('Q','W','H',' ')}, /* Jauja Wanca Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','w',' '), HB_TAG('Q','U','Z',' ')}, /* Jauja Wanca Quechua -> Quechua */ + {HB_TAG('r','a','g',' '), HB_TAG('L','U','H',' ')}, /* Logooli -> Luyia */ +/*{HB_TAG('r','a','j',' '), HB_TAG('R','A','J',' ')},*/ /* Rajasthani [macrolanguage] */ + {HB_TAG('r','a','l',' '), HB_TAG('Q','I','N',' ')}, /* Ralte -> Chin */ +/*{HB_TAG('r','a','r',' '), HB_TAG('R','A','R',' ')},*/ /* Rarotongan */ + {HB_TAG('r','b','b',' '), HB_TAG('P','L','G',' ')}, /* Rumai Palaung -> Palaung */ + {HB_TAG('r','b','l',' '), HB_TAG('B','I','K',' ')}, /* Miraya Bikol -> Bikol */ + {HB_TAG('r','c','f',' '), HB_TAG('C','P','P',' ')}, /* Réunion Creole French -> Creoles */ +/*{HB_TAG('r','e','j',' '), HB_TAG('R','E','J',' ')},*/ /* Rejang */ +/*{HB_TAG('r','h','g',' '), HB_TAG('R','H','G',' ')},*/ /* Rohingya */ +/*{HB_TAG('r','i','a',' '), HB_TAG('R','I','A',' ')},*/ /* Riang (India) */ + {HB_TAG('r','i','f',' '), HB_TAG('R','I','F',' ')}, /* Tarifit */ + {HB_TAG('r','i','f',' '), HB_TAG('B','B','R',' ')}, /* Tarifit -> Berber */ +/*{HB_TAG('r','i','t',' '), HB_TAG('R','I','T',' ')},*/ /* Ritharrngu -> Ritarungo */ + {HB_TAG('r','k','i',' '), HB_TAG('A','R','K',' ')}, /* Rakhine */ +/*{HB_TAG('r','k','w',' '), HB_TAG('R','K','W',' ')},*/ /* Arakwal */ + {HB_TAG('r','m','c',' '), HB_TAG('R','O','Y',' ')}, /* Carpathian Romani -> Romany */ + {HB_TAG('r','m','f',' '), HB_TAG('R','O','Y',' ')}, /* Kalo Finnish Romani -> Romany */ + {HB_TAG('r','m','l',' '), HB_TAG('R','O','Y',' ')}, /* Baltic Romani -> Romany */ + {HB_TAG('r','m','n',' '), HB_TAG('R','O','Y',' ')}, /* Balkan Romani -> Romany */ + {HB_TAG('r','m','o',' '), HB_TAG('R','O','Y',' ')}, /* Sinte Romani -> Romany */ + {HB_TAG('r','m','s',' '), HB_TAG_NONE }, /* Romanian Sign Language != Romansh */ + {HB_TAG('r','m','w',' '), HB_TAG('R','O','Y',' ')}, /* Welsh Romani -> Romany */ + {HB_TAG('r','m','y',' '), HB_TAG('R','M','Y',' ')}, /* Vlax Romani */ + {HB_TAG('r','m','y',' '), HB_TAG('R','O','Y',' ')}, /* Vlax Romani -> Romany */ + {HB_TAG('r','m','z',' '), HB_TAG('A','R','K',' ')}, /* Marma -> Rakhine */ + {HB_TAG('r','o','m',' '), HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */ + {HB_TAG('r','o','p',' '), HB_TAG('C','P','P',' ')}, /* Kriol -> Creoles */ + {HB_TAG('r','t','c',' '), HB_TAG('Q','I','N',' ')}, /* Rungtu Chin -> Chin */ +/*{HB_TAG('r','t','m',' '), HB_TAG('R','T','M',' ')},*/ /* Rotuman */ + {HB_TAG('r','u','e',' '), HB_TAG('R','S','Y',' ')}, /* Rusyn */ +/*{HB_TAG('r','u','p',' '), HB_TAG('R','U','P',' ')},*/ /* Aromanian */ + {HB_TAG('r','w','r',' '), HB_TAG('M','A','W',' ')}, /* Marwari (India) */ + {HB_TAG('s','a','d',' '), HB_TAG_NONE }, /* Sandawe != Sadri */ + {HB_TAG('s','a','h',' '), HB_TAG('Y','A','K',' ')}, /* Yakut -> Sakha */ + {HB_TAG('s','a','m',' '), HB_TAG('P','A','A',' ')}, /* Samaritan Aramaic -> Palestinian Aramaic */ +/*{HB_TAG('s','a','s',' '), HB_TAG('S','A','S',' ')},*/ /* Sasak */ +/*{HB_TAG('s','a','t',' '), HB_TAG('S','A','T',' ')},*/ /* Santali */ + {HB_TAG('s','a','y',' '), HB_TAG_NONE }, /* Saya != Sayisi */ + {HB_TAG('s','c','f',' '), HB_TAG('C','P','P',' ')}, /* San Miguel Creole French -> Creoles */ + {HB_TAG('s','c','h',' '), HB_TAG('Q','I','N',' ')}, /* Sakachep -> Chin */ + {HB_TAG('s','c','i',' '), HB_TAG('C','P','P',' ')}, /* Sri Lankan Creole Malay -> Creoles */ + {HB_TAG('s','c','k',' '), HB_TAG('S','A','D',' ')}, /* Sadri */ +/*{HB_TAG('s','c','n',' '), HB_TAG('S','C','N',' ')},*/ /* Sicilian */ +/*{HB_TAG('s','c','o',' '), HB_TAG('S','C','O',' ')},*/ /* Scots */ + {HB_TAG('s','c','s',' '), HB_TAG('S','C','S',' ')}, /* North Slavey */ + {HB_TAG('s','c','s',' '), HB_TAG('S','L','A',' ')}, /* North Slavey -> Slavey */ + {HB_TAG('s','c','s',' '), HB_TAG('A','T','H',' ')}, /* North Slavey -> Athapaskan */ + {HB_TAG('s','d','c',' '), HB_TAG('S','R','D',' ')}, /* Sassarese Sardinian -> Sardinian */ + {HB_TAG('s','d','h',' '), HB_TAG('K','U','R',' ')}, /* Southern Kurdish -> Kurdish */ + {HB_TAG('s','d','n',' '), HB_TAG('S','R','D',' ')}, /* Gallurese Sardinian -> Sardinian */ + {HB_TAG('s','d','s',' '), HB_TAG('B','B','R',' ')}, /* Sened -> Berber */ + {HB_TAG('s','e','h',' '), HB_TAG('S','N','A',' ')}, /* Sena */ + {HB_TAG('s','e','k',' '), HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */ +/*{HB_TAG('s','e','l',' '), HB_TAG('S','E','L',' ')},*/ /* Selkup */ + {HB_TAG('s','e','z',' '), HB_TAG('Q','I','N',' ')}, /* Senthang Chin -> Chin */ + {HB_TAG('s','f','m',' '), HB_TAG('S','F','M',' ')}, /* Small Flowery Miao */ + {HB_TAG('s','f','m',' '), HB_TAG('H','M','N',' ')}, /* Small Flowery Miao -> Hmong */ +/*{HB_TAG('s','g','a',' '), HB_TAG('S','G','A',' ')},*/ /* Old Irish (to 900) */ + {HB_TAG('s','g','c',' '), HB_TAG('K','A','L',' ')}, /* Kipsigis -> Kalenjin */ + {HB_TAG('s','g','o',' '), HB_TAG_NONE }, /* Songa (retired code) != Sango */ +/*{HB_TAG('s','g','s',' '), HB_TAG('S','G','S',' ')},*/ /* Samogitian */ + {HB_TAG('s','g','w',' '), HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage -> Chaha Gurage */ + {HB_TAG('s','h','i',' '), HB_TAG('S','H','I',' ')}, /* Tachelhit */ + {HB_TAG('s','h','i',' '), HB_TAG('B','B','R',' ')}, /* Tachelhit -> Berber */ + {HB_TAG('s','h','l',' '), HB_TAG('Q','I','N',' ')}, /* Shendu -> Chin */ +/*{HB_TAG('s','h','n',' '), HB_TAG('S','H','N',' ')},*/ /* Shan */ + {HB_TAG('s','h','u',' '), HB_TAG('A','R','A',' ')}, /* Chadian Arabic -> Arabic */ + {HB_TAG('s','h','y',' '), HB_TAG('B','B','R',' ')}, /* Tachawit -> Berber */ + {HB_TAG('s','i','b',' '), HB_TAG_NONE }, /* Sebop != Sibe */ +/*{HB_TAG('s','i','d',' '), HB_TAG('S','I','D',' ')},*/ /* Sidamo */ + {HB_TAG('s','i','g',' '), HB_TAG_NONE }, /* Paasaal != Silte Gurage */ + {HB_TAG('s','i','z',' '), HB_TAG('B','B','R',' ')}, /* Siwi -> Berber */ + {HB_TAG('s','j','d',' '), HB_TAG('K','S','M',' ')}, /* Kildin Sami */ + {HB_TAG('s','j','o',' '), HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */ + {HB_TAG('s','j','s',' '), HB_TAG('B','B','R',' ')}, /* Senhaja De Srair -> Berber */ + {HB_TAG('s','k','g',' '), HB_TAG('M','L','G',' ')}, /* Sakalava Malagasy -> Malagasy */ + {HB_TAG('s','k','r',' '), HB_TAG('S','R','K',' ')}, /* Saraiki */ + {HB_TAG('s','k','s',' '), HB_TAG_NONE }, /* Maia != Skolt Sami */ + {HB_TAG('s','k','w',' '), HB_TAG('C','P','P',' ')}, /* Skepi Creole Dutch -> Creoles */ + {HB_TAG('s','k','y',' '), HB_TAG_NONE }, /* Sikaiana != Slovak */ + {HB_TAG('s','l','a',' '), HB_TAG_NONE }, /* Slavic [collection] != Slavey */ + {HB_TAG('s','m','a',' '), HB_TAG('S','S','M',' ')}, /* Southern Sami */ + {HB_TAG('s','m','d',' '), HB_TAG('M','B','N',' ')}, /* Sama (retired code) -> Mbundu */ + {HB_TAG('s','m','j',' '), HB_TAG('L','S','M',' ')}, /* Lule Sami */ + {HB_TAG('s','m','l',' '), HB_TAG_NONE }, /* Central Sama != Somali */ + {HB_TAG('s','m','n',' '), HB_TAG('I','S','M',' ')}, /* Inari Sami */ + {HB_TAG('s','m','s',' '), HB_TAG('S','K','S',' ')}, /* Skolt Sami */ + {HB_TAG('s','m','t',' '), HB_TAG('Q','I','N',' ')}, /* Simte -> Chin */ + {HB_TAG('s','n','b',' '), HB_TAG('I','B','A',' ')}, /* Sebuyau (retired code) -> Iban */ + {HB_TAG('s','n','h',' '), HB_TAG_NONE }, /* Shinabo (retired code) != Sinhala (Sinhalese) */ +/*{HB_TAG('s','n','k',' '), HB_TAG('S','N','K',' ')},*/ /* Soninke */ + {HB_TAG('s','o','g',' '), HB_TAG_NONE }, /* Sogdian != Sodo Gurage */ +/*{HB_TAG('s','o','p',' '), HB_TAG('S','O','P',' ')},*/ /* Songe */ + {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */ + {HB_TAG('s','p','y',' '), HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ + {HB_TAG('s','r','b',' '), HB_TAG_NONE }, /* Sora != Serbian */ + {HB_TAG('s','r','c',' '), HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ + {HB_TAG('s','r','k',' '), HB_TAG_NONE }, /* Serudung Murut != Saraiki */ + {HB_TAG('s','r','m',' '), HB_TAG('C','P','P',' ')}, /* Saramaccan -> Creoles */ + {HB_TAG('s','r','n',' '), HB_TAG('C','P','P',' ')}, /* Sranan Tongo -> Creoles */ + {HB_TAG('s','r','o',' '), HB_TAG('S','R','D',' ')}, /* Campidanese Sardinian -> Sardinian */ +/*{HB_TAG('s','r','r',' '), HB_TAG('S','R','R',' ')},*/ /* Serer */ + {HB_TAG('s','r','s',' '), HB_TAG('A','T','H',' ')}, /* Sarsi -> Athapaskan */ + {HB_TAG('s','s','h',' '), HB_TAG('A','R','A',' ')}, /* Shihhi Arabic -> Arabic */ + {HB_TAG('s','s','l',' '), HB_TAG_NONE }, /* Western Sisaala != South Slavey */ + {HB_TAG('s','s','m',' '), HB_TAG_NONE }, /* Semnam != Southern Sami */ + {HB_TAG('s','t','a',' '), HB_TAG('C','P','P',' ')}, /* Settla -> Creoles */ +/*{HB_TAG('s','t','q',' '), HB_TAG('S','T','Q',' ')},*/ /* Saterfriesisch -> Saterland Frisian */ + {HB_TAG('s','t','v',' '), HB_TAG('S','I','G',' ')}, /* Silt'e -> Silte Gurage */ +/*{HB_TAG('s','u','k',' '), HB_TAG('S','U','K',' ')},*/ /* Sukuma */ + {HB_TAG('s','u','q',' '), HB_TAG('S','U','R',' ')}, /* Suri */ + {HB_TAG('s','u','r',' '), HB_TAG_NONE }, /* Mwaghavul != Suri */ +/*{HB_TAG('s','v','a',' '), HB_TAG('S','V','A',' ')},*/ /* Svan */ + {HB_TAG('s','v','c',' '), HB_TAG('C','P','P',' ')}, /* Vincentian Creole English -> Creoles */ + {HB_TAG('s','v','e',' '), HB_TAG_NONE }, /* Serili != Swedish */ + {HB_TAG('s','w','b',' '), HB_TAG('C','M','R',' ')}, /* Maore Comorian -> Comorian */ + {HB_TAG('s','w','c',' '), HB_TAG('S','W','K',' ')}, /* Congo Swahili -> Swahili */ + {HB_TAG('s','w','h',' '), HB_TAG('S','W','K',' ')}, /* Swahili */ + {HB_TAG('s','w','k',' '), HB_TAG_NONE }, /* Malawi Sena != Swahili */ + {HB_TAG('s','w','n',' '), HB_TAG('B','B','R',' ')}, /* Sawknah -> Berber */ + {HB_TAG('s','w','v',' '), HB_TAG('M','A','W',' ')}, /* Shekhawati -> Marwari */ +/*{HB_TAG('s','x','u',' '), HB_TAG('S','X','U',' ')},*/ /* Upper Saxon */ + {HB_TAG('s','y','c',' '), HB_TAG('S','Y','R',' ')}, /* Classical Syriac -> Syriac */ +/*{HB_TAG('s','y','l',' '), HB_TAG('S','Y','L',' ')},*/ /* Sylheti */ +/*{HB_TAG('s','y','r',' '), HB_TAG('S','Y','R',' ')},*/ /* Syriac [macrolanguage] */ +/*{HB_TAG('s','z','l',' '), HB_TAG('S','Z','L',' ')},*/ /* Silesian */ + {HB_TAG('t','a','a',' '), HB_TAG('A','T','H',' ')}, /* Lower Tanana -> Athapaskan */ +/*{HB_TAG('t','a','b',' '), HB_TAG('T','A','B',' ')},*/ /* Tabassaran -> Tabasaran */ + {HB_TAG('t','a','j',' '), HB_TAG_NONE }, /* Eastern Tamang != Tajiki */ + {HB_TAG('t','a','q',' '), HB_TAG('T','M','H',' ')}, /* Tamasheq -> Tamashek */ + {HB_TAG('t','a','q',' '), HB_TAG('B','B','R',' ')}, /* Tamasheq -> Berber */ + {HB_TAG('t','a','s',' '), HB_TAG('C','P','P',' ')}, /* Tay Boi -> Creoles */ + {HB_TAG('t','a','u',' '), HB_TAG('A','T','H',' ')}, /* Upper Tanana -> Athapaskan */ + {HB_TAG('t','c','b',' '), HB_TAG('A','T','H',' ')}, /* Tanacross -> Athapaskan */ + {HB_TAG('t','c','e',' '), HB_TAG('A','T','H',' ')}, /* Southern Tutchone -> Athapaskan */ + {HB_TAG('t','c','h',' '), HB_TAG('C','P','P',' ')}, /* Turks And Caicos Creole English -> Creoles */ + {HB_TAG('t','c','p',' '), HB_TAG('Q','I','N',' ')}, /* Tawr Chin -> Chin */ + {HB_TAG('t','c','s',' '), HB_TAG('C','P','P',' ')}, /* Torres Strait Creole -> Creoles */ + {HB_TAG('t','c','y',' '), HB_TAG('T','U','L',' ')}, /* Tulu */ + {HB_TAG('t','c','z',' '), HB_TAG('Q','I','N',' ')}, /* Thado Chin -> Chin */ +/*{HB_TAG('t','d','d',' '), HB_TAG('T','D','D',' ')},*/ /* Tai Nüa -> Dehong Dai */ + {HB_TAG('t','d','x',' '), HB_TAG('M','L','G',' ')}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ + {HB_TAG('t','e','c',' '), HB_TAG('K','A','L',' ')}, /* Terik -> Kalenjin */ + {HB_TAG('t','e','m',' '), HB_TAG('T','M','N',' ')}, /* Timne -> Temne */ +/*{HB_TAG('t','e','t',' '), HB_TAG('T','E','T',' ')},*/ /* Tetum */ + {HB_TAG('t','e','z',' '), HB_TAG('B','B','R',' ')}, /* Tetserret -> Berber */ + {HB_TAG('t','f','n',' '), HB_TAG('A','T','H',' ')}, /* Tanaina -> Athapaskan */ + {HB_TAG('t','g','h',' '), HB_TAG('C','P','P',' ')}, /* Tobagonian Creole English -> Creoles */ + {HB_TAG('t','g','j',' '), HB_TAG('N','I','S',' ')}, /* Tagin -> Nisi */ + {HB_TAG('t','g','n',' '), HB_TAG_NONE }, /* Tandaganon != Tongan */ + {HB_TAG('t','g','r',' '), HB_TAG_NONE }, /* Tareng != Tigre */ + {HB_TAG('t','g','x',' '), HB_TAG('A','T','H',' ')}, /* Tagish -> Athapaskan */ + {HB_TAG('t','g','y',' '), HB_TAG_NONE }, /* Togoyo != Tigrinya */ + {HB_TAG('t','h','t',' '), HB_TAG('A','T','H',' ')}, /* Tahltan -> Athapaskan */ + {HB_TAG('t','h','v',' '), HB_TAG('T','M','H',' ')}, /* Tahaggart Tamahaq -> Tamashek */ + {HB_TAG('t','h','v',' '), HB_TAG('B','B','R',' ')}, /* Tahaggart Tamahaq -> Berber */ + {HB_TAG('t','h','z',' '), HB_TAG('T','M','H',' ')}, /* Tayart Tamajeq -> Tamashek */ + {HB_TAG('t','h','z',' '), HB_TAG('B','B','R',' ')}, /* Tayart Tamajeq -> Berber */ + {HB_TAG('t','i','a',' '), HB_TAG('B','B','R',' ')}, /* Tidikelt Tamazight -> Berber */ + {HB_TAG('t','i','g',' '), HB_TAG('T','G','R',' ')}, /* Tigre */ +/*{HB_TAG('t','i','v',' '), HB_TAG('T','I','V',' ')},*/ /* Tiv */ +/*{HB_TAG('t','j','l',' '), HB_TAG('T','J','L',' ')},*/ /* Tai Laing */ + {HB_TAG('t','j','o',' '), HB_TAG('B','B','R',' ')}, /* Temacine Tamazight -> Berber */ + {HB_TAG('t','k','g',' '), HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */ + {HB_TAG('t','k','m',' '), HB_TAG_NONE }, /* Takelma != Turkmen */ +/*{HB_TAG('t','l','i',' '), HB_TAG('T','L','I',' ')},*/ /* Tlingit */ + {HB_TAG('t','m','g',' '), HB_TAG('C','P','P',' ')}, /* Ternateño -> Creoles */ + {HB_TAG('t','m','h',' '), HB_TAG('T','M','H',' ')}, /* Tamashek [macrolanguage] */ + {HB_TAG('t','m','h',' '), HB_TAG('B','B','R',' ')}, /* Tamashek [macrolanguage] -> Berber */ + {HB_TAG('t','m','n',' '), HB_TAG_NONE }, /* Taman (Indonesia) != Temne */ + {HB_TAG('t','m','w',' '), HB_TAG('M','L','Y',' ')}, /* Temuan -> Malay */ + {HB_TAG('t','n','a',' '), HB_TAG_NONE }, /* Tacana != Tswana */ + {HB_TAG('t','n','e',' '), HB_TAG_NONE }, /* Tinoc Kallahan (retired code) != Tundra Enets */ + {HB_TAG('t','n','f',' '), HB_TAG('D','R','I',' ')}, /* Tangshewi (retired code) -> Dari */ + {HB_TAG('t','n','f',' '), HB_TAG('F','A','R',' ')}, /* Tangshewi (retired code) -> Persian */ + {HB_TAG('t','n','g',' '), HB_TAG_NONE }, /* Tobanga != Tonga */ + {HB_TAG('t','o','d',' '), HB_TAG('T','O','D','0')}, /* Toma */ + {HB_TAG('t','o','i',' '), HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */ + {HB_TAG('t','o','j',' '), HB_TAG('M','Y','N',' ')}, /* Tojolabal -> Mayan */ + {HB_TAG('t','o','l',' '), HB_TAG('A','T','H',' ')}, /* Tolowa -> Athapaskan */ + {HB_TAG('t','o','r',' '), HB_TAG('B','A','D','0')}, /* Togbo-Vara Banda -> Banda */ + {HB_TAG('t','p','i',' '), HB_TAG('T','P','I',' ')}, /* Tok Pisin */ + {HB_TAG('t','p','i',' '), HB_TAG('C','P','P',' ')}, /* Tok Pisin -> Creoles */ + {HB_TAG('t','r','f',' '), HB_TAG('C','P','P',' ')}, /* Trinidadian Creole English -> Creoles */ + {HB_TAG('t','r','k',' '), HB_TAG_NONE }, /* Turkic [collection] != Turkish */ + {HB_TAG('t','r','u',' '), HB_TAG('T','U','A',' ')}, /* Turoyo -> Turoyo Aramaic */ + {HB_TAG('t','r','u',' '), HB_TAG('S','Y','R',' ')}, /* Turoyo -> Syriac */ + {HB_TAG('t','s','g',' '), HB_TAG_NONE }, /* Tausug != Tsonga */ +/*{HB_TAG('t','s','j',' '), HB_TAG('T','S','J',' ')},*/ /* Tshangla */ + {HB_TAG('t','t','c',' '), HB_TAG('M','Y','N',' ')}, /* Tektiteko -> Mayan */ + {HB_TAG('t','t','m',' '), HB_TAG('A','T','H',' ')}, /* Northern Tutchone -> Athapaskan */ + {HB_TAG('t','t','q',' '), HB_TAG('T','M','H',' ')}, /* Tawallammat Tamajaq -> Tamashek */ + {HB_TAG('t','t','q',' '), HB_TAG('B','B','R',' ')}, /* Tawallammat Tamajaq -> Berber */ + {HB_TAG('t','u','a',' '), HB_TAG_NONE }, /* Wiarumus != Turoyo Aramaic */ + {HB_TAG('t','u','l',' '), HB_TAG_NONE }, /* Tula != Tulu */ +/*{HB_TAG('t','u','m',' '), HB_TAG('T','U','M',' ')},*/ /* Tumbuka */ + {HB_TAG('t','u','u',' '), HB_TAG('A','T','H',' ')}, /* Tututni -> Athapaskan */ + {HB_TAG('t','u','v',' '), HB_TAG_NONE }, /* Turkana != Tuvin */ + {HB_TAG('t','u','y',' '), HB_TAG('K','A','L',' ')}, /* Tugen -> Kalenjin */ +/*{HB_TAG('t','v','l',' '), HB_TAG('T','V','L',' ')},*/ /* Tuvalu */ + {HB_TAG('t','v','y',' '), HB_TAG('C','P','P',' ')}, /* Timor Pidgin -> Creoles */ + {HB_TAG('t','x','c',' '), HB_TAG('A','T','H',' ')}, /* Tsetsaut -> Athapaskan */ + {HB_TAG('t','x','y',' '), HB_TAG('M','L','G',' ')}, /* Tanosy Malagasy -> Malagasy */ + {HB_TAG('t','y','v',' '), HB_TAG('T','U','V',' ')}, /* Tuvinian -> Tuvin */ +/*{HB_TAG('t','y','z',' '), HB_TAG('T','Y','Z',' ')},*/ /* Tày */ + {HB_TAG('t','z','h',' '), HB_TAG('M','Y','N',' ')}, /* Tzeltal -> Mayan */ + {HB_TAG('t','z','j',' '), HB_TAG('M','Y','N',' ')}, /* Tz'utujil -> Mayan */ + {HB_TAG('t','z','m',' '), HB_TAG('T','Z','M',' ')}, /* Central Atlas Tamazight -> Tamazight */ + {HB_TAG('t','z','m',' '), HB_TAG('B','B','R',' ')}, /* Central Atlas Tamazight -> Berber */ + {HB_TAG('t','z','o',' '), HB_TAG('T','Z','O',' ')}, /* Tzotzil */ + {HB_TAG('t','z','o',' '), HB_TAG('M','Y','N',' ')}, /* Tzotzil -> Mayan */ + {HB_TAG('u','b','l',' '), HB_TAG('B','I','K',' ')}, /* Buhi'non Bikol -> Bikol */ +/*{HB_TAG('u','d','m',' '), HB_TAG('U','D','M',' ')},*/ /* Udmurt */ + {HB_TAG('u','k','i',' '), HB_TAG('K','U','I',' ')}, /* Kui (India) */ + {HB_TAG('u','l','n',' '), HB_TAG('C','P','P',' ')}, /* Unserdeutsch -> Creoles */ +/*{HB_TAG('u','m','b',' '), HB_TAG('U','M','B',' ')},*/ /* Umbundu */ + {HB_TAG('u','n','r',' '), HB_TAG('M','U','N',' ')}, /* Mundari */ + {HB_TAG('u','r','k',' '), HB_TAG('M','L','Y',' ')}, /* Urak Lawoi' -> Malay */ + {HB_TAG('u','s','p',' '), HB_TAG('M','Y','N',' ')}, /* Uspanteco -> Mayan */ + {HB_TAG('u','z','n',' '), HB_TAG('U','Z','B',' ')}, /* Northern Uzbek -> Uzbek */ + {HB_TAG('u','z','s',' '), HB_TAG('U','Z','B',' ')}, /* Southern Uzbek -> Uzbek */ + {HB_TAG('v','a','p',' '), HB_TAG('Q','I','N',' ')}, /* Vaiphei -> Chin */ +/*{HB_TAG('v','e','c',' '), HB_TAG('V','E','C',' ')},*/ /* Venetian */ + {HB_TAG('v','i','c',' '), HB_TAG('C','P','P',' ')}, /* Virgin Islands Creole English -> Creoles */ + {HB_TAG('v','i','t',' '), HB_TAG_NONE }, /* Viti != Vietnamese */ + {HB_TAG('v','k','k',' '), HB_TAG('M','L','Y',' ')}, /* Kaur -> Malay */ + {HB_TAG('v','k','p',' '), HB_TAG('C','P','P',' ')}, /* Korlai Creole Portuguese -> Creoles */ + {HB_TAG('v','k','t',' '), HB_TAG('M','L','Y',' ')}, /* Tenggarong Kutai Malay -> Malay */ + {HB_TAG('v','l','s',' '), HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ + {HB_TAG('v','m','w',' '), HB_TAG('M','A','K',' ')}, /* Makhuwa */ +/*{HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')},*/ /* Võro */ + {HB_TAG('w','a','g',' '), HB_TAG_NONE }, /* Wa'ema != Wagdi */ +/*{HB_TAG('w','a','r',' '), HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ + {HB_TAG('w','b','m',' '), HB_TAG('W','A',' ',' ')}, /* Wa */ + {HB_TAG('w','b','r',' '), HB_TAG('W','A','G',' ')}, /* Wagdi */ + {HB_TAG('w','b','r',' '), HB_TAG('R','A','J',' ')}, /* Wagdi -> Rajasthani */ +/*{HB_TAG('w','c','i',' '), HB_TAG('W','C','I',' ')},*/ /* Waci Gbe */ + {HB_TAG('w','e','a',' '), HB_TAG('K','R','N',' ')}, /* Wewaw -> Karen */ + {HB_TAG('w','e','s',' '), HB_TAG('C','P','P',' ')}, /* Cameroon Pidgin -> Creoles */ + {HB_TAG('w','e','u',' '), HB_TAG('Q','I','N',' ')}, /* Rawngtu Chin -> Chin */ + {HB_TAG('w','l','c',' '), HB_TAG('C','M','R',' ')}, /* Mwali Comorian -> Comorian */ + {HB_TAG('w','l','e',' '), HB_TAG('S','I','G',' ')}, /* Wolane -> Silte Gurage */ + {HB_TAG('w','l','k',' '), HB_TAG('A','T','H',' ')}, /* Wailaki -> Athapaskan */ + {HB_TAG('w','n','i',' '), HB_TAG('C','M','R',' ')}, /* Ndzwani Comorian -> Comorian */ + {HB_TAG('w','r','y',' '), HB_TAG('M','A','W',' ')}, /* Merwari -> Marwari */ + {HB_TAG('w','s','g',' '), HB_TAG('G','O','N',' ')}, /* Adilabad Gondi -> Gondi */ +/*{HB_TAG('w','t','m',' '), HB_TAG('W','T','M',' ')},*/ /* Mewati */ + {HB_TAG('w','u','u',' '), HB_TAG('Z','H','S',' ')}, /* Wu Chinese -> Chinese, Simplified */ + {HB_TAG('x','a','l',' '), HB_TAG('K','L','M',' ')}, /* Kalmyk */ + {HB_TAG('x','a','l',' '), HB_TAG('T','O','D',' ')}, /* Kalmyk -> Todo */ + {HB_TAG('x','a','n',' '), HB_TAG('S','E','K',' ')}, /* Xamtanga -> Sekota */ + {HB_TAG('x','b','d',' '), HB_TAG_NONE }, /* Bindal != Lü */ +/*{HB_TAG('x','j','b',' '), HB_TAG('X','J','B',' ')},*/ /* Minjungbal -> Minjangbal */ +/*{HB_TAG('x','k','f',' '), HB_TAG('X','K','F',' ')},*/ /* Khengkha */ + {HB_TAG('x','m','g',' '), HB_TAG('B','M','L',' ')}, /* Mengaka -> Bamileke */ + {HB_TAG('x','m','m',' '), HB_TAG('M','L','Y',' ')}, /* Manado Malay -> Malay */ + {HB_TAG('x','m','m',' '), HB_TAG('C','P','P',' ')}, /* Manado Malay -> Creoles */ + {HB_TAG('x','m','v',' '), HB_TAG('M','L','G',' ')}, /* Antankarana Malagasy -> Malagasy */ + {HB_TAG('x','m','w',' '), HB_TAG('M','L','G',' ')}, /* Tsimihety Malagasy -> Malagasy */ + {HB_TAG('x','n','j',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (Tanzania) -> Sutu */ + {HB_TAG('x','n','q',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (Mozambique) -> Sutu */ + {HB_TAG('x','n','r',' '), HB_TAG('D','G','R',' ')}, /* Kangri -> Dogri (macrolanguage) */ +/*{HB_TAG('x','o','g',' '), HB_TAG('X','O','G',' ')},*/ /* Soga */ + {HB_TAG('x','p','e',' '), HB_TAG('X','P','E',' ')}, /* Liberia Kpelle -> Kpelle (Liberia) */ + {HB_TAG('x','p','e',' '), HB_TAG('K','P','L',' ')}, /* Liberia Kpelle -> Kpelle */ + {HB_TAG('x','s','l',' '), HB_TAG('S','S','L',' ')}, /* South Slavey */ + {HB_TAG('x','s','l',' '), HB_TAG('S','L','A',' ')}, /* South Slavey -> Slavey */ + {HB_TAG('x','s','l',' '), HB_TAG('A','T','H',' ')}, /* South Slavey -> Athapaskan */ + {HB_TAG('x','s','t',' '), HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) -> Silte Gurage */ +/*{HB_TAG('x','u','b',' '), HB_TAG('X','U','B',' ')},*/ /* Betta Kurumba -> Bette Kuruma */ +/*{HB_TAG('x','u','j',' '), HB_TAG('X','U','J',' ')},*/ /* Jennu Kurumba -> Jennu Kuruma */ + {HB_TAG('x','u','p',' '), HB_TAG('A','T','H',' ')}, /* Upper Umpqua -> Athapaskan */ + {HB_TAG('x','w','o',' '), HB_TAG('T','O','D',' ')}, /* Written Oirat -> Todo */ + {HB_TAG('y','a','j',' '), HB_TAG('B','A','D','0')}, /* Banda-Yangere -> Banda */ + {HB_TAG('y','a','k',' '), HB_TAG_NONE }, /* Yakama != Sakha */ +/*{HB_TAG('y','a','o',' '), HB_TAG('Y','A','O',' ')},*/ /* Yao */ +/*{HB_TAG('y','a','p',' '), HB_TAG('Y','A','P',' ')},*/ /* Yapese */ + {HB_TAG('y','b','a',' '), HB_TAG_NONE }, /* Yala != Yoruba */ + {HB_TAG('y','b','b',' '), HB_TAG('B','M','L',' ')}, /* Yemba -> Bamileke */ + {HB_TAG('y','b','d',' '), HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */ + {HB_TAG('y','c','r',' '), HB_TAG_NONE }, /* Yilan Creole != Y-Cree */ + {HB_TAG('y','d','d',' '), HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */ +/*{HB_TAG('y','g','p',' '), HB_TAG('Y','G','P',' ')},*/ /* Gepo */ + {HB_TAG('y','i','h',' '), HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */ + {HB_TAG('y','i','m',' '), HB_TAG_NONE }, /* Yimchungru Naga != Yi Modern */ +/*{HB_TAG('y','n','a',' '), HB_TAG('Y','N','A',' ')},*/ /* Aluo */ + {HB_TAG('y','o','s',' '), HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */ + {HB_TAG('y','u','a',' '), HB_TAG('M','Y','N',' ')}, /* Yucateco -> Mayan */ + {HB_TAG('y','u','e',' '), HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */ +/*{HB_TAG('y','w','q',' '), HB_TAG('Y','W','Q',' ')},*/ /* Wuding-Luquan Yi */ + {HB_TAG('z','c','h',' '), HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */ + {HB_TAG('z','d','j',' '), HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */ +/*{HB_TAG('z','e','a',' '), HB_TAG('Z','E','A',' ')},*/ /* Zeeuws -> Zealandic */ + {HB_TAG('z','e','h',' '), HB_TAG('Z','H','A',' ')}, /* Eastern Hongshuihe Zhuang -> Zhuang */ + {HB_TAG('z','e','n',' '), HB_TAG('B','B','R',' ')}, /* Zenaga -> Berber */ + {HB_TAG('z','g','b',' '), HB_TAG('Z','H','A',' ')}, /* Guibei Zhuang -> Zhuang */ + {HB_TAG('z','g','h',' '), HB_TAG('Z','G','H',' ')}, /* Standard Moroccan Tamazight */ + {HB_TAG('z','g','h',' '), HB_TAG('B','B','R',' ')}, /* Standard Moroccan Tamazight -> Berber */ + {HB_TAG('z','g','m',' '), HB_TAG('Z','H','A',' ')}, /* Minz Zhuang -> Zhuang */ + {HB_TAG('z','g','n',' '), HB_TAG('Z','H','A',' ')}, /* Guibian Zhuang -> Zhuang */ + {HB_TAG('z','h','d',' '), HB_TAG('Z','H','A',' ')}, /* Dai Zhuang -> Zhuang */ + {HB_TAG('z','h','n',' '), HB_TAG('Z','H','A',' ')}, /* Nong Zhuang -> Zhuang */ + {HB_TAG('z','k','b',' '), HB_TAG('K','H','A',' ')}, /* Koibal (retired code) -> Khakass */ + {HB_TAG('z','l','j',' '), HB_TAG('Z','H','A',' ')}, /* Liujiang Zhuang -> Zhuang */ + {HB_TAG('z','l','m',' '), HB_TAG('M','L','Y',' ')}, /* Malay */ + {HB_TAG('z','l','n',' '), HB_TAG('Z','H','A',' ')}, /* Lianshan Zhuang -> Zhuang */ + {HB_TAG('z','l','q',' '), HB_TAG('Z','H','A',' ')}, /* Liuqian Zhuang -> Zhuang */ + {HB_TAG('z','m','i',' '), HB_TAG('M','L','Y',' ')}, /* Negeri Sembilan Malay -> Malay */ + {HB_TAG('z','m','z',' '), HB_TAG('B','A','D','0')}, /* Mbandja -> Banda */ + {HB_TAG('z','n','d',' '), HB_TAG_NONE }, /* Zande [collection] != Zande */ + {HB_TAG('z','n','e',' '), HB_TAG('Z','N','D',' ')}, /* Zande */ + {HB_TAG('z','o','m',' '), HB_TAG('Q','I','N',' ')}, /* Zou -> Chin */ + {HB_TAG('z','q','e',' '), HB_TAG('Z','H','A',' ')}, /* Qiubei Zhuang -> Zhuang */ + {HB_TAG('z','s','m',' '), HB_TAG('M','L','Y',' ')}, /* Standard Malay -> Malay */ + {HB_TAG('z','u','m',' '), HB_TAG('L','R','C',' ')}, /* Kumzari -> Luri */ + {HB_TAG('z','y','b',' '), HB_TAG('Z','H','A',' ')}, /* Yongbei Zhuang -> Zhuang */ + {HB_TAG('z','y','g',' '), HB_TAG('Z','H','A',' ')}, /* Yang Zhuang -> Zhuang */ + {HB_TAG('z','y','j',' '), HB_TAG('Z','H','A',' ')}, /* Youjiang Zhuang -> Zhuang */ + {HB_TAG('z','y','n',' '), HB_TAG('Z','H','A',' ')}, /* Yongnan Zhuang -> Zhuang */ + {HB_TAG('z','y','p',' '), HB_TAG('Q','I','N',' ')}, /* Zyphe Chin -> Chin */ +/*{HB_TAG('z','z','a',' '), HB_TAG('Z','Z','A',' ')},*/ /* Zazaki [macrolanguage] */ + {HB_TAG('z','z','j',' '), HB_TAG('Z','H','A',' ')}, /* Zuojiang Zhuang -> Zhuang */ +}; +#endif + +/** + * hb_ot_tags_from_complex_language: + * @lang_str: a BCP 47 language tag to convert. + * @limit: a pointer to the end of the substring of @lang_str to consider for + * conversion. + * @count: maximum number of language tags to retrieve (IN) and actual number of + * language tags retrieved (OUT). If no tags are retrieved, it is not modified. + * @tags: array of size at least @language_count to store the language tag + * results + * + * Converts a multi-subtag BCP 47 language tag to language tags. + * + * Return value: Whether any language systems were retrieved. + **/ +static inline bool +hb_ot_tags_from_complex_language (const char *lang_str, + const char *limit, + unsigned int *count /* IN/OUT */, + hb_tag_t *tags /* OUT */) +{ + if (limit - lang_str >= 7) + { + const char *p = strchr (lang_str, '-'); + if (!p || p >= limit || limit - p < 5) goto out; + if (subtag_matches (p, limit, "-fonnapa", 8)) + { + /* Undetermined; North American Phonetic Alphabet */ + tags[0] = HB_TAG('A','P','P','H'); /* Phonetic transcription—Americanist conventions */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-polyton", 8)) + { + /* Modern Greek (1453-); Polytonic Greek */ + tags[0] = HB_TAG('P','G','R',' '); /* Polytonic Greek */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-arevmda", 8)) + { + /* Armenian; Western Armenian (retired code) */ + tags[0] = HB_TAG('H','Y','E',' '); /* Armenian */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-provenc", 8)) + { + /* Occitan (post 1500); Provençal */ + tags[0] = HB_TAG('P','R','O',' '); /* Provençal / Old Provençal */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-fonipa", 7)) + { + /* Undetermined; International Phonetic Alphabet */ + tags[0] = HB_TAG('I','P','P','H'); /* Phonetic transcription—IPA conventions */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-geok", 5)) + { + /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */ + tags[0] = HB_TAG('K','G','E',' '); /* Khutsuri Georgian */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-syre", 5)) + { + /* Undetermined; Syriac (Estrangelo variant) */ + tags[0] = HB_TAG('S','Y','R','E'); /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-syrj", 5)) + { + /* Undetermined; Syriac (Western variant) */ + tags[0] = HB_TAG('S','Y','R','J'); /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */ + *count = 1; + return true; + } + if (subtag_matches (p, limit, "-syrn", 5)) + { + /* Undetermined; Syriac (Eastern variant) */ + tags[0] = HB_TAG('S','Y','R','N'); /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */ + *count = 1; + return true; + } + } +out: + switch (lang_str[0]) + { + case 'a': + if (0 == strcmp (&lang_str[1], "rt-lojban")) + { + /* Lojban (retired code) */ + tags[0] = HB_TAG('J','B','O',' '); /* Lojban */ + *count = 1; + return true; + } + break; + case 'c': + if (lang_matches (&lang_str[1], limit, "do-hant-hk", 10)) + { + /* Min Dong Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "do-hant-mo", 10)) + { + /* Min Dong Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hant-hk", 10)) + { + /* Jinyu Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hant-mo", 10)) + { + /* Jinyu Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hant-hk", 10)) + { + /* Mandarin Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hant-mo", 10)) + { + /* Mandarin Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10)) + { + /* Northern Ping Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10)) + { + /* Northern Ping Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hant-hk", 10)) + { + /* Pu-Xian Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hant-mo", 10)) + { + /* Pu-Xian Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hant-hk", 10)) + { + /* Southern Ping Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hant-mo", 10)) + { + /* Southern Ping Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hant-hk", 10)) + { + /* Huizhou Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hant-mo", 10)) + { + /* Huizhou Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hant-hk", 10)) + { + /* Min Zhong Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hant-mo", 10)) + { + /* Min Zhong Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "do-hans", 7)) + { + /* Min Dong Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "do-hant", 7)) + { + /* Min Dong Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hans", 7)) + { + /* Jinyu Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "jy-hant", 7)) + { + /* Jinyu Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hans", 7)) + { + /* Mandarin Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "mn-hant", 7)) + { + /* Mandarin Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hans", 7)) + { + /* Northern Ping Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant", 7)) + { + /* Northern Ping Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hans", 7)) + { + /* Pu-Xian Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "px-hant", 7)) + { + /* Pu-Xian Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hans", 7)) + { + /* Southern Ping Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sp-hant", 7)) + { + /* Southern Ping Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hans", 7)) + { + /* Huizhou Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zh-hant", 7)) + { + /* Huizhou Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hans", 7)) + { + /* Min Zhong Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "zo-hant", 7)) + { + /* Min Zhong Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "do-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Dong Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "do-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Dong Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "do-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Dong Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "jy-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Jinyu Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "jy-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Jinyu Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "jy-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Jinyu Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "mn-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Mandarin Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "mn-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Mandarin Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "mn-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Mandarin Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Northern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Northern Ping Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Northern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "px-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Pu-Xian Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "px-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Pu-Xian Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "px-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Pu-Xian Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Southern Ping Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Southern Ping Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "sp-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Southern Ping Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zh-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Huizhou Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zh-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Huizhou Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "zh-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Huizhou Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zo-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Zhong Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "zo-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Zhong Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "zo-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Zhong Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'g': + if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10)) + { + /* Gan Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10)) + { + /* Gan Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hans", 7)) + { + /* Gan Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant", 7)) + { + /* Gan Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "a-latg", 6)) + { + /* Irish; Latin (Gaelic variant) */ + tags[0] = HB_TAG('I','R','T',' '); /* Irish Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Gan Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Gan Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Gan Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'h': + if (lang_matches (&lang_str[1], limit, "ak-hant-hk", 10)) + { + /* Hakka Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "ak-hant-mo", 10)) + { + /* Hakka Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10)) + { + /* Xiang Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hant-mo", 10)) + { + /* Xiang Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "ak-hans", 7)) + { + /* Hakka Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "ak-hant", 7)) + { + /* Hakka Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hans", 7)) + { + /* Xiang Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "sn-hant", 7)) + { + /* Xiang Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "ak-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Hakka Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "ak-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Hakka Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "ak-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Hakka Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sn-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Xiang Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "sn-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Xiang Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "sn-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Xiang Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'i': + if (0 == strcmp (&lang_str[1], "-navajo")) + { + /* Navajo (retired code) */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('N','A','V',' '), /* Navajo */ + HB_TAG('A','T','H',' '), /* Athapaskan */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strcmp (&lang_str[1], "-hak")) + { + /* Hakka (retired code) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "-lux")) + { + /* Luxembourgish (retired code) */ + tags[0] = HB_TAG('L','T','Z',' '); /* Luxembourgish */ + *count = 1; + return true; + } + break; + case 'l': + if (lang_matches (&lang_str[1], limit, "zh-hans", 7)) + { + /* Literary Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + break; + case 'm': + if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10)) + { + /* Min Bei Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant-mo", 10)) + { + /* Min Bei Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hans", 7)) + { + /* Min Bei Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "np-hant", 7)) + { + /* Min Bei Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Bei Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Bei Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "np-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Bei Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "nw-", 3) + && subtag_matches (lang_str, limit, "-th", 3)) + { + /* Mon; Thailand */ + tags[0] = HB_TAG('M','O','N','T'); /* Thailand Mon */ + *count = 1; + return true; + } + break; + case 'n': + if (lang_matches (&lang_str[1], limit, "an-hant-hk", 10)) + { + /* Min Nan Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant-mo", 10)) + { + /* Min Nan Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hans", 7)) + { + /* Min Nan Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "an-hant", 7)) + { + /* Min Nan Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Min Nan Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Min Nan Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "an-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Min Nan Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "o-bok")) + { + /* Norwegian Bokmal (retired code) */ + tags[0] = HB_TAG('N','O','R',' '); /* Norwegian */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "o-nyn")) + { + /* Norwegian Nynorsk (retired code) */ + tags[0] = HB_TAG('N','Y','N',' '); /* Norwegian Nynorsk (Nynorsk, Norwegian) */ + *count = 1; + return true; + } + break; + case 'r': + if (0 == strncmp (&lang_str[1], "o-", 2) + && subtag_matches (lang_str, limit, "-md", 3)) + { + /* Romanian; Moldova */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('M','O','L',' '), /* Moldavian */ + HB_TAG('R','O','M',' '), /* Romanian */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + break; + case 'w': + if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10)) + { + /* Wu Chinese; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "uu-hant-mo", 10)) + { + /* Wu Chinese; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (lang_matches (&lang_str[1], limit, "uu-hans", 7)) + { + /* Wu Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "uu-hant", 7)) + { + /* Wu Chinese; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "uu-", 3) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Wu Chinese; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "uu-", 3) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Wu Chinese; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "uu-", 3) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Wu Chinese; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + case 'y': + if (lang_matches (&lang_str[1], limit, "ue-hans", 7)) + { + /* Yue Chinese; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + break; + case 'z': + if (lang_matches (&lang_str[1], limit, "h-hant-hk", 9)) + { + /* Chinese [macrolanguage]; Han (Traditional variant); Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "h-hant-mo", 9)) + { + /* Chinese [macrolanguage]; Han (Traditional variant); Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strcmp (&lang_str[1], "h-min-nan")) + { + /* Minnan, Hokkien, Amoy, Taiwanese, Southern Min, Southern Fujian, Hoklo, Southern Fukien, Ho-lo (retired code) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "h-hans", 6)) + { + /* Chinese [macrolanguage]; Han (Simplified variant) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (lang_matches (&lang_str[1], limit, "h-hant", 6)) + { + /* Chinese [macrolanguage]; Han (Traditional variant) */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + if (0 == strcmp (&lang_str[1], "h-min")) + { + /* Min, Fuzhou, Hokkien, Amoy, or Taiwanese (retired code) */ + tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "h-", 2) + && subtag_matches (lang_str, limit, "-hk", 3)) + { + /* Chinese [macrolanguage]; Hong Kong */ + tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */ + *count = 1; + return true; + } + if (0 == strncmp (&lang_str[1], "h-", 2) + && subtag_matches (lang_str, limit, "-mo", 3)) + { + /* Chinese [macrolanguage]; Macao */ + unsigned int i; + hb_tag_t possible_tags[] = { + HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */ + HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */ + }; + for (i = 0; i < 2 && i < *count; i++) + tags[i] = possible_tags[i]; + *count = i; + return true; + } + if (0 == strncmp (&lang_str[1], "h-", 2) + && subtag_matches (lang_str, limit, "-tw", 3)) + { + /* Chinese [macrolanguage]; Taiwan, Province of China */ + tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */ + *count = 1; + return true; + } + break; + } + return false; +} + +/** + * hb_ot_ambiguous_tag_to_language + * @tag: A language tag. + * + * Converts @tag to a BCP 47 language tag if it is ambiguous (it corresponds to + * many language tags) and the best tag is not the alphabetically first, or if + * the best tag consists of multiple subtags, or if the best tag does not appear + * in #ot_languages. + * + * Return value: The #hb_language_t corresponding to the BCP 47 language tag, + * or #HB_LANGUAGE_INVALID if @tag is not ambiguous. + **/ +static inline hb_language_t +hb_ot_ambiguous_tag_to_language (hb_tag_t tag) +{ + switch (tag) + { + case HB_TAG('A','L','T',' '): /* Altai */ + return hb_language_from_string ("alt", -1); /* Southern Altai */ + case HB_TAG('A','P','P','H'): /* Phonetic transcription—Americanist conventions */ + return hb_language_from_string ("und-fonnapa", -1); /* Undetermined; North American Phonetic Alphabet */ + case HB_TAG('A','R','A',' '): /* Arabic */ + return hb_language_from_string ("ar", -1); /* Arabic [macrolanguage] */ + case HB_TAG('A','R','K',' '): /* Rakhine */ + return hb_language_from_string ("rki", -1); /* Rakhine */ + case HB_TAG('A','T','H',' '): /* Athapaskan */ + return hb_language_from_string ("ath", -1); /* Athapascan [collection] */ + case HB_TAG('B','B','R',' '): /* Berber */ + return hb_language_from_string ("ber", -1); /* Berber [collection] */ + case HB_TAG('B','I','K',' '): /* Bikol */ + return hb_language_from_string ("bik", -1); /* Bikol [macrolanguage] */ + case HB_TAG('B','T','K',' '): /* Batak */ + return hb_language_from_string ("btk", -1); /* Batak [collection] */ + case HB_TAG('C','P','P',' '): /* Creoles */ + return hb_language_from_string ("crp", -1); /* Creoles and pidgins [collection] */ + case HB_TAG('C','R','R',' '): /* Carrier */ + return hb_language_from_string ("crx", -1); /* Carrier */ + case HB_TAG('D','G','R',' '): /* Dogri (macrolanguage) */ + return hb_language_from_string ("doi", -1); /* Dogri [macrolanguage] */ + case HB_TAG('D','N','K',' '): /* Dinka */ + return hb_language_from_string ("din", -1); /* Dinka [macrolanguage] */ + case HB_TAG('D','R','I',' '): /* Dari */ + return hb_language_from_string ("prs", -1); /* Dari */ + case HB_TAG('D','Z','N',' '): /* Dzongkha */ + return hb_language_from_string ("dz", -1); /* Dzongkha */ + case HB_TAG('E','T','I',' '): /* Estonian */ + return hb_language_from_string ("et", -1); /* Estonian [macrolanguage] */ + case HB_TAG('F','A','R',' '): /* Persian */ + return hb_language_from_string ("fa", -1); /* Persian [macrolanguage] */ + case HB_TAG('G','O','N',' '): /* Gondi */ + return hb_language_from_string ("gon", -1); /* Gondi [macrolanguage] */ + case HB_TAG('H','M','A',' '): /* High Mari */ + return hb_language_from_string ("mrj", -1); /* Western Mari */ + case HB_TAG('H','M','N',' '): /* Hmong */ + return hb_language_from_string ("hmn", -1); /* Hmong [macrolanguage] */ + case HB_TAG('H','N','D',' '): /* Hindko */ + return hb_language_from_string ("hnd", -1); /* Southern Hindko */ + case HB_TAG('H','Y','E',' '): /* Armenian */ + return hb_language_from_string ("hyw", -1); /* Western Armenian */ + case HB_TAG('I','B','A',' '): /* Iban */ + return hb_language_from_string ("iba", -1); /* Iban */ + case HB_TAG('I','J','O',' '): /* Ijo */ + return hb_language_from_string ("ijo", -1); /* Ijo [collection] */ + case HB_TAG('I','N','U',' '): /* Inuktitut */ + return hb_language_from_string ("iu", -1); /* Inuktitut [macrolanguage] */ + case HB_TAG('I','P','K',' '): /* Inupiat */ + return hb_language_from_string ("ik", -1); /* Inupiaq [macrolanguage] */ + case HB_TAG('I','P','P','H'): /* Phonetic transcription—IPA conventions */ + return hb_language_from_string ("und-fonipa", -1); /* Undetermined; International Phonetic Alphabet */ + case HB_TAG('I','R','T',' '): /* Irish Traditional */ + return hb_language_from_string ("ga-Latg", -1); /* Irish; Latin (Gaelic variant) */ + case HB_TAG('J','I','I',' '): /* Yiddish */ + return hb_language_from_string ("yi", -1); /* Yiddish [macrolanguage] */ + case HB_TAG('K','A','L',' '): /* Kalenjin */ + return hb_language_from_string ("kln", -1); /* Kalenjin [macrolanguage] */ + case HB_TAG('K','G','E',' '): /* Khutsuri Georgian */ + return hb_language_from_string ("und-Geok", -1); /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */ + case HB_TAG('K','N','R',' '): /* Kanuri */ + return hb_language_from_string ("kr", -1); /* Kanuri [macrolanguage] */ + case HB_TAG('K','O','H',' '): /* Korean Old Hangul */ + return hb_language_from_string ("okm", -1); /* Middle Korean (10th-16th cent.) */ + case HB_TAG('K','O','K',' '): /* Konkani */ + return hb_language_from_string ("kok", -1); /* Konkani [macrolanguage] */ + case HB_TAG('K','O','M',' '): /* Komi */ + return hb_language_from_string ("kv", -1); /* Komi [macrolanguage] */ + case HB_TAG('K','P','L',' '): /* Kpelle */ + return hb_language_from_string ("kpe", -1); /* Kpelle [macrolanguage] */ + case HB_TAG('K','R','N',' '): /* Karen */ + return hb_language_from_string ("kar", -1); /* Karen [collection] */ + case HB_TAG('K','U','I',' '): /* Kui */ + return hb_language_from_string ("uki", -1); /* Kui (India) */ + case HB_TAG('K','U','R',' '): /* Kurdish */ + return hb_language_from_string ("ku", -1); /* Kurdish [macrolanguage] */ + case HB_TAG('L','M','A',' '): /* Low Mari */ + return hb_language_from_string ("mhr", -1); /* Eastern Mari */ + case HB_TAG('L','U','H',' '): /* Luyia */ + return hb_language_from_string ("luy", -1); /* Luyia [macrolanguage] */ + case HB_TAG('L','V','I',' '): /* Latvian */ + return hb_language_from_string ("lv", -1); /* Latvian [macrolanguage] */ + case HB_TAG('M','A','W',' '): /* Marwari */ + return hb_language_from_string ("mwr", -1); /* Marwari [macrolanguage] */ + case HB_TAG('M','L','G',' '): /* Malagasy */ + return hb_language_from_string ("mg", -1); /* Malagasy [macrolanguage] */ + case HB_TAG('M','L','Y',' '): /* Malay */ + return hb_language_from_string ("ms", -1); /* Malay [macrolanguage] */ + case HB_TAG('M','N','G',' '): /* Mongolian */ + return hb_language_from_string ("mn", -1); /* Mongolian [macrolanguage] */ + case HB_TAG('M','N','K',' '): /* Maninka */ + return hb_language_from_string ("man", -1); /* Mandingo [macrolanguage] */ + case HB_TAG('M','O','L',' '): /* Moldavian */ + return hb_language_from_string ("ro-MD", -1); /* Romanian; Moldova */ + case HB_TAG('M','O','N','T'): /* Thailand Mon */ + return hb_language_from_string ("mnw-TH", -1); /* Mon; Thailand */ + case HB_TAG('M','Y','N',' '): /* Mayan */ + return hb_language_from_string ("myn", -1); /* Mayan [collection] */ + case HB_TAG('N','A','H',' '): /* Nahuatl */ + return hb_language_from_string ("nah", -1); /* Nahuatl [collection] */ + case HB_TAG('N','E','P',' '): /* Nepali */ + return hb_language_from_string ("ne", -1); /* Nepali [macrolanguage] */ + case HB_TAG('N','I','S',' '): /* Nisi */ + return hb_language_from_string ("njz", -1); /* Nyishi */ + case HB_TAG('N','O','R',' '): /* Norwegian */ + return hb_language_from_string ("no", -1); /* Norwegian [macrolanguage] */ + case HB_TAG('O','J','B',' '): /* Ojibway */ + return hb_language_from_string ("oj", -1); /* Ojibwa [macrolanguage] */ + case HB_TAG('O','R','O',' '): /* Oromo */ + return hb_language_from_string ("om", -1); /* Oromo [macrolanguage] */ + case HB_TAG('P','A','S',' '): /* Pashto */ + return hb_language_from_string ("ps", -1); /* Pashto [macrolanguage] */ + case HB_TAG('P','G','R',' '): /* Polytonic Greek */ + return hb_language_from_string ("el-polyton", -1); /* Modern Greek (1453-); Polytonic Greek */ + case HB_TAG('P','R','O',' '): /* Provençal / Old Provençal */ + return hb_language_from_string ("pro", -1); /* Old Provençal (to 1500) */ + case HB_TAG('Q','U','H',' '): /* Quechua (Bolivia) */ + return hb_language_from_string ("quh", -1); /* South Bolivian Quechua */ + case HB_TAG('Q','U','Z',' '): /* Quechua */ + return hb_language_from_string ("qu", -1); /* Quechua [macrolanguage] */ + case HB_TAG('Q','V','I',' '): /* Quechua (Ecuador) */ + return hb_language_from_string ("qvi", -1); /* Imbabura Highland Quichua */ + case HB_TAG('Q','W','H',' '): /* Quechua (Peru) */ + return hb_language_from_string ("qwh", -1); /* Huaylas Ancash Quechua */ + case HB_TAG('R','A','J',' '): /* Rajasthani */ + return hb_language_from_string ("raj", -1); /* Rajasthani [macrolanguage] */ + case HB_TAG('R','O','M',' '): /* Romanian */ + return hb_language_from_string ("ro", -1); /* Romanian */ + case HB_TAG('R','O','Y',' '): /* Romany */ + return hb_language_from_string ("rom", -1); /* Romany [macrolanguage] */ + case HB_TAG('S','Q','I',' '): /* Albanian */ + return hb_language_from_string ("sq", -1); /* Albanian [macrolanguage] */ + case HB_TAG('S','R','B',' '): /* Serbian */ + return hb_language_from_string ("sr", -1); /* Serbian */ + case HB_TAG('S','X','T',' '): /* Sutu */ + return hb_language_from_string ("xnj", -1); /* Ngoni (Tanzania) */ + case HB_TAG('S','Y','R',' '): /* Syriac */ + return hb_language_from_string ("syr", -1); /* Syriac [macrolanguage] */ + case HB_TAG('S','Y','R','E'): /* Syriac, Estrangela script-variant (equivalent to ISO 15924 'Syre') */ + return hb_language_from_string ("und-Syre", -1); /* Undetermined; Syriac (Estrangelo variant) */ + case HB_TAG('S','Y','R','J'): /* Syriac, Western script-variant (equivalent to ISO 15924 'Syrj') */ + return hb_language_from_string ("und-Syrj", -1); /* Undetermined; Syriac (Western variant) */ + case HB_TAG('S','Y','R','N'): /* Syriac, Eastern script-variant (equivalent to ISO 15924 'Syrn') */ + return hb_language_from_string ("und-Syrn", -1); /* Undetermined; Syriac (Eastern variant) */ + case HB_TAG('T','M','H',' '): /* Tamashek */ + return hb_language_from_string ("tmh", -1); /* Tamashek [macrolanguage] */ + case HB_TAG('T','O','D',' '): /* Todo */ + return hb_language_from_string ("xwo", -1); /* Written Oirat */ + case HB_TAG('Z','H','H',' '): /* Chinese, Traditional, Hong Kong SAR */ + return hb_language_from_string ("zh-HK", -1); /* Chinese [macrolanguage]; Hong Kong */ + case HB_TAG('Z','H','S',' '): /* Chinese, Simplified */ + return hb_language_from_string ("zh-Hans", -1); /* Chinese [macrolanguage]; Han (Simplified variant) */ + case HB_TAG('Z','H','T',' '): /* Chinese, Traditional */ + return hb_language_from_string ("zh-Hant", -1); /* Chinese [macrolanguage]; Han (Traditional variant) */ + case HB_TAG('Z','H','T','M'): /* Chinese, Traditional, Macao SAR */ + return hb_language_from_string ("zh-MO", -1); /* Chinese [macrolanguage]; Macao */ + case HB_TAG('Z','Z','A',' '): /* Zazaki */ + return hb_language_from_string ("zza", -1); /* Zazaki [macrolanguage] */ + default: + return HB_LANGUAGE_INVALID; + } +} + +#endif /* HB_OT_TAG_TABLE_HH */ + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ot-tag.cc b/gfx/harfbuzz/src/hb-ot-tag.cc new file mode 100644 index 0000000000..53b6b38f66 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-tag.cc @@ -0,0 +1,658 @@ +/* + * 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, Roozbeh Pournader + */ + +#include "hb.hh" + +#ifndef HB_NO_OT_TAG + + +/* hb_script_t */ + +static hb_tag_t +hb_ot_old_tag_from_script (hb_script_t script) +{ + /* This seems to be accurate as of end of 2012. */ + + switch ((hb_tag_t) script) + { + case HB_SCRIPT_INVALID: return HB_OT_TAG_DEFAULT_SCRIPT; + case HB_SCRIPT_MATH: return HB_OT_TAG_MATH_SCRIPT; + + /* KATAKANA and HIRAGANA both map to 'kana' */ + case HB_SCRIPT_HIRAGANA: return HB_TAG('k','a','n','a'); + + /* Spaces at the end are preserved, unlike ISO 15924 */ + case HB_SCRIPT_LAO: return HB_TAG('l','a','o',' '); + case HB_SCRIPT_YI: return HB_TAG('y','i',' ',' '); + /* Unicode-5.0 additions */ + case HB_SCRIPT_NKO: return HB_TAG('n','k','o',' '); + /* Unicode-5.1 additions */ + case HB_SCRIPT_VAI: return HB_TAG('v','a','i',' '); + } + + /* Else, just change first char to lowercase and return */ + return ((hb_tag_t) script) | 0x20000000u; +} + +static hb_script_t +hb_ot_old_tag_to_script (hb_tag_t tag) +{ + if (unlikely (tag == HB_OT_TAG_DEFAULT_SCRIPT)) + return HB_SCRIPT_INVALID; + if (unlikely (tag == HB_OT_TAG_MATH_SCRIPT)) + return HB_SCRIPT_MATH; + + /* This side of the conversion is fully algorithmic. */ + + /* Any spaces at the end of the tag are replaced by repeating the last + * letter. Eg 'nko ' -> 'Nkoo' */ + if (unlikely ((tag & 0x0000FF00u) == 0x00002000u)) + tag |= (tag >> 8) & 0x0000FF00u; /* Copy second letter to third */ + if (unlikely ((tag & 0x000000FFu) == 0x00000020u)) + tag |= (tag >> 8) & 0x000000FFu; /* Copy third letter to fourth */ + + /* Change first char to uppercase and return */ + return (hb_script_t) (tag & ~0x20000000u); +} + +static hb_tag_t +hb_ot_new_tag_from_script (hb_script_t script) +{ + switch ((hb_tag_t) script) { + case HB_SCRIPT_BENGALI: return HB_TAG('b','n','g','2'); + case HB_SCRIPT_DEVANAGARI: return HB_TAG('d','e','v','2'); + case HB_SCRIPT_GUJARATI: return HB_TAG('g','j','r','2'); + case HB_SCRIPT_GURMUKHI: return HB_TAG('g','u','r','2'); + case HB_SCRIPT_KANNADA: return HB_TAG('k','n','d','2'); + case HB_SCRIPT_MALAYALAM: return HB_TAG('m','l','m','2'); + case HB_SCRIPT_ORIYA: return HB_TAG('o','r','y','2'); + case HB_SCRIPT_TAMIL: return HB_TAG('t','m','l','2'); + case HB_SCRIPT_TELUGU: return HB_TAG('t','e','l','2'); + case HB_SCRIPT_MYANMAR: return HB_TAG('m','y','m','2'); + } + + return HB_OT_TAG_DEFAULT_SCRIPT; +} + +static hb_script_t +hb_ot_new_tag_to_script (hb_tag_t tag) +{ + switch (tag) { + case HB_TAG('b','n','g','2'): return HB_SCRIPT_BENGALI; + case HB_TAG('d','e','v','2'): return HB_SCRIPT_DEVANAGARI; + case HB_TAG('g','j','r','2'): return HB_SCRIPT_GUJARATI; + case HB_TAG('g','u','r','2'): return HB_SCRIPT_GURMUKHI; + case HB_TAG('k','n','d','2'): return HB_SCRIPT_KANNADA; + case HB_TAG('m','l','m','2'): return HB_SCRIPT_MALAYALAM; + case HB_TAG('o','r','y','2'): return HB_SCRIPT_ORIYA; + case HB_TAG('t','m','l','2'): return HB_SCRIPT_TAMIL; + case HB_TAG('t','e','l','2'): return HB_SCRIPT_TELUGU; + case HB_TAG('m','y','m','2'): return HB_SCRIPT_MYANMAR; + } + + return HB_SCRIPT_UNKNOWN; +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_tags_from_script: + * @script: an #hb_script_t to convert. + * @script_tag_1: (out): output #hb_tag_t. + * @script_tag_2: (out): output #hb_tag_t. + * + * Converts an #hb_script_t to script tags. + * + * Since: 0.6.0 + * Deprecated: 2.0.0: use hb_ot_tags_from_script_and_language() instead + **/ +void +hb_ot_tags_from_script (hb_script_t script, + hb_tag_t *script_tag_1, + hb_tag_t *script_tag_2) +{ + unsigned int count = 2; + hb_tag_t tags[2]; + hb_ot_tags_from_script_and_language (script, HB_LANGUAGE_INVALID, &count, tags, nullptr, nullptr); + *script_tag_1 = count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_SCRIPT; + *script_tag_2 = count > 1 ? tags[1] : HB_OT_TAG_DEFAULT_SCRIPT; +} +#endif + +/* + * Complete list at: + * https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags + * + * Most of the script tags are the same as the ISO 15924 tag but lowercased. + * So we just do that, and handle the exceptional cases in a switch. + */ + +static void +hb_ot_all_tags_from_script (hb_script_t script, + unsigned int *count /* IN/OUT */, + hb_tag_t *tags /* OUT */) +{ + unsigned int i = 0; + + hb_tag_t new_tag = hb_ot_new_tag_from_script (script); + if (unlikely (new_tag != HB_OT_TAG_DEFAULT_SCRIPT)) + { + /* HB_SCRIPT_MYANMAR maps to 'mym2', but there is no 'mym3'. */ + if (new_tag != HB_TAG('m','y','m','2')) + tags[i++] = new_tag | '3'; + if (*count > i) + tags[i++] = new_tag; + } + + if (*count > i) + { + hb_tag_t old_tag = hb_ot_old_tag_from_script (script); + if (old_tag != HB_OT_TAG_DEFAULT_SCRIPT) + tags[i++] = old_tag; + } + + *count = i; +} + +/** + * hb_ot_tag_to_script: + * @tag: a script tag + * + * Converts a script tag to an #hb_script_t. + * + * Return value: The #hb_script_t corresponding to @tag. + * + **/ +hb_script_t +hb_ot_tag_to_script (hb_tag_t tag) +{ + unsigned char digit = tag & 0x000000FFu; + if (unlikely (digit == '2' || digit == '3')) + return hb_ot_new_tag_to_script (tag & 0xFFFFFF32); + + return hb_ot_old_tag_to_script (tag); +} + + +/* hb_language_t */ + +static inline bool +subtag_matches (const char *lang_str, + const char *limit, + const char *subtag, + unsigned subtag_len) +{ + if (likely ((unsigned) (limit - lang_str) < subtag_len)) + return false; + + do { + const char *s = strstr (lang_str, subtag); + if (!s || s >= limit) + return false; + if (!ISALNUM (s[subtag_len])) + return true; + lang_str = s + subtag_len; + } while (true); +} + +static bool +lang_matches (const char *lang_str, + const char *limit, + const char *spec, + unsigned spec_len) +{ + /* Same as hb_language_matches(); duplicated. */ + + if (likely ((unsigned) (limit - lang_str) < spec_len)) + return false; + + return strncmp (lang_str, spec, spec_len) == 0 && + (lang_str[spec_len] == '\0' || lang_str[spec_len] == '-'); +} + +struct LangTag +{ + hb_tag_t language; + hb_tag_t tag; + + int cmp (hb_tag_t a) const + { + return a < this->language ? -1 : a > this->language ? +1 : 0; + } + int cmp (const LangTag *that) const + { return cmp (that->language); } +}; + +#include "hb-ot-tag-table.hh" + +/* The corresponding languages IDs for the following IDs are unclear, + * overlap, or are architecturally weird. Needs more research. */ + +/*{"??", {HB_TAG('B','C','R',' ')}},*/ /* Bible Cree */ +/*{"zh?", {HB_TAG('C','H','N',' ')}},*/ /* Chinese (seen in Microsoft fonts) */ +/*{"ar-Syrc?", {HB_TAG('G','A','R',' ')}},*/ /* Garshuni */ +/*{"??", {HB_TAG('N','G','R',' ')}},*/ /* Nagari */ +/*{"??", {HB_TAG('Y','I','C',' ')}},*/ /* Yi Classic */ +/*{"zh?", {HB_TAG('Z','H','P',' ')}},*/ /* Chinese Phonetic */ + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_tag_from_language: + * @language: an #hb_language_t to convert. + * + * Converts an #hb_language_t to an #hb_tag_t. + * + * Since: 0.6.0 + * Deprecated: 2.0.0: use hb_ot_tags_from_script_and_language() instead + **/ +hb_tag_t +hb_ot_tag_from_language (hb_language_t language) +{ + unsigned int count = 1; + hb_tag_t tags[1]; + hb_ot_tags_from_script_and_language (HB_SCRIPT_UNKNOWN, language, nullptr, nullptr, &count, tags); + return count > 0 ? tags[0] : HB_OT_TAG_DEFAULT_LANGUAGE; +} +#endif + +static void +hb_ot_tags_from_language (const char *lang_str, + const char *limit, + unsigned int *count, + hb_tag_t *tags) +{ + +#ifndef HB_NO_LANGUAGE_LONG + /* Check for matches of multiple subtags. */ + if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags)) + return; +#endif + + /* Find a language matching in the first component. */ +#ifndef HB_NO_LANGUAGE_LONG + const char *s; s = strchr (lang_str, '-'); +#endif + { +#ifndef HB_NO_LANGUAGE_LONG + if (s && limit - lang_str >= 6) + { + const char *extlang_end = strchr (s + 1, '-'); + /* If there is an extended language tag, use it. */ + if (3 == (extlang_end ? extlang_end - s - 1 : strlen (s + 1)) && + ISALPHA (s[1])) + lang_str = s + 1; + } +#endif + const LangTag *ot_languages = nullptr; + unsigned ot_languages_len = 0; + const char *dash = strchr (lang_str, '-'); + unsigned first_len = dash ? dash - lang_str : limit - lang_str; + if (first_len == 2) + { + ot_languages = ot_languages2; + ot_languages_len = ARRAY_LENGTH (ot_languages2); + } +#ifndef HB_NO_LANGUAGE_LONG + else if (first_len == 3) + { + ot_languages = ot_languages3; + ot_languages_len = ARRAY_LENGTH (ot_languages3); + } +#endif + + hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len); + + static hb_atomic_int_t last_tag_idx; /* Poor man's cache. */ + unsigned tag_idx = last_tag_idx; + + if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) || + hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_tag, &tag_idx)) + { + last_tag_idx = tag_idx; + unsigned int i; + while (tag_idx != 0 && + ot_languages[tag_idx].language == ot_languages[tag_idx - 1].language) + tag_idx--; + for (i = 0; + i < *count && + tag_idx + i < ot_languages_len && + ot_languages[tag_idx + i].tag != HB_TAG_NONE && + ot_languages[tag_idx + i].language == ot_languages[tag_idx].language; + i++) + tags[i] = ot_languages[tag_idx + i].tag; + *count = i; + return; + } + } + +#ifndef HB_NO_LANGUAGE_LONG + if (!s) + s = lang_str + strlen (lang_str); + if (s - lang_str == 3) { + /* Assume it's ISO-639-3 and upper-case and use it. */ + tags[0] = hb_tag_from_string (lang_str, s - lang_str) & ~0x20202000u; + *count = 1; + return; + } +#endif + + *count = 0; +} + +static bool +parse_private_use_subtag (const char *private_use_subtag, + unsigned int *count, + hb_tag_t *tags, + const char *prefix, + unsigned char (*normalize) (unsigned char)) +{ +#ifdef HB_NO_LANGUAGE_PRIVATE_SUBTAG + return false; +#endif + + if (!(private_use_subtag && count && tags && *count)) return false; + + const char *s = strstr (private_use_subtag, prefix); + if (!s) return false; + + char tag[4]; + int i; + s += strlen (prefix); + if (s[0] == '-') { + s += 1; + char c; + for (i = 0; i < 8 && ISHEX (s[i]); i++) + { + c = FROMHEX (s[i]); + if (i % 2 == 0) + tag[i / 2] = c << 4; + else + tag[i / 2] += c; + } + if (i != 8) return false; + } else { + for (i = 0; i < 4 && ISALNUM (s[i]); i++) + tag[i] = normalize (s[i]); + if (!i) return false; + + for (; i < 4; i++) + tag[i] = ' '; + } + tags[0] = HB_TAG (tag[0], tag[1], tag[2], tag[3]); + if ((tags[0] & 0xDFDFDFDF) == HB_OT_TAG_DEFAULT_SCRIPT) + tags[0] ^= ~0xDFDFDFDF; + *count = 1; + return true; +} + +/** + * hb_ot_tags_from_script_and_language: + * @script: an #hb_script_t to convert. + * @language: (nullable): an #hb_language_t to convert. + * @script_count: (inout) (optional): maximum number of script tags to retrieve (IN) + * and actual number of script tags retrieved (OUT) + * @script_tags: (out) (optional): array of size at least @script_count to store the + * script tag results + * @language_count: (inout) (optional): maximum number of language tags to retrieve + * (IN) and actual number of language tags retrieved (OUT) + * @language_tags: (out) (optional): array of size at least @language_count to store + * the language tag results + * + * Converts an #hb_script_t and an #hb_language_t to script and language tags. + * + * Since: 2.0.0 + **/ +void +hb_ot_tags_from_script_and_language (hb_script_t script, + hb_language_t language, + unsigned int *script_count /* IN/OUT */, + hb_tag_t *script_tags /* OUT */, + unsigned int *language_count /* IN/OUT */, + hb_tag_t *language_tags /* OUT */) +{ + bool needs_script = true; + + if (language == HB_LANGUAGE_INVALID) + { + if (language_count && language_tags && *language_count) + *language_count = 0; + } + else + { + const char *lang_str, *s, *limit, *private_use_subtag; + bool needs_language; + + lang_str = hb_language_to_string (language); + limit = nullptr; + private_use_subtag = nullptr; + if (lang_str[0] == 'x' && lang_str[1] == '-') + { + private_use_subtag = lang_str; + } else { + for (s = lang_str + 1; *s; s++) + { + if (s[-1] == '-' && s[1] == '-') + { + if (s[0] == 'x') + { + private_use_subtag = s; + if (!limit) + limit = s - 1; + break; + } else if (!limit) + { + limit = s - 1; + } + } + } + if (!limit) + limit = s; + } + + needs_script = !parse_private_use_subtag (private_use_subtag, script_count, script_tags, "-hbsc", TOLOWER); + needs_language = !parse_private_use_subtag (private_use_subtag, language_count, language_tags, "-hbot", TOUPPER); + + if (needs_language && language_count && language_tags && *language_count) + hb_ot_tags_from_language (lang_str, limit, language_count, language_tags); + } + + if (needs_script && script_count && script_tags && *script_count) + hb_ot_all_tags_from_script (script, script_count, script_tags); +} + +/** + * hb_ot_tag_to_language: + * @tag: an language tag + * + * Converts a language tag to an #hb_language_t. + * + * Return value: (transfer none) (nullable): + * The #hb_language_t corresponding to @tag. + * + * Since: 0.9.2 + **/ +hb_language_t +hb_ot_tag_to_language (hb_tag_t tag) +{ + unsigned int i; + + if (tag == HB_OT_TAG_DEFAULT_LANGUAGE) + return nullptr; + +#ifndef HB_NO_LANGUAGE_LONG + { + hb_language_t disambiguated_tag = hb_ot_ambiguous_tag_to_language (tag); + if (disambiguated_tag != HB_LANGUAGE_INVALID) + return disambiguated_tag; + } +#endif + + char buf[4]; + for (i = 0; i < ARRAY_LENGTH (ot_languages2); i++) + if (ot_languages2[i].tag == tag) + { + hb_tag_to_string (ot_languages2[i].language, buf); + return hb_language_from_string (buf, 2); + } +#ifndef HB_NO_LANGUAGE_LONG + for (i = 0; i < ARRAY_LENGTH (ot_languages3); i++) + if (ot_languages3[i].tag == tag) + { + hb_tag_to_string (ot_languages3[i].language, buf); + return hb_language_from_string (buf, 3); + } +#endif + + /* Return a custom language in the form of "x-hbot-AABBCCDD". + * If it's three letters long, also guess it's ISO 639-3 and lower-case and + * prepend it (if it's not a registered tag, the private use subtags will + * ensure that calling hb_ot_tag_from_language on the result will still return + * the same tag as the original tag). + */ + { + char buf[20]; + char *str = buf; + if (ISALPHA (tag >> 24) + && ISALPHA ((tag >> 16) & 0xFF) + && ISALPHA ((tag >> 8) & 0xFF) + && (tag & 0xFF) == ' ') + { + buf[0] = TOLOWER (tag >> 24); + buf[1] = TOLOWER ((tag >> 16) & 0xFF); + buf[2] = TOLOWER ((tag >> 8) & 0xFF); + buf[3] = '-'; + str += 4; + } + snprintf (str, 16, "x-hbot-%08x", tag); + return hb_language_from_string (&*buf, -1); + } +} + +/** + * hb_ot_tags_to_script_and_language: + * @script_tag: a script tag + * @language_tag: a language tag + * @script: (out) (optional): the #hb_script_t corresponding to @script_tag. + * @language: (out) (optional): the #hb_language_t corresponding to @script_tag and + * @language_tag. + * + * Converts a script tag and a language tag to an #hb_script_t and an + * #hb_language_t. + * + * Since: 2.0.0 + **/ +void +hb_ot_tags_to_script_and_language (hb_tag_t script_tag, + hb_tag_t language_tag, + hb_script_t *script /* OUT */, + hb_language_t *language /* OUT */) +{ + hb_script_t script_out = hb_ot_tag_to_script (script_tag); + if (script) + *script = script_out; + if (language) + { + unsigned int script_count = 1; + hb_tag_t primary_script_tag[1]; + hb_ot_tags_from_script_and_language (script_out, + HB_LANGUAGE_INVALID, + &script_count, + primary_script_tag, + nullptr, nullptr); + *language = hb_ot_tag_to_language (language_tag); + if (script_count == 0 || primary_script_tag[0] != script_tag) + { + unsigned char *buf; + const char *lang_str = hb_language_to_string (*language); + size_t len = strlen (lang_str); + buf = (unsigned char *) hb_malloc (len + 16); + if (unlikely (!buf)) + { + *language = nullptr; + } + else + { + int shift; + hb_memcpy (buf, lang_str, len); + if (lang_str[0] != 'x' || lang_str[1] != '-') { + buf[len++] = '-'; + buf[len++] = 'x'; + } + buf[len++] = '-'; + buf[len++] = 'h'; + buf[len++] = 'b'; + buf[len++] = 's'; + buf[len++] = 'c'; + buf[len++] = '-'; + for (shift = 28; shift >= 0; shift -= 4) + buf[len++] = TOHEX (script_tag >> shift); + *language = hb_language_from_string ((char *) buf, len); + hb_free (buf); + } + } + } +} + +#ifdef MAIN +static inline void +test_langs_sorted () +{ + for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages2); i++) + { + int c = ot_languages2[i].cmp (&ot_languages2[i - 1]); + if (c > 0) + { + fprintf (stderr, "ot_languages2 not sorted at index %u: %08x %d %08x\n", + i, ot_languages2[i-1].language, c, ot_languages2[i].language); + abort(); + } + } +#ifndef HB_NO_LANGUAGE_LONG + for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3); i++) + { + int c = ot_languages3[i].cmp (&ot_languages3[i - 1]); + if (c > 0) + { + fprintf (stderr, "ot_languages3 not sorted at index %u: %08x %d %08x\n", + i, ot_languages3[i-1].language, c, ot_languages3[i].language); + abort(); + } + } +#endif +} + +int +main () +{ + test_langs_sorted (); + return 0; +} + +#endif + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-var-avar-table.hh b/gfx/harfbuzz/src/hb-ot-var-avar-table.hh new file mode 100644 index 0000000000..b2e5d87a3c --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var-avar-table.hh @@ -0,0 +1,411 @@ +/* + * 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_OT_VAR_AVAR_TABLE_HH +#define HB_OT_VAR_AVAR_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-var-common.hh" + + +/* + * avar -- Axis Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/avar + */ + +#define HB_OT_TAG_avar HB_TAG('a','v','a','r') + + +namespace OT { + + +/* "Spec": https://github.com/be-fonts/boring-expansion-spec/issues/14 */ +struct avarV2Tail +{ + friend struct avar; + + bool sanitize (hb_sanitize_context_t *c, + const void *base) const + { + TRACE_SANITIZE (this); + return_trace (varIdxMap.sanitize (c, base) && + varStore.sanitize (c, base)); + } + + protected: + Offset32To<DeltaSetIndexMap> varIdxMap; /* Offset from the beginning of 'avar' table. */ + Offset32To<VariationStore> varStore; /* Offset from the beginning of 'avar' table. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + + +struct AxisValueMap +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void set_mapping (float from_coord, float to_coord) + { + coords[0].set_float (from_coord); + coords[1].set_float (to_coord); + } + + bool is_outside_axis_range (const Triple& axis_range) const + { + float from_coord = coords[0].to_float (); + return !axis_range.contains (from_coord); + } + + bool must_include () const + { + float from_coord = coords[0].to_float (); + float to_coord = coords[1].to_float (); + return (from_coord == -1.f && to_coord == -1.f) || + (from_coord == 0.f && to_coord == 0.f) || + (from_coord == 1.f && to_coord == 1.f); + } + + void instantiate (const Triple& axis_range, + const Triple& unmapped_range, + const TripleDistances& triple_distances) + { + float from_coord = coords[0].to_float (); + float to_coord = coords[1].to_float (); + + from_coord = renormalizeValue (from_coord, unmapped_range, triple_distances); + to_coord = renormalizeValue (to_coord, axis_range, triple_distances); + + coords[0].set_float (from_coord); + coords[1].set_float (to_coord); + } + + HB_INTERNAL static int cmp (const void *pa, const void *pb) + { + const AxisValueMap *a = (const AxisValueMap *) pa; + const AxisValueMap *b = (const AxisValueMap *) pb; + + int a_from = a->coords[0].to_int (); + int b_from = b->coords[0].to_int (); + if (a_from != b_from) + return a_from - b_from; + + /* this should never be reached. according to the spec, all of the axis + * value map records for a given axis must have different fromCoord values + * */ + int a_to = a->coords[1].to_int (); + int b_to = b->coords[1].to_int (); + return a_to - b_to; + } + + bool serialize (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + public: + F2DOT14 coords[2]; +// F2DOT14 fromCoord; /* A normalized coordinate value obtained using +// * default normalization. */ +// F2DOT14 toCoord; /* The modified, normalized coordinate value. */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct SegmentMaps : Array16Of<AxisValueMap> +{ + int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const + { +#define fromCoord coords[from_offset].to_int () +#define toCoord coords[to_offset].to_int () + /* The following special-cases are not part of OpenType, which requires + * that at least -1, 0, and +1 must be mapped. But we include these as + * part of a better error recovery scheme. */ + if (len < 2) + { + if (!len) + return value; + else /* len == 1*/ + return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + } + + if (value <= arrayZ[0].fromCoord) + return value - arrayZ[0].fromCoord + arrayZ[0].toCoord; + + unsigned int i; + unsigned int count = len - 1; + for (i = 1; i < count && value > arrayZ[i].fromCoord; i++) + ; + + if (value >= arrayZ[i].fromCoord) + return value - arrayZ[i].fromCoord + arrayZ[i].toCoord; + + if (unlikely (arrayZ[i-1].fromCoord == arrayZ[i].fromCoord)) + return arrayZ[i-1].toCoord; + + int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord; + return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) * + (value - arrayZ[i-1].fromCoord)) / denom); +#undef toCoord +#undef fromCoord + } + + int unmap (int value) const { return map (value, 1, 0); } + + Triple unmap_axis_range (const Triple& axis_range) const + { + F2DOT14 val, unmapped_val; + + val.set_float (axis_range.minimum); + unmapped_val.set_int (unmap (val.to_int ())); + float unmapped_min = unmapped_val.to_float (); + + val.set_float (axis_range.middle); + unmapped_val.set_int (unmap (val.to_int ())); + float unmapped_middle = unmapped_val.to_float (); + + val.set_float (axis_range.maximum); + unmapped_val.set_int (unmap (val.to_int ())); + float unmapped_max = unmapped_val.to_float (); + + return Triple{unmapped_min, unmapped_middle, unmapped_max}; + } + + bool subset (hb_subset_context_t *c, hb_tag_t axis_tag) const + { + TRACE_SUBSET (this); + /* avar mapped normalized axis range*/ + Triple *axis_range; + if (!c->plan->axes_location.has (axis_tag, &axis_range)) + return c->serializer->embed (*this); + + TripleDistances *axis_triple_distances; + if (!c->plan->axes_triple_distances.has (axis_tag, &axis_triple_distances)) + return_trace (false); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + Triple unmapped_range = unmap_axis_range (*axis_range); + + /* create a vector of retained mappings and sort */ + hb_vector_t<AxisValueMap> value_mappings; + for (const auto& _ : as_array ()) + { + if (_.is_outside_axis_range (unmapped_range)) + continue; + AxisValueMap mapping; + mapping = _; + mapping.instantiate (*axis_range, unmapped_range, *axis_triple_distances); + /* (-1, -1), (0, 0), (1, 1) mappings will be added later, so avoid + * duplicates here */ + if (mapping.must_include ()) + continue; + value_mappings.push (std::move (mapping)); + } + + AxisValueMap m; + m.set_mapping (-1.f, -1.f); + value_mappings.push (m); + + m.set_mapping (0.f, 0.f); + value_mappings.push (m); + + m.set_mapping (1.f, 1.f); + value_mappings.push (m); + + value_mappings.qsort (); + + for (const auto& _ : value_mappings) + { + if (!_.serialize (c->serializer)) + return_trace (false); + } + return_trace (c->serializer->check_assign (out->len, value_mappings.length, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + public: + DEFINE_SIZE_ARRAY (2, *this); +}; + +struct avar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_avar; + + bool has_data () const { return version.to_int (); } + + const SegmentMaps* get_segment_maps () const + { return &firstAxisSegmentMaps; } + + unsigned get_axis_count () const + { return axisCount; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!(version.sanitize (c) && + hb_barrier () && + (version.major == 1 +#ifndef HB_NO_AVAR2 + || version.major == 2 +#endif + ) && + c->check_struct (this))) + return_trace (false); + + const SegmentMaps *map = &firstAxisSegmentMaps; + unsigned int count = axisCount; + for (unsigned int i = 0; i < count; i++) + { + if (unlikely (!map->sanitize (c))) + return_trace (false); + map = &StructAfter<SegmentMaps> (*map); + } + +#ifndef HB_NO_AVAR2 + if (version.major < 2) + return_trace (true); + hb_barrier (); + + const auto &v2 = * (const avarV2Tail *) map; + if (unlikely (!v2.sanitize (c, this))) + return_trace (false); +#endif + + return_trace (true); + } + + void map_coords (int *coords, unsigned int coords_length) const + { + unsigned int count = hb_min (coords_length, axisCount); + + const SegmentMaps *map = &firstAxisSegmentMaps; + for (unsigned int i = 0; i < count; i++) + { + coords[i] = map->map (coords[i]); + map = &StructAfter<SegmentMaps> (*map); + } + +#ifndef HB_NO_AVAR2 + if (version.major < 2) + return; + hb_barrier (); + + for (; count < axisCount; count++) + map = &StructAfter<SegmentMaps> (*map); + + const auto &v2 = * (const avarV2Tail *) map; + + const auto &varidx_map = this+v2.varIdxMap; + const auto &var_store = this+v2.varStore; + auto *var_store_cache = var_store.create_cache (); + + hb_vector_t<int> out; + out.alloc (coords_length); + for (unsigned i = 0; i < coords_length; i++) + { + int v = coords[i]; + uint32_t varidx = varidx_map.map (i); + float delta = var_store.get_delta (varidx, coords, coords_length, var_store_cache); + v += roundf (delta); + v = hb_clamp (v, -(1<<14), +(1<<14)); + out.push (v); + } + for (unsigned i = 0; i < coords_length; i++) + coords[i] = out[i]; + + OT::VariationStore::destroy_cache (var_store_cache); +#endif + } + + void unmap_coords (int *coords, unsigned int coords_length) const + { + unsigned int count = hb_min (coords_length, axisCount); + + const SegmentMaps *map = &firstAxisSegmentMaps; + for (unsigned int i = 0; i < count; i++) + { + coords[i] = map->unmap (coords[i]); + map = &StructAfter<SegmentMaps> (*map); + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + unsigned retained_axis_count = c->plan->axes_index_map.get_population (); + if (!retained_axis_count) //all axes are pinned/dropped + return_trace (false); + + avar *out = c->serializer->allocate_min<avar> (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + const hb_map_t& axes_index_map = c->plan->axes_index_map; + const SegmentMaps *map = &firstAxisSegmentMaps; + unsigned count = axisCount; + for (unsigned int i = 0; i < count; i++) + { + if (axes_index_map.has (i)) + { + hb_tag_t *axis_tag; + if (!c->plan->axes_old_index_tag_map.has (i, &axis_tag)) + return_trace (false); + if (!map->subset (c, *axis_tag)) + return_trace (false); + } + map = &StructAfter<SegmentMaps> (*map); + } + return_trace (true); + } + + protected: + FixedVersion<>version; /* Version of the avar table + * initially set to 0x00010000u */ + HBUINT16 reserved; /* This field is permanently reserved. Set to 0. */ + HBUINT16 axisCount; /* The number of variation axes in the font. This + * must be the same number as axisCount in the + * 'fvar' table. */ + SegmentMaps firstAxisSegmentMaps; + + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_AVAR_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-var-common.hh b/gfx/harfbuzz/src/hb-ot-var-common.hh new file mode 100644 index 0000000000..eff6df380f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var-common.hh @@ -0,0 +1,2248 @@ +/* + * Copyright © 2021 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 HB_OT_VAR_COMMON_HH +#define HB_OT_VAR_COMMON_HH + +#include "hb-ot-layout-common.hh" +#include "hb-priority-queue.hh" + + +namespace OT { + +template <typename MapCountT> +struct DeltaSetIndexMapFormat01 +{ + friend struct DeltaSetIndexMap; + + unsigned get_size () const + { return min_size + mapCount * get_width (); } + + private: + DeltaSetIndexMapFormat01* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + return_trace (c->embed (this)); + } + + template <typename T> + bool serialize (hb_serialize_context_t *c, const T &plan) + { + unsigned int width = plan.get_width (); + unsigned int inner_bit_count = plan.get_inner_bit_count (); + const hb_array_t<const uint32_t> output_map = plan.get_output_map (); + + TRACE_SERIALIZE (this); + if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + return_trace (false); + if (unlikely (!c->extend_min (this))) return_trace (false); + + entryFormat = ((width-1)<<4)|(inner_bit_count-1); + mapCount = output_map.length; + HBUINT8 *p = c->allocate_size<HBUINT8> (width * output_map.length); + if (unlikely (!p)) return_trace (false); + for (unsigned int i = 0; i < output_map.length; i++) + { + unsigned int v = output_map.arrayZ[i]; + if (v) + { + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + unsigned int u = (outer << inner_bit_count) | inner; + for (unsigned int w = width; w > 0;) + { + p[--w] = u; + u >>= 8; + } + } + p += width; + } + return_trace (true); + } + + uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */ + { + /* If count is zero, pass value unchanged. This takes + * care of direct mapping for advance map. */ + if (!mapCount) + return v; + + if (v >= mapCount) + v = mapCount - 1; + + unsigned int u = 0; + { /* Fetch it. */ + unsigned int w = get_width (); + const HBUINT8 *p = mapDataZ.arrayZ + w * v; + for (; w; w--) + u = (u << 8) + *p++; + } + + { /* Repack it. */ + unsigned int n = get_inner_bit_count (); + unsigned int outer = u >> n; + unsigned int inner = u & ((1 << n) - 1); + u = (outer<<16) | inner; + } + + return u; + } + + unsigned get_map_count () const { return mapCount; } + unsigned get_width () const { return ((entryFormat >> 4) & 3) + 1; } + unsigned get_inner_bit_count () const { return (entryFormat & 0xF) + 1; } + + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + c->check_range (mapDataZ.arrayZ, + mapCount, + get_width ())); + } + + protected: + HBUINT8 format; /* Format identifier--format = 0 */ + HBUINT8 entryFormat; /* A packed field that describes the compressed + * representation of delta-set indices. */ + MapCountT mapCount; /* The number of mapping entries. */ + UnsizedArrayOf<HBUINT8> + mapDataZ; /* The delta-set index mapping data. */ + + public: + DEFINE_SIZE_ARRAY (2+MapCountT::static_size, mapDataZ); +}; + +struct DeltaSetIndexMap +{ + template <typename T> + bool serialize (hb_serialize_context_t *c, const T &plan) + { + TRACE_SERIALIZE (this); + unsigned length = plan.get_output_map ().length; + u.format = length <= 0xFFFF ? 0 : 1; + switch (u.format) { + case 0: return_trace (u.format0.serialize (c, plan)); + case 1: return_trace (u.format1.serialize (c, plan)); + default:return_trace (false); + } + } + + uint32_t map (unsigned v) const + { + switch (u.format) { + case 0: return (u.format0.map (v)); + case 1: return (u.format1.map (v)); + default:return v; + } + } + + unsigned get_map_count () const + { + switch (u.format) { + case 0: return u.format0.get_map_count (); + case 1: return u.format1.get_map_count (); + default:return 0; + } + } + + unsigned get_width () const + { + switch (u.format) { + case 0: return u.format0.get_width (); + case 1: return u.format1.get_width (); + default:return 0; + } + } + + unsigned get_inner_bit_count () const + { + switch (u.format) { + case 0: return u.format0.get_inner_bit_count (); + case 1: return u.format1.get_inner_bit_count (); + default:return 0; + } + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + if (!u.format.sanitize (c)) return_trace (false); + hb_barrier (); + switch (u.format) { + case 0: return_trace (u.format0.sanitize (c)); + case 1: return_trace (u.format1.sanitize (c)); + default:return_trace (true); + } + } + + DeltaSetIndexMap* copy (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + switch (u.format) { + case 0: return_trace (reinterpret_cast<DeltaSetIndexMap *> (u.format0.copy (c))); + case 1: return_trace (reinterpret_cast<DeltaSetIndexMap *> (u.format1.copy (c))); + default:return_trace (nullptr); + } + } + + protected: + union { + HBUINT8 format; /* Format identifier */ + DeltaSetIndexMapFormat01<HBUINT16> format0; + DeltaSetIndexMapFormat01<HBUINT32> format1; + } u; + public: + DEFINE_SIZE_UNION (1, format); +}; + + +struct VarStoreInstancer +{ + VarStoreInstancer (const VariationStore *varStore, + const DeltaSetIndexMap *varIdxMap, + hb_array_t<int> coords) : + varStore (varStore), varIdxMap (varIdxMap), coords (coords) {} + + operator bool () const { return varStore && bool (coords); } + + /* according to the spec, if colr table has varStore but does not have + * varIdxMap, then an implicit identity mapping is used */ + float operator() (uint32_t varIdx, unsigned short offset = 0) const + { return coords ? varStore->get_delta (varIdxMap ? varIdxMap->map (VarIdx::add (varIdx, offset)) : varIdx + offset, coords) : 0; } + + const VariationStore *varStore; + const DeltaSetIndexMap *varIdxMap; + hb_array_t<int> coords; +}; + +/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */ +struct TupleVariationHeader +{ + friend struct tuple_delta_t; + unsigned get_size (unsigned axis_count) const + { return min_size + get_all_tuples (axis_count).get_size (); } + + unsigned get_data_size () const { return varDataSize; } + + const TupleVariationHeader &get_next (unsigned axis_count) const + { return StructAtOffset<TupleVariationHeader> (this, get_size (axis_count)); } + + bool unpack_axis_tuples (unsigned axis_count, + const hb_array_t<const F2DOT14> shared_tuples, + const hb_map_t *axes_old_index_tag_map, + hb_hashmap_t<hb_tag_t, Triple>& axis_tuples /* OUT */) const + { + const F2DOT14 *peak_tuple = nullptr; + if (has_peak ()) + peak_tuple = get_peak_tuple (axis_count).arrayZ; + else + { + unsigned int index = get_index (); + if (unlikely ((index + 1) * axis_count > shared_tuples.length)) + return false; + peak_tuple = shared_tuples.sub_array (axis_count * index, axis_count).arrayZ; + } + + const F2DOT14 *start_tuple = nullptr; + const F2DOT14 *end_tuple = nullptr; + bool has_interm = has_intermediate (); + + if (has_interm) + { + start_tuple = get_start_tuple (axis_count).arrayZ; + end_tuple = get_end_tuple (axis_count).arrayZ; + } + + for (unsigned i = 0; i < axis_count; i++) + { + float peak = peak_tuple[i].to_float (); + if (peak == 0.f) continue; + + hb_tag_t *axis_tag; + if (!axes_old_index_tag_map->has (i, &axis_tag)) + return false; + + float start, end; + if (has_interm) + { + start = start_tuple[i].to_float (); + end = end_tuple[i].to_float (); + } + else + { + start = hb_min (peak, 0.f); + end = hb_max (peak, 0.f); + } + axis_tuples.set (*axis_tag, Triple (start, peak, end)); + } + + return true; + } + + float calculate_scalar (hb_array_t<int> coords, unsigned int coord_count, + const hb_array_t<const F2DOT14> shared_tuples, + const hb_vector_t<hb_pair_t<int,int>> *shared_tuple_active_idx = nullptr) const + { + const F2DOT14 *peak_tuple; + + unsigned start_idx = 0; + unsigned end_idx = coord_count; + unsigned step = 1; + + if (has_peak ()) + peak_tuple = get_peak_tuple (coord_count).arrayZ; + else + { + unsigned int index = get_index (); + if (unlikely ((index + 1) * coord_count > shared_tuples.length)) + return 0.f; + peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count).arrayZ; + + if (shared_tuple_active_idx) + { + if (unlikely (index >= shared_tuple_active_idx->length)) + return 0.f; + auto _ = (*shared_tuple_active_idx).arrayZ[index]; + if (_.second != -1) + { + start_idx = _.first; + end_idx = _.second + 1; + step = _.second - _.first; + } + else if (_.first != -1) + { + start_idx = _.first; + end_idx = start_idx + 1; + } + } + } + + const F2DOT14 *start_tuple = nullptr; + const F2DOT14 *end_tuple = nullptr; + bool has_interm = has_intermediate (); + if (has_interm) + { + start_tuple = get_start_tuple (coord_count).arrayZ; + end_tuple = get_end_tuple (coord_count).arrayZ; + } + + float scalar = 1.f; + for (unsigned int i = start_idx; i < end_idx; i += step) + { + int peak = peak_tuple[i].to_int (); + if (!peak) continue; + + int v = coords[i]; + if (v == peak) continue; + + if (has_interm) + { + int start = start_tuple[i].to_int (); + int end = end_tuple[i].to_int (); + if (unlikely (start > peak || peak > end || + (start < 0 && end > 0 && peak))) continue; + if (v < start || v > end) return 0.f; + if (v < peak) + { if (peak != start) scalar *= (float) (v - start) / (peak - start); } + else + { if (peak != end) scalar *= (float) (end - v) / (end - peak); } + } + else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.f; + else + scalar *= (float) v / peak; + } + return scalar; + } + + bool has_peak () const { return tupleIndex & TuppleIndex::EmbeddedPeakTuple; } + bool has_intermediate () const { return tupleIndex & TuppleIndex::IntermediateRegion; } + bool has_private_points () const { return tupleIndex & TuppleIndex::PrivatePointNumbers; } + unsigned get_index () const { return tupleIndex & TuppleIndex::TupleIndexMask; } + + protected: + struct TuppleIndex : HBUINT16 + { + enum Flags { + EmbeddedPeakTuple = 0x8000u, + IntermediateRegion = 0x4000u, + PrivatePointNumbers = 0x2000u, + TupleIndexMask = 0x0FFFu + }; + + TuppleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } + DEFINE_SIZE_STATIC (2); + }; + + hb_array_t<const F2DOT14> get_all_tuples (unsigned axis_count) const + { return StructAfter<UnsizedArrayOf<F2DOT14>> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); } + hb_array_t<const F2DOT14> get_peak_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (0, axis_count); } + hb_array_t<const F2DOT14> get_start_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); } + hb_array_t<const F2DOT14> get_end_tuple (unsigned axis_count) const + { return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); } + + HBUINT16 varDataSize; /* The size in bytes of the serialized + * data for this tuple variation table. */ + TuppleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). + The low 12 bits are an index into a shared tuple + records array. */ + /* UnsizedArrayOf<F2DOT14> peakTuple - optional */ + /* Peak tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. + * + * Note that this must always be included in the 'cvar' table. */ + /* UnsizedArrayOf<F2DOT14> intermediateStartTuple - optional */ + /* Intermediate start tuple record for this tuple variation table — optional, + determined by flags in the tupleIndex value. */ + /* UnsizedArrayOf<F2DOT14> intermediateEndTuple - optional */ + /* Intermediate end tuple record for this tuple variation table — optional, + * determined by flags in the tupleIndex value. */ + public: + DEFINE_SIZE_MIN (4); +}; + +enum packed_delta_flag_t +{ + DELTAS_ARE_ZERO = 0x80, + DELTAS_ARE_WORDS = 0x40, + DELTA_RUN_COUNT_MASK = 0x3F +}; + +struct tuple_delta_t +{ + static constexpr bool realloc_move = true; // Watch out when adding new members! + + public: + hb_hashmap_t<hb_tag_t, Triple> axis_tuples; + + /* indices_length = point_count, indice[i] = 1 means point i is referenced */ + hb_vector_t<bool> indices; + + hb_vector_t<float> deltas_x; + /* empty for cvar tuples */ + hb_vector_t<float> deltas_y; + + /* compiled data: header and deltas + * compiled point data is saved in a hashmap within tuple_variations_t cause + * some point sets might be reused by different tuple variations */ + hb_vector_t<char> compiled_tuple_header; + hb_vector_t<char> compiled_deltas; + + /* compiled peak coords, empty for non-gvar tuples */ + hb_vector_t<char> compiled_peak_coords; + + tuple_delta_t () = default; + tuple_delta_t (const tuple_delta_t& o) = default; + + friend void swap (tuple_delta_t& a, tuple_delta_t& b) + { + hb_swap (a.axis_tuples, b.axis_tuples); + hb_swap (a.indices, b.indices); + hb_swap (a.deltas_x, b.deltas_x); + hb_swap (a.deltas_y, b.deltas_y); + hb_swap (a.compiled_tuple_header, b.compiled_tuple_header); + hb_swap (a.compiled_deltas, b.compiled_deltas); + hb_swap (a.compiled_peak_coords, b.compiled_peak_coords); + } + + tuple_delta_t (tuple_delta_t&& o) : tuple_delta_t () + { hb_swap (*this, o); } + + tuple_delta_t& operator = (tuple_delta_t&& o) + { + hb_swap (*this, o); + return *this; + } + + void remove_axis (hb_tag_t axis_tag) + { axis_tuples.del (axis_tag); } + + bool set_tent (hb_tag_t axis_tag, Triple tent) + { return axis_tuples.set (axis_tag, tent); } + + tuple_delta_t& operator += (const tuple_delta_t& o) + { + unsigned num = indices.length; + for (unsigned i = 0; i < num; i++) + { + if (indices.arrayZ[i]) + { + if (o.indices.arrayZ[i]) + { + deltas_x[i] += o.deltas_x[i]; + if (deltas_y && o.deltas_y) + deltas_y[i] += o.deltas_y[i]; + } + } + else + { + if (!o.indices.arrayZ[i]) continue; + indices.arrayZ[i] = true; + deltas_x[i] = o.deltas_x[i]; + if (deltas_y && o.deltas_y) + deltas_y[i] = o.deltas_y[i]; + } + } + return *this; + } + + tuple_delta_t& operator *= (float scalar) + { + if (scalar == 1.0f) + return *this; + + unsigned num = indices.length; + if (deltas_y) + for (unsigned i = 0; i < num; i++) + { + if (!indices.arrayZ[i]) continue; + deltas_x[i] *= scalar; + deltas_y[i] *= scalar; + } + else + for (unsigned i = 0; i < num; i++) + { + if (!indices.arrayZ[i]) continue; + deltas_x[i] *= scalar; + } + return *this; + } + + hb_vector_t<tuple_delta_t> change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit, + TripleDistances axis_triple_distances) const + { + hb_vector_t<tuple_delta_t> out; + Triple *tent; + if (!axis_tuples.has (axis_tag, &tent)) + { + out.push (*this); + return out; + } + + if ((tent->minimum < 0.f && tent->maximum > 0.f) || + !(tent->minimum <= tent->middle && tent->middle <= tent->maximum)) + return out; + + if (tent->middle == 0.f) + { + out.push (*this); + return out; + } + + result_t solutions = rebase_tent (*tent, axis_limit, axis_triple_distances); + for (auto t : solutions) + { + tuple_delta_t new_var = *this; + if (t.second == Triple ()) + new_var.remove_axis (axis_tag); + else + new_var.set_tent (axis_tag, t.second); + + new_var *= t.first; + out.push (std::move (new_var)); + } + + return out; + } + + bool compile_peak_coords (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map) + { + unsigned axis_count = axes_index_map.get_population (); + if (unlikely (!compiled_peak_coords.alloc (axis_count * F2DOT14::static_size))) + return false; + + unsigned orig_axis_count = axes_old_index_tag_map.get_population (); + for (unsigned i = 0; i < orig_axis_count; i++) + { + if (!axes_index_map.has (i)) + continue; + + hb_tag_t axis_tag = axes_old_index_tag_map.get (i); + Triple *coords; + F2DOT14 peak_coord; + if (axis_tuples.has (axis_tag, &coords)) + peak_coord.set_float (coords->middle); + else + peak_coord.set_int (0); + + /* push F2DOT14 value into char vector */ + int16_t val = peak_coord.to_int (); + compiled_peak_coords.push (static_cast<char> (val >> 8)); + compiled_peak_coords.push (static_cast<char> (val & 0xFF)); + } + + return !compiled_peak_coords.in_error (); + } + + /* deltas should be compiled already before we compile tuple + * variation header cause we need to fill in the size of the + * serialized data for this tuple variation */ + bool compile_tuple_var_header (const hb_map_t& axes_index_map, + unsigned points_data_length, + const hb_map_t& axes_old_index_tag_map, + const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* shared_tuples_idx_map) + { + if (!compiled_deltas) return false; + + unsigned cur_axis_count = axes_index_map.get_population (); + /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */ + unsigned alloc_len = 3 * cur_axis_count * (F2DOT14::static_size) + 4; + if (unlikely (!compiled_tuple_header.resize (alloc_len))) return false; + + unsigned flag = 0; + /* skip the first 4 header bytes: variationDataSize+tupleIndex */ + F2DOT14* p = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.begin () + 4); + F2DOT14* end = reinterpret_cast<F2DOT14 *> (compiled_tuple_header.end ()); + hb_array_t<F2DOT14> coords (p, end - p); + + /* encode peak coords */ + unsigned peak_count = 0; + unsigned *shared_tuple_idx; + if (shared_tuples_idx_map && + shared_tuples_idx_map->has (&compiled_peak_coords, &shared_tuple_idx)) + { + flag = *shared_tuple_idx; + } + else + { + peak_count = encode_peak_coords(coords, flag, axes_index_map, axes_old_index_tag_map); + if (!peak_count) return false; + } + + /* encode interim coords, it's optional so returned num could be 0 */ + unsigned interim_count = encode_interm_coords (coords.sub_array (peak_count), flag, axes_index_map, axes_old_index_tag_map); + + /* pointdata length = 0 implies "use shared points" */ + if (points_data_length) + flag |= TupleVariationHeader::TuppleIndex::PrivatePointNumbers; + + unsigned serialized_data_size = points_data_length + compiled_deltas.length; + TupleVariationHeader *o = reinterpret_cast<TupleVariationHeader *> (compiled_tuple_header.begin ()); + o->varDataSize = serialized_data_size; + o->tupleIndex = flag; + + unsigned total_header_len = 4 + (peak_count + interim_count) * (F2DOT14::static_size); + return compiled_tuple_header.resize (total_header_len); + } + + unsigned encode_peak_coords (hb_array_t<F2DOT14> peak_coords, + unsigned& flag, + const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map) const + { + unsigned orig_axis_count = axes_old_index_tag_map.get_population (); + auto it = peak_coords.iter (); + unsigned count = 0; + for (unsigned i = 0; i < orig_axis_count; i++) + { + if (!axes_index_map.has (i)) /* axis pinned */ + continue; + hb_tag_t axis_tag = axes_old_index_tag_map.get (i); + Triple *coords; + if (!axis_tuples.has (axis_tag, &coords)) + (*it).set_int (0); + else + (*it).set_float (coords->middle); + it++; + count++; + } + flag |= TupleVariationHeader::TuppleIndex::EmbeddedPeakTuple; + return count; + } + + /* if no need to encode intermediate coords, then just return p */ + unsigned encode_interm_coords (hb_array_t<F2DOT14> coords, + unsigned& flag, + const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map) const + { + unsigned orig_axis_count = axes_old_index_tag_map.get_population (); + unsigned cur_axis_count = axes_index_map.get_population (); + + auto start_coords_iter = coords.sub_array (0, cur_axis_count).iter (); + auto end_coords_iter = coords.sub_array (cur_axis_count).iter (); + bool encode_needed = false; + unsigned count = 0; + for (unsigned i = 0; i < orig_axis_count; i++) + { + if (!axes_index_map.has (i)) /* axis pinned */ + continue; + hb_tag_t axis_tag = axes_old_index_tag_map.get (i); + Triple *coords; + float min_val = 0.f, val = 0.f, max_val = 0.f; + if (axis_tuples.has (axis_tag, &coords)) + { + min_val = coords->minimum; + val = coords->middle; + max_val = coords->maximum; + } + + (*start_coords_iter).set_float (min_val); + (*end_coords_iter).set_float (max_val); + + start_coords_iter++; + end_coords_iter++; + count += 2; + if (min_val != hb_min (val, 0.f) || max_val != hb_max (val, 0.f)) + encode_needed = true; + } + + if (encode_needed) + { + flag |= TupleVariationHeader::TuppleIndex::IntermediateRegion; + return count; + } + return 0; + } + + bool compile_deltas () + { + hb_vector_t<int> rounded_deltas; + if (unlikely (!rounded_deltas.alloc (indices.length))) + return false; + + for (unsigned i = 0; i < indices.length; i++) + { + if (!indices[i]) continue; + int rounded_delta = (int) roundf (deltas_x[i]); + rounded_deltas.push (rounded_delta); + } + + if (!rounded_deltas) return false; + /* allocate enough memories 3 * num_deltas */ + unsigned alloc_len = 3 * rounded_deltas.length; + if (deltas_y) + alloc_len *= 2; + + if (unlikely (!compiled_deltas.resize (alloc_len))) return false; + + unsigned i = 0; + unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas); + + if (deltas_y) + { + /* reuse the rounded_deltas vector, check that deltas_y have the same num of deltas as deltas_x */ + unsigned j = 0; + for (unsigned idx = 0; idx < indices.length; idx++) + { + if (!indices[idx]) continue; + int rounded_delta = (int) roundf (deltas_y[idx]); + + if (j >= rounded_deltas.length) return false; + + rounded_deltas[j++] = rounded_delta; + } + + if (j != rounded_deltas.length) return false; + /* reset i because we reuse rounded_deltas for deltas_y */ + i = 0; + encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); + } + return compiled_deltas.resize (encoded_len); + } + + unsigned encode_delta_run (unsigned& i, + hb_array_t<char> encoded_bytes, + const hb_vector_t<int>& deltas) const + { + unsigned num_deltas = deltas.length; + unsigned encoded_len = 0; + while (i < num_deltas) + { + int val = deltas.arrayZ[i]; + if (val == 0) + encoded_len += encode_delta_run_as_zeroes (i, encoded_bytes.sub_array (encoded_len), deltas); + else if (val >= -128 && val <= 127) + encoded_len += encode_delta_run_as_bytes (i, encoded_bytes.sub_array (encoded_len), deltas); + else + encoded_len += encode_delta_run_as_words (i, encoded_bytes.sub_array (encoded_len), deltas); + } + return encoded_len; + } + + unsigned encode_delta_run_as_zeroes (unsigned& i, + hb_array_t<char> encoded_bytes, + const hb_vector_t<int>& deltas) const + { + unsigned num_deltas = deltas.length; + unsigned run_length = 0; + auto it = encoded_bytes.iter (); + unsigned encoded_len = 0; + while (i < num_deltas && deltas.arrayZ[i] == 0) + { + i++; + run_length++; + } + + while (run_length >= 64) + { + *it++ = char (DELTAS_ARE_ZERO | 63); + run_length -= 64; + encoded_len++; + } + + if (run_length) + { + *it++ = char (DELTAS_ARE_ZERO | (run_length - 1)); + encoded_len++; + } + return encoded_len; + } + + unsigned encode_delta_run_as_bytes (unsigned &i, + hb_array_t<char> encoded_bytes, + const hb_vector_t<int>& deltas) const + { + unsigned start = i; + unsigned num_deltas = deltas.length; + while (i < num_deltas) + { + int val = deltas.arrayZ[i]; + if (val > 127 || val < -128) + break; + + /* from fonttools: if there're 2 or more zeros in a sequence, + * it is better to start a new run to save bytes. */ + if (val == 0 && i + 1 < num_deltas && deltas.arrayZ[i+1] == 0) + break; + + i++; + } + unsigned run_length = i - start; + + unsigned encoded_len = 0; + auto it = encoded_bytes.iter (); + + while (run_length >= 64) + { + *it++ = 63; + encoded_len++; + + for (unsigned j = 0; j < 64; j++) + { + *it++ = static_cast<char> (deltas.arrayZ[start + j]); + encoded_len++; + } + + start += 64; + run_length -= 64; + } + + if (run_length) + { + *it++ = run_length - 1; + encoded_len++; + + while (start < i) + { + *it++ = static_cast<char> (deltas.arrayZ[start++]); + encoded_len++; + } + } + + return encoded_len; + } + + unsigned encode_delta_run_as_words (unsigned &i, + hb_array_t<char> encoded_bytes, + const hb_vector_t<int>& deltas) const + { + unsigned start = i; + unsigned num_deltas = deltas.length; + while (i < num_deltas) + { + int val = deltas.arrayZ[i]; + + /* start a new run for a single zero value*/ + if (val == 0) break; + + /* from fonttools: continue word-encoded run if there's only one + * single value in the range [-128, 127] because it is more compact. + * Only start a new run when there're 2 continuous such values. */ + if (val >= -128 && val <= 127 && + i + 1 < num_deltas && + deltas.arrayZ[i+1] >= -128 && deltas.arrayZ[i+1] <= 127) + break; + + i++; + } + + unsigned run_length = i - start; + auto it = encoded_bytes.iter (); + unsigned encoded_len = 0; + while (run_length >= 64) + { + *it++ = (DELTAS_ARE_WORDS | 63); + encoded_len++; + + for (unsigned j = 0; j < 64; j++) + { + int16_t delta_val = deltas.arrayZ[start + j]; + *it++ = static_cast<char> (delta_val >> 8); + *it++ = static_cast<char> (delta_val & 0xFF); + + encoded_len += 2; + } + + start += 64; + run_length -= 64; + } + + if (run_length) + { + *it++ = (DELTAS_ARE_WORDS | (run_length - 1)); + encoded_len++; + while (start < i) + { + int16_t delta_val = deltas.arrayZ[start++]; + *it++ = static_cast<char> (delta_val >> 8); + *it++ = static_cast<char> (delta_val & 0xFF); + + encoded_len += 2; + } + } + return encoded_len; + } + + bool calc_inferred_deltas (const contour_point_vector_t& orig_points) + { + unsigned point_count = orig_points.length; + if (point_count != indices.length) + return false; + + unsigned ref_count = 0; + hb_vector_t<unsigned> end_points; + + for (unsigned i = 0; i < point_count; i++) + { + if (indices.arrayZ[i]) + ref_count++; + if (orig_points.arrayZ[i].is_end_point) + end_points.push (i); + } + /* all points are referenced, nothing to do */ + if (ref_count == point_count) + return true; + if (unlikely (end_points.in_error ())) return false; + + hb_set_t inferred_idxes; + unsigned start_point = 0; + for (unsigned end_point : end_points) + { + /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ + unsigned unref_count = 0; + for (unsigned i = start_point; i < end_point + 1; i++) + unref_count += indices.arrayZ[i]; + unref_count = (end_point - start_point + 1) - unref_count; + + unsigned j = start_point; + if (unref_count == 0 || unref_count > end_point - start_point) + goto no_more_gaps; + for (;;) + { + /* Locate the next gap of unreferenced points between two referenced points prev and next. + * Note that a gap may wrap around at left (start_point) and/or at right (end_point). + */ + unsigned int prev, next, i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (indices.arrayZ[i] && !indices.arrayZ[j]) break; + } + prev = j = i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (!indices.arrayZ[i] && indices.arrayZ[j]) break; + } + next = j; + /* Infer deltas for all unref points in the gap between prev and next */ + i = prev; + for (;;) + { + i = next_index (i, start_point, end_point); + if (i == next) break; + deltas_x.arrayZ[i] = infer_delta (orig_points.arrayZ[i].x, orig_points.arrayZ[prev].x, orig_points.arrayZ[next].x, + deltas_x.arrayZ[prev], deltas_x.arrayZ[next]); + deltas_y.arrayZ[i] = infer_delta (orig_points.arrayZ[i].y, orig_points.arrayZ[prev].y, orig_points.arrayZ[next].y, + deltas_y.arrayZ[prev], deltas_y.arrayZ[next]); + inferred_idxes.add (i); + if (--unref_count == 0) goto no_more_gaps; + } + } + no_more_gaps: + start_point = end_point + 1; + } + + for (unsigned i = 0; i < point_count; i++) + { + /* if points are not referenced and deltas are not inferred, set to 0. + * reference all points for gvar */ + if ( !indices[i]) + { + if (!inferred_idxes.has (i)) + { + deltas_x.arrayZ[i] = 0.f; + deltas_y.arrayZ[i] = 0.f; + } + indices[i] = true; + } + } + return true; + } + + static float infer_delta (float target_val, float prev_val, float next_val, float prev_delta, float next_delta) + { + if (prev_val == next_val) + return (prev_delta == next_delta) ? prev_delta : 0.f; + else if (target_val <= hb_min (prev_val, next_val)) + return (prev_val < next_val) ? prev_delta : next_delta; + else if (target_val >= hb_max (prev_val, next_val)) + return (prev_val > next_val) ? prev_delta : next_delta; + + float r = (target_val - prev_val) / (next_val - prev_val); + return prev_delta + r * (next_delta - prev_delta); + } + + static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end) + { return (i >= end) ? start : (i + 1); } +}; + +struct TupleVariationData +{ + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + // here check on min_size only, TupleVariationHeader and var data will be + // checked while accessing through iterator. + return_trace (c->check_struct (this)); + } + + unsigned get_size (unsigned axis_count) const + { + unsigned total_size = min_size; + unsigned count = tupleVarCount.get_count (); + const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header()); + for (unsigned i = 0; i < count; i++) + { + total_size += tuple_var_header->get_size (axis_count) + tuple_var_header->get_data_size (); + tuple_var_header = &tuple_var_header->get_next (axis_count); + } + + return total_size; + } + + const TupleVariationHeader &get_tuple_var_header (void) const + { return StructAfter<TupleVariationHeader> (data); } + + struct tuple_iterator_t; + struct tuple_variations_t + { + hb_vector_t<tuple_delta_t> tuple_vars; + + private: + /* referenced point set->compiled point data map */ + hb_hashmap_t<const hb_vector_t<bool>*, hb_bytes_t> point_data_map; + /* referenced point set-> count map, used in finding shared points */ + hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map; + + /* empty for non-gvar tuples. + * shared_points_bytes is just a copy of some value in the point_data_map, + * which will be freed during map destruction. Save it for serialization, so + * no need to do find_shared_points () again */ + hb_bytes_t shared_points_bytes; + + /* total compiled byte size as TupleVariationData format, initialized to its + * min_size: 4 */ + unsigned compiled_byte_size = 4; + + public: + tuple_variations_t () = default; + tuple_variations_t (const tuple_variations_t&) = delete; + tuple_variations_t& operator=(const tuple_variations_t&) = delete; + tuple_variations_t (tuple_variations_t&&) = default; + tuple_variations_t& operator=(tuple_variations_t&&) = default; + ~tuple_variations_t () { fini (); } + void fini () + { + for (auto _ : point_data_map.values ()) + _.fini (); + + point_set_count_map.fini (); + tuple_vars.fini (); + } + + explicit operator bool () const { return bool (tuple_vars); } + unsigned get_var_count () const + { + unsigned count = tuple_vars.length; + if (shared_points_bytes.length) + count |= TupleVarCount::SharedPointNumbers; + return count; + } + + unsigned get_compiled_byte_size () const + { return compiled_byte_size; } + + bool create_from_tuple_var_data (tuple_iterator_t iterator, + unsigned tuple_var_count, + unsigned point_count, + bool is_gvar, + const hb_map_t *axes_old_index_tag_map, + const hb_vector_t<unsigned> &shared_indices, + const hb_array_t<const F2DOT14> shared_tuples) + { + do + { + const HBUINT8 *p = iterator.get_serialized_data (); + unsigned int length = iterator.current_tuple->get_data_size (); + if (unlikely (!iterator.var_data_bytes.check_range (p, length))) + { fini (); return false; } + + hb_hashmap_t<hb_tag_t, Triple> axis_tuples; + if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples) + || axis_tuples.is_empty ()) + { fini (); return false; } + + hb_vector_t<unsigned> private_indices; + bool has_private_points = iterator.current_tuple->has_private_points (); + const HBUINT8 *end = p + length; + if (has_private_points && + !TupleVariationData::unpack_points (p, private_indices, end)) + { fini (); return false; } + + const hb_vector_t<unsigned> &indices = has_private_points ? private_indices : shared_indices; + bool apply_to_all = (indices.length == 0); + unsigned num_deltas = apply_to_all ? point_count : indices.length; + + hb_vector_t<int> deltas_x; + + if (unlikely (!deltas_x.resize (num_deltas, false) || + !TupleVariationData::unpack_deltas (p, deltas_x, end))) + { fini (); return false; } + + hb_vector_t<int> deltas_y; + if (is_gvar) + { + if (unlikely (!deltas_y.resize (num_deltas, false) || + !TupleVariationData::unpack_deltas (p, deltas_y, end))) + { fini (); return false; } + } + + tuple_delta_t var; + var.axis_tuples = std::move (axis_tuples); + if (unlikely (!var.indices.resize (point_count) || + !var.deltas_x.resize (point_count, false))) + { fini (); return false; } + + if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false))) + { fini (); return false; } + + for (unsigned i = 0; i < num_deltas; i++) + { + unsigned idx = apply_to_all ? i : indices[i]; + if (idx >= point_count) continue; + var.indices[idx] = true; + var.deltas_x[idx] = static_cast<float> (deltas_x[i]); + if (is_gvar) + var.deltas_y[idx] = static_cast<float> (deltas_y[i]); + } + tuple_vars.push (std::move (var)); + } while (iterator.move_to_next ()); + return true; + } + + bool create_from_item_var_data (const VarData &var_data, + const hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>>& regions, + const hb_map_t& axes_old_index_tag_map, + unsigned& item_count, + const hb_inc_bimap_t* inner_map = nullptr) + { + /* NULL offset, to keep original varidx valid, just return */ + if (&var_data == &Null (VarData)) + return true; + + unsigned num_regions = var_data.get_region_index_count (); + if (!tuple_vars.alloc (num_regions)) return false; + + item_count = inner_map ? inner_map->get_population () : var_data.get_item_count (); + if (!item_count) return true; + unsigned row_size = var_data.get_row_size (); + const HBUINT8 *delta_bytes = var_data.get_delta_bytes (); + + for (unsigned r = 0; r < num_regions; r++) + { + /* In VarData, deltas are organized in rows, convert them into + * column(region) based tuples, resize deltas_x first */ + tuple_delta_t tuple; + if (!tuple.deltas_x.resize (item_count, false) || + !tuple.indices.resize (item_count, false)) + return false; + + for (unsigned i = 0; i < item_count; i++) + { + tuple.indices.arrayZ[i] = true; + tuple.deltas_x.arrayZ[i] = var_data.get_item_delta_fast (inner_map ? inner_map->backward (i) : i, + r, delta_bytes, row_size); + } + + unsigned region_index = var_data.get_region_index (r); + if (region_index >= regions.length) return false; + tuple.axis_tuples = regions.arrayZ[region_index]; + + tuple_vars.push (std::move (tuple)); + } + return !tuple_vars.in_error (); + } + + private: + static int _cmp_axis_tag (const void *pa, const void *pb) + { + const hb_tag_t *a = (const hb_tag_t*) pa; + const hb_tag_t *b = (const hb_tag_t*) pb; + return (int)(*a) - (int)(*b); + } + + bool change_tuple_variations_axis_limits (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, + const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances) + { + /* sort axis_tag/axis_limits, make result deterministic */ + hb_vector_t<hb_tag_t> axis_tags; + if (!axis_tags.alloc (normalized_axes_location.get_population ())) + return false; + for (auto t : normalized_axes_location.keys ()) + axis_tags.push (t); + + axis_tags.qsort (_cmp_axis_tag); + for (auto axis_tag : axis_tags) + { + Triple *axis_limit; + if (!normalized_axes_location.has (axis_tag, &axis_limit)) + return false; + TripleDistances axis_triple_distances{1.f, 1.f}; + if (axes_triple_distances.has (axis_tag)) + axis_triple_distances = axes_triple_distances.get (axis_tag); + + hb_vector_t<tuple_delta_t> new_vars; + for (const tuple_delta_t& var : tuple_vars) + { + hb_vector_t<tuple_delta_t> out = var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances); + if (!out) continue; + + unsigned new_len = new_vars.length + out.length; + + if (unlikely (!new_vars.alloc (new_len, false))) + { fini (); return false;} + + for (unsigned i = 0; i < out.length; i++) + new_vars.push (std::move (out[i])); + } + tuple_vars.fini (); + tuple_vars = std::move (new_vars); + } + return true; + } + + /* merge tuple variations with overlapping tents */ + void merge_tuple_variations () + { + hb_vector_t<tuple_delta_t> new_vars; + hb_hashmap_t<const hb_hashmap_t<hb_tag_t, Triple>*, unsigned> m; + unsigned i = 0; + for (const tuple_delta_t& var : tuple_vars) + { + /* if all axes are pinned, drop the tuple variation */ + if (var.axis_tuples.is_empty ()) continue; + + unsigned *idx; + if (m.has (&(var.axis_tuples), &idx)) + { + new_vars[*idx] += var; + } + else + { + new_vars.push (var); + m.set (&(var.axis_tuples), i); + i++; + } + } + tuple_vars.fini (); + tuple_vars = std::move (new_vars); + } + + hb_bytes_t compile_point_set (const hb_vector_t<bool> &point_indices) + { + unsigned num_points = 0; + for (bool i : point_indices) + if (i) num_points++; + + unsigned indices_length = point_indices.length; + /* If the points set consists of all points in the glyph, it's encoded with a + * single zero byte */ + if (num_points == indices_length) + { + char *p = (char *) hb_calloc (1, sizeof (char)); + if (unlikely (!p)) return hb_bytes_t (); + + return hb_bytes_t (p, 1); + } + + /* allocate enough memories: 2 bytes for count + 3 bytes for each point */ + unsigned num_bytes = 2 + 3 *num_points; + char *p = (char *) hb_calloc (num_bytes, sizeof (char)); + if (unlikely (!p)) return hb_bytes_t (); + + unsigned pos = 0; + /* binary data starts with the total number of reference points */ + if (num_points < 0x80) + p[pos++] = num_points; + else + { + p[pos++] = ((num_points >> 8) | 0x80); + p[pos++] = num_points & 0xFF; + } + + const unsigned max_run_length = 0x7F; + unsigned i = 0; + unsigned last_value = 0; + unsigned num_encoded = 0; + while (i < indices_length && num_encoded < num_points) + { + unsigned run_length = 0; + unsigned header_pos = pos; + p[pos++] = 0; + + bool use_byte_encoding = false; + bool new_run = true; + while (i < indices_length && num_encoded < num_points && + run_length <= max_run_length) + { + // find out next referenced point index + while (i < indices_length && !point_indices[i]) + i++; + + if (i >= indices_length) break; + + unsigned cur_value = i; + unsigned delta = cur_value - last_value; + + if (new_run) + { + use_byte_encoding = (delta <= 0xFF); + new_run = false; + } + + if (use_byte_encoding && delta > 0xFF) + break; + + if (use_byte_encoding) + p[pos++] = delta; + else + { + p[pos++] = delta >> 8; + p[pos++] = delta & 0xFF; + } + i++; + last_value = cur_value; + run_length++; + num_encoded++; + } + + if (use_byte_encoding) + p[header_pos] = run_length - 1; + else + p[header_pos] = (run_length - 1) | 0x80; + } + return hb_bytes_t (p, pos); + } + + /* compile all point set and store byte data in a point_set->hb_bytes_t hashmap, + * also update point_set->count map, which will be used in finding shared + * point set*/ + bool compile_all_point_sets () + { + for (const auto& tuple: tuple_vars) + { + const hb_vector_t<bool>* points_set = &(tuple.indices); + if (point_data_map.has (points_set)) + { + unsigned *count; + if (unlikely (!point_set_count_map.has (points_set, &count) || + !point_set_count_map.set (points_set, (*count) + 1))) + return false; + continue; + } + + hb_bytes_t compiled_data = compile_point_set (*points_set); + if (unlikely (compiled_data == hb_bytes_t ())) + return false; + + if (!point_data_map.set (points_set, compiled_data) || + !point_set_count_map.set (points_set, 1)) + return false; + } + return true; + } + + /* find shared points set which saves most bytes */ + hb_bytes_t find_shared_points () + { + unsigned max_saved_bytes = 0; + hb_bytes_t res{}; + + for (const auto& _ : point_data_map.iter ()) + { + const hb_vector_t<bool>* points_set = _.first; + unsigned data_length = _.second.length; + unsigned *count; + if (unlikely (!point_set_count_map.has (points_set, &count) || + *count <= 1)) + return hb_bytes_t (); + + unsigned saved_bytes = data_length * ((*count) -1); + if (saved_bytes > max_saved_bytes) + { + max_saved_bytes = saved_bytes; + res = _.second; + } + } + return res; + } + + bool calc_inferred_deltas (contour_point_vector_t& contour_points) + { + for (tuple_delta_t& var : tuple_vars) + if (!var.calc_inferred_deltas (contour_points)) + return false; + + return true; + } + + public: + bool instantiate (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, + const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances, + contour_point_vector_t* contour_points = nullptr) + { + if (!tuple_vars) return true; + if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances)) + return false; + /* compute inferred deltas only for gvar */ + if (contour_points) + if (!calc_inferred_deltas (*contour_points)) + return false; + + merge_tuple_variations (); + return !tuple_vars.in_error (); + } + + bool compile_bytes (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map, + bool use_shared_points, + const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* shared_tuples_idx_map = nullptr) + { + // compile points set and store data in hashmap + if (!compile_all_point_sets ()) + return false; + + if (use_shared_points) + { + shared_points_bytes = find_shared_points (); + compiled_byte_size += shared_points_bytes.length; + } + // compile delta and tuple var header for each tuple variation + for (auto& tuple: tuple_vars) + { + const hb_vector_t<bool>* points_set = &(tuple.indices); + hb_bytes_t *points_data; + if (unlikely (!point_data_map.has (points_set, &points_data))) + return false; + + if (!tuple.compile_deltas ()) + return false; + + unsigned points_data_length = (*points_data != shared_points_bytes) ? points_data->length : 0; + if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map, + shared_tuples_idx_map)) + return false; + compiled_byte_size += tuple.compiled_tuple_header.length + points_data_length + tuple.compiled_deltas.length; + } + return true; + } + + bool serialize_var_headers (hb_serialize_context_t *c, unsigned& total_header_len) const + { + TRACE_SERIALIZE (this); + for (const auto& tuple: tuple_vars) + { + tuple.compiled_tuple_header.as_array ().copy (c); + if (c->in_error ()) return_trace (false); + total_header_len += tuple.compiled_tuple_header.length; + } + return_trace (true); + } + + bool serialize_var_data (hb_serialize_context_t *c, bool is_gvar) const + { + TRACE_SERIALIZE (this); + if (is_gvar) + shared_points_bytes.copy (c); + + for (const auto& tuple: tuple_vars) + { + const hb_vector_t<bool>* points_set = &(tuple.indices); + hb_bytes_t *point_data; + if (!point_data_map.has (points_set, &point_data)) + return_trace (false); + + if (!is_gvar || *point_data != shared_points_bytes) + point_data->copy (c); + + tuple.compiled_deltas.as_array ().copy (c); + if (c->in_error ()) return_trace (false); + } + + /* padding for gvar */ + if (is_gvar && (compiled_byte_size % 2)) + { + HBUINT8 pad; + pad = 0; + if (!c->embed (pad)) return_trace (false); + } + return_trace (true); + } + }; + + struct tuple_iterator_t + { + unsigned get_axis_count () const { return axis_count; } + + void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_, const void *table_base_) + { + var_data_bytes = var_data_bytes_; + var_data = var_data_bytes_.as<TupleVariationData> (); + index = 0; + axis_count = axis_count_; + current_tuple = &var_data->get_tuple_var_header (); + data_offset = 0; + table_base = table_base_; + } + + bool get_shared_indices (hb_vector_t<unsigned int> &shared_indices /* OUT */) + { + if (var_data->has_shared_point_numbers ()) + { + const HBUINT8 *base = &(table_base+var_data->data); + const HBUINT8 *p = base; + if (!unpack_points (p, shared_indices, (const HBUINT8 *) (var_data_bytes.arrayZ + var_data_bytes.length))) return false; + data_offset = p - base; + } + return true; + } + + bool is_valid () const + { + return (index < var_data->tupleVarCount.get_count ()) && + var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) && + var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (), + current_tuple->get_size (axis_count))); + } + + bool move_to_next () + { + data_offset += current_tuple->get_data_size (); + current_tuple = ¤t_tuple->get_next (axis_count); + index++; + return is_valid (); + } + + const HBUINT8 *get_serialized_data () const + { return &(table_base+var_data->data) + data_offset; } + + private: + const TupleVariationData *var_data; + unsigned int index; + unsigned int axis_count; + unsigned int data_offset; + const void *table_base; + + public: + hb_bytes_t var_data_bytes; + const TupleVariationHeader *current_tuple; + }; + + static bool get_tuple_iterator (hb_bytes_t var_data_bytes, unsigned axis_count, + const void *table_base, + hb_vector_t<unsigned int> &shared_indices /* OUT */, + tuple_iterator_t *iterator /* OUT */) + { + iterator->init (var_data_bytes, axis_count, table_base); + if (!iterator->get_shared_indices (shared_indices)) + return false; + return iterator->is_valid (); + } + + bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } + + static bool unpack_points (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t<unsigned int> &points /* OUT */, + const HBUINT8 *end) + { + enum packed_point_flag_t + { + POINTS_ARE_WORDS = 0x80, + POINT_RUN_COUNT_MASK = 0x7F + }; + + if (unlikely (p + 1 > end)) return false; + + unsigned count = *p++; + if (count & POINTS_ARE_WORDS) + { + if (unlikely (p + 1 > end)) return false; + count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; + } + if (unlikely (!points.resize (count, false))) return false; + + unsigned n = 0; + unsigned i = 0; + while (i < count) + { + if (unlikely (p + 1 > end)) return false; + unsigned control = *p++; + unsigned run_count = (control & POINT_RUN_COUNT_MASK) + 1; + unsigned stop = i + run_count; + if (unlikely (stop > count)) return false; + if (control & POINTS_ARE_WORDS) + { + if (unlikely (p + run_count * HBUINT16::static_size > end)) return false; + for (; i < stop; i++) + { + n += *(const HBUINT16 *)p; + points.arrayZ[i] = n; + p += HBUINT16::static_size; + } + } + else + { + if (unlikely (p + run_count > end)) return false; + for (; i < stop; i++) + { + n += *p++; + points.arrayZ[i] = n; + } + } + } + return true; + } + + static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t<int> &deltas /* IN/OUT */, + const HBUINT8 *end) + { + unsigned i = 0; + unsigned count = deltas.length; + while (i < count) + { + if (unlikely (p + 1 > end)) return false; + unsigned control = *p++; + unsigned run_count = (control & DELTA_RUN_COUNT_MASK) + 1; + unsigned stop = i + run_count; + if (unlikely (stop > count)) return false; + if (control & DELTAS_ARE_ZERO) + { + for (; i < stop; i++) + deltas.arrayZ[i] = 0; + } + else if (control & DELTAS_ARE_WORDS) + { + if (unlikely (p + run_count * HBUINT16::static_size > end)) return false; + for (; i < stop; i++) + { + deltas.arrayZ[i] = * (const HBINT16 *) p; + p += HBUINT16::static_size; + } + } + else + { + if (unlikely (p + run_count > end)) return false; + for (; i < stop; i++) + { + deltas.arrayZ[i] = * (const HBINT8 *) p++; + } + } + } + return true; + } + + bool has_data () const { return tupleVarCount; } + + bool decompile_tuple_variations (unsigned point_count, + bool is_gvar, + tuple_iterator_t iterator, + const hb_map_t *axes_old_index_tag_map, + const hb_vector_t<unsigned> &shared_indices, + const hb_array_t<const F2DOT14> shared_tuples, + tuple_variations_t& tuple_variations /* OUT */) const + { + return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount, + point_count, is_gvar, + axes_old_index_tag_map, + shared_indices, + shared_tuples); + } + + bool serialize (hb_serialize_context_t *c, + bool is_gvar, + const tuple_variations_t& tuple_variations) const + { + TRACE_SERIALIZE (this); + /* empty tuple variations, just return and skip serialization. */ + if (!tuple_variations) return_trace (true); + + auto *out = c->start_embed (this); + if (unlikely (!c->extend_min (out))) return_trace (false); + + if (!c->check_assign (out->tupleVarCount, tuple_variations.get_var_count (), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + + unsigned total_header_len = 0; + + if (!tuple_variations.serialize_var_headers (c, total_header_len)) + return_trace (false); + + unsigned data_offset = min_size + total_header_len; + if (!is_gvar) data_offset += 4; + if (!c->check_assign (out->data, data_offset, HB_SERIALIZE_ERROR_INT_OVERFLOW)) return_trace (false); + + return tuple_variations.serialize_var_data (c, is_gvar); + } + + protected: + struct TupleVarCount : HBUINT16 + { + friend struct tuple_variations_t; + bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); } + unsigned int get_count () const { return (*this) & CountMask; } + TupleVarCount& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } + explicit operator bool () const { return get_count (); } + + protected: + enum Flags + { + SharedPointNumbers= 0x8000u, + CountMask = 0x0FFFu + }; + public: + DEFINE_SIZE_STATIC (2); + }; + + TupleVarCount tupleVarCount; /* A packed field. The high 4 bits are flags, and the + * low 12 bits are the number of tuple variation tables + * for this glyph. The number of tuple variation tables + * can be any number between 1 and 4095. */ + Offset16To<HBUINT8> + data; /* Offset from the start of the base table + * to the serialized data. */ + /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */ + public: + DEFINE_SIZE_MIN (4); +}; + +using tuple_variations_t = TupleVariationData::tuple_variations_t; +struct item_variations_t +{ + using region_t = const hb_hashmap_t<hb_tag_t, Triple>*; + private: + /* each subtable is decompiled into a tuple_variations_t, in which all tuples + * have the same num of deltas (rows) */ + hb_vector_t<tuple_variations_t> vars; + + /* num of retained rows for each subtable, there're 2 cases when var_data is empty: + * 1. retained item_count is zero + * 2. regions is empty and item_count is non-zero. + * when converting to tuples, both will be dropped because the tuple is empty, + * however, we need to retain 2. as all-zero rows to keep original varidx + * valid, so we need a way to remember the num of rows for each subtable */ + hb_vector_t<unsigned> var_data_num_rows; + + /* original region list, decompiled from item varstore, used when rebuilding + * region list after instantiation */ + hb_vector_t<hb_hashmap_t<hb_tag_t, Triple>> orig_region_list; + + /* region list: vector of Regions, maintain the original order for the regions + * that existed before instantiate (), append the new regions at the end. + * Regions are stored in each tuple already, save pointers only. + * When converting back to item varstore, unused regions will be pruned */ + hb_vector_t<region_t> region_list; + + /* region -> idx map after instantiation and pruning unused regions */ + hb_hashmap_t<region_t, unsigned> region_map; + + /* all delta rows after instantiation */ + hb_vector_t<hb_vector_t<int>> delta_rows; + /* final optimized vector of encoding objects used to assemble the varstore */ + hb_vector_t<delta_row_encoding_t> encodings; + + /* old varidxes -> new var_idxes map */ + hb_map_t varidx_map; + + /* has long words */ + bool has_long = false; + + public: + bool has_long_word () const + { return has_long; } + + const hb_vector_t<region_t>& get_region_list () const + { return region_list; } + + const hb_vector_t<delta_row_encoding_t>& get_vardata_encodings () const + { return encodings; } + + const hb_map_t& get_varidx_map () const + { return varidx_map; } + + bool instantiate (const VariationStore& varStore, + const hb_subset_plan_t *plan, + bool optimize=true, + bool use_no_variation_idx=true, + const hb_array_t <const hb_inc_bimap_t> inner_maps = hb_array_t<const hb_inc_bimap_t> ()) + { + if (!create_from_item_varstore (varStore, plan->axes_old_index_tag_map, inner_maps)) + return false; + if (!instantiate_tuple_vars (plan->axes_location, plan->axes_triple_distances)) + return false; + return as_item_varstore (optimize, use_no_variation_idx); + } + + /* keep below APIs public only for unit test: test-item-varstore */ + bool create_from_item_varstore (const VariationStore& varStore, + const hb_map_t& axes_old_index_tag_map, + const hb_array_t <const hb_inc_bimap_t> inner_maps = hb_array_t<const hb_inc_bimap_t> ()) + { + const VarRegionList& regionList = varStore.get_region_list (); + if (!regionList.get_var_regions (axes_old_index_tag_map, orig_region_list)) + return false; + + unsigned num_var_data = varStore.get_sub_table_count (); + if (inner_maps && inner_maps.length != num_var_data) return false; + if (!vars.alloc (num_var_data) || + !var_data_num_rows.alloc (num_var_data)) return false; + + for (unsigned i = 0; i < num_var_data; i++) + { + if (inner_maps && !inner_maps.arrayZ[i].get_population ()) + continue; + tuple_variations_t var_data_tuples; + unsigned item_count = 0; + if (!var_data_tuples.create_from_item_var_data (varStore.get_sub_table (i), + orig_region_list, + axes_old_index_tag_map, + item_count, + inner_maps ? &(inner_maps.arrayZ[i]) : nullptr)) + return false; + + var_data_num_rows.push (item_count); + vars.push (std::move (var_data_tuples)); + } + return !vars.in_error () && !var_data_num_rows.in_error () && vars.length == var_data_num_rows.length; + } + + bool instantiate_tuple_vars (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, + const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances) + { + for (tuple_variations_t& tuple_vars : vars) + if (!tuple_vars.instantiate (normalized_axes_location, axes_triple_distances)) + return false; + + if (!build_region_list ()) return false; + return true; + } + + bool build_region_list () + { + /* scan all tuples and collect all unique regions, prune unused regions */ + hb_hashmap_t<region_t, unsigned> all_regions; + hb_hashmap_t<region_t, unsigned> used_regions; + + /* use a vector when inserting new regions, make result deterministic */ + hb_vector_t<region_t> all_unique_regions; + for (const tuple_variations_t& sub_table : vars) + { + for (const tuple_delta_t& tuple : sub_table.tuple_vars) + { + region_t r = &(tuple.axis_tuples); + if (!used_regions.has (r)) + { + bool all_zeros = true; + for (float d : tuple.deltas_x) + { + int delta = (int) roundf (d); + if (delta != 0) + { + all_zeros = false; + break; + } + } + if (!all_zeros) + { + if (!used_regions.set (r, 1)) + return false; + } + } + if (all_regions.has (r)) + continue; + if (!all_regions.set (r, 1)) + return false; + all_unique_regions.push (r); + } + } + + if (!all_regions || !all_unique_regions) return false; + if (!region_list.alloc (all_regions.get_population ())) + return false; + + unsigned idx = 0; + /* append the original regions that pre-existed */ + for (const auto& r : orig_region_list) + { + if (!all_regions.has (&r) || !used_regions.has (&r)) + continue; + + region_list.push (&r); + if (!region_map.set (&r, idx)) + return false; + all_regions.del (&r); + idx++; + } + + /* append the new regions at the end */ + for (const auto& r: all_unique_regions) + { + if (!all_regions.has (r) || !used_regions.has (r)) + continue; + region_list.push (r); + if (!region_map.set (r, idx)) + return false; + all_regions.del (r); + idx++; + } + return (!region_list.in_error ()) && (!region_map.in_error ()); + } + + /* main algorithm ported from fonttools VarStore_optimize() method, optimize + * varstore by default */ + + struct combined_gain_idx_tuple_t + { + int gain; + unsigned idx_1; + unsigned idx_2; + + combined_gain_idx_tuple_t () = default; + combined_gain_idx_tuple_t (int gain_, unsigned i, unsigned j) + :gain (gain_), idx_1 (i), idx_2 (j) {} + + bool operator < (const combined_gain_idx_tuple_t& o) + { + if (gain != o.gain) + return gain < o.gain; + + if (idx_1 != o.idx_1) + return idx_1 < o.idx_1; + + return idx_2 < o.idx_2; + } + + bool operator <= (const combined_gain_idx_tuple_t& o) + { + if (*this < o) return true; + return gain == o.gain && idx_1 == o.idx_1 && idx_2 == o.idx_2; + } + }; + + bool as_item_varstore (bool optimize=true, bool use_no_variation_idx=true) + { + if (!region_list) return false; + unsigned num_cols = region_list.length; + /* pre-alloc a 2D vector for all sub_table's VarData rows */ + unsigned total_rows = 0; + for (unsigned major = 0; major < var_data_num_rows.length; major++) + total_rows += var_data_num_rows[major]; + + if (!delta_rows.resize (total_rows)) return false; + /* init all rows to [0]*num_cols */ + for (unsigned i = 0; i < total_rows; i++) + if (!(delta_rows[i].resize (num_cols))) return false; + + /* old VarIdxes -> full encoding_row mapping */ + hb_hashmap_t<unsigned, const hb_vector_t<int>*> front_mapping; + unsigned start_row = 0; + hb_vector_t<delta_row_encoding_t> encoding_objs; + hb_hashmap_t<hb_vector_t<uint8_t>, unsigned> chars_idx_map; + + /* delta_rows map, used for filtering out duplicate rows */ + hb_hashmap_t<const hb_vector_t<int>*, unsigned> delta_rows_map; + for (unsigned major = 0; major < vars.length; major++) + { + /* deltas are stored in tuples(column based), convert them back into items + * (row based) delta */ + const tuple_variations_t& tuples = vars[major]; + unsigned num_rows = var_data_num_rows[major]; + for (const tuple_delta_t& tuple: tuples.tuple_vars) + { + if (tuple.deltas_x.length != num_rows) + return false; + + /* skip unused regions */ + unsigned *col_idx; + if (!region_map.has (&(tuple.axis_tuples), &col_idx)) + continue; + + for (unsigned i = 0; i < num_rows; i++) + { + int rounded_delta = roundf (tuple.deltas_x[i]); + delta_rows[start_row + i][*col_idx] += rounded_delta; + if ((!has_long) && (rounded_delta < -65536 || rounded_delta > 65535)) + has_long = true; + } + } + + if (!optimize) + { + /* assemble a delta_row_encoding_t for this subtable, skip optimization so + * chars is not initialized, we only need delta rows for serialization */ + delta_row_encoding_t obj; + for (unsigned r = start_row; r < start_row + num_rows; r++) + obj.add_row (&(delta_rows.arrayZ[r])); + + encodings.push (std::move (obj)); + start_row += num_rows; + continue; + } + + for (unsigned minor = 0; minor < num_rows; minor++) + { + const hb_vector_t<int>& row = delta_rows[start_row + minor]; + if (use_no_variation_idx) + { + bool all_zeros = true; + for (int delta : row) + { + if (delta != 0) + { + all_zeros = false; + break; + } + } + if (all_zeros) + continue; + } + + if (!front_mapping.set ((major<<16) + minor, &row)) + return false; + + hb_vector_t<uint8_t> chars = delta_row_encoding_t::get_row_chars (row); + if (!chars) return false; + + if (delta_rows_map.has (&row)) + continue; + + delta_rows_map.set (&row, 1); + unsigned *obj_idx; + if (chars_idx_map.has (chars, &obj_idx)) + { + delta_row_encoding_t& obj = encoding_objs[*obj_idx]; + if (!obj.add_row (&row)) + return false; + } + else + { + if (!chars_idx_map.set (chars, encoding_objs.length)) + return false; + delta_row_encoding_t obj (std::move (chars), &row); + encoding_objs.push (std::move (obj)); + } + } + + start_row += num_rows; + } + + /* return directly if no optimization, maintain original VariationIndex so + * varidx_map would be empty */ + if (!optimize) return !encodings.in_error (); + + /* sort encoding_objs */ + encoding_objs.qsort (); + + /* main algorithm: repeatedly pick 2 best encodings to combine, and combine + * them */ + hb_priority_queue_t<combined_gain_idx_tuple_t> queue; + unsigned num_todos = encoding_objs.length; + for (unsigned i = 0; i < num_todos; i++) + { + for (unsigned j = i + 1; j < num_todos; j++) + { + int combining_gain = encoding_objs.arrayZ[i].gain_from_merging (encoding_objs.arrayZ[j]); + if (combining_gain > 0) + queue.insert (combined_gain_idx_tuple_t (-combining_gain, i, j), 0); + } + } + + hb_set_t removed_todo_idxes; + while (queue) + { + auto t = queue.pop_minimum ().first; + unsigned i = t.idx_1; + unsigned j = t.idx_2; + + if (removed_todo_idxes.has (i) || removed_todo_idxes.has (j)) + continue; + + delta_row_encoding_t& encoding = encoding_objs.arrayZ[i]; + delta_row_encoding_t& other_encoding = encoding_objs.arrayZ[j]; + + removed_todo_idxes.add (i); + removed_todo_idxes.add (j); + + hb_vector_t<uint8_t> combined_chars; + if (!combined_chars.alloc (encoding.chars.length)) + return false; + + for (unsigned idx = 0; idx < encoding.chars.length; idx++) + { + uint8_t v = hb_max (encoding.chars.arrayZ[idx], other_encoding.chars.arrayZ[idx]); + combined_chars.push (v); + } + + delta_row_encoding_t combined_encoding_obj (std::move (combined_chars)); + for (const auto& row : hb_concat (encoding.items, other_encoding.items)) + combined_encoding_obj.add_row (row); + + for (unsigned idx = 0; idx < encoding_objs.length; idx++) + { + if (removed_todo_idxes.has (idx)) continue; + + const delta_row_encoding_t& obj = encoding_objs.arrayZ[idx]; + if (obj.chars == combined_chars) + { + for (const auto& row : obj.items) + combined_encoding_obj.add_row (row); + + removed_todo_idxes.add (idx); + continue; + } + + int combined_gain = combined_encoding_obj.gain_from_merging (obj); + if (combined_gain > 0) + queue.insert (combined_gain_idx_tuple_t (-combined_gain, idx, encoding_objs.length), 0); + } + + encoding_objs.push (std::move (combined_encoding_obj)); + } + + int num_final_encodings = (int) encoding_objs.length - (int) removed_todo_idxes.get_population (); + if (num_final_encodings <= 0) return false; + + if (!encodings.alloc (num_final_encodings)) return false; + for (unsigned i = 0; i < encoding_objs.length; i++) + { + if (removed_todo_idxes.has (i)) continue; + encodings.push (std::move (encoding_objs.arrayZ[i])); + } + + /* sort again based on width, make result deterministic */ + encodings.qsort (delta_row_encoding_t::cmp_width); + + return compile_varidx_map (front_mapping); + } + + private: + /* compile varidx_map for one VarData subtable (index specified by major) */ + bool compile_varidx_map (const hb_hashmap_t<unsigned, const hb_vector_t<int>*>& front_mapping) + { + /* full encoding_row -> new VarIdxes mapping */ + hb_hashmap_t<const hb_vector_t<int>*, unsigned> back_mapping; + + for (unsigned major = 0; major < encodings.length; major++) + { + delta_row_encoding_t& encoding = encodings[major]; + /* just sanity check, this shouldn't happen */ + if (encoding.is_empty ()) + return false; + + unsigned num_rows = encoding.items.length; + + /* sort rows, make result deterministic */ + encoding.items.qsort (_cmp_row); + + /* compile old to new var_idxes mapping */ + for (unsigned minor = 0; minor < num_rows; minor++) + { + unsigned new_varidx = (major << 16) + minor; + back_mapping.set (encoding.items.arrayZ[minor], new_varidx); + } + } + + for (auto _ : front_mapping.iter ()) + { + unsigned old_varidx = _.first; + unsigned *new_varidx; + if (back_mapping.has (_.second, &new_varidx)) + varidx_map.set (old_varidx, *new_varidx); + else + varidx_map.set (old_varidx, HB_OT_LAYOUT_NO_VARIATIONS_INDEX); + } + return !varidx_map.in_error (); + } + + static int _cmp_row (const void *pa, const void *pb) + { + /* compare pointers of vectors(const hb_vector_t<int>*) that represent a row */ + const hb_vector_t<int>** a = (const hb_vector_t<int>**) pa; + const hb_vector_t<int>** b = (const hb_vector_t<int>**) pb; + + for (unsigned i = 0; i < (*b)->length; i++) + { + int va = (*a)->arrayZ[i]; + int vb = (*b)->arrayZ[i]; + if (va != vb) + return va < vb ? -1 : 1; + } + return 0; + } +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh new file mode 100644 index 0000000000..3798ad3e3e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh @@ -0,0 +1,220 @@ +/* + * Copyright © 2023 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 HB_OT_VAR_CVAR_TABLE_HH +#define HB_OT_VAR_CVAR_TABLE_HH + +#include "hb-ot-var-common.hh" +#include "hb-ot-var-fvar-table.hh" + + +namespace OT { +/* + * cvar -- control value table (CVT) Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/cvar + */ +#define HB_OT_TAG_cvar HB_TAG('c','v','a','r') + +struct cvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_cvar; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + likely (version.major == 1) && + tupleVariationData.sanitize (c)); + } + + const TupleVariationData* get_tuple_var_data (void) const + { return &tupleVariationData; } + + bool decompile_tuple_variations (unsigned axis_count, + unsigned point_count, + hb_blob_t *blob, + bool is_gvar, + const hb_map_t *axes_old_index_tag_map, + TupleVariationData::tuple_variations_t& tuple_variations /* OUT */) const + { + hb_vector_t<unsigned> shared_indices; + TupleVariationData::tuple_iterator_t iterator; + hb_bytes_t var_data_bytes = blob->as_bytes ().sub_array (4); + if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, this, + shared_indices, &iterator)) + return false; + + return tupleVariationData.decompile_tuple_variations (point_count, is_gvar, iterator, + axes_old_index_tag_map, + shared_indices, + hb_array<const F2DOT14> (), + tuple_variations); + } + + static bool calculate_cvt_deltas (unsigned axis_count, + hb_array_t<int> coords, + unsigned num_cvt_item, + const TupleVariationData *tuple_var_data, + const void *base, + hb_vector_t<float>& cvt_deltas /* OUT */) + { + if (!coords) return true; + hb_vector_t<unsigned> shared_indices; + TupleVariationData::tuple_iterator_t iterator; + unsigned var_data_length = tuple_var_data->get_size (axis_count); + hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast<const char*> (tuple_var_data), var_data_length); + if (!TupleVariationData::get_tuple_iterator (var_data_bytes, axis_count, base, + shared_indices, &iterator)) + return true; /* isn't applied at all */ + + hb_array_t<const F2DOT14> shared_tuples = hb_array<F2DOT14> (); + hb_vector_t<unsigned> private_indices; + hb_vector_t<int> unpacked_deltas; + + do + { + float scalar = iterator.current_tuple->calculate_scalar (coords, axis_count, shared_tuples); + if (scalar == 0.f) continue; + const HBUINT8 *p = iterator.get_serialized_data (); + unsigned int length = iterator.current_tuple->get_data_size (); + if (unlikely (!iterator.var_data_bytes.check_range (p, length))) + return false; + + const HBUINT8 *end = p + length; + + bool has_private_points = iterator.current_tuple->has_private_points (); + if (has_private_points && + !TupleVariationData::unpack_points (p, private_indices, end)) + return false; + const hb_vector_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices; + + bool apply_to_all = (indices.length == 0); + unsigned num_deltas = apply_to_all ? num_cvt_item : indices.length; + if (unlikely (!unpacked_deltas.resize (num_deltas, false))) return false; + if (unlikely (!TupleVariationData::unpack_deltas (p, unpacked_deltas, end))) return false; + + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int idx = apply_to_all ? i : indices[i]; + if (unlikely (idx >= num_cvt_item)) continue; + if (scalar != 1.0f) cvt_deltas[idx] += unpacked_deltas[i] * scalar ; + else cvt_deltas[idx] += unpacked_deltas[i]; + } + } while (iterator.move_to_next ()); + + return true; + } + + bool serialize (hb_serialize_context_t *c, + TupleVariationData::tuple_variations_t& tuple_variations) const + { + TRACE_SERIALIZE (this); + if (!tuple_variations) return_trace (false); + if (unlikely (!c->embed (version))) return_trace (false); + + return_trace (tupleVariationData.serialize (c, false, tuple_variations)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (c->plan->all_axes_pinned) + return_trace (false); + + OT::TupleVariationData::tuple_variations_t tuple_variations; + unsigned axis_count = c->plan->axes_old_index_tag_map.get_population (); + + const hb_tag_t cvt = HB_TAG('c','v','t',' '); + hb_blob_t *cvt_blob = hb_face_reference_table (c->plan->source, cvt); + unsigned point_count = hb_blob_get_length (cvt_blob) / FWORD::static_size; + hb_blob_destroy (cvt_blob); + + if (!decompile_tuple_variations (axis_count, point_count, + c->source_blob, false, + &(c->plan->axes_old_index_tag_map), + tuple_variations)) + return_trace (false); + + if (!tuple_variations.instantiate (c->plan->axes_location, c->plan->axes_triple_distances)) + return_trace (false); + + if (!tuple_variations.compile_bytes (c->plan->axes_index_map, c->plan->axes_old_index_tag_map, + false /* do not use shared points */)) + return_trace (false); + + return_trace (serialize (c->serializer, tuple_variations)); + } + + static bool add_cvt_and_apply_deltas (hb_subset_plan_t *plan, + const TupleVariationData *tuple_var_data, + const void *base) + { + const hb_tag_t cvt = HB_TAG('c','v','t',' '); + hb_blob_t *cvt_blob = hb_face_reference_table (plan->source, cvt); + hb_blob_t *cvt_prime_blob = hb_blob_copy_writable_or_fail (cvt_blob); + hb_blob_destroy (cvt_blob); + + if (unlikely (!cvt_prime_blob)) + return false; + + unsigned cvt_blob_length = hb_blob_get_length (cvt_prime_blob); + unsigned num_cvt_item = cvt_blob_length / FWORD::static_size; + + hb_vector_t<float> cvt_deltas; + if (unlikely (!cvt_deltas.resize (num_cvt_item))) + { + hb_blob_destroy (cvt_prime_blob); + return false; + } + + if (!calculate_cvt_deltas (plan->normalized_coords.length, plan->normalized_coords.as_array (), + num_cvt_item, tuple_var_data, base, cvt_deltas)) + { + hb_blob_destroy (cvt_prime_blob); + return false; + } + + FWORD *cvt_prime = (FWORD *) hb_blob_get_data_writable (cvt_prime_blob, nullptr); + for (unsigned i = 0; i < num_cvt_item; i++) + cvt_prime[i] += (int) roundf (cvt_deltas[i]); + + bool success = plan->add_table (cvt, cvt_prime_blob); + hb_blob_destroy (cvt_prime_blob); + return success; + } + + protected: + FixedVersion<>version; /* Version of the CVT variation table + * initially set to 0x00010000u */ + TupleVariationData tupleVariationData; /* TupleVariationDate for cvar table */ + public: + DEFINE_SIZE_MIN (8); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_CVAR_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh new file mode 100644 index 0000000000..07d7586baa --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var-fvar-table.hh @@ -0,0 +1,502 @@ +/* + * 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_OT_VAR_FVAR_TABLE_HH +#define HB_OT_VAR_FVAR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * fvar -- Font Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/fvar + */ + +#define HB_OT_TAG_fvar HB_TAG('f','v','a','r') + + +namespace OT { + +static bool axis_coord_pinned_or_within_axis_range (const hb_array_t<const F16DOT16> coords, + unsigned axis_index, + Triple axis_limit) +{ + float axis_coord = coords[axis_index].to_float (); + if (axis_limit.is_point ()) + { + if (axis_limit.minimum != axis_coord) + return false; + } + else + { + if (axis_coord < axis_limit.minimum || + axis_coord > axis_limit.maximum) + return false; + } + return true; +} + +struct InstanceRecord +{ + friend struct fvar; + + hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const + { return coordinatesZ.as_array (axis_count); } + + bool keep_instance (unsigned axis_count, + const hb_map_t *axes_index_tag_map, + const hb_hashmap_t<hb_tag_t, Triple> *axes_location) const + { + if (axes_location->is_empty ()) return true; + const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count); + for (unsigned i = 0 ; i < axis_count; i++) + { + uint32_t *axis_tag; + if (!axes_index_tag_map->has (i, &axis_tag)) + return false; + if (!axes_location->has (*axis_tag)) + continue; + + Triple axis_limit = axes_location->get (*axis_tag); + if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit)) + return false; + } + return true; + } + + bool subset (hb_subset_context_t *c, + unsigned axis_count, + bool has_postscript_nameid) const + { + TRACE_SUBSET (this); + if (unlikely (!c->serializer->embed (subfamilyNameID))) return_trace (false); + if (unlikely (!c->serializer->embed (flags))) return_trace (false); + + const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count); + const hb_hashmap_t<hb_tag_t, Triple> *axes_location = &c->plan->user_axes_location; + for (unsigned i = 0 ; i < axis_count; i++) + { + uint32_t *axis_tag; + Triple *axis_limit; + // only keep instances whose coordinates == pinned axis location + if (!c->plan->axes_old_index_tag_map.has (i, &axis_tag)) return_trace (false); + if (axes_location->has (*axis_tag, &axis_limit)) + { + if (!axis_coord_pinned_or_within_axis_range (coords, i, *axis_limit)) + return_trace (false); + + //skip pinned axis + if (axis_limit->is_point ()) + continue; + } + + if (!c->serializer->embed (coords[i])) + return_trace (false); + } + + if (has_postscript_nameid) + { + NameID name_id; + name_id = StructAfter<NameID> (coords); + if (!c->serializer->embed (name_id)) + return_trace (false); + } + + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + c->check_array (coordinatesZ.arrayZ, axis_count)); + } + + protected: + NameID subfamilyNameID;/* The name ID for entries in the 'name' table + * that provide subfamily names for this instance. */ + HBUINT16 flags; /* Reserved for future use — set to 0. */ + UnsizedArrayOf<F16DOT16> + coordinatesZ; /* The coordinates array for this instance. */ + //NameID postScriptNameIDX;/*Optional. The name ID for entries in the 'name' + // * table that provide PostScript names for this + // * instance. */ + + public: + DEFINE_SIZE_UNBOUNDED (4); +}; + +struct AxisRecord +{ + int cmp (hb_tag_t key) const { return axisTag.cmp (key); } + + enum + { + AXIS_FLAG_HIDDEN = 0x0001, + }; + +#ifndef HB_DISABLE_DEPRECATED + void get_axis_deprecated (hb_ot_var_axis_t *info) const + { + info->tag = axisTag; + info->name_id = axisNameID; + get_coordinates (info->min_value, info->default_value, info->max_value); + } +#endif + + void get_axis_info (unsigned axis_index, hb_ot_var_axis_info_t *info) const + { + info->axis_index = axis_index; + info->tag = axisTag; + info->name_id = axisNameID; + info->flags = (hb_ot_var_axis_flags_t) (unsigned int) flags; + get_coordinates (info->min_value, info->default_value, info->max_value); + info->reserved = 0; + } + + hb_tag_t get_axis_tag () const { return axisTag; } + + int normalize_axis_value (float v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + v = hb_clamp (v, min_value, max_value); + + if (v == default_value) + return 0; + else if (v < default_value) + v = (v - default_value) / (default_value - min_value); + else + v = (v - default_value) / (max_value - default_value); + return roundf (v * 16384.f); + } + + float unnormalize_axis_value (int v) const + { + float min_value, default_value, max_value; + get_coordinates (min_value, default_value, max_value); + + if (v == 0) + return default_value; + else if (v < 0) + return v * (default_value - min_value) / 16384.f + default_value; + else + return v * (max_value - default_value) / 16384.f + default_value; + } + + hb_ot_name_id_t get_name_id () const { return axisNameID; } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + void get_coordinates (float &min, float &default_, float &max) const + { + default_ = defaultValue.to_float (); + /* Ensure order, to simplify client math. */ + min = hb_min (default_, minValue.to_float ()); + max = hb_max (default_, maxValue.to_float ()); + } + + float get_default () const + { + return defaultValue.to_float (); + } + + TripleDistances get_triple_distances () const + { + float min, default_, max; + get_coordinates (min, default_, max); + return TripleDistances (min, default_, max); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + const hb_hashmap_t<hb_tag_t, Triple>& user_axes_location = c->plan->user_axes_location; + Triple *axis_limit; + if (user_axes_location.has (axisTag, &axis_limit)) + { + out->minValue.set_float (axis_limit->minimum); + out->defaultValue.set_float (axis_limit->middle); + out->maxValue.set_float (axis_limit->maximum); + } + return_trace (true); + } + + public: + Tag axisTag; /* Tag identifying the design variation for the axis. */ + protected: + F16DOT16 minValue; /* The minimum coordinate value for the axis. */ + F16DOT16 defaultValue; /* The default coordinate value for the axis. */ + F16DOT16 maxValue; /* The maximum coordinate value for the axis. */ + public: + HBUINT16 flags; /* Axis flags. */ + NameID axisNameID; /* The name ID for entries in the 'name' table that + * provide a display name for this axis. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct fvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_fvar; + + bool has_data () const { return version.to_int (); } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + hb_barrier () && + likely (version.major == 1) && + c->check_struct (this) && + hb_barrier () && + axisSize == 20 && /* Assumed in our code. */ + instanceSize >= axisCount * 4 + 4 && + get_axes ().sanitize (c) && + c->check_range (&StructAfter<InstanceRecord> (get_axes ()), + instanceCount, instanceSize)); + } + + unsigned int get_axis_count () const { return axisCount; } + +#ifndef HB_DISABLE_DEPRECATED + unsigned int get_axes_deprecated (unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_t *axes_array /* OUT */) const + { + if (axes_count) + { + hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_deprecated (&axes_array[i]); + } + return axisCount; + } +#endif + + unsigned int get_axis_infos (unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_info_t *axes_array /* OUT */) const + { + if (axes_count) + { + hb_array_t<const AxisRecord> arr = get_axes ().sub_array (start_offset, axes_count); + for (unsigned i = 0; i < arr.length; ++i) + arr[i].get_axis_info (start_offset + i, &axes_array[i]); + } + return axisCount; + } + +#ifndef HB_DISABLE_DEPRECATED + bool + find_axis_deprecated (hb_tag_t tag, unsigned *axis_index, hb_ot_var_axis_t *info) const + { + unsigned i; + if (!axis_index) axis_index = &i; + *axis_index = HB_OT_VAR_NO_AXIS_INDEX; + auto axes = get_axes (); + return axes.lfind (tag, axis_index) && ((void) axes[*axis_index].get_axis_deprecated (info), true); + } +#endif + bool + find_axis_info (hb_tag_t tag, hb_ot_var_axis_info_t *info) const + { + unsigned i; + auto axes = get_axes (); + return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true); + } + + int normalize_axis_value (unsigned int axis_index, float v) const + { return get_axes ()[axis_index].normalize_axis_value (v); } + + float unnormalize_axis_value (unsigned int axis_index, int v) const + { return get_axes ()[axis_index].unnormalize_axis_value (v); } + + unsigned int get_instance_count () const { return instanceCount; } + + hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const + { + const InstanceRecord *instance = get_instance (instance_index); + if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID; + return instance->subfamilyNameID; + } + + hb_ot_name_id_t get_instance_postscript_name_id (unsigned int instance_index) const + { + const InstanceRecord *instance = get_instance (instance_index); + if (unlikely (!instance)) return HB_OT_NAME_ID_INVALID; + if (instanceSize >= axisCount * 4 + 6) + return StructAfter<NameID> (instance->get_coordinates (axisCount)); + return HB_OT_NAME_ID_INVALID; + } + + unsigned int get_instance_coords (unsigned int instance_index, + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */) const + { + const InstanceRecord *instance = get_instance (instance_index); + if (unlikely (!instance)) + { + if (coords_length) + *coords_length = 0; + return 0; + } + + if (coords_length && *coords_length) + { + hb_array_t<const F16DOT16> instanceCoords = instance->get_coordinates (axisCount) + .sub_array (0, coords_length); + for (unsigned int i = 0; i < instanceCoords.length; i++) + coords[i] = instanceCoords.arrayZ[i].to_float (); + } + return axisCount; + } + + void collect_name_ids (hb_hashmap_t<hb_tag_t, Triple> *user_axes_location, + hb_map_t *axes_old_index_tag_map, + hb_set_t *nameids /* IN/OUT */) const + { + if (!has_data ()) return; + + auto axis_records = get_axes (); + for (unsigned i = 0 ; i < (unsigned)axisCount; i++) + { + hb_tag_t axis_tag = axis_records[i].get_axis_tag (); + if (user_axes_location->has (axis_tag) && + user_axes_location->get (axis_tag).is_point ()) + continue; + + nameids->add (axis_records[i].get_name_id ()); + } + + for (unsigned i = 0 ; i < (unsigned)instanceCount; i++) + { + const InstanceRecord *instance = get_instance (i); + + if (!instance->keep_instance (axisCount, axes_old_index_tag_map, user_axes_location)) + continue; + + nameids->add (instance->subfamilyNameID); + + if (instanceSize >= axisCount * 4 + 6) + { + unsigned post_script_name_id = StructAfter<NameID> (instance->get_coordinates (axisCount)); + if (post_script_name_id != HB_OT_NAME_ID_INVALID) nameids->add (post_script_name_id); + } + } + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + unsigned retained_axis_count = c->plan->axes_index_map.get_population (); + if (!retained_axis_count) //all axes are pinned + return_trace (false); + + fvar *out = c->serializer->embed (this); + if (unlikely (!out)) return_trace (false); + + if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + bool has_postscript_nameid = false; + if (instanceSize >= axisCount * 4 + 6) + has_postscript_nameid = true; + + if (!c->serializer->check_assign (out->instanceSize, retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4), + HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + auto axes_records = get_axes (); + for (unsigned i = 0 ; i < (unsigned)axisCount; i++) + { + if (!c->plan->axes_index_map.has (i)) continue; + if (unlikely (!axes_records[i].subset (c))) + return_trace (false); + } + + if (!c->serializer->check_assign (out->firstAxis, get_size (), HB_SERIALIZE_ERROR_INT_OVERFLOW)) + return_trace (false); + + unsigned num_retained_instances = 0; + for (unsigned i = 0 ; i < (unsigned)instanceCount; i++) + { + const InstanceRecord *instance = get_instance (i); + auto snap = c->serializer->snapshot (); + if (!instance->subset (c, axisCount, has_postscript_nameid)) + c->serializer->revert (snap); + else + num_retained_instances++; + } + + return_trace (c->serializer->check_assign (out->instanceCount, num_retained_instances, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + public: + hb_array_t<const AxisRecord> get_axes () const + { return hb_array (&(this+firstAxis), axisCount); } + + const InstanceRecord *get_instance (unsigned int i) const + { + if (unlikely (i >= instanceCount)) return nullptr; + return &StructAtOffset<InstanceRecord> (&StructAfter<InstanceRecord> (get_axes ()), + i * instanceSize); + } + + protected: + FixedVersion<>version; /* Version of the fvar table + * initially set to 0x00010000u */ + Offset16To<AxisRecord> + firstAxis; /* Offset in bytes from the beginning of the table + * to the start of the AxisRecord array. */ + HBUINT16 reserved; /* This field is permanently reserved. Set to 2. */ + HBUINT16 axisCount; /* The number of variation axes in the font (the + * number of records in the axes array). */ + HBUINT16 axisSize; /* The size in bytes of each VariationAxisRecord — + * set to 20 (0x0014) for this version. */ + HBUINT16 instanceCount; /* The number of named instances defined in the font + * (the number of records in the instances array). */ + HBUINT16 instanceSize; /* The size in bytes of each InstanceRecord — set + * to either axisCount * sizeof(F16DOT16) + 4, or to + * axisCount * sizeof(F16DOT16) + 6. */ + + public: + DEFINE_SIZE_STATIC (16); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_FVAR_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh new file mode 100644 index 0000000000..1c7a1f6c1e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -0,0 +1,878 @@ +/* + * Copyright © 2019 Adobe Inc. + * 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_VAR_GVAR_TABLE_HH +#define HB_OT_VAR_GVAR_TABLE_HH + +#include "hb-open-type.hh" +#include "hb-ot-var-common.hh" + +/* + * gvar -- Glyph Variation Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar + */ +#define HB_OT_TAG_gvar HB_TAG('g','v','a','r') + +namespace OT { + +struct GlyphVariationData : TupleVariationData +{}; + +struct glyph_variations_t +{ + using tuple_variations_t = TupleVariationData::tuple_variations_t; + hb_vector_t<tuple_variations_t> glyph_variations; + + hb_vector_t<char> compiled_shared_tuples; + private: + unsigned shared_tuples_count = 0; + + /* shared coords-> index map after instantiation */ + hb_hashmap_t<const hb_vector_t<char>*, unsigned> shared_tuples_idx_map; + + public: + unsigned compiled_shared_tuples_count () const + { return shared_tuples_count; } + + unsigned compiled_byte_size () const + { + unsigned byte_size = 0; + for (const auto& _ : glyph_variations) + byte_size += _.get_compiled_byte_size (); + + return byte_size; + } + + bool create_from_glyphs_var_data (unsigned axis_count, + const hb_array_t<const F2DOT14> shared_tuples, + const hb_subset_plan_t *plan, + const hb_hashmap_t<hb_codepoint_t, hb_bytes_t>& new_gid_var_data_map) + { + if (unlikely (!glyph_variations.alloc (plan->new_to_old_gid_list.length, true))) + return false; + + auto it = hb_iter (plan->new_to_old_gid_list); + for (auto &_ : it) + { + hb_codepoint_t new_gid = _.first; + contour_point_vector_t *all_contour_points; + if (!new_gid_var_data_map.has (new_gid) || + !plan->new_gid_contour_points_map.has (new_gid, &all_contour_points)) + return false; + hb_bytes_t var_data = new_gid_var_data_map.get (new_gid); + + const GlyphVariationData* p = reinterpret_cast<const GlyphVariationData*> (var_data.arrayZ); + hb_vector_t<unsigned> shared_indices; + GlyphVariationData::tuple_iterator_t iterator; + tuple_variations_t tuple_vars; + + /* in case variation data is empty, push an empty struct into the vector, + * keep the vector in sync with the new_to_old_gid_list */ + if (!var_data || ! p->has_data () || !all_contour_points->length || + !GlyphVariationData::get_tuple_iterator (var_data, axis_count, + var_data.arrayZ, + shared_indices, &iterator)) + { + glyph_variations.push (std::move (tuple_vars)); + continue; + } + + if (!p->decompile_tuple_variations (all_contour_points->length, true /* is_gvar */, + iterator, &(plan->axes_old_index_tag_map), + shared_indices, shared_tuples, + tuple_vars /* OUT */)) + return false; + glyph_variations.push (std::move (tuple_vars)); + } + return !glyph_variations.in_error () && glyph_variations.length == plan->new_to_old_gid_list.length; + } + + bool instantiate (const hb_subset_plan_t *plan) + { + unsigned count = plan->new_to_old_gid_list.length; + for (unsigned i = 0; i < count; i++) + { + hb_codepoint_t new_gid = plan->new_to_old_gid_list[i].first; + contour_point_vector_t *all_points; + if (!plan->new_gid_contour_points_map.has (new_gid, &all_points)) + return false; + if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points)) + return false; + } + return true; + } + + bool compile_bytes (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map) + { + if (!compile_shared_tuples (axes_index_map, axes_old_index_tag_map)) + return false; + for (tuple_variations_t& vars: glyph_variations) + if (!vars.compile_bytes (axes_index_map, axes_old_index_tag_map, + true, /* use shared points*/ + &shared_tuples_idx_map)) + return false; + + return true; + } + + bool compile_shared_tuples (const hb_map_t& axes_index_map, + const hb_map_t& axes_old_index_tag_map) + { + /* key is pointer to compiled_peak_coords inside each tuple, hashing + * function will always deref pointers first */ + hb_hashmap_t<const hb_vector_t<char>*, unsigned> coords_count_map; + + /* count the num of shared coords */ + for (tuple_variations_t& vars: glyph_variations) + { + for (tuple_delta_t& var : vars.tuple_vars) + { + if (!var.compile_peak_coords (axes_index_map, axes_old_index_tag_map)) + return false; + unsigned* count; + if (coords_count_map.has (&(var.compiled_peak_coords), &count)) + coords_count_map.set (&(var.compiled_peak_coords), *count + 1); + else + coords_count_map.set (&(var.compiled_peak_coords), 1); + } + } + + if (!coords_count_map || coords_count_map.in_error ()) + return false; + + /* add only those coords that are used more than once into the vector and sort */ + hb_vector_t<const hb_vector_t<char>*> shared_coords; + if (unlikely (!shared_coords.alloc (coords_count_map.get_population ()))) + return false; + + for (const auto _ : coords_count_map.iter ()) + { + if (_.second == 1) continue; + shared_coords.push (_.first); + } + + /* no shared tuples: no coords are used more than once */ + if (!shared_coords) return true; + /* sorting based on the coords frequency first (high to low), then compare + * the coords bytes */ + hb_qsort (shared_coords.arrayZ, shared_coords.length, sizeof (hb_vector_t<char>*), _cmp_coords, (void *) (&coords_count_map)); + + /* build shared_coords->idx map and shared tuples byte array */ + + shared_tuples_count = hb_min (0xFFFu + 1, shared_coords.length); + unsigned len = shared_tuples_count * (shared_coords[0]->length); + if (unlikely (!compiled_shared_tuples.alloc (len))) + return false; + + for (unsigned i = 0; i < shared_tuples_count; i++) + { + shared_tuples_idx_map.set (shared_coords[i], i); + /* add a concat() in hb_vector_t? */ + for (char c : shared_coords[i]->iter ()) + compiled_shared_tuples.push (c); + } + + return true; + } + + static int _cmp_coords (const void *pa, const void *pb, void *arg) + { + const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* coords_count_map = + reinterpret_cast<const hb_hashmap_t<const hb_vector_t<char>*, unsigned>*> (arg); + + /* shared_coords is hb_vector_t<const hb_vector_t<char>*> so casting pa/pb + * to be a pointer to a pointer */ + const hb_vector_t<char>** a = reinterpret_cast<const hb_vector_t<char>**> (const_cast<void*>(pa)); + const hb_vector_t<char>** b = reinterpret_cast<const hb_vector_t<char>**> (const_cast<void*>(pb)); + + bool has_a = coords_count_map->has (*a); + bool has_b = coords_count_map->has (*b); + + if (has_a && has_b) + { + unsigned a_num = coords_count_map->get (*a); + unsigned b_num = coords_count_map->get (*b); + + if (a_num != b_num) + return b_num - a_num; + + return (*b)->as_array().cmp ((*a)->as_array ()); + } + else if (has_a) return -1; + else if (has_b) return 1; + else return 0; + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool serialize_glyph_var_data (hb_serialize_context_t *c, + Iterator it, + bool long_offset, + unsigned num_glyphs, + char* glyph_var_data_offsets /* OUT: glyph var data offsets array */) const + { + TRACE_SERIALIZE (this); + + if (long_offset) + { + ((HBUINT32 *) glyph_var_data_offsets)[0] = 0; + glyph_var_data_offsets += 4; + } + else + { + ((HBUINT16 *) glyph_var_data_offsets)[0] = 0; + glyph_var_data_offsets += 2; + } + unsigned glyph_offset = 0; + hb_codepoint_t last_gid = 0; + unsigned idx = 0; + + TupleVariationData* cur_glyph = c->start_embed<TupleVariationData> (); + if (!cur_glyph) return_trace (false); + for (auto &_ : it) + { + hb_codepoint_t gid = _.first; + if (long_offset) + for (; last_gid < gid; last_gid++) + ((HBUINT32 *) glyph_var_data_offsets)[last_gid] = glyph_offset; + else + for (; last_gid < gid; last_gid++) + ((HBUINT16 *) glyph_var_data_offsets)[last_gid] = glyph_offset / 2; + + if (idx >= glyph_variations.length) return_trace (false); + if (!cur_glyph->serialize (c, true, glyph_variations[idx])) return_trace (false); + TupleVariationData* next_glyph = c->start_embed<TupleVariationData> (); + glyph_offset += (char *) next_glyph - (char *) cur_glyph; + + if (long_offset) + ((HBUINT32 *) glyph_var_data_offsets)[gid] = glyph_offset; + else + ((HBUINT16 *) glyph_var_data_offsets)[gid] = glyph_offset / 2; + + last_gid++; + idx++; + cur_glyph = next_glyph; + } + + if (long_offset) + for (; last_gid < num_glyphs; last_gid++) + ((HBUINT32 *) glyph_var_data_offsets)[last_gid] = glyph_offset; + else + for (; last_gid < num_glyphs; last_gid++) + ((HBUINT16 *) glyph_var_data_offsets)[last_gid] = glyph_offset / 2; + return_trace (true); + } +}; + +struct gvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + (version.major == 1) && + sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) && + (is_long_offset () ? + c->check_array (get_long_offset_array (), c->get_num_glyphs () + 1) : + c->check_array (get_short_offset_array (), c->get_num_glyphs () + 1))); + } + + /* GlyphVariationData not sanitized here; must be checked while accessing each glyph variation data */ + bool sanitize (hb_sanitize_context_t *c) const + { return sanitize_shallow (c); } + + bool decompile_glyph_variations (hb_subset_context_t *c, + glyph_variations_t& glyph_vars /* OUT */) const + { + hb_hashmap_t<hb_codepoint_t, hb_bytes_t> new_gid_var_data_map; + auto it = hb_iter (c->plan->new_to_old_gid_list); + if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + { + new_gid_var_data_map.set (0, hb_bytes_t ()); + it++; + } + + for (auto &_ : it) + { + hb_codepoint_t new_gid = _.first; + hb_codepoint_t old_gid = _.second; + hb_bytes_t var_data_bytes = get_glyph_var_data_bytes (c->source_blob, glyphCountX, old_gid); + new_gid_var_data_map.set (new_gid, var_data_bytes); + } + + if (new_gid_var_data_map.in_error ()) return false; + + hb_array_t<const F2DOT14> shared_tuples = (this+sharedTuples).as_array ((unsigned) sharedTupleCount * (unsigned) axisCount); + return glyph_vars.create_from_glyphs_var_data (axisCount, shared_tuples, c->plan, new_gid_var_data_map); + } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + bool serialize (hb_serialize_context_t *c, + const glyph_variations_t& glyph_vars, + Iterator it, + unsigned axis_count, + unsigned num_glyphs) const + { + TRACE_SERIALIZE (this); + gvar *out = c->allocate_min<gvar> (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + out->axisCount = axis_count; + out->glyphCountX = hb_min (0xFFFFu, num_glyphs); + + unsigned glyph_var_data_size = glyph_vars.compiled_byte_size (); + bool long_offset = glyph_var_data_size & ~0xFFFFu; + out->flags = long_offset ? 1 : 0; + + HBUINT8 *glyph_var_data_offsets = c->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1), false); + if (!glyph_var_data_offsets) return_trace (false); + + /* shared tuples */ + unsigned shared_tuple_count = glyph_vars.compiled_shared_tuples_count (); + out->sharedTupleCount = shared_tuple_count; + + if (!shared_tuple_count) + out->sharedTuples = 0; + else + { + hb_array_t<const char> shared_tuples = glyph_vars.compiled_shared_tuples.as_array ().copy (c); + if (!shared_tuples.arrayZ) return_trace (false); + out->sharedTuples = shared_tuples.arrayZ - (char *) out; + } + + char *glyph_var_data = c->start_embed<char> (); + if (!glyph_var_data) return_trace (false); + out->dataZ = glyph_var_data - (char *) out; + + return_trace (glyph_vars.serialize_glyph_var_data (c, it, long_offset, num_glyphs, + (char *) glyph_var_data_offsets)); + } + + bool instantiate (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + glyph_variations_t glyph_vars; + if (!decompile_glyph_variations (c, glyph_vars)) + return_trace (false); + + if (!glyph_vars.instantiate (c->plan)) return_trace (false); + if (!glyph_vars.compile_bytes (c->plan->axes_index_map, c->plan->axes_old_index_tag_map)) + return_trace (false); + + unsigned axis_count = c->plan->axes_index_map.get_population (); + unsigned num_glyphs = c->plan->num_output_glyphs (); + auto it = hb_iter (c->plan->new_to_old_gid_list); + return_trace (serialize (c->serializer, glyph_vars, it, axis_count, num_glyphs)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (c->plan->all_axes_pinned) + return_trace (false); + + if (c->plan->normalized_coords) + return_trace (instantiate (c)); + + unsigned glyph_count = version.to_int () ? c->plan->source->get_num_glyphs () : 0; + + gvar *out = c->serializer->allocate_min<gvar> (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + out->axisCount = axisCount; + out->sharedTupleCount = sharedTupleCount; + + unsigned int num_glyphs = c->plan->num_output_glyphs (); + out->glyphCountX = hb_min (0xFFFFu, num_glyphs); + + auto it = hb_iter (c->plan->new_to_old_gid_list); + if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + it++; + unsigned int subset_data_size = 0; + for (auto &_ : it) + { + hb_codepoint_t old_gid = _.second; + subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length; + } + + bool long_offset = (subset_data_size & ~0xFFFFu); + #ifdef HB_EXPERIMENTAL_API + long_offset = long_offset || (c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); +#endif + out->flags = long_offset ? 1 : 0; + + HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1), false); + if (!subset_offsets) return_trace (false); + + /* shared tuples */ + if (!sharedTupleCount || !sharedTuples) + out->sharedTuples = 0; + else + { + unsigned int shared_tuple_size = F2DOT14::static_size * axisCount * sharedTupleCount; + F2DOT14 *tuples = c->serializer->allocate_size<F2DOT14> (shared_tuple_size); + if (!tuples) return_trace (false); + out->sharedTuples = (char *) tuples - (char *) out; + hb_memcpy (tuples, this+sharedTuples, shared_tuple_size); + } + + /* This ordering relative to the shared tuples array, which puts the glyphVariationData + last in the table, is required when HB_SUBSET_FLAGS_IFTB_REQUIREMENTS is set */ + char *subset_data = c->serializer->allocate_size<char> (subset_data_size, false); + if (!subset_data) return_trace (false); + out->dataZ = subset_data - (char *) out; + + + if (long_offset) + { + ((HBUINT32 *) subset_offsets)[0] = 0; + subset_offsets += 4; + } + else + { + ((HBUINT16 *) subset_offsets)[0] = 0; + subset_offsets += 2; + } + unsigned int glyph_offset = 0; + + hb_codepoint_t last = 0; + it = hb_iter (c->plan->new_to_old_gid_list); + if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + it++; + for (auto &_ : it) + { + hb_codepoint_t gid = _.first; + hb_codepoint_t old_gid = _.second; + + if (long_offset) + for (; last < gid; last++) + ((HBUINT32 *) subset_offsets)[last] = glyph_offset; + else + for (; last < gid; last++) + ((HBUINT16 *) subset_offsets)[last] = glyph_offset / 2; + + hb_bytes_t var_data_bytes = get_glyph_var_data_bytes (c->source_blob, + glyph_count, + old_gid); + + hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length); + subset_data += var_data_bytes.length; + glyph_offset += var_data_bytes.length; + + if (long_offset) + ((HBUINT32 *) subset_offsets)[gid] = glyph_offset; + else + ((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2; + + last++; // Skip over gid + } + + if (long_offset) + for (; last < num_glyphs; last++) + ((HBUINT32 *) subset_offsets)[last] = glyph_offset; + else + for (; last < num_glyphs; last++) + ((HBUINT16 *) subset_offsets)[last] = glyph_offset / 2; + + return_trace (true); + } + + protected: + const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, + unsigned glyph_count, + hb_codepoint_t glyph) const + { + unsigned start_offset = get_offset (glyph_count, glyph); + unsigned end_offset = get_offset (glyph_count, glyph+1); + if (unlikely (end_offset < start_offset)) return hb_bytes_t (); + unsigned length = end_offset - start_offset; + hb_bytes_t var_data = blob->as_bytes ().sub_array (((unsigned) dataZ) + start_offset, length); + return likely (var_data.length >= GlyphVariationData::min_size) ? var_data : hb_bytes_t (); + } + + bool is_long_offset () const { return flags & 1; } + + unsigned get_offset (unsigned glyph_count, unsigned i) const + { + if (unlikely (i > glyph_count)) return 0; + _hb_compiler_memory_r_barrier (); + return is_long_offset () ? get_long_offset_array ()[i] : get_short_offset_array ()[i] * 2; + } + + const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; } + const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; } + + public: + struct accelerator_t + { + accelerator_t (hb_face_t *face) + { + table = hb_sanitize_context_t ().reference_table<gvar> (face); + /* If sanitize failed, set glyphCount to 0. */ + glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0; + + /* For shared tuples that only have one axis active, shared the index of + * that axis as a cache. This will speed up caclulate_scalar() a lot + * for fonts with lots of axes and many "monovar" tuples. */ + hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount); + unsigned count = table->sharedTupleCount; + if (unlikely (!shared_tuple_active_idx.resize (count, false))) return; + unsigned axis_count = table->axisCount; + for (unsigned i = 0; i < count; i++) + { + hb_array_t<const F2DOT14> tuple = shared_tuples.sub_array (axis_count * i, axis_count); + int idx1 = -1, idx2 = -1; + for (unsigned j = 0; j < axis_count; j++) + { + const F2DOT14 &peak = tuple.arrayZ[j]; + if (peak.to_int () != 0) + { + if (idx1 == -1) + idx1 = j; + else if (idx2 == -1) + idx2 = j; + else + { + idx1 = idx2 = -1; + break; + } + } + } + shared_tuple_active_idx.arrayZ[i] = {idx1, idx2}; + } + } + ~accelerator_t () { table.destroy (); } + + private: + + static float infer_delta (const hb_array_t<contour_point_t> points, + const hb_array_t<contour_point_t> deltas, + unsigned int target, unsigned int prev, unsigned int next, + float contour_point_t::*m) + { + float target_val = points.arrayZ[target].*m; + float prev_val = points.arrayZ[prev].*m; + float next_val = points.arrayZ[next].*m; + float prev_delta = deltas.arrayZ[prev].*m; + float next_delta = deltas.arrayZ[next].*m; + + if (prev_val == next_val) + return (prev_delta == next_delta) ? prev_delta : 0.f; + else if (target_val <= hb_min (prev_val, next_val)) + return (prev_val < next_val) ? prev_delta : next_delta; + else if (target_val >= hb_max (prev_val, next_val)) + return (prev_val > next_val) ? prev_delta : next_delta; + + /* linear interpolation */ + float r = (target_val - prev_val) / (next_val - prev_val); + return prev_delta + r * (next_delta - prev_delta); + } + + static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end) + { return (i >= end) ? start : (i + 1); } + + public: + bool apply_deltas_to_points (hb_codepoint_t glyph, + hb_array_t<int> coords, + const hb_array_t<contour_point_t> points, + bool phantom_only = false) const + { + if (unlikely (glyph >= glyphCount)) return true; + + hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), glyphCount, glyph); + if (!var_data_bytes.as<GlyphVariationData> ()->has_data ()) return true; + hb_vector_t<unsigned int> shared_indices; + GlyphVariationData::tuple_iterator_t iterator; + if (!GlyphVariationData::get_tuple_iterator (var_data_bytes, table->axisCount, + var_data_bytes.arrayZ, + shared_indices, &iterator)) + return true; /* so isn't applied at all */ + + /* Save original points for inferred delta calculation */ + contour_point_vector_t orig_points_vec; // Populated lazily + auto orig_points = orig_points_vec.as_array (); + + /* flag is used to indicate referenced point */ + contour_point_vector_t deltas_vec; // Populated lazily + auto deltas = deltas_vec.as_array (); + + hb_vector_t<unsigned> end_points; // Populated lazily + + unsigned num_coords = table->axisCount; + hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * num_coords); + + hb_vector_t<unsigned int> private_indices; + hb_vector_t<int> x_deltas; + hb_vector_t<int> y_deltas; + unsigned count = points.length; + bool flush = false; + do + { + float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples, + &shared_tuple_active_idx); + if (scalar == 0.f) continue; + const HBUINT8 *p = iterator.get_serialized_data (); + unsigned int length = iterator.current_tuple->get_data_size (); + if (unlikely (!iterator.var_data_bytes.check_range (p, length))) + return false; + + if (!deltas) + { + if (unlikely (!deltas_vec.resize (count, false))) return false; + deltas = deltas_vec.as_array (); + hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0, + (phantom_only ? 4 : count) * sizeof (deltas[0])); + } + + const HBUINT8 *end = p + length; + + bool has_private_points = iterator.current_tuple->has_private_points (); + if (has_private_points && + !GlyphVariationData::unpack_points (p, private_indices, end)) + return false; + const hb_array_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices; + + bool apply_to_all = (indices.length == 0); + unsigned int num_deltas = apply_to_all ? points.length : indices.length; + if (unlikely (!x_deltas.resize (num_deltas, false))) return false; + if (unlikely (!GlyphVariationData::unpack_deltas (p, x_deltas, end))) return false; + if (unlikely (!y_deltas.resize (num_deltas, false))) return false; + if (unlikely (!GlyphVariationData::unpack_deltas (p, y_deltas, end))) return false; + + if (!apply_to_all) + { + if (!orig_points && !phantom_only) + { + orig_points_vec.extend (points); + if (unlikely (orig_points_vec.in_error ())) return false; + orig_points = orig_points_vec.as_array (); + } + + if (flush) + { + for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++) + points.arrayZ[i].translate (deltas.arrayZ[i]); + flush = false; + + } + hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0, + (phantom_only ? 4 : count) * sizeof (deltas[0])); + } + + if (HB_OPTIMIZE_SIZE_VAL) + { + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index; + if (apply_to_all) + pt_index = i; + else + { + pt_index = indices[i]; + if (unlikely (pt_index >= deltas.length)) continue; + } + if (phantom_only && pt_index < count - 4) continue; + auto &delta = deltas.arrayZ[pt_index]; + delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + delta.x += x_deltas.arrayZ[i] * scalar; + delta.y += y_deltas.arrayZ[i] * scalar; + } + } + else + { + /* Ouch. Four cases... for optimization. */ + if (scalar != 1.0f) + { + if (apply_to_all) + for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++) + { + unsigned int pt_index = i; + auto &delta = deltas.arrayZ[pt_index]; + delta.x += x_deltas.arrayZ[i] * scalar; + delta.y += y_deltas.arrayZ[i] * scalar; + } + else + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = indices[i]; + if (unlikely (pt_index >= deltas.length)) continue; + if (phantom_only && pt_index < count - 4) continue; + auto &delta = deltas.arrayZ[pt_index]; + delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + delta.x += x_deltas.arrayZ[i] * scalar; + delta.y += y_deltas.arrayZ[i] * scalar; + } + } + else + { + if (apply_to_all) + for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++) + { + unsigned int pt_index = i; + auto &delta = deltas.arrayZ[pt_index]; + delta.x += x_deltas.arrayZ[i]; + delta.y += y_deltas.arrayZ[i]; + } + else + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = indices[i]; + if (unlikely (pt_index >= deltas.length)) continue; + if (phantom_only && pt_index < count - 4) continue; + auto &delta = deltas.arrayZ[pt_index]; + delta.flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + delta.x += x_deltas.arrayZ[i]; + delta.y += y_deltas.arrayZ[i]; + } + } + } + + /* infer deltas for unreferenced points */ + if (!apply_to_all && !phantom_only) + { + if (!end_points) + { + for (unsigned i = 0; i < count; ++i) + if (points.arrayZ[i].is_end_point) + end_points.push (i); + if (unlikely (end_points.in_error ())) return false; + } + + unsigned start_point = 0; + for (unsigned end_point : end_points) + { + /* Check the number of unreferenced points in a contour. If no unref points or no ref points, nothing to do. */ + unsigned unref_count = 0; + for (unsigned i = start_point; i < end_point + 1; i++) + unref_count += deltas.arrayZ[i].flag; + unref_count = (end_point - start_point + 1) - unref_count; + + unsigned j = start_point; + if (unref_count == 0 || unref_count > end_point - start_point) + goto no_more_gaps; + + for (;;) + { + /* Locate the next gap of unreferenced points between two referenced points prev and next. + * Note that a gap may wrap around at left (start_point) and/or at right (end_point). + */ + unsigned int prev, next, i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (deltas.arrayZ[i].flag && !deltas.arrayZ[j].flag) break; + } + prev = j = i; + for (;;) + { + i = j; + j = next_index (i, start_point, end_point); + if (!deltas.arrayZ[i].flag && deltas.arrayZ[j].flag) break; + } + next = j; + /* Infer deltas for all unref points in the gap between prev and next */ + i = prev; + for (;;) + { + i = next_index (i, start_point, end_point); + if (i == next) break; + deltas.arrayZ[i].x = infer_delta (orig_points, deltas, i, prev, next, &contour_point_t::x); + deltas.arrayZ[i].y = infer_delta (orig_points, deltas, i, prev, next, &contour_point_t::y); + if (--unref_count == 0) goto no_more_gaps; + } + } + no_more_gaps: + start_point = end_point + 1; + } + } + + flush = true; + + } while (iterator.move_to_next ()); + + if (flush) + { + for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++) + points.arrayZ[i].translate (deltas.arrayZ[i]); + } + + return true; + } + + unsigned int get_axis_count () const { return table->axisCount; } + + private: + hb_blob_ptr_t<gvar> table; + unsigned glyphCount; + hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx; + }; + + protected: + FixedVersion<>version; /* Version number of the glyph variations table + * Set to 0x00010000u. */ + HBUINT16 axisCount; /* The number of variation axes for this font. This must be + * the same number as axisCount in the 'fvar' table. */ + HBUINT16 sharedTupleCount; + /* The number of shared tuple records. Shared tuple records + * can be referenced within glyph variation data tables for + * multiple glyphs, as opposed to other tuple records stored + * directly within a glyph variation data table. */ + NNOffset32To<UnsizedArrayOf<F2DOT14>> + sharedTuples; /* Offset from the start of this table to the shared tuple records. + * Array of tuple records shared across all glyph variation data tables. */ + HBUINT16 glyphCountX; /* The number of glyphs in this font. This must match the number of + * glyphs stored elsewhere in the font. */ + HBUINT16 flags; /* Bit-field that gives the format of the offset array that follows. + * If bit 0 is clear, the offsets are uint16; if bit 0 is set, the + * offsets are uint32. */ + Offset32To<GlyphVariationData> + dataZ; /* Offset from the start of this table to the array of + * GlyphVariationData tables. */ + UnsizedArrayOf<HBUINT8> + offsetZ; /* Offsets from the start of the GlyphVariationData array + * to each GlyphVariationData table. */ + public: + DEFINE_SIZE_ARRAY (20, offsetZ); +}; + +struct gvar_accelerator_t : gvar::accelerator_t { + gvar_accelerator_t (hb_face_t *face) : gvar::accelerator_t (face) {} +}; + +} /* namespace OT */ + +#endif /* HB_OT_VAR_GVAR_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh new file mode 100644 index 0000000000..53a4642d38 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var-hvar-table.hh @@ -0,0 +1,478 @@ +/* + * 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_OT_VAR_HVAR_TABLE_HH +#define HB_OT_VAR_HVAR_TABLE_HH + +#include "hb-ot-layout-common.hh" +#include "hb-ot-var-common.hh" + +namespace OT { + + +struct index_map_subset_plan_t +{ + enum index_map_index_t { + ADV_INDEX, + LSB_INDEX, /* dual as TSB */ + RSB_INDEX, /* dual as BSB */ + VORG_INDEX + }; + + void init (const DeltaSetIndexMap &index_map, + hb_inc_bimap_t &outer_map, + hb_vector_t<hb_set_t *> &inner_sets, + const hb_subset_plan_t *plan, + bool bypass_empty = true) + { + map_count = 0; + outer_bit_count = 0; + inner_bit_count = 1; + max_inners.init (); + output_map.init (); + + if (bypass_empty && !index_map.get_map_count ()) return; + + unsigned int last_val = (unsigned int)-1; + hb_codepoint_t last_gid = HB_CODEPOINT_INVALID; + + outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count (); + max_inners.resize (inner_sets.length); + for (unsigned i = 0; i < inner_sets.length; i++) max_inners[i] = 0; + + /* Search backwards for a map value different from the last map value */ + auto &new_to_old_gid_list = plan->new_to_old_gid_list; + unsigned count = new_to_old_gid_list.length; + for (unsigned j = count; j; j--) + { + hb_codepoint_t gid = new_to_old_gid_list.arrayZ[j - 1].first; + hb_codepoint_t old_gid = new_to_old_gid_list.arrayZ[j - 1].second; + + unsigned int v = index_map.map (old_gid); + if (last_gid == HB_CODEPOINT_INVALID) + { + last_val = v; + last_gid = gid; + continue; + } + if (v != last_val) + break; + + last_gid = gid; + } + + if (unlikely (last_gid == (hb_codepoint_t)-1)) return; + map_count = last_gid + 1; + for (auto _ : plan->new_to_old_gid_list) + { + hb_codepoint_t gid = _.first; + if (gid >= map_count) break; + + hb_codepoint_t old_gid = _.second; + unsigned int v = index_map.map (old_gid); + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + outer_map.add (outer); + if (inner > max_inners[outer]) max_inners[outer] = inner; + if (outer >= inner_sets.length) return; + inner_sets[outer]->add (inner); + } + } + + void fini () + { + max_inners.fini (); + output_map.fini (); + } + + void remap (const DeltaSetIndexMap *input_map, + const hb_inc_bimap_t &outer_map, + const hb_vector_t<hb_inc_bimap_t> &inner_maps, + const hb_subset_plan_t *plan) + { + for (unsigned int i = 0; i < max_inners.length; i++) + { + if (inner_maps[i].get_population () == 0) continue; + unsigned int bit_count = (max_inners[i]==0)? 1: hb_bit_storage (inner_maps[i][max_inners[i]]); + if (bit_count > inner_bit_count) inner_bit_count = bit_count; + } + + if (unlikely (!output_map.resize (map_count))) return; + for (const auto &_ : plan->new_to_old_gid_list) + { + hb_codepoint_t new_gid = _.first; + hb_codepoint_t old_gid = _.second; + + if (unlikely (new_gid >= map_count)) break; + + uint32_t v = input_map->map (old_gid); + unsigned int outer = v >> 16; + output_map.arrayZ[new_gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]); + } + } + + bool remap_after_instantiation (const hb_subset_plan_t *plan, + const hb_map_t& varidx_map) + { + /* recalculate bit_count after remapping */ + outer_bit_count = 1; + inner_bit_count = 1; + + for (const auto &_ : plan->new_to_old_gid_list) + { + hb_codepoint_t new_gid = _.first; + if (unlikely (new_gid >= map_count)) break; + + uint32_t v = output_map.arrayZ[new_gid]; + uint32_t *new_varidx; + if (!varidx_map.has (v, &new_varidx)) + return false; + + output_map.arrayZ[new_gid] = *new_varidx; + + unsigned outer = (*new_varidx) >> 16; + unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer); + outer_bit_count = hb_max (bit_count, outer_bit_count); + + unsigned inner = (*new_varidx) & 0xFFFF; + bit_count = (inner == 0) ? 1 : hb_bit_storage (inner); + inner_bit_count = hb_max (bit_count, inner_bit_count); + } + return true; + } + + unsigned int get_inner_bit_count () const { return inner_bit_count; } + unsigned int get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } + unsigned int get_map_count () const { return map_count; } + + unsigned int get_size () const + { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + + bool is_identity () const { return get_output_map ().length == 0; } + hb_array_t<const uint32_t> get_output_map () const { return output_map.as_array (); } + + protected: + unsigned int map_count; + hb_vector_t<unsigned int> max_inners; + unsigned int outer_bit_count; + unsigned int inner_bit_count; + hb_vector_t<uint32_t> output_map; +}; + +struct hvarvvar_subset_plan_t +{ + hvarvvar_subset_plan_t() : inner_maps (), index_map_plans () {} + ~hvarvvar_subset_plan_t() { fini (); } + + void init (const hb_array_t<const DeltaSetIndexMap *> &index_maps, + const VariationStore &_var_store, + const hb_subset_plan_t *plan) + { + index_map_plans.resize (index_maps.length); + + var_store = &_var_store; + inner_sets.resize (var_store->get_sub_table_count ()); + for (unsigned int i = 0; i < inner_sets.length; i++) + inner_sets[i] = hb_set_create (); + adv_set = hb_set_create (); + + inner_maps.resize (var_store->get_sub_table_count ()); + + if (unlikely (!index_map_plans.length || !inner_sets.length || !inner_maps.length)) return; + + bool retain_adv_map = false; + index_map_plans[0].init (*index_maps[0], outer_map, inner_sets, plan, false); + if (index_maps[0] == &Null (DeltaSetIndexMap)) + { + retain_adv_map = plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS; + outer_map.add (0); + for (hb_codepoint_t old_gid : plan->glyphset()->iter()) + inner_sets[0]->add (old_gid); + hb_set_union (adv_set, inner_sets[0]); + } + + for (unsigned int i = 1; i < index_maps.length; i++) + index_map_plans[i].init (*index_maps[i], outer_map, inner_sets, plan); + + outer_map.sort (); + + if (retain_adv_map) + { + for (const auto &_ : plan->new_to_old_gid_list) + { + hb_codepoint_t old_gid = _.second; + inner_maps[0].add (old_gid); + } + } + else + { + inner_maps[0].add_set (adv_set); + hb_set_subtract (inner_sets[0], adv_set); + inner_maps[0].add_set (inner_sets[0]); + } + + for (unsigned int i = 1; i < inner_maps.length; i++) + inner_maps[i].add_set (inner_sets[i]); + + for (unsigned int i = 0; i < index_maps.length; i++) + index_map_plans[i].remap (index_maps[i], outer_map, inner_maps, plan); + } + + /* remap */ + bool remap_index_map_plans (const hb_subset_plan_t *plan, + const hb_map_t& varidx_map) + { + for (unsigned i = 0; i < index_map_plans.length; i++) + if (!index_map_plans[i].remap_after_instantiation (plan, varidx_map)) + return false; + return true; + } + + void fini () + { + for (unsigned int i = 0; i < inner_sets.length; i++) + hb_set_destroy (inner_sets[i]); + hb_set_destroy (adv_set); + inner_maps.fini (); + index_map_plans.fini (); + } + + hb_inc_bimap_t outer_map; + hb_vector_t<hb_inc_bimap_t> inner_maps; + hb_vector_t<index_map_subset_plan_t> index_map_plans; + const VariationStore *var_store; + + protected: + hb_vector_t<hb_set_t *> inner_sets; + hb_set_t *adv_set; +}; + +/* + * HVAR -- Horizontal Metrics Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/hvar + * VVAR -- Vertical Metrics Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/vvar + */ +#define HB_OT_TAG_HVAR HB_TAG('H','V','A','R') +#define HB_OT_TAG_VVAR HB_TAG('V','V','A','R') + +struct HVARVVAR +{ + static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR; + static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + hb_barrier () && + likely (version.major == 1) && + varStore.sanitize (c, this) && + advMap.sanitize (c, this) && + lsbMap.sanitize (c, this) && + rsbMap.sanitize (c, this)); + } + + const VariationStore& get_var_store () const + { return this+varStore; } + + void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const + { + index_maps.push (&(this+advMap)); + index_maps.push (&(this+lsbMap)); + index_maps.push (&(this+rsbMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t<index_map_subset_plan_t> &im_plans) + { + TRACE_SERIALIZE (this); + if (im_plans[index_map_subset_plan_t::ADV_INDEX].is_identity ()) + advMap = 0; + else if (unlikely (!advMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::LSB_INDEX].is_identity ()) + lsbMap = 0; + else if (unlikely (!lsbMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX]))) + return_trace (false); + if (im_plans[index_map_subset_plan_t::RSB_INDEX].is_identity ()) + rsbMap = 0; + else if (unlikely (!rsbMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX]))) + return_trace (false); + + return_trace (true); + } + + template <typename T> + bool _subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + if (c->plan->all_axes_pinned) + return_trace (false); + + hvarvvar_subset_plan_t hvar_plan; + hb_vector_t<const DeltaSetIndexMap *> + index_maps; + + ((T*)this)->listup_index_maps (index_maps); + hvar_plan.init (index_maps.as_array (), this+varStore, c->plan); + + T *out = c->serializer->allocate_min<T> (); + if (unlikely (!out)) return_trace (false); + + out->version.major = 1; + out->version.minor = 0; + + if (c->plan->normalized_coords) + { + item_variations_t item_vars; + if (!item_vars.instantiate (this+varStore, c->plan, + advMap == 0 ? false : true, + false, /* use_no_variation_idx = false */ + hvar_plan.inner_maps.as_array ())) + return_trace (false); + + if (!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 ())) + return_trace (false); + + /* if varstore is optimized, remap output_map */ + if (advMap) + { + if (!hvar_plan.remap_index_map_plans (c->plan, item_vars.get_varidx_map ())) + return_trace (false); + } + } + else + { + if (unlikely (!out->varStore + .serialize_serialize (c->serializer, + hvar_plan.var_store, + hvar_plan.inner_maps.as_array ()))) + return_trace (false); + } + + return_trace (out->T::serialize_index_maps (c->serializer, + hvar_plan.index_map_plans.as_array ())); + } + + float get_advance_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + VariationStore::cache_t *store_cache = nullptr) const + { + uint32_t varidx = (this+advMap).map (glyph); + return (this+varStore).get_delta (varidx, + coords, coord_count, + store_cache); + } + + bool get_lsb_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + float *lsb) const + { + if (!lsbMap) return false; + uint32_t varidx = (this+lsbMap).map (glyph); + *lsb = (this+varStore).get_delta (varidx, coords, coord_count); + return true; + } + + public: + FixedVersion<>version; /* Version of the metrics variation table + * initially set to 0x00010000u */ + Offset32To<VariationStore> + varStore; /* Offset to item variation store table. */ + Offset32To<DeltaSetIndexMap> + advMap; /* Offset to advance var-idx mapping. */ + Offset32To<DeltaSetIndexMap> + lsbMap; /* Offset to lsb/tsb var-idx mapping. */ + Offset32To<DeltaSetIndexMap> + rsbMap; /* Offset to rsb/bsb var-idx mapping. */ + + public: + DEFINE_SIZE_STATIC (20); +}; + +struct HVAR : HVARVVAR { + static constexpr hb_tag_t tableTag = HB_OT_TAG_HVAR; + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset<HVAR> (c); } +}; +struct VVAR : HVARVVAR { + static constexpr hb_tag_t tableTag = HB_OT_TAG_VVAR; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (static_cast<const HVARVVAR *> (this)->sanitize (c) && + vorgMap.sanitize (c, this)); + } + + void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const + { + HVARVVAR::listup_index_maps (index_maps); + index_maps.push (&(this+vorgMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t<index_map_subset_plan_t> &im_plans) + { + TRACE_SERIALIZE (this); + if (unlikely (!HVARVVAR::serialize_index_maps (c, im_plans))) + return_trace (false); + if (!im_plans[index_map_subset_plan_t::VORG_INDEX].get_map_count ()) + vorgMap = 0; + else if (unlikely (!vorgMap.serialize_serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX]))) + return_trace (false); + + return_trace (true); + } + + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset<VVAR> (c); } + + bool get_vorg_delta_unscaled (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + float *delta) const + { + if (!vorgMap) return false; + uint32_t varidx = (this+vorgMap).map (glyph); + *delta = (this+varStore).get_delta (varidx, coords, coord_count); + return true; + } + + protected: + Offset32To<DeltaSetIndexMap> + vorgMap; /* Offset to vertical-origin var-idx mapping. */ + + public: + DEFINE_SIZE_STATIC (24); +}; + +} /* namespace OT */ + + +#endif /* HB_OT_VAR_HVAR_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh new file mode 100644 index 0000000000..6d69777618 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var-mvar-table.hh @@ -0,0 +1,184 @@ +/* + * 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_OT_VAR_MVAR_TABLE_HH +#define HB_OT_VAR_MVAR_TABLE_HH + +#include "hb-ot-var-common.hh" + + +namespace OT { + + +struct VariationValueRecord +{ + 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 hb_map_t& varidx_map) const + { + TRACE_SUBSET (this); + auto *out = c->serializer->embed (*this); + if (unlikely (!out)) return_trace (false); + + hb_codepoint_t *new_idx; + return_trace (c->serializer->check_assign (out->varIdx, + (varidx_map.has (varIdx, &new_idx)) ? *new_idx : HB_OT_LAYOUT_NO_VARIATIONS_INDEX, + HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + public: + Tag valueTag; /* Four-byte tag identifying a font-wide measure. */ + VarIdx varIdx; /* Outer/inner index into VariationStore item. */ + + public: + DEFINE_SIZE_STATIC (8); +}; + + +/* + * MVAR -- Metrics Variations + * https://docs.microsoft.com/en-us/typography/opentype/spec/mvar + */ +#define HB_OT_TAG_MVAR HB_TAG('M','V','A','R') + +struct MVAR +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_MVAR; + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (version.sanitize (c) && + hb_barrier () && + likely (version.major == 1) && + c->check_struct (this) && + hb_barrier () && + valueRecordSize >= VariationValueRecord::static_size && + varStore.sanitize (c, this) && + c->check_range (valuesZ.arrayZ, + valueRecordCount, + valueRecordSize)); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); +#ifdef HB_NO_VAR + return_trace (false); +#endif + + if (c->plan->all_axes_pinned) + return_trace (false); + + MVAR *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->version = version; + out->reserved = reserved; + out->valueRecordSize = valueRecordSize; + out->valueRecordCount = valueRecordCount; + + item_variations_t item_vars; + const VariationStore& src_var_store = this+varStore; + + if (!item_vars.instantiate (src_var_store, c->plan)) + return_trace (false); + + /* serialize varstore */ + if (!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 ())) + return_trace (false); + + /* serialize value records array */ + unsigned value_rec_count = valueRecordCount; + const VariationValueRecord *record = reinterpret_cast<const VariationValueRecord*> (valuesZ.arrayZ); + for (unsigned i = 0; i < value_rec_count; i++) + { + if (!record->subset (c, item_vars.get_varidx_map ())) return_trace (false); + record++; + } + return_trace (true); + } + + float get_var (hb_tag_t tag, + const int *coords, unsigned int coord_count) const + { + const VariationValueRecord *record; + record = (VariationValueRecord *) hb_bsearch (tag, + (const VariationValueRecord *) + (const HBUINT8 *) valuesZ, + valueRecordCount, valueRecordSize, + tag_compare); + if (!record) + return 0.; + + return (this+varStore).get_delta (record->varIdx, coords, coord_count); + } + +protected: + static int tag_compare (const void *pa, const void *pb) + { + const hb_tag_t *a = (const hb_tag_t *) pa; + const Tag *b = (const Tag *) pb; + return b->cmp (*a); + } + + protected: + FixedVersion<>version; /* Version of the metrics variation table + * initially set to 0x00010000u */ + HBUINT16 reserved; /* Not used; set to 0. */ + HBUINT16 valueRecordSize;/* The size in bytes of each value record — + * must be greater than zero. */ + HBUINT16 valueRecordCount;/* The number of value records — may be zero. */ + Offset16To<VariationStore> + varStore; /* Offset to item variation store table. */ + UnsizedArrayOf<HBUINT8> + valuesZ; /* Array of value records. The records must be + * in binary order of their valueTag field. */ + + public: + DEFINE_SIZE_ARRAY (12, valuesZ); +}; + +} /* namespace OT */ + + +#define HB_ADD_MVAR_VAR(tag, field) \ + c->serializer->check_assign (table->field, \ + roundf (table->field + \ + MVAR.get_var (tag, \ + c->plan->normalized_coords.arrayZ, \ + c->plan->normalized_coords.length)), \ + HB_SERIALIZE_ERROR_INT_OVERFLOW) + + +#endif /* HB_OT_VAR_MVAR_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot-var.cc b/gfx/harfbuzz/src/hb-ot-var.cc new file mode 100644 index 0000000000..f000f27263 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var.cc @@ -0,0 +1,328 @@ +/* + * 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 + */ + +#include "hb.hh" + +#ifndef HB_NO_VAR + +#include "hb-ot-var.h" + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-var-mvar-table.hh" + + +/** + * SECTION:hb-ot-var + * @title: hb-ot-var + * @short_description: OpenType Font Variations + * @include: hb-ot.h + * + * Functions for fetching information about OpenType Variable Fonts. + **/ + + +/* + * fvar/avar + */ + + +/** + * hb_ot_var_has_data: + * @face: The #hb_face_t to work on + * + * Tests whether a face includes any OpenType variation data in the `fvar` table. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 1.4.2 + **/ +hb_bool_t +hb_ot_var_has_data (hb_face_t *face) +{ + return face->table.fvar->has_data (); +} + +/** + * hb_ot_var_get_axis_count: + * @face: The #hb_face_t to work on + * + * Fetches the number of OpenType variation axes included in the face. + * + * Return value: the number of variation axes defined + * + * Since: 1.4.2 + **/ +unsigned int +hb_ot_var_get_axis_count (hb_face_t *face) +{ + return face->table.fvar->get_axis_count (); +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_ot_var_get_axes: + * @face: #hb_face_t to work upon + * @start_offset: offset of the first lookup to retrieve + * @axes_count: (inout) (optional): Input = the maximum number of variation axes to return; + * Output = the actual number of variation axes returned (may be zero) + * @axes_array: (out caller-allocates) (array length=axes_count): The array of variation axes found + * + * Fetches a list of all variation axes in the specified face. The list returned will begin + * at the offset provided. + * + * Since: 1.4.2 + * Deprecated: 2.2.0: use hb_ot_var_get_axis_infos() instead + **/ +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 */) +{ + return face->table.fvar->get_axes_deprecated (start_offset, axes_count, axes_array); +} + +/** + * hb_ot_var_find_axis: + * @face: #hb_face_t to work upon + * @axis_tag: The #hb_tag_t of the variation axis to query + * @axis_index: The index of the variation axis + * @axis_info: (out): The #hb_ot_var_axis_info_t of the axis tag queried + * + * Fetches the variation-axis information corresponding to the specified axis tag + * in the specified face. + * + * Since: 1.4.2 + * Deprecated: 2.2.0 - use hb_ot_var_find_axis_info() instead + **/ +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) +{ + return face->table.fvar->find_axis_deprecated (axis_tag, axis_index, axis_info); +} +#endif + +/** + * hb_ot_var_get_axis_infos: + * @face: #hb_face_t to work upon + * @start_offset: offset of the first lookup to retrieve + * @axes_count: (inout) (optional): Input = the maximum number of variation axes to return; + * Output = the actual number of variation axes returned (may be zero) + * @axes_array: (out caller-allocates) (array length=axes_count): The array of variation axes found + * + * Fetches a list of all variation axes in the specified face. The list returned will begin + * at the offset provided. + * + * Return value: the number of variation axes in the face + * + * Since: 2.2.0 + **/ +HB_EXTERN unsigned int +hb_ot_var_get_axis_infos (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_info_t *axes_array /* OUT */) +{ + return face->table.fvar->get_axis_infos (start_offset, axes_count, axes_array); +} + +/** + * hb_ot_var_find_axis_info: + * @face: #hb_face_t to work upon + * @axis_tag: The #hb_tag_t of the variation axis to query + * @axis_info: (out): The #hb_ot_var_axis_info_t of the axis tag queried + * + * Fetches the variation-axis information corresponding to the specified axis tag + * in the specified face. + * + * Return value: `true` if data found, `false` otherwise + * + * Since: 2.2.0 + **/ +HB_EXTERN hb_bool_t +hb_ot_var_find_axis_info (hb_face_t *face, + hb_tag_t axis_tag, + hb_ot_var_axis_info_t *axis_info) +{ + return face->table.fvar->find_axis_info (axis_tag, axis_info); +} + + +/* + * Named instances. + */ + +/** + * hb_ot_var_get_named_instance_count: + * @face: The #hb_face_t to work on + * + * Fetches the number of named instances included in the face. + * + * Return value: the number of named instances defined + * + * Since: 2.2.0 + **/ +unsigned int +hb_ot_var_get_named_instance_count (hb_face_t *face) +{ + return face->table.fvar->get_instance_count (); +} + +/** + * hb_ot_var_named_instance_get_subfamily_name_id: + * @face: The #hb_face_t to work on + * @instance_index: The index of the named instance to query + * + * Fetches the `name` table Name ID that provides display names for + * the "Subfamily name" defined for the given named instance in the face. + * + * Return value: the Name ID found for the Subfamily name + * + * Since: 2.2.0 + **/ +hb_ot_name_id_t +hb_ot_var_named_instance_get_subfamily_name_id (hb_face_t *face, + unsigned int instance_index) +{ + return face->table.fvar->get_instance_subfamily_name_id (instance_index); +} + +/** + * hb_ot_var_named_instance_get_postscript_name_id: + * @face: The #hb_face_t to work on + * @instance_index: The index of the named instance to query + * + * Fetches the `name` table Name ID that provides display names for + * the "PostScript name" defined for the given named instance in the face. + * + * Return value: the Name ID found for the PostScript name + * + * Since: 2.2.0 + **/ +hb_ot_name_id_t +hb_ot_var_named_instance_get_postscript_name_id (hb_face_t *face, + unsigned int instance_index) +{ + return face->table.fvar->get_instance_postscript_name_id (instance_index); +} + +/** + * hb_ot_var_named_instance_get_design_coords: + * @face: The #hb_face_t to work on + * @instance_index: The index of the named instance to query + * @coords_length: (inout) (optional): Input = the maximum number of coordinates to return; + * Output = the actual number of coordinates returned (may be zero) + * @coords: (out) (array length=coords_length): The array of coordinates found for the query + * + * Fetches the design-space coordinates corresponding to the given + * named instance in the face. + * + * Return value: the number of variation axes in the face + * + * Since: 2.2.0 + **/ +unsigned int +hb_ot_var_named_instance_get_design_coords (hb_face_t *face, + unsigned int instance_index, + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */) +{ + return face->table.fvar->get_instance_coords (instance_index, coords_length, coords); +} + + +/** + * hb_ot_var_normalize_variations: + * @face: The #hb_face_t to work on + * @variations: The array of variations to normalize + * @variations_length: The number of variations to normalize + * @coords: (out) (array length=coords_length): The array of normalized coordinates + * @coords_length: The length of the coordinate array + * + * Normalizes all of the coordinates in the given list of variation axes. + * + * Since: 1.4.2 + **/ +void +hb_ot_var_normalize_variations (hb_face_t *face, + const hb_variation_t *variations, /* IN */ + unsigned int variations_length, + int *coords, /* OUT */ + unsigned int coords_length) +{ + for (unsigned int i = 0; i < coords_length; i++) + coords[i] = 0; + + const OT::fvar &fvar = *face->table.fvar; + for (unsigned int i = 0; i < variations_length; i++) + { + hb_ot_var_axis_info_t info; + if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) && + info.axis_index < coords_length) + coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value); + } + + face->table.avar->map_coords (coords, coords_length); +} + +/** + * hb_ot_var_normalize_coords: + * @face: The #hb_face_t to work on + * @coords_length: The length of the coordinate array + * @design_coords: The design-space coordinates to normalize + * @normalized_coords: (out): The normalized coordinates + * + * Normalizes the given design-space coordinates. The minimum and maximum + * values for the axis are mapped to the interval [-1,1], with the default + * axis value mapped to 0. + * + * The normalized values have 14 bits of fixed-point sub-integer precision as per + * OpenType specification. + * + * Any additional scaling defined in the face's `avar` table is also + * applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar + * + * Since: 1.4.2 + **/ +void +hb_ot_var_normalize_coords (hb_face_t *face, + unsigned int coords_length, + const float *design_coords, /* IN */ + int *normalized_coords /* OUT */) +{ + const OT::fvar &fvar = *face->table.fvar; + for (unsigned int i = 0; i < coords_length; i++) + normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]); + + face->table.avar->map_coords (normalized_coords, coords_length); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-ot-var.h b/gfx/harfbuzz/src/hb-ot-var.h new file mode 100644 index 0000000000..05147cc25e --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-var.h @@ -0,0 +1,191 @@ +/* + * 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#if !defined(HB_OT_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb-ot.h> instead." +#endif + +#ifndef HB_OT_VAR_H +#define HB_OT_VAR_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * HB_OT_TAG_VAR_AXIS_ITALIC: + * + * Registered tag for the roman/italic axis. + */ +#define HB_OT_TAG_VAR_AXIS_ITALIC HB_TAG('i','t','a','l') + +/** + * HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE: + * + * Registered tag for the optical-size axis. + * <note>Note: The optical-size axis supersedes the OpenType `size` feature.</note> + */ +#define HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE HB_TAG('o','p','s','z') + +/** + * HB_OT_TAG_VAR_AXIS_SLANT: + * + * Registered tag for the slant axis + */ +#define HB_OT_TAG_VAR_AXIS_SLANT HB_TAG('s','l','n','t') + +/** + * HB_OT_TAG_VAR_AXIS_WIDTH: + * + * Registered tag for the width axis. + */ +#define HB_OT_TAG_VAR_AXIS_WIDTH HB_TAG('w','d','t','h') + +/** + * HB_OT_TAG_VAR_AXIS_WEIGHT: + * + * Registered tag for the weight axis. + */ +#define HB_OT_TAG_VAR_AXIS_WEIGHT HB_TAG('w','g','h','t') + + +/* + * fvar / avar + */ + +HB_EXTERN hb_bool_t +hb_ot_var_has_data (hb_face_t *face); + + +/* + * Variation axes. + */ + + +HB_EXTERN unsigned int +hb_ot_var_get_axis_count (hb_face_t *face); + +/** + * hb_ot_var_axis_flags_t: + * @HB_OT_VAR_AXIS_FLAG_HIDDEN: The axis should not be exposed directly in user interfaces. + * + * Flags for #hb_ot_var_axis_info_t. + * + * Since: 2.2.0 + */ +typedef enum { /*< flags >*/ + HB_OT_VAR_AXIS_FLAG_HIDDEN = 0x00000001u, + + /*< private >*/ + _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_ot_var_axis_flags_t; + +/** + * hb_ot_var_axis_info_t: + * @axis_index: Index of the axis in the variation-axis array + * @tag: The #hb_tag_t tag identifying the design variation of the axis + * @name_id: The `name` table Name ID that provides display names for the axis + * @flags: The #hb_ot_var_axis_flags_t flags for the axis + * @min_value: The minimum value on the variation axis that the font covers + * @default_value: The position on the variation axis corresponding to the font's defaults + * @max_value: The maximum value on the variation axis that the font covers + * + * Data type for holding variation-axis values. + * + * The minimum, default, and maximum values are in un-normalized, user scales. + * + * <note>Note: at present, the only flag defined for @flags is + * #HB_OT_VAR_AXIS_FLAG_HIDDEN.</note> + * + * Since: 2.2.0 + */ +typedef struct hb_ot_var_axis_info_t { + unsigned int axis_index; + hb_tag_t tag; + hb_ot_name_id_t name_id; + hb_ot_var_axis_flags_t flags; + float min_value; + float default_value; + float max_value; + /*< private >*/ + unsigned int reserved; +} hb_ot_var_axis_info_t; + +HB_EXTERN unsigned int +hb_ot_var_get_axis_infos (hb_face_t *face, + unsigned int start_offset, + unsigned int *axes_count /* IN/OUT */, + hb_ot_var_axis_info_t *axes_array /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_var_find_axis_info (hb_face_t *face, + hb_tag_t axis_tag, + hb_ot_var_axis_info_t *axis_info); + + +/* + * Named instances. + */ + +HB_EXTERN unsigned int +hb_ot_var_get_named_instance_count (hb_face_t *face); + +HB_EXTERN hb_ot_name_id_t +hb_ot_var_named_instance_get_subfamily_name_id (hb_face_t *face, + unsigned int instance_index); + +HB_EXTERN hb_ot_name_id_t +hb_ot_var_named_instance_get_postscript_name_id (hb_face_t *face, + unsigned int instance_index); + +HB_EXTERN unsigned int +hb_ot_var_named_instance_get_design_coords (hb_face_t *face, + unsigned int instance_index, + unsigned int *coords_length, /* IN/OUT */ + float *coords /* OUT */); + + +/* + * Conversions. + */ + +HB_EXTERN void +hb_ot_var_normalize_variations (hb_face_t *face, + const hb_variation_t *variations, /* IN */ + unsigned int variations_length, + int *coords, /* OUT */ + unsigned int coords_length); + +HB_EXTERN void +hb_ot_var_normalize_coords (hb_face_t *face, + unsigned int coords_length, + const float *design_coords, /* IN */ + int *normalized_coords /* OUT */); + + +HB_END_DECLS + +#endif /* HB_OT_VAR_H */ diff --git a/gfx/harfbuzz/src/hb-ot-vorg-table.hh b/gfx/harfbuzz/src/hb-ot-vorg-table.hh new file mode 100644 index 0000000000..95ae8ef559 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot-vorg-table.hh @@ -0,0 +1,137 @@ +/* + * 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_VORG_TABLE_HH +#define HB_OT_VORG_TABLE_HH + +#include "hb-open-type.hh" + +/* + * VORG -- Vertical Origin Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/vorg + */ +#define HB_OT_TAG_VORG HB_TAG('V','O','R','G') + +namespace OT { + +struct VertOriginMetric +{ + 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)); + } + + public: + HBGlyphID16 glyph; + FWORD vertOriginY; + + public: + DEFINE_SIZE_STATIC (4); +}; + +struct VORG +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_VORG; + + bool has_data () const { return version.to_int (); } + + int get_y_origin (hb_codepoint_t glyph) const + { + unsigned int i; + if (!vertYOrigins.bfind (glyph, &i)) + return defaultVertOriginY; + return vertYOrigins[i].vertOriginY; + } + + template <typename Iterator, + hb_requires (hb_is_iterator (Iterator))> + void serialize (hb_serialize_context_t *c, + Iterator it, + FWORD defaultVertOriginY) + { + + if (unlikely (!c->extend_min ((*this)))) return; + + this->version.major = 1; + this->version.minor = 0; + + this->defaultVertOriginY = defaultVertOriginY; + this->vertYOrigins.len = it.len (); + + c->copy_all (it); + } + + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + auto *vorg_prime = c->serializer->start_embed<VORG> (); + if (unlikely (!c->serializer->check_success (vorg_prime))) return_trace (false); + + auto it = + + vertYOrigins.as_array () + | hb_filter (c->plan->glyphset (), &VertOriginMetric::glyph) + | hb_map ([&] (const VertOriginMetric& _) + { + hb_codepoint_t new_glyph = HB_SET_VALUE_INVALID; + c->plan->new_gid_for_old_gid (_.glyph, &new_glyph); + + VertOriginMetric metric; + metric.glyph = new_glyph; + metric.vertOriginY = _.vertOriginY; + return metric; + }) + ; + + /* serialize the new table */ + vorg_prime->serialize (c->serializer, it, defaultVertOriginY); + return_trace (true); + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + hb_barrier () && + version.major == 1 && + vertYOrigins.sanitize (c)); + } + + protected: + FixedVersion<>version; /* Version of VORG table. Set to 0x00010000u. */ + FWORD defaultVertOriginY; + /* The default vertical origin. */ + SortedArray16Of<VertOriginMetric> + vertYOrigins; /* The array of vertical origins. */ + + public: + DEFINE_SIZE_ARRAY(8, vertYOrigins); +}; +} /* namespace OT */ + +#endif /* HB_OT_VORG_TABLE_HH */ diff --git a/gfx/harfbuzz/src/hb-ot.h b/gfx/harfbuzz/src/hb-ot.h new file mode 100644 index 0000000000..f2dbaa1b31 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ot.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2009 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): Behdad Esfahbod + */ + +#ifndef HB_OT_H +#define HB_OT_H +#define HB_OT_H_IN + +#include "hb.h" + +#include "hb-ot-color.h" +#include "hb-ot-deprecated.h" +#include "hb-ot-font.h" +#include "hb-ot-layout.h" +#include "hb-ot-math.h" +#include "hb-ot-meta.h" +#include "hb-ot-metrics.h" +#include "hb-ot-name.h" +#include "hb-ot-shape.h" +#include "hb-ot-var.h" + +HB_BEGIN_DECLS + +HB_END_DECLS + +#undef HB_OT_H_IN +#endif /* HB_OT_H */ diff --git a/gfx/harfbuzz/src/hb-outline.cc b/gfx/harfbuzz/src/hb-outline.cc new file mode 100644 index 0000000000..29b1f530d5 --- /dev/null +++ b/gfx/harfbuzz/src/hb-outline.cc @@ -0,0 +1,321 @@ +/* + * Copyright © 2023 Behdad Esfahbod + * Copyright © 1999 David Turner + * Copyright © 2005 Werner Lemberg + * Copyright © 2013-2015 Alexei Podtelezhnikov + * + * 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_OUTLINE + +#include "hb-outline.hh" + +#include "hb-machinery.hh" + + +void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + + unsigned first = 0; + for (unsigned contour : contours) + { + auto it = points.as_array ().sub_array (first, contour - first); + while (it) + { + hb_outline_point_t p1 = *it++; + switch (p1.type) + { + case hb_outline_point_t::type_t::MOVE_TO: + { + pen->move_to (pen_data, st, + p1.x, p1.y); + } + break; + case hb_outline_point_t::type_t::LINE_TO: + { + pen->line_to (pen_data, st, + p1.x, p1.y); + } + break; + case hb_outline_point_t::type_t::QUADRATIC_TO: + { + hb_outline_point_t p2 = *it++; + pen->quadratic_to (pen_data, st, + p1.x, p1.y, + p2.x, p2.y); + } + break; + case hb_outline_point_t::type_t::CUBIC_TO: + { + hb_outline_point_t p2 = *it++; + hb_outline_point_t p3 = *it++; + pen->cubic_to (pen_data, st, + p1.x, p1.y, + p2.x, p2.y, + p3.x, p3.y); + } + break; + } + } + pen->close_path (pen_data, st); + first = contour; + } +} + +float hb_outline_t::control_area () const +{ + float a = 0; + unsigned first = 0; + for (unsigned contour : contours) + { + for (unsigned i = first; i < contour; i++) + { + unsigned j = i + 1 < contour ? i + 1 : first; + + auto &pi = points[i]; + auto &pj = points[j]; + a += pi.x * pj.y - pi.y * pj.x; + } + + first = contour; + } + return a * .5f; +} + +void hb_outline_t::embolden (float x_strength, float y_strength, + float x_shift, float y_shift) +{ + /* This function is a straight port of FreeType's FT_Outline_EmboldenXY. + * Permission has been obtained from the FreeType authors of the code + * to relicense it under the HarfBuzz license. */ + + if (!x_strength && !y_strength) return; + if (!points) return; + + x_strength /= 2.f; + y_strength /= 2.f; + + bool orientation_negative = control_area () < 0; + + signed first = 0; + for (unsigned c = 0; c < contours.length; c++) + { + hb_outline_vector_t in, out, anchor, shift; + float l_in, l_out, l_anchor = 0, l, q, d; + + l_in = 0; + signed last = (int) contours[c] - 1; + + /* pacify compiler */ + in.x = in.y = anchor.x = anchor.y = 0; + + /* Counter j cycles though the points; counter i advances only */ + /* when points are moved; anchor k marks the first moved point. */ + for ( signed i = last, j = first, k = -1; + j != i && i != k; + j = j < last ? j + 1 : first ) + { + if ( j != k ) + { + out.x = points[j].x - points[i].x; + out.y = points[j].y - points[i].y; + l_out = out.normalize_len (); + + if ( l_out == 0 ) + continue; + } + else + { + out = anchor; + l_out = l_anchor; + } + + if ( l_in != 0 ) + { + if ( k < 0 ) + { + k = i; + anchor = in; + l_anchor = l_in; + } + + d = in.x * out.x + in.y * out.y; + + /* shift only if turn is less than ~160 degrees */ + if ( d > -15.f/16.f ) + { + d = d + 1.f; + + /* shift components along lateral bisector in proper orientation */ + shift.x = in.y + out.y; + shift.y = in.x + out.x; + + if ( orientation_negative ) + shift.x = -shift.x; + else + shift.y = -shift.y; + + /* restrict shift magnitude to better handle collapsing segments */ + q = out.x * in.y - out.y * in.x; + if ( orientation_negative ) + q = -q; + + l = hb_min (l_in, l_out); + + /* non-strict inequalities avoid divide-by-zero when q == l == 0 */ + if (x_strength * q <= l * d) + shift.x = shift.x * x_strength / d; + else + shift.x = shift.x * l / q; + + + if (y_strength * q <= l * d) + shift.y = shift.y * y_strength / d; + else + shift.y = shift.y * l / q; + } + else + shift.x = shift.y = 0; + + for ( ; + i != j; + i = i < last ? i + 1 : first ) + { + points[i].x += x_shift + shift.x; + points[i].y += y_shift + shift.y; + } + } + else + i = j; + + in = out; + l_in = l_out; + } + + first = last + 1; + } +} + +static void +hb_outline_recording_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_outline_t *c = (hb_outline_t *) data; + + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::MOVE_TO}); +} + +static void +hb_outline_recording_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_outline_t *c = (hb_outline_t *) data; + + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::LINE_TO}); +} + +static void +hb_outline_recording_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_outline_t *c = (hb_outline_t *) data; + + c->points.push (hb_outline_point_t {control_x, control_y, hb_outline_point_t::type_t::QUADRATIC_TO}); + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::QUADRATIC_TO}); +} + +static void +hb_outline_recording_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *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_outline_t *c = (hb_outline_t *) data; + + c->points.push (hb_outline_point_t {control1_x, control1_y, hb_outline_point_t::type_t::CUBIC_TO}); + c->points.push (hb_outline_point_t {control2_x, control2_y, hb_outline_point_t::type_t::CUBIC_TO}); + c->points.push (hb_outline_point_t {to_x, to_y, hb_outline_point_t::type_t::CUBIC_TO}); +} + +static void +hb_outline_recording_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + void *user_data HB_UNUSED) +{ + hb_outline_t *c = (hb_outline_t *) data; + + c->contours.push (c->points.length); +} + +static inline void free_static_outline_recording_pen_funcs (); + +static struct hb_outline_recording_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_outline_recording_pen_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_outline_recording_pen_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_outline_recording_pen_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_outline_recording_pen_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_outline_recording_pen_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_outline_recording_pen_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_outline_recording_pen_funcs); + + return funcs; + } +} static_outline_recording_pen_funcs; + +static inline +void free_static_outline_recording_pen_funcs () +{ + static_outline_recording_pen_funcs.free_instance (); +} + +hb_draw_funcs_t * +hb_outline_recording_pen_get_funcs () +{ + return static_outline_recording_pen_funcs.get_unconst (); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-outline.hh b/gfx/harfbuzz/src/hb-outline.hh new file mode 100644 index 0000000000..c43c06596b --- /dev/null +++ b/gfx/harfbuzz/src/hb-outline.hh @@ -0,0 +1,83 @@ +/* + * Copyright © 2023 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_OUTLINE_HH +#define HB_OUTLINE_HH + +#include "hb.hh" + +#include "hb-draw.hh" + + +struct hb_outline_point_t +{ + enum class type_t + { + MOVE_TO, + LINE_TO, + QUADRATIC_TO, + CUBIC_TO, + }; + + hb_outline_point_t (float x, float y, type_t type) : + x (x), y (y), type (type) {} + + float x, y; + type_t type; +}; + +struct hb_outline_vector_t +{ + float normalize_len () + { + float len = hypotf (x, y); + if (len) + { + x /= len; + y /= len; + } + return len; + } + + float x, y; +}; + +struct hb_outline_t +{ + void reset () { points.shrink (0, false); contours.resize (0); } + + HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; + HB_INTERNAL float control_area () const; + HB_INTERNAL void embolden (float x_strength, float y_strength, + float x_shift, float y_shift); + + hb_vector_t<hb_outline_point_t> points; + hb_vector_t<unsigned> contours; +}; + +HB_INTERNAL hb_draw_funcs_t * +hb_outline_recording_pen_get_funcs (); + + +#endif /* HB_OUTLINE_HH */ diff --git a/gfx/harfbuzz/src/hb-paint-extents.cc b/gfx/harfbuzz/src/hb-paint-extents.cc new file mode 100644 index 0000000000..2393322b71 --- /dev/null +++ b/gfx/harfbuzz/src/hb-paint-extents.cc @@ -0,0 +1,330 @@ +/* + * 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. + */ + +#include "hb.hh" + +#ifndef HB_NO_PAINT + +#include "hb-paint-extents.hh" + +#include "hb-draw.h" + +#include "hb-machinery.hh" + + +/* + * This file implements bounds-extraction as well as boundedness + * computation of COLRv1 fonts as described in: + * + * https://learn.microsoft.com/en-us/typography/opentype/spec/colr#glyph-metrics-and-boundedness + */ + +static void +hb_paint_extents_push_transform (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->push_transform (hb_transform_t {xx, yx, xy, yy, dx, dy}); +} + +static void +hb_paint_extents_pop_transform (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->pop_transform (); +} + +static void +hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (control_x, control_y); + extents->add_point (to_x, to_y); +} + +static void +hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *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_extents_t *extents = (hb_extents_t *) data; + + extents->add_point (control1_x, control1_y); + extents->add_point (control2_x, control2_y); + extents->add_point (to_x, to_y); +} + +static inline void free_static_draw_extents_funcs (); + +static struct hb_draw_extents_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_draw_extents_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_draw_extents_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_draw_extents_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_draw_extents_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_draw_extents_cubic_to, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_draw_extents_funcs); + + return funcs; + } +} static_draw_extents_funcs; + +static inline +void free_static_draw_extents_funcs () +{ + static_draw_extents_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_draw_extents_get_funcs () +{ + return static_draw_extents_funcs.get_unconst (); +} + +static void +hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + hb_extents_t extents; + hb_draw_funcs_t *draw_extent_funcs = hb_draw_extents_get_funcs (); + hb_font_draw_glyph (font, glyph, draw_extent_funcs, &extents); + c->push_clip (extents); +} + +static void +hb_paint_extents_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + hb_extents_t extents = {xmin, ymin, xmax, ymax}; + c->push_clip (extents); +} + +static void +hb_paint_extents_pop_clip (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->pop_clip (); +} + +static void +hb_paint_extents_push_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->push_group (); +} + +static void +hb_paint_extents_pop_group (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->pop_group (mode); +} + +static hb_bool_t +hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_blob_t *blob HB_UNUSED, + unsigned int width HB_UNUSED, + unsigned int height HB_UNUSED, + hb_tag_t format HB_UNUSED, + float slant HB_UNUSED, + hb_glyph_extents_t *glyph_extents, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + hb_extents_t extents = {(float) glyph_extents->x_bearing, + (float) glyph_extents->y_bearing + glyph_extents->height, + (float) glyph_extents->x_bearing + glyph_extents->width, + (float) glyph_extents->y_bearing}; + c->push_clip (extents); + c->paint (); + c->pop_clip (); + + return true; +} + +static void +hb_paint_extents_paint_color (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_bool_t use_foreground HB_UNUSED, + hb_color_t color HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_extents_paint_linear_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, + float x2 HB_UNUSED, float y2 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_extents_paint_radial_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float x0 HB_UNUSED, float y0 HB_UNUSED, float r0 HB_UNUSED, + float x1 HB_UNUSED, float y1 HB_UNUSED, float r1 HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static void +hb_paint_extents_paint_sweep_gradient (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line HB_UNUSED, + float cx HB_UNUSED, float cy HB_UNUSED, + float start_angle HB_UNUSED, + float end_angle HB_UNUSED, + void *user_data HB_UNUSED) +{ + hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data; + + c->paint (); +} + +static inline void free_static_paint_extents_funcs (); + +static struct hb_paint_extents_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t<hb_paint_extents_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_paint_extents_push_transform, nullptr, nullptr); + hb_paint_funcs_set_pop_transform_func (funcs, hb_paint_extents_pop_transform, nullptr, nullptr); + hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_paint_extents_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_paint_extents_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, hb_paint_extents_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, hb_paint_extents_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, hb_paint_extents_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, hb_paint_extents_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, hb_paint_extents_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, hb_paint_extents_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, hb_paint_extents_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, hb_paint_extents_paint_sweep_gradient, nullptr, nullptr); + + hb_paint_funcs_make_immutable (funcs); + + hb_atexit (free_static_paint_extents_funcs); + + return funcs; + } +} static_paint_extents_funcs; + +static inline +void free_static_paint_extents_funcs () +{ + static_paint_extents_funcs.free_instance (); +} + +hb_paint_funcs_t * +hb_paint_extents_get_funcs () +{ + return static_paint_extents_funcs.get_unconst (); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-paint-extents.hh b/gfx/harfbuzz/src/hb-paint-extents.hh new file mode 100644 index 0000000000..f172bd42f9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-paint-extents.hh @@ -0,0 +1,293 @@ +/* + * 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_PAINT_EXTENTS_HH +#define HB_PAINT_EXTENTS_HH + +#include "hb.hh" +#include "hb-paint.h" + + +typedef struct hb_extents_t +{ + hb_extents_t () {} + hb_extents_t (float xmin, float ymin, float xmax, float ymax) : + xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {} + + bool is_empty () const { return xmin >= xmax || ymin >= ymax; } + bool is_void () const { return xmin > xmax; } + + void union_ (const hb_extents_t &o) + { + xmin = hb_min (xmin, o.xmin); + ymin = hb_min (ymin, o.ymin); + xmax = hb_max (xmax, o.xmax); + ymax = hb_max (ymax, o.ymax); + } + + void intersect (const hb_extents_t &o) + { + xmin = hb_max (xmin, o.xmin); + ymin = hb_max (ymin, o.ymin); + xmax = hb_min (xmax, o.xmax); + ymax = hb_min (ymax, o.ymax); + } + + void + add_point (float x, float y) + { + if (unlikely (is_void ())) + { + xmin = xmax = x; + ymin = ymax = y; + } + else + { + xmin = hb_min (xmin, x); + ymin = hb_min (ymin, y); + xmax = hb_max (xmax, x); + ymax = hb_max (ymax, y); + } + } + + float xmin = 0.f; + float ymin = 0.f; + float xmax = -1.f; + float ymax = -1.f; +} hb_extents_t; + +typedef struct hb_transform_t +{ + hb_transform_t () {} + hb_transform_t (float xx, float yx, + float xy, float yy, + float x0, float y0) : + xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} + + void multiply (const hb_transform_t &o) + { + /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ + hb_transform_t r; + + r.xx = o.xx * xx + o.yx * xy; + r.yx = o.xx * yx + o.yx * yy; + + r.xy = o.xy * xx + o.yy * xy; + r.yy = o.xy * yx + o.yy * yy; + + r.x0 = o.x0 * xx + o.y0 * xy + x0; + r.y0 = o.x0 * yx + o.y0 * yy + y0; + + *this = r; + } + + void transform_distance (float &dx, float &dy) const + { + float new_x = xx * dx + xy * dy; + float new_y = yx * dx + yy * dy; + dx = new_x; + dy = new_y; + } + + void transform_point (float &x, float &y) const + { + transform_distance (x, y); + x += x0; + y += y0; + } + + void transform_extents (hb_extents_t &extents) const + { + float quad_x[4], quad_y[4]; + + quad_x[0] = extents.xmin; + quad_y[0] = extents.ymin; + quad_x[1] = extents.xmin; + quad_y[1] = extents.ymax; + quad_x[2] = extents.xmax; + quad_y[2] = extents.ymin; + quad_x[3] = extents.xmax; + quad_y[3] = extents.ymax; + + extents = hb_extents_t {}; + for (unsigned i = 0; i < 4; i++) + { + transform_point (quad_x[i], quad_y[i]); + extents.add_point (quad_x[i], quad_y[i]); + } + } + + float xx = 1.f; + float yx = 0.f; + float xy = 0.f; + float yy = 1.f; + float x0 = 0.f; + float y0 = 0.f; +} hb_transform_t; + +typedef struct hb_bounds_t +{ + enum status_t { + UNBOUNDED, + BOUNDED, + EMPTY, + }; + + hb_bounds_t (status_t status) : status (status) {} + hb_bounds_t (const hb_extents_t &extents) : + status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {} + + void union_ (const hb_bounds_t &o) + { + if (o.status == UNBOUNDED) + status = UNBOUNDED; + else if (o.status == BOUNDED) + { + if (status == EMPTY) + *this = o; + else if (status == BOUNDED) + extents.union_ (o.extents); + } + } + + void intersect (const hb_bounds_t &o) + { + if (o.status == EMPTY) + status = EMPTY; + else if (o.status == BOUNDED) + { + if (status == UNBOUNDED) + *this = o; + else if (status == BOUNDED) + { + extents.intersect (o.extents); + if (extents.is_empty ()) + status = EMPTY; + } + } + } + + status_t status; + hb_extents_t extents; +} hb_bounds_t; + +typedef struct hb_paint_extents_context_t hb_paint_extents_context_t; + +struct hb_paint_extents_context_t +{ + hb_paint_extents_context_t () + { + transforms.push (hb_transform_t{}); + clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED}); + groups.push (hb_bounds_t{hb_bounds_t::EMPTY}); + } + + hb_extents_t get_extents () + { + return groups.tail().extents; + } + + bool is_bounded () + { + return groups.tail().status != hb_bounds_t::UNBOUNDED; + } + + void push_transform (const hb_transform_t &trans) + { + hb_transform_t t = transforms.tail (); + t.multiply (trans); + transforms.push (t); + } + + void pop_transform () + { + transforms.pop (); + } + + void push_clip (hb_extents_t extents) + { + /* Transform extents and push a new clip. */ + const hb_transform_t &t = transforms.tail (); + t.transform_extents (extents); + + clips.push (hb_bounds_t {extents}); + } + + void pop_clip () + { + clips.pop (); + } + + void push_group () + { + groups.push (hb_bounds_t {hb_bounds_t::EMPTY}); + } + + void pop_group (hb_paint_composite_mode_t mode) + { + const hb_bounds_t src_bounds = groups.pop (); + hb_bounds_t &backdrop_bounds = groups.tail (); + + // https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite + switch ((int) mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: + backdrop_bounds.status = hb_bounds_t::EMPTY; + break; + case HB_PAINT_COMPOSITE_MODE_SRC: + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: + backdrop_bounds = src_bounds; + break; + case HB_PAINT_COMPOSITE_MODE_DEST: + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: + break; + case HB_PAINT_COMPOSITE_MODE_SRC_IN: + case HB_PAINT_COMPOSITE_MODE_DEST_IN: + backdrop_bounds.intersect (src_bounds); + break; + default: + backdrop_bounds.union_ (src_bounds); + break; + } + } + + void paint () + { + const hb_bounds_t &clip = clips.tail (); + hb_bounds_t &group = groups.tail (); + + group.union_ (clip); + } + + protected: + hb_vector_t<hb_transform_t> transforms; + hb_vector_t<hb_bounds_t> clips; + hb_vector_t<hb_bounds_t> groups; +}; + +HB_INTERNAL hb_paint_funcs_t * +hb_paint_extents_get_funcs (); + + +#endif /* HB_PAINT_EXTENTS_HH */ diff --git a/gfx/harfbuzz/src/hb-paint.cc b/gfx/harfbuzz/src/hb-paint.cc new file mode 100644 index 0000000000..8eb24eb28b --- /dev/null +++ b/gfx/harfbuzz/src/hb-paint.cc @@ -0,0 +1,728 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * 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_PAINT + +#include "hb-paint.hh" + +/** + * SECTION: hb-paint + * @title: hb-paint + * @short_description: Glyph painting + * @include: hb.h + * + * Functions for painting glyphs. + * + * The main purpose of these functions is to paint (extract) color glyph layers + * from the COLRv1 table, but the API works for drawing ordinary outlines and + * images as well. + * + * The #hb_paint_funcs_t struct can be used with hb_font_paint_glyph(). + **/ + +static void +hb_paint_push_transform_nil (hb_paint_funcs_t *funcs, void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data) {} + +static void +hb_paint_pop_transform_nil (hb_paint_funcs_t *funcs, void *paint_data, + void *user_data) {} + +static hb_bool_t +hb_paint_color_glyph_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data) { return false; } + +static void +hb_paint_push_clip_glyph_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data) {} + +static void +hb_paint_push_clip_rectangle_nil (hb_paint_funcs_t *funcs, void *paint_data, + float xmin, float ymin, float xmax, float ymax, + void *user_data) {} + +static void +hb_paint_pop_clip_nil (hb_paint_funcs_t *funcs, void *paint_data, + void *user_data) {} + +static void +hb_paint_color_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_bool_t is_foreground, + hb_color_t color, + void *user_data) {} + +static hb_bool_t +hb_paint_image_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_blob_t *image, + unsigned int width, + unsigned int height, + hb_tag_t format, + float slant_xy, + hb_glyph_extents_t *extents, + void *user_data) { return false; } + +static void +hb_paint_linear_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data) {} + +static void +hb_paint_radial_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data) {} + +static void +hb_paint_sweep_gradient_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, + float end_angle, + void *user_data) {} + +static void +hb_paint_push_group_nil (hb_paint_funcs_t *funcs, void *paint_data, + void *user_data) {} + +static void +hb_paint_pop_group_nil (hb_paint_funcs_t *funcs, void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data) {} + +static hb_bool_t +hb_paint_custom_palette_color_nil (hb_paint_funcs_t *funcs, void *paint_data, + unsigned int color_index, + hb_color_t *color, + void *user_data) { return false; } + +static bool +_hb_paint_funcs_set_preamble (hb_paint_funcs_t *funcs, + bool func_is_null, + void **user_data, + hb_destroy_func_t *destroy) +{ + if (hb_object_is_immutable (funcs)) + { + 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_paint_funcs_set_middle (hb_paint_funcs_t *funcs, + void *user_data, + hb_destroy_func_t destroy) +{ + if (user_data && !funcs->user_data) + { + funcs->user_data = (decltype (funcs->user_data)) hb_calloc (1, sizeof (*funcs->user_data)); + if (unlikely (!funcs->user_data)) + goto fail; + } + if (destroy && !funcs->destroy) + { + funcs->destroy = (decltype (funcs->destroy)) hb_calloc (1, sizeof (*funcs->destroy)); + if (unlikely (!funcs->destroy)) + goto fail; + } + + return true; + +fail: + if (destroy) + (destroy) (user_data); + return false; +} + +#define HB_PAINT_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_paint_funcs_set_##name##_func (hb_paint_funcs_t *funcs, \ + hb_paint_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (!_hb_paint_funcs_set_preamble (funcs, !func, &user_data, &destroy)) \ + return; \ + \ + if (funcs->destroy && funcs->destroy->name) \ + funcs->destroy->name (!funcs->user_data ? nullptr : funcs->user_data->name);\ + \ + if (!_hb_paint_funcs_set_middle (funcs, user_data, destroy)) \ + return; \ + \ + if (func) \ + funcs->func.name = func; \ + else \ + funcs->func.name = hb_paint_##name##_nil; \ + \ + if (funcs->user_data) \ + funcs->user_data->name = user_data; \ + if (funcs->destroy) \ + funcs->destroy->name = destroy; \ +} + +HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + +/** + * hb_paint_funcs_create: + * + * Creates a new #hb_paint_funcs_t structure of paint functions. + * + * The initial reference count of 1 should be released with hb_paint_funcs_destroy() + * when you are done using the #hb_paint_funcs_t. This function never returns + * `NULL`. If memory cannot be allocated, a special singleton #hb_paint_funcs_t + * object will be returned. + * + * Returns value: (transfer full): the paint-functions structure + * + * Since: 7.0.0 + */ +hb_paint_funcs_t * +hb_paint_funcs_create () +{ + hb_paint_funcs_t *funcs; + if (unlikely (!(funcs = hb_object_create<hb_paint_funcs_t> ()))) + return const_cast<hb_paint_funcs_t *> (&Null (hb_paint_funcs_t)); + + funcs->func = Null (hb_paint_funcs_t).func; + + return funcs; +} + +DEFINE_NULL_INSTANCE (hb_paint_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + { +#define HB_PAINT_FUNC_IMPLEMENT(name) hb_paint_##name##_nil, + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } +}; + +/** + * hb_paint_funcs_get_empty: + * + * Fetches the singleton empty paint-functions structure. + * + * Return value: (transfer full): The empty paint-functions structure + * + * Since: 7.0.0 + **/ +hb_paint_funcs_t * +hb_paint_funcs_get_empty () +{ + return const_cast<hb_paint_funcs_t *> (&Null (hb_paint_funcs_t)); +} + +/** + * hb_paint_funcs_reference: (skip) + * @funcs: The paint-functions structure + * + * Increases the reference count on a paint-functions structure. + * + * This prevents @funcs from being destroyed until a matching + * call to hb_paint_funcs_destroy() is made. + * + * Return value: The paint-functions structure + * + * Since: 7.0.0 + */ +hb_paint_funcs_t * +hb_paint_funcs_reference (hb_paint_funcs_t *funcs) +{ + return hb_object_reference (funcs); +} + +/** + * hb_paint_funcs_destroy: (skip) + * @funcs: The paint-functions structure + * + * Decreases the reference count on a paint-functions structure. + * + * When the reference count reaches zero, the structure + * is destroyed, freeing all memory. + * + * Since: 7.0.0 + */ +void +hb_paint_funcs_destroy (hb_paint_funcs_t *funcs) +{ + if (!hb_object_destroy (funcs)) return; + + if (funcs->destroy) + { +#define HB_PAINT_FUNC_IMPLEMENT(name) \ + if (funcs->destroy->name) funcs->destroy->name (!funcs->user_data ? nullptr : funcs->user_data->name); + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } + + hb_free (funcs->destroy); + hb_free (funcs->user_data); + hb_free (funcs); +} + +/** + * hb_paint_funcs_set_user_data: (skip) + * @funcs: The paint-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 paint-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_paint_funcs_set_user_data (hb_paint_funcs_t *funcs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (funcs, key, data, destroy, replace); +} + +/** + * hb_paint_funcs_get_user_data: (skip) + * @funcs: The paint-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified paint-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 7.0.0 + **/ +void * +hb_paint_funcs_get_user_data (const hb_paint_funcs_t *funcs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (funcs, key); +} + +/** + * hb_paint_funcs_make_immutable: + * @funcs: The paint-functions structure + * + * Makes a paint-functions structure immutable. + * + * After this call, all attempts to set one of the callbacks + * on @funcs will fail. + * + * Since: 7.0.0 + */ +void +hb_paint_funcs_make_immutable (hb_paint_funcs_t *funcs) +{ + if (hb_object_is_immutable (funcs)) + return; + + hb_object_make_immutable (funcs); +} + +/** + * hb_paint_funcs_is_immutable: + * @funcs: The paint-functions structure + * + * Tests whether a paint-functions structure is immutable. + * + * Return value: `true` if @funcs is immutable, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_paint_funcs_is_immutable (hb_paint_funcs_t *funcs) +{ + return hb_object_is_immutable (funcs); +} + + +/** + * hb_color_line_get_color_stops: + * @color_line: a #hb_color_line_t object + * @start: the index of the first color stop to return + * @count: (inout) (optional): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @color_stops: (out) (array length=count) (optional): Array of #hb_color_stop_t to populate + * + * Fetches a list of color stops from the given color line object. + * + * Note that due to variations being applied, the returned color stops + * may be out of order. It is the callers responsibility to ensure that + * color stops are sorted by their offset before they are used. + * + * Return value: the total number of color stops in @color_line + * + * Since: 7.0.0 + */ +unsigned int +hb_color_line_get_color_stops (hb_color_line_t *color_line, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops) +{ + return color_line->get_color_stops (color_line, + color_line->data, + start, count, + color_stops, + color_line->get_color_stops_user_data); +} + +/** + * hb_color_line_get_extend: + * @color_line: a #hb_color_line_t object + * + * Fetches the extend mode of the color line object. + * + * Return value: the extend mode of @color_line + * + * Since: 7.0.0 + */ +hb_paint_extend_t +hb_color_line_get_extend (hb_color_line_t *color_line) +{ + return color_line->get_extend (color_line, + color_line->data, + color_line->get_extend_user_data); +} + + +/** + * hb_paint_push_transform: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @xx: xx component of the transform matrix + * @yx: yx component of the transform matrix + * @xy: xy component of the transform matrix + * @yy: yy component of the transform matrix + * @dx: dx component of the transform matrix + * @dy: dy component of the transform matrix + * + * Perform a "push-transform" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_push_transform (hb_paint_funcs_t *funcs, void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy) +{ + funcs->push_transform (paint_data, xx, yx, xy, yy, dx, dy); +} + +/** + * hb_paint_pop_transform: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * + * Perform a "pop-transform" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_pop_transform (hb_paint_funcs_t *funcs, void *paint_data) +{ + funcs->pop_transform (paint_data); +} + +/** + * hb_paint_color_glyph: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @glyph: the glyph ID + * @font: the font + * + * Perform a "color-glyph" paint operation. + * + * Since: 8.2.0 + */ +hb_bool_t +hb_paint_color_glyph (hb_paint_funcs_t *funcs, void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font) +{ + return funcs->color_glyph (paint_data, glyph, font); +} + +/** + * hb_paint_push_clip_glyph: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @glyph: the glyph ID + * @font: the font + * + * Perform a "push-clip-glyph" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_push_clip_glyph (hb_paint_funcs_t *funcs, void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font) +{ + funcs->push_clip_glyph (paint_data, glyph, font); +} + +/** + * hb_paint_push_clip_rectangle: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @xmin: min X for the rectangle + * @ymin: min Y for the rectangle + * @xmax: max X for the rectangle + * @ymax: max Y for the rectangle + * + * Perform a "push-clip-rect" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_push_clip_rectangle (hb_paint_funcs_t *funcs, void *paint_data, + float xmin, float ymin, float xmax, float ymax) +{ + funcs->push_clip_rectangle (paint_data, xmin, ymin, xmax, ymax); +} + +/** + * hb_paint_pop_clip: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * + * Perform a "pop-clip" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_pop_clip (hb_paint_funcs_t *funcs, void *paint_data) +{ + funcs->pop_clip (paint_data); +} + +/** + * hb_paint_color: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @is_foreground: whether the color is the foreground + * @color: The color to use + * + * Perform a "color" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_color (hb_paint_funcs_t *funcs, void *paint_data, + hb_bool_t is_foreground, + hb_color_t color) +{ + funcs->color (paint_data, is_foreground, color); +} + +/** + * hb_paint_image: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @image: image data + * @width: width of the raster image in pixels, or 0 + * @height: height of the raster image in pixels, or 0 + * @format: the image format as a tag + * @slant: the synthetic slant ratio to be applied to the image during rendering + * @extents: (nullable): the extents of the glyph + * + * Perform a "image" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_image (hb_paint_funcs_t *funcs, void *paint_data, + hb_blob_t *image, + unsigned int width, + unsigned int height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents) +{ + funcs->image (paint_data, image, width, height, format, slant, extents); +} + +/** + * hb_paint_linear_gradient: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @color_line: Color information for the gradient + * @x0: X coordinate of the first point + * @y0: Y coordinate of the first point + * @x1: X coordinate of the second point + * @y1: Y coordinate of the second point + * @x2: X coordinate of the third point + * @y2: Y coordinate of the third point + * + * Perform a "linear-gradient" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_linear_gradient (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2) +{ + funcs->linear_gradient (paint_data, color_line, x0, y0, x1, y1, x2, y2); +} + +/** + * hb_paint_radial_gradient: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @color_line: Color information for the gradient + * @x0: X coordinate of the first circle's center + * @y0: Y coordinate of the first circle's center + * @r0: radius of the first circle + * @x1: X coordinate of the second circle's center + * @y1: Y coordinate of the second circle's center + * @r1: radius of the second circle + * + * Perform a "radial-gradient" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_radial_gradient (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1) +{ + funcs->radial_gradient (paint_data, color_line, x0, y0, r0, y1, x1, r1); +} + +/** + * hb_paint_sweep_gradient: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @color_line: Color information for the gradient + * @x0: X coordinate of the circle's center + * @y0: Y coordinate of the circle's center + * @start_angle: the start angle + * @end_angle: the end angle + * + * Perform a "sweep-gradient" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_sweep_gradient (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle) +{ + funcs->sweep_gradient (paint_data, color_line, x0, y0, start_angle, end_angle); +} + +/** + * hb_paint_push_group: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * + * Perform a "push-group" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_push_group (hb_paint_funcs_t *funcs, void *paint_data) +{ + funcs->push_group (paint_data); +} + +/** + * hb_paint_pop_group: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @mode: the compositing mode to use + * + * Perform a "pop-group" paint operation. + * + * Since: 7.0.0 + */ +void +hb_paint_pop_group (hb_paint_funcs_t *funcs, void *paint_data, + hb_paint_composite_mode_t mode) +{ + funcs->pop_group (paint_data, mode); +} + +/** + * hb_paint_custom_palette_color: + * @funcs: paint functions + * @paint_data: associated data passed by the caller + * @color_index: color index + * @color: (out): fetched color + * + * Gets the custom palette color for @color_index. + * + * Return value: `true` if found, `false` otherwise + * + * Since: 7.0.0 + */ +hb_bool_t +hb_paint_custom_palette_color (hb_paint_funcs_t *funcs, void *paint_data, + unsigned int color_index, + hb_color_t *color) +{ + return funcs->custom_palette_color (paint_data, color_index, color); +} + +#endif diff --git a/gfx/harfbuzz/src/hb-paint.h b/gfx/harfbuzz/src/hb-paint.h new file mode 100644 index 0000000000..b0cd384e28 --- /dev/null +++ b/gfx/harfbuzz/src/hb-paint.h @@ -0,0 +1,1029 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * 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 <hb.h> instead." +#endif + +#ifndef HB_PAINT_H +#define HB_PAINT_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * hb_paint_funcs_t: + * + * Glyph paint callbacks. + * + * The callbacks assume that the caller maintains a stack + * of current transforms, clips and intermediate surfaces, + * as evidenced by the pairs of push/pop callbacks. The + * push/pop calls will be properly nested, so it is fine + * to store the different kinds of object on a single stack. + * + * Not all callbacks are required for all kinds of glyphs. + * For rendering COLRv0 or non-color outline glyphs, the + * gradient callbacks are not needed, and the composite + * callback only needs to handle simple alpha compositing + * (#HB_PAINT_COMPOSITE_MODE_SRC_OVER). + * + * The paint-image callback is only needed for glyphs + * with image blobs in the CBDT, sbix or SVG tables. + * + * The custom-palette-color callback is only necessary if + * you want to override colors from the font palette with + * custom colors. + * + * Since: 7.0.0 + **/ +typedef struct hb_paint_funcs_t hb_paint_funcs_t; + +HB_EXTERN hb_paint_funcs_t * +hb_paint_funcs_create (void); + +HB_EXTERN hb_paint_funcs_t * +hb_paint_funcs_get_empty (void); + +HB_EXTERN hb_paint_funcs_t * +hb_paint_funcs_reference (hb_paint_funcs_t *funcs); + +HB_EXTERN void +hb_paint_funcs_destroy (hb_paint_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_paint_funcs_set_user_data (hb_paint_funcs_t *funcs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_paint_funcs_get_user_data (const hb_paint_funcs_t *funcs, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_paint_funcs_make_immutable (hb_paint_funcs_t *funcs); + +HB_EXTERN hb_bool_t +hb_paint_funcs_is_immutable (hb_paint_funcs_t *funcs); + +/** + * hb_paint_push_transform_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @xx: xx component of the transform matrix + * @yx: yx component of the transform matrix + * @xy: xy component of the transform matrix + * @yy: yy component of the transform matrix + * @dx: dx component of the transform matrix + * @dy: dy component of the transform matrix + * @user_data: User data pointer passed to hb_paint_funcs_set_push_transform_func() + * + * A virtual method for the #hb_paint_funcs_t to apply + * a transform to subsequent paint calls. + * + * This transform is applied after the current transform, + * and remains in effect until a matching call to + * the #hb_paint_funcs_pop_transform_func_t vfunc. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_push_transform_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data); + +/** + * hb_paint_pop_transform_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @user_data: User data pointer passed to hb_paint_funcs_set_pop_transform_func() + * + * A virtual method for the #hb_paint_funcs_t to undo + * the effect of a prior call to the #hb_paint_funcs_push_transform_func_t + * vfunc. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_pop_transform_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data); + +/** + * hb_paint_color_glyph_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @glyph: the glyph ID + * @font: the font + * @user_data: User data pointer passed to hb_paint_funcs_set_color_glyph_func() + * + * A virtual method for the #hb_paint_funcs_t to render a color glyph by glyph index. + * + * Return value: %true if the glyph was painted, %false otherwise. + * + * Since: 8.2.0 + */ +typedef hb_bool_t (*hb_paint_color_glyph_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data); + +/** + * hb_paint_push_clip_glyph_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @glyph: the glyph ID + * @font: the font + * @user_data: User data pointer passed to hb_paint_funcs_set_push_clip_glyph_func() + * + * A virtual method for the #hb_paint_funcs_t to clip + * subsequent paint calls to the outline of a glyph. + * + * The coordinates of the glyph outline are interpreted according + * to the current transform. + * + * This clip is applied in addition to the current clip, + * and remains in effect until a matching call to + * the #hb_paint_funcs_pop_clip_func_t vfunc. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_push_clip_glyph_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data); + +/** + * hb_paint_push_clip_rectangle_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @xmin: min X for the rectangle + * @ymin: min Y for the rectangle + * @xmax: max X for the rectangle + * @ymax: max Y for the rectangle + * @user_data: User data pointer passed to hb_paint_funcs_set_push_clip_rectangle_func() + * + * A virtual method for the #hb_paint_funcs_t to clip + * subsequent paint calls to a rectangle. + * + * The coordinates of the rectangle are interpreted according + * to the current transform. + * + * This clip is applied in addition to the current clip, + * and remains in effect until a matching call to + * the #hb_paint_funcs_pop_clip_func_t vfunc. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_push_clip_rectangle_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + float xmin, float ymin, + float xmax, float ymax, + void *user_data); + +/** + * hb_paint_pop_clip_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @user_data: User data pointer passed to hb_paint_funcs_set_pop_clip_func() + * + * A virtual method for the #hb_paint_funcs_t to undo + * the effect of a prior call to the #hb_paint_funcs_push_clip_glyph_func_t + * or #hb_paint_funcs_push_clip_rectangle_func_t vfuncs. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_pop_clip_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data); + +/** + * hb_paint_color_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @is_foreground: whether the color is the foreground + * @color: The color to use, unpremultiplied + * @user_data: User data pointer passed to hb_paint_funcs_set_color_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a + * color everywhere within the current clip. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_color_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_bool_t is_foreground, + hb_color_t color, + void *user_data); + +/** + * HB_PAINT_IMAGE_FORMAT_PNG: + * + * Tag identifying PNG images in #hb_paint_image_func_t callbacks. + * + * Since: 7.0.0 + */ +#define HB_PAINT_IMAGE_FORMAT_PNG HB_TAG('p','n','g',' ') + +/** + * HB_PAINT_IMAGE_FORMAT_SVG: + * + * Tag identifying SVG images in #hb_paint_image_func_t callbacks. + * + * Since: 7.0.0 + */ +#define HB_PAINT_IMAGE_FORMAT_SVG HB_TAG('s','v','g',' ') + +/** + * HB_PAINT_IMAGE_FORMAT_BGRA: + * + * Tag identifying raw pixel-data images in #hb_paint_image_func_t callbacks. + * The data is in BGRA pre-multiplied sRGBA color-space format. + * + * Since: 7.0.0 + */ +#define HB_PAINT_IMAGE_FORMAT_BGRA HB_TAG('B','G','R','A') + +/** + * hb_paint_image_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @image: the image data + * @width: width of the raster image in pixels, or 0 + * @height: height of the raster image in pixels, or 0 + * @format: the image format as a tag + * @slant: the synthetic slant ratio to be applied to the image during rendering + * @extents: (nullable): glyph extents for desired rendering + * @user_data: User data pointer passed to hb_paint_funcs_set_image_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a glyph image. + * + * This method is called for glyphs with image blobs in the CBDT, + * sbix or SVG tables. The @format identifies the kind of data that + * is contained in @image. Possible values include #HB_PAINT_IMAGE_FORMAT_PNG, + * #HB_PAINT_IMAGE_FORMAT_SVG and #HB_PAINT_IMAGE_FORMAT_BGRA. + * + * The image dimensions and glyph extents are provided if available, + * and should be used to size and position the image. + * + * Return value: Whether the operation was successful. + * + * Since: 7.0.0 + */ +typedef hb_bool_t (*hb_paint_image_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_blob_t *image, + unsigned int width, + unsigned int height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents, + void *user_data); + +/** + * hb_color_stop_t: + * @offset: the offset of the color stop + * @is_foreground: whether the color is the foreground + * @color: the color, unpremultiplied + * + * Information about a color stop on a color line. + * + * Color lines typically have offsets ranging between 0 and 1, + * but that is not required. + * + * Note: despite @color being unpremultiplied here, interpolation in + * gradients shall happen in premultiplied space. See the OpenType spec + * [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details. + * + * Since: 7.0.0 + */ +typedef struct { + float offset; + hb_bool_t is_foreground; + hb_color_t color; +} hb_color_stop_t; + +/** + * hb_paint_extend_t: + * @HB_PAINT_EXTEND_PAD: Outside the defined interval, + * the color of the closest color stop is used. + * @HB_PAINT_EXTEND_REPEAT: The color line is repeated over + * repeated multiples of the defined interval + * @HB_PAINT_EXTEND_REFLECT: The color line is repeated over + * repeated intervals, as for the repeat mode. + * However, in each repeated interval, the ordering of + * color stops is the reverse of the adjacent interval. + * + * The values of this enumeration determine how color values + * outside the minimum and maximum defined offset on a #hb_color_line_t + * are determined. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details. + * + * Since: 7.0.0 + */ +typedef enum { + HB_PAINT_EXTEND_PAD, + HB_PAINT_EXTEND_REPEAT, + HB_PAINT_EXTEND_REFLECT +} hb_paint_extend_t; + +typedef struct hb_color_line_t hb_color_line_t; + +/** + * hb_color_line_get_color_stops_func_t: + * @color_line: a #hb_color_line_t object + * @color_line_data: the data accompanying @color_line + * @start: the index of the first color stop to return + * @count: (inout) (optional): Input = the maximum number of feature tags to return; + * Output = the actual number of feature tags returned (may be zero) + * @color_stops: (out) (array length=count) (optional): Array of #hb_color_stop_t to populate + * @user_data: the data accompanying this method + * + * A virtual method for the #hb_color_line_t to fetch color stops. + * + * Return value: the total number of color stops in @color_line + * + * Since: 7.0.0 + */ +typedef unsigned int (*hb_color_line_get_color_stops_func_t) (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); + +/** + * hb_color_line_get_extend_func_t: + * @color_line: a #hb_color_line_t object + * @color_line_data: the data accompanying @color_line + * @user_data: the data accompanying this method + * + * A virtual method for the @hb_color_line_t to fetches the extend mode. + * + * Return value: the extend mode of @color_line + * + * Since: 7.0.0 + */ +typedef hb_paint_extend_t (*hb_color_line_get_extend_func_t) (hb_color_line_t *color_line, + void *color_line_data, + void *user_data); + +/** + * hb_color_line_t: + * + * A struct containing color information for a gradient. + * + * Since: 7.0.0 + */ +struct hb_color_line_t { + void *data; + + hb_color_line_get_color_stops_func_t get_color_stops; + void *get_color_stops_user_data; + + hb_color_line_get_extend_func_t get_extend; + void *get_extend_user_data; + + void *reserved0; + void *reserved1; + void *reserved2; + void *reserved3; + void *reserved5; + void *reserved6; + void *reserved7; + void *reserved8; +}; + +HB_EXTERN unsigned int +hb_color_line_get_color_stops (hb_color_line_t *color_line, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops); + +HB_EXTERN hb_paint_extend_t +hb_color_line_get_extend (hb_color_line_t *color_line); + +/** + * hb_paint_linear_gradient_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @color_line: Color information for the gradient + * @x0: X coordinate of the first point + * @y0: Y coordinate of the first point + * @x1: X coordinate of the second point + * @y1: Y coordinate of the second point + * @x2: X coordinate of the third point + * @y2: Y coordinate of the third point + * @user_data: User data pointer passed to hb_paint_funcs_set_linear_gradient_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a linear + * gradient everywhere within the current clip. + * + * The @color_line object contains information about the colors of the gradients. + * It is only valid for the duration of the callback, you cannot keep it around. + * + * The coordinates of the points are interpreted according + * to the current transform. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details on how the points define the direction + * of the gradient, and how to interpret the @color_line. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_linear_gradient_func_t) (hb_paint_funcs_t *funcs, + 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_paint_radial_gradient_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @color_line: Color information for the gradient + * @x0: X coordinate of the first circle's center + * @y0: Y coordinate of the first circle's center + * @r0: radius of the first circle + * @x1: X coordinate of the second circle's center + * @y1: Y coordinate of the second circle's center + * @r1: radius of the second circle + * @user_data: User data pointer passed to hb_paint_funcs_set_radial_gradient_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a radial + * gradient everywhere within the current clip. + * + * The @color_line object contains information about the colors of the gradients. + * It is only valid for the duration of the callback, you cannot keep it around. + * + * The coordinates of the points are interpreted according + * to the current transform. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details on how the points define the direction + * of the gradient, and how to interpret the @color_line. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_radial_gradient_func_t) (hb_paint_funcs_t *funcs, + 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_paint_sweep_gradient_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @color_line: Color information for the gradient + * @x0: X coordinate of the circle's center + * @y0: Y coordinate of the circle's center + * @start_angle: the start angle, in radians + * @end_angle: the end angle, in radians + * @user_data: User data pointer passed to hb_paint_funcs_set_sweep_gradient_func() + * + * A virtual method for the #hb_paint_funcs_t to paint a sweep + * gradient everywhere within the current clip. + * + * The @color_line object contains information about the colors of the gradients. + * It is only valid for the duration of the callback, you cannot keep it around. + * + * The coordinates of the points are interpreted according + * to the current transform. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details on how the points define the direction + * of the gradient, and how to interpret the @color_line. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_sweep_gradient_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, + float end_angle, + void *user_data); + +/** + * hb_paint_composite_mode_t: + * @HB_PAINT_COMPOSITE_MODE_CLEAR: clear destination layer (bounded) + * @HB_PAINT_COMPOSITE_MODE_SRC: replace destination layer (bounded) + * @HB_PAINT_COMPOSITE_MODE_SRC_OVER: draw source layer on top of destination layer + * (bounded) + * @HB_PAINT_COMPOSITE_MODE_SRC_IN: draw source where there was destination content + * (unbounded) + * @HB_PAINT_COMPOSITE_MODE_SRC_OUT: draw source where there was no destination + * content (unbounded) + * @HB_PAINT_COMPOSITE_MODE_SRC_ATOP: draw source on top of destination content and + * only there + * @HB_PAINT_COMPOSITE_MODE_DEST: ignore the source + * @HB_PAINT_COMPOSITE_MODE_DEST_OVER: draw destination on top of source + * @HB_PAINT_COMPOSITE_MODE_DEST_IN: leave destination only where there was + * source content (unbounded) + * @HB_PAINT_COMPOSITE_MODE_DEST_OUT: leave destination only where there was no + * source content + * @HB_PAINT_COMPOSITE_MODE_DEST_ATOP: leave destination on top of source content + * and only there (unbounded) + * @HB_PAINT_COMPOSITE_MODE_XOR: source and destination are shown where there is only + * one of them + * @HB_PAINT_COMPOSITE_MODE_PLUS: source and destination layers are accumulated + * @HB_PAINT_COMPOSITE_MODE_MULTIPLY: source and destination layers are multiplied. + * This causes the result to be at least as dark as the darker inputs. + * @HB_PAINT_COMPOSITE_MODE_SCREEN: source and destination are complemented and + * multiplied. This causes the result to be at least as light as the lighter + * inputs. + * @HB_PAINT_COMPOSITE_MODE_OVERLAY: multiplies or screens, depending on the + * lightness of the destination color. + * @HB_PAINT_COMPOSITE_MODE_DARKEN: replaces the destination with the source if it + * is darker, otherwise keeps the source. + * @HB_PAINT_COMPOSITE_MODE_LIGHTEN: replaces the destination with the source if it + * is lighter, otherwise keeps the source. + * @HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: brightens the destination color to reflect + * the source color. + * @HB_PAINT_COMPOSITE_MODE_COLOR_BURN: darkens the destination color to reflect + * the source color. + * @HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: Multiplies or screens, dependent on source + * color. + * @HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: Darkens or lightens, dependent on source + * color. + * @HB_PAINT_COMPOSITE_MODE_DIFFERENCE: Takes the difference of the source and + * destination color. + * @HB_PAINT_COMPOSITE_MODE_EXCLUSION: Produces an effect similar to difference, but + * with lower contrast. + * @HB_PAINT_COMPOSITE_MODE_HSL_HUE: Creates a color with the hue of the source + * and the saturation and luminosity of the target. + * @HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: Creates a color with the saturation + * of the source and the hue and luminosity of the target. Painting with + * this mode onto a gray area produces no change. + * @HB_PAINT_COMPOSITE_MODE_HSL_COLOR: Creates a color with the hue and saturation + * of the source and the luminosity of the target. This preserves the gray + * levels of the target and is useful for coloring monochrome images or + * tinting color images. + * @HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: Creates a color with the luminosity of + * the source and the hue and saturation of the target. This produces an + * inverse effect to @HB_PAINT_COMPOSITE_MODE_HSL_COLOR. + * + * The values of this enumeration describe the compositing modes + * that can be used when combining temporary redirected drawing + * with the backdrop. + * + * See the OpenType spec [COLR](https://learn.microsoft.com/en-us/typography/opentype/spec/colr) + * section for details. + * + * Since: 7.0.0 + */ +typedef enum { + HB_PAINT_COMPOSITE_MODE_CLEAR, + HB_PAINT_COMPOSITE_MODE_SRC, + HB_PAINT_COMPOSITE_MODE_DEST, + HB_PAINT_COMPOSITE_MODE_SRC_OVER, + HB_PAINT_COMPOSITE_MODE_DEST_OVER, + HB_PAINT_COMPOSITE_MODE_SRC_IN, + HB_PAINT_COMPOSITE_MODE_DEST_IN, + HB_PAINT_COMPOSITE_MODE_SRC_OUT, + HB_PAINT_COMPOSITE_MODE_DEST_OUT, + HB_PAINT_COMPOSITE_MODE_SRC_ATOP, + HB_PAINT_COMPOSITE_MODE_DEST_ATOP, + HB_PAINT_COMPOSITE_MODE_XOR, + HB_PAINT_COMPOSITE_MODE_PLUS, + HB_PAINT_COMPOSITE_MODE_SCREEN, + HB_PAINT_COMPOSITE_MODE_OVERLAY, + HB_PAINT_COMPOSITE_MODE_DARKEN, + HB_PAINT_COMPOSITE_MODE_LIGHTEN, + HB_PAINT_COMPOSITE_MODE_COLOR_DODGE, + HB_PAINT_COMPOSITE_MODE_COLOR_BURN, + HB_PAINT_COMPOSITE_MODE_HARD_LIGHT, + HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT, + HB_PAINT_COMPOSITE_MODE_DIFFERENCE, + HB_PAINT_COMPOSITE_MODE_EXCLUSION, + HB_PAINT_COMPOSITE_MODE_MULTIPLY, + HB_PAINT_COMPOSITE_MODE_HSL_HUE, + HB_PAINT_COMPOSITE_MODE_HSL_SATURATION, + HB_PAINT_COMPOSITE_MODE_HSL_COLOR, + HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY +} hb_paint_composite_mode_t; + +/** + * hb_paint_push_group_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @user_data: User data pointer passed to hb_paint_funcs_set_push_group_func() + * + * A virtual method for the #hb_paint_funcs_t to use + * an intermediate surface for subsequent paint calls. + * + * The drawing will be redirected to an intermediate surface + * until a matching call to the #hb_paint_funcs_pop_group_func_t + * vfunc. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_push_group_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + void *user_data); + +/** + * hb_paint_pop_group_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @mode: the compositing mode to use + * @user_data: User data pointer passed to hb_paint_funcs_set_pop_group_func() + * + * A virtual method for the #hb_paint_funcs_t to undo + * the effect of a prior call to the #hb_paint_funcs_push_group_func_t + * vfunc. + * + * This call stops the redirection to the intermediate surface, + * and then composites it on the previous surface, using the + * compositing mode passed to this call. + * + * Since: 7.0.0 + */ +typedef void (*hb_paint_pop_group_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data); + +/** + * hb_paint_custom_palette_color_func_t: + * @funcs: paint functions object + * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() + * @color_index: the color index + * @color: (out): fetched color + * @user_data: User data pointer passed to hb_paint_funcs_set_pop_group_func() + * + * A virtual method for the #hb_paint_funcs_t to fetch a color from the custom + * color palette. + * + * Custom palette colors override the colors from the fonts selected color + * palette. It is not necessary to override all palette entries; for entries + * that should be taken from the font palette, return `false`. + * + * This function might get called multiple times, but the custom palette is + * expected to remain unchanged for duration of a hb_font_paint_glyph() call. + * + * Return value: `true` if found, `false` otherwise + * + * Since: 7.0.0 + */ +typedef hb_bool_t (*hb_paint_custom_palette_color_func_t) (hb_paint_funcs_t *funcs, + void *paint_data, + unsigned int color_index, + hb_color_t *color, + void *user_data); + + +/** + * hb_paint_funcs_set_push_transform_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-transform callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-transform callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_push_transform_func (hb_paint_funcs_t *funcs, + hb_paint_push_transform_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_pop_transform_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The pop-transform callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the pop-transform callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_pop_transform_func (hb_paint_funcs_t *funcs, + hb_paint_pop_transform_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_color_glyph_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The color-glyph callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the color-glyph callback on the paint functions struct. + * + * Since: 8.2.0 + */ +HB_EXTERN void +hb_paint_funcs_set_color_glyph_func (hb_paint_funcs_t *funcs, + hb_paint_color_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_push_clip_glyph_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-clip-glyph callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-clip-glyph callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_push_clip_glyph_func (hb_paint_funcs_t *funcs, + hb_paint_push_clip_glyph_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_push_clip_rectangle_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-clip-rectangle callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-clip-rect callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_push_clip_rectangle_func (hb_paint_funcs_t *funcs, + hb_paint_push_clip_rectangle_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_pop_clip_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The pop-clip callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the pop-clip callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_pop_clip_func (hb_paint_funcs_t *funcs, + hb_paint_pop_clip_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_color_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The paint-color callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the paint-color callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_color_func (hb_paint_funcs_t *funcs, + hb_paint_color_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_image_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The paint-image callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the paint-image callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_image_func (hb_paint_funcs_t *funcs, + hb_paint_image_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_linear_gradient_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The linear-gradient callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the linear-gradient callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_linear_gradient_func (hb_paint_funcs_t *funcs, + hb_paint_linear_gradient_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_radial_gradient_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The radial-gradient callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the radial-gradient callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_radial_gradient_func (hb_paint_funcs_t *funcs, + hb_paint_radial_gradient_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_sweep_gradient_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The sweep-gradient callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the sweep-gradient callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_sweep_gradient_func (hb_paint_funcs_t *funcs, + hb_paint_sweep_gradient_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_push_group_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The push-group callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the push-group callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_push_group_func (hb_paint_funcs_t *funcs, + hb_paint_push_group_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_pop_group_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The pop-group callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the pop-group callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_pop_group_func (hb_paint_funcs_t *funcs, + hb_paint_pop_group_func_t func, + void *user_data, + hb_destroy_func_t destroy); + +/** + * hb_paint_funcs_set_custom_palette_color_func: + * @funcs: A paint functions struct + * @func: (closure user_data) (destroy destroy) (scope notified): The custom-palette-color callback + * @user_data: Data to pass to @func + * @destroy: (nullable): Function to call when @user_data is no longer needed + * + * Sets the custom-palette-color callback on the paint functions struct. + * + * Since: 7.0.0 + */ +HB_EXTERN void +hb_paint_funcs_set_custom_palette_color_func (hb_paint_funcs_t *funcs, + hb_paint_custom_palette_color_func_t func, + void *user_data, + hb_destroy_func_t destroy); +/* + * Manual API + */ + +HB_EXTERN void +hb_paint_push_transform (hb_paint_funcs_t *funcs, void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy); + +HB_EXTERN void +hb_paint_pop_transform (hb_paint_funcs_t *funcs, void *paint_data); + +HB_EXTERN hb_bool_t +hb_paint_color_glyph (hb_paint_funcs_t *funcs, void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font); + +HB_EXTERN void +hb_paint_push_clip_glyph (hb_paint_funcs_t *funcs, void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font); + +HB_EXTERN void +hb_paint_push_clip_rectangle (hb_paint_funcs_t *funcs, void *paint_data, + float xmin, float ymin, + float xmax, float ymax); + +HB_EXTERN void +hb_paint_pop_clip (hb_paint_funcs_t *funcs, void *paint_data); + +HB_EXTERN void +hb_paint_color (hb_paint_funcs_t *funcs, void *paint_data, + hb_bool_t is_foreground, + hb_color_t color); + +HB_EXTERN void +hb_paint_image (hb_paint_funcs_t *funcs, void *paint_data, + hb_blob_t *image, + unsigned int width, + unsigned int height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents); + +HB_EXTERN void +hb_paint_linear_gradient (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2); + +HB_EXTERN void +hb_paint_radial_gradient (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float r0, + float x1, float y1, + float r1); + +HB_EXTERN void +hb_paint_sweep_gradient (hb_paint_funcs_t *funcs, void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, float end_angle); + +HB_EXTERN void +hb_paint_push_group (hb_paint_funcs_t *funcs, void *paint_data); + +HB_EXTERN void +hb_paint_pop_group (hb_paint_funcs_t *funcs, void *paint_data, + hb_paint_composite_mode_t mode); + +HB_EXTERN hb_bool_t +hb_paint_custom_palette_color (hb_paint_funcs_t *funcs, void *paint_data, + unsigned int color_index, + hb_color_t *color); + +HB_END_DECLS + +#endif /* HB_PAINT_H */ diff --git a/gfx/harfbuzz/src/hb-paint.hh b/gfx/harfbuzz/src/hb-paint.hh new file mode 100644 index 0000000000..56b790dbee --- /dev/null +++ b/gfx/harfbuzz/src/hb-paint.hh @@ -0,0 +1,236 @@ +/* + * Copyright © 2022 Matthias Clasen + * + * 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_PAINT_HH +#define HB_PAINT_HH + +#include "hb.hh" +#include "hb-face.hh" +#include "hb-font.hh" + +#define HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS \ + HB_PAINT_FUNC_IMPLEMENT (push_transform) \ + HB_PAINT_FUNC_IMPLEMENT (pop_transform) \ + HB_PAINT_FUNC_IMPLEMENT (color_glyph) \ + HB_PAINT_FUNC_IMPLEMENT (push_clip_glyph) \ + HB_PAINT_FUNC_IMPLEMENT (push_clip_rectangle) \ + HB_PAINT_FUNC_IMPLEMENT (pop_clip) \ + HB_PAINT_FUNC_IMPLEMENT (color) \ + HB_PAINT_FUNC_IMPLEMENT (image) \ + HB_PAINT_FUNC_IMPLEMENT (linear_gradient) \ + HB_PAINT_FUNC_IMPLEMENT (radial_gradient) \ + HB_PAINT_FUNC_IMPLEMENT (sweep_gradient) \ + HB_PAINT_FUNC_IMPLEMENT (push_group) \ + HB_PAINT_FUNC_IMPLEMENT (pop_group) \ + HB_PAINT_FUNC_IMPLEMENT (custom_palette_color) \ + /* ^--- Add new callbacks here */ + +struct hb_paint_funcs_t +{ + hb_object_header_t header; + + struct { +#define HB_PAINT_FUNC_IMPLEMENT(name) hb_paint_##name##_func_t name; + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } func; + + struct { +#define HB_PAINT_FUNC_IMPLEMENT(name) void *name; + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } *user_data; + + struct { +#define HB_PAINT_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_PAINT_FUNC_IMPLEMENT + } *destroy; + + void push_transform (void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy) + { func.push_transform (this, paint_data, + xx, yx, xy, yy, dx, dy, + !user_data ? nullptr : user_data->push_transform); } + void pop_transform (void *paint_data) + { func.pop_transform (this, paint_data, + !user_data ? nullptr : user_data->pop_transform); } + bool color_glyph (void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font) + { return func.color_glyph (this, paint_data, + glyph, + font, + !user_data ? nullptr : user_data->push_clip_glyph); } + void push_clip_glyph (void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font) + { func.push_clip_glyph (this, paint_data, + glyph, + font, + !user_data ? nullptr : user_data->push_clip_glyph); } + void push_clip_rectangle (void *paint_data, + float xmin, float ymin, float xmax, float ymax) + { func.push_clip_rectangle (this, paint_data, + xmin, ymin, xmax, ymax, + !user_data ? nullptr : user_data->push_clip_rectangle); } + void pop_clip (void *paint_data) + { func.pop_clip (this, paint_data, + !user_data ? nullptr : user_data->pop_clip); } + void color (void *paint_data, + hb_bool_t is_foreground, + hb_color_t color) + { func.color (this, paint_data, + is_foreground, color, + !user_data ? nullptr : user_data->color); } + bool image (void *paint_data, + hb_blob_t *image, + unsigned width, unsigned height, + hb_tag_t format, + float slant, + hb_glyph_extents_t *extents) + { return func.image (this, paint_data, + image, width, height, format, slant, extents, + !user_data ? nullptr : user_data->image); } + void linear_gradient (void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2) + { func.linear_gradient (this, paint_data, + color_line, x0, y0, x1, y1, x2, y2, + !user_data ? nullptr : user_data->linear_gradient); } + void radial_gradient (void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1) + { func.radial_gradient (this, paint_data, + color_line, x0, y0, r0, x1, y1, r1, + !user_data ? nullptr : user_data->radial_gradient); } + void sweep_gradient (void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float start_angle, + float end_angle) + { func.sweep_gradient (this, paint_data, + color_line, x0, y0, start_angle, end_angle, + !user_data ? nullptr : user_data->sweep_gradient); } + void push_group (void *paint_data) + { func.push_group (this, paint_data, + !user_data ? nullptr : user_data->push_group); } + void pop_group (void *paint_data, + hb_paint_composite_mode_t mode) + { func.pop_group (this, paint_data, + mode, + !user_data ? nullptr : user_data->pop_group); } + bool custom_palette_color (void *paint_data, + unsigned int color_index, + hb_color_t *color) + { return func.custom_palette_color (this, paint_data, + color_index, + color, + !user_data ? nullptr : user_data->custom_palette_color); } + + + /* Internal specializations. */ + + void push_root_transform (void *paint_data, + const hb_font_t *font) + { + float upem = font->face->get_upem (); + int xscale = font->x_scale, yscale = font->y_scale; + float slant = font->slant_xy; + + push_transform (paint_data, + xscale/upem, 0, slant * yscale/upem, yscale/upem, 0, 0); + } + + void push_inverse_root_transform (void *paint_data, + hb_font_t *font) + { + float upem = font->face->get_upem (); + int xscale = font->x_scale ? font->x_scale : upem; + int yscale = font->y_scale ? font->y_scale : upem; + float slant = font->slant_xy; + + push_transform (paint_data, + upem/xscale, 0, -slant * upem/xscale, upem/yscale, 0, 0); + } + + HB_NODISCARD + bool push_translate (void *paint_data, + float dx, float dy) + { + if (!dx && !dy) + return false; + + push_transform (paint_data, + 1.f, 0.f, 0.f, 1.f, dx, dy); + return true; + } + + HB_NODISCARD + bool push_scale (void *paint_data, + float sx, float sy) + { + if (sx == 1.f && sy == 1.f) + return false; + + push_transform (paint_data, + sx, 0.f, 0.f, sy, 0.f, 0.f); + return true; + } + + HB_NODISCARD + bool push_rotate (void *paint_data, + float a) + { + if (!a) + return false; + + float cc = cosf (a * HB_PI); + float ss = sinf (a * HB_PI); + push_transform (paint_data, cc, ss, -ss, cc, 0.f, 0.f); + return true; + } + + HB_NODISCARD + bool push_skew (void *paint_data, + float sx, float sy) + { + if (!sx && !sy) + return false; + + float x = tanf (-sx * HB_PI); + float y = tanf (+sy * HB_PI); + push_transform (paint_data, 1.f, y, x, 1.f, 0.f, 0.f); + return true; + } +}; +DECLARE_NULL_INSTANCE (hb_paint_funcs_t); + + +#endif /* HB_PAINT_HH */ diff --git a/gfx/harfbuzz/src/hb-pool.hh b/gfx/harfbuzz/src/hb-pool.hh new file mode 100644 index 0000000000..fcf10666b0 --- /dev/null +++ b/gfx/harfbuzz/src/hb-pool.hh @@ -0,0 +1,107 @@ +/* + * 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_POOL_HH +#define HB_POOL_HH + +#include "hb.hh" + +/* Memory pool for persistent allocation of small objects. + * + * Some AI musings on this, not necessarily true: + * + * This is a very simple implementation, but it's good enough for our + * purposes. It's not thread-safe. It's not very fast. It's not + * very memory efficient. It's not very cache efficient. It's not + * very anything efficient. But it's simple and it works. And it's + * good enough for our purposes. If you need something more + * sophisticated, use a real allocator. Or use a real language. */ + +template <typename T, unsigned ChunkLen = 32> +struct hb_pool_t +{ + hb_pool_t () : next (nullptr) {} + ~hb_pool_t () + { + next = nullptr; + + + hb_iter (chunks) + | hb_apply (hb_free) + ; + } + + T* alloc () + { + if (unlikely (!next)) + { + if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr; + chunk_t *chunk = (chunk_t *) hb_malloc (sizeof (chunk_t)); + if (unlikely (!chunk)) return nullptr; + chunks.push (chunk); + next = chunk->thread (); + } + + T* obj = next; + next = * ((T**) next); + + hb_memset (obj, 0, sizeof (T)); + + return obj; + } + + void release (T* obj) + { + * (T**) obj = next; + next = obj; + } + + private: + + static_assert (ChunkLen > 1, ""); + static_assert (sizeof (T) >= sizeof (void *), ""); + static_assert (alignof (T) % alignof (void *) == 0, ""); + + struct chunk_t + { + T* thread () + { + for (unsigned i = 0; i < ARRAY_LENGTH (arrayZ) - 1; i++) + * (T**) &arrayZ[i] = &arrayZ[i + 1]; + + * (T**) &arrayZ[ARRAY_LENGTH (arrayZ) - 1] = nullptr; + + return arrayZ; + } + + T arrayZ[ChunkLen]; + }; + + T* next; + hb_vector_t<chunk_t *> chunks; +}; + + +#endif /* HB_POOL_HH */ diff --git a/gfx/harfbuzz/src/hb-priority-queue.hh b/gfx/harfbuzz/src/hb-priority-queue.hh new file mode 100644 index 0000000000..9b962a29d9 --- /dev/null +++ b/gfx/harfbuzz/src/hb-priority-queue.hh @@ -0,0 +1,174 @@ +/* + * 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): Garret Rieger + */ + +#ifndef HB_PRIORITY_QUEUE_HH +#define HB_PRIORITY_QUEUE_HH + +#include "hb.hh" +#include "hb-vector.hh" + +/* + * hb_priority_queue_t + * + * Priority queue implemented as a binary heap. Supports extract minimum + * and insert operations. + * + * The priority queue is implemented as a binary heap, which is a complete + * binary tree. The root of the tree is the minimum element. The heap + * property is that the priority of a node is less than or equal to the + * priority of its children. The heap is stored in an array, with the + * children of node i stored at indices 2i + 1 and 2i + 2. + */ +template <typename K> +struct hb_priority_queue_t +{ + private: + typedef hb_pair_t<K, unsigned> item_t; + hb_vector_t<item_t> heap; + + public: + + void reset () { heap.resize (0); } + + bool in_error () const { return heap.in_error (); } + + bool alloc (unsigned size) + { return heap.alloc (size); } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + void insert (K priority, unsigned value) + { + heap.push (item_t (priority, value)); + if (unlikely (heap.in_error ())) return; + bubble_up (heap.length - 1); + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + item_t pop_minimum () + { + assert (!is_empty ()); + + item_t result = heap.arrayZ[0]; + + heap.arrayZ[0] = heap.arrayZ[heap.length - 1]; + heap.resize (heap.length - 1); + + if (!is_empty ()) + bubble_down (0); + + return result; + } + + const item_t& minimum () + { + return heap[0]; + } + + bool is_empty () const { return heap.length == 0; } + explicit operator bool () const { return !is_empty (); } + unsigned int get_population () const { return heap.length; } + + /* Sink interface. */ + hb_priority_queue_t& operator << (item_t item) + { insert (item.first, item.second); return *this; } + + private: + + static constexpr unsigned parent (unsigned index) + { + return (index - 1) / 2; + } + + static constexpr unsigned left_child (unsigned index) + { + return 2 * index + 1; + } + + static constexpr unsigned right_child (unsigned index) + { + return 2 * index + 2; + } + + HB_ALWAYS_INLINE + void bubble_down (unsigned index) + { + repeat: + assert (index < heap.length); + + unsigned left = left_child (index); + unsigned right = right_child (index); + + bool has_left = left < heap.length; + if (!has_left) + // If there's no left, then there's also no right. + return; + + bool has_right = right < heap.length; + if (heap.arrayZ[index].first <= heap.arrayZ[left].first + && (!has_right || heap.arrayZ[index].first <= heap.arrayZ[right].first)) + return; + + unsigned child; + if (!has_right || heap.arrayZ[left].first < heap.arrayZ[right].first) + child = left; + else + child = right; + + swap (index, child); + index = child; + goto repeat; + } + + HB_ALWAYS_INLINE + void bubble_up (unsigned index) + { + repeat: + assert (index < heap.length); + + if (index == 0) return; + + unsigned parent_index = parent (index); + if (heap.arrayZ[parent_index].first <= heap.arrayZ[index].first) + return; + + swap (index, parent_index); + index = parent_index; + goto repeat; + } + + void swap (unsigned a, unsigned b) + { + assert (a < heap.length); + assert (b < heap.length); + hb_swap (heap.arrayZ[a], heap.arrayZ[b]); + } +}; + +#endif /* HB_PRIORITY_QUEUE_HH */ diff --git a/gfx/harfbuzz/src/hb-repacker.hh b/gfx/harfbuzz/src/hb-repacker.hh new file mode 100644 index 0000000000..e9cd376ad3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-repacker.hh @@ -0,0 +1,420 @@ +/* + * 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): Garret Rieger + */ + +#ifndef HB_REPACKER_HH +#define HB_REPACKER_HH + +#include "hb-open-type.hh" +#include "hb-map.hh" +#include "hb-vector.hh" +#include "graph/graph.hh" +#include "graph/gsubgpos-graph.hh" +#include "graph/serialize.hh" + +using graph::graph_t; + +/* + * For a detailed writeup on the overflow resolution algorithm see: + * docs/repacker.md + */ + +struct lookup_size_t +{ + unsigned lookup_index; + size_t size; + unsigned num_subtables; + + static int cmp (const void* a, const void* b) + { + return cmp ((const lookup_size_t*) a, + (const lookup_size_t*) b); + } + + static int cmp (const lookup_size_t* a, const lookup_size_t* b) + { + double subtables_per_byte_a = (double) a->num_subtables / (double) a->size; + double subtables_per_byte_b = (double) b->num_subtables / (double) b->size; + if (subtables_per_byte_a == subtables_per_byte_b) { + return b->lookup_index - a->lookup_index; + } + + double cmp = subtables_per_byte_b - subtables_per_byte_a; + if (cmp < 0) return -1; + if (cmp > 0) return 1; + return 0; + } +}; + +static inline +bool _presplit_subtables_if_needed (graph::gsubgpos_graph_context_t& ext_context) +{ + // For each lookup this will check the size of subtables and split them as needed + // so that no subtable is at risk of overflowing. (where we support splitting for + // that subtable type). + // + // TODO(grieger): de-dup newly added nodes as necessary. Probably just want a full de-dup + // pass after this processing is done. Not super necessary as splits are + // only done where overflow is likely, so de-dup probably will get undone + // later anyways. + + // The loop below can modify the contents of ext_context.lookups if new subtables are added + // to a lookup during a split. So save the initial set of lookup indices so the iteration doesn't + // risk access free'd memory if ext_context.lookups gets resized. + hb_set_t lookup_indices(ext_context.lookups.keys ()); + for (unsigned lookup_index : lookup_indices) + { + graph::Lookup* lookup = ext_context.lookups.get(lookup_index); + if (!lookup->split_subtables_if_needed (ext_context, lookup_index)) + return false; + } + + return true; +} + +/* + * Analyze the lookups in a GSUB/GPOS table and decide if any should be promoted + * to extension lookups. + */ +static inline +bool _promote_extensions_if_needed (graph::gsubgpos_graph_context_t& ext_context) +{ + // Simple Algorithm (v1, current): + // 1. Calculate how many bytes each non-extension lookup consumes. + // 2. Select up to 64k of those to remain as non-extension (greedy, highest subtables per byte first) + // 3. Promote the rest. + // + // Advanced Algorithm (v2, not implemented): + // 1. Perform connected component analysis using lookups as roots. + // 2. Compute size of each connected component. + // 3. Select up to 64k worth of connected components to remain as non-extensions. + // (greedy, highest subtables per byte first) + // 4. Promote the rest. + + // TODO(garretrieger): support extension demotion, then consider all lookups. Requires advanced algo. + // TODO(garretrieger): also support extension promotion during iterative resolution phase, then + // we can use a less conservative threshold here. + // TODO(grieger): skip this for the 24 bit case. + if (!ext_context.lookups) return true; + + unsigned total_lookup_table_sizes = 0; + hb_vector_t<lookup_size_t> lookup_sizes; + lookup_sizes.alloc (ext_context.lookups.get_population (), true); + + for (unsigned lookup_index : ext_context.lookups.keys ()) + { + const auto& lookup_v = ext_context.graph.vertices_[lookup_index]; + total_lookup_table_sizes += lookup_v.table_size (); + + const graph::Lookup* lookup = ext_context.lookups.get(lookup_index); + hb_set_t visited; + lookup_sizes.push (lookup_size_t { + lookup_index, + ext_context.graph.find_subgraph_size (lookup_index, visited), + lookup->number_of_subtables (), + }); + } + + lookup_sizes.qsort (); + + size_t lookup_list_size = ext_context.graph.vertices_[ext_context.lookup_list_index].table_size (); + size_t l2_l3_size = lookup_list_size + total_lookup_table_sizes; // Lookup List + Lookups + size_t l3_l4_size = total_lookup_table_sizes; // Lookups + SubTables + size_t l4_plus_size = 0; // SubTables + their descendants + + // Start by assuming all lookups are using extension subtables, this size will be removed later + // if it's decided to not make a lookup extension. + for (auto p : lookup_sizes) + { + // TODO(garretrieger): this overestimates the extension subtables size because some extension subtables may be + // reused. However, we can't correct this until we have connected component analysis in place. + unsigned subtables_size = p.num_subtables * 8; + l3_l4_size += subtables_size; + l4_plus_size += subtables_size; + } + + bool layers_full = false; + for (auto p : lookup_sizes) + { + const graph::Lookup* lookup = ext_context.lookups.get(p.lookup_index); + if (lookup->is_extension (ext_context.table_tag)) + // already an extension so size is counted by the loop above. + continue; + + if (!layers_full) + { + size_t lookup_size = ext_context.graph.vertices_[p.lookup_index].table_size (); + hb_set_t visited; + size_t subtables_size = ext_context.graph.find_subgraph_size (p.lookup_index, visited, 1) - lookup_size; + size_t remaining_size = p.size - subtables_size - lookup_size; + + l3_l4_size += subtables_size; + l3_l4_size -= p.num_subtables * 8; + l4_plus_size += subtables_size + remaining_size; + + if (l2_l3_size < (1 << 16) + && l3_l4_size < (1 << 16) + && l4_plus_size < (1 << 16)) continue; // this lookup fits within all layers groups + + layers_full = true; + } + + if (!ext_context.lookups.get(p.lookup_index)->make_extension (ext_context, p.lookup_index)) + return false; + } + + return true; +} + +static inline +bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& overflows, + graph_t& sorted_graph) +{ + unsigned space = 0; + hb_set_t roots_to_isolate; + + for (int i = overflows.length - 1; i >= 0; i--) + { + const graph::overflow_record_t& r = overflows[i]; + + unsigned root; + unsigned overflow_space = sorted_graph.space_for (r.parent, &root); + if (!overflow_space) continue; + if (sorted_graph.num_roots_for_space (overflow_space) <= 1) continue; + + if (!space) { + space = overflow_space; + } + + if (space == overflow_space) + roots_to_isolate.add(root); + } + + if (!roots_to_isolate) return false; + + unsigned maximum_to_move = hb_max ((sorted_graph.num_roots_for_space (space) / 2u), 1u); + if (roots_to_isolate.get_population () > maximum_to_move) { + // Only move at most half of the roots in a space at a time. + unsigned extra = roots_to_isolate.get_population () - maximum_to_move; + while (extra--) { + uint32_t root = HB_SET_VALUE_INVALID; + roots_to_isolate.previous (&root); + roots_to_isolate.del (root); + } + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Overflow in space %u (%u roots). Moving %u roots to space %u.", + space, + sorted_graph.num_roots_for_space (space), + roots_to_isolate.get_population (), + sorted_graph.next_space ()); + + sorted_graph.isolate_subgraph (roots_to_isolate); + sorted_graph.move_to_new_space (roots_to_isolate); + + return true; +} + +static inline +bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows, + hb_set_t& priority_bumped_parents, + graph_t& sorted_graph) +{ + bool resolution_attempted = false; + + // Try resolving the furthest overflows first. + for (int i = overflows.length - 1; i >= 0; i--) + { + const graph::overflow_record_t& r = overflows[i]; + const auto& child = sorted_graph.vertices_[r.child]; + if (child.is_shared ()) + { + // The child object is shared, we may be able to eliminate the overflow + // by duplicating it. + if (sorted_graph.duplicate (r.parent, r.child) == (unsigned) -1) continue; + return true; + } + + if (child.is_leaf () && !priority_bumped_parents.has (r.parent)) + { + // This object is too far from it's parent, attempt to move it closer. + // + // TODO(garretrieger): initially limiting this to leaf's since they can be + // moved closer with fewer consequences. However, this can + // likely can be used for non-leafs as well. + // TODO(garretrieger): also try lowering priority of the parent. Make it + // get placed further up in the ordering, closer to it's children. + // this is probably preferable if the total size of the parent object + // is < then the total size of the children (and the parent can be moved). + // Since in that case moving the parent will cause a smaller increase in + // the length of other offsets. + if (sorted_graph.raise_childrens_priority (r.parent)) { + priority_bumped_parents.add (r.parent); + resolution_attempted = true; + } + continue; + } + + // TODO(garretrieger): add additional offset resolution strategies + // - Promotion to extension lookups. + // - Table splitting. + } + + return resolution_attempted; +} + +inline bool +hb_resolve_graph_overflows (hb_tag_t table_tag, + unsigned max_rounds , + bool recalculate_extensions, + graph_t& sorted_graph /* IN/OUT */) +{ + sorted_graph.sort_shortest_distance (); + if (sorted_graph.in_error ()) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Sorted graph in error state after initial sort."); + return false; + } + + bool will_overflow = graph::will_overflow (sorted_graph); + if (!will_overflow) + return true; + + graph::gsubgpos_graph_context_t ext_context (table_tag, sorted_graph); + if ((table_tag == HB_OT_TAG_GPOS + || table_tag == HB_OT_TAG_GSUB) + && will_overflow) + { + if (recalculate_extensions) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Splitting subtables if needed."); + if (!_presplit_subtables_if_needed (ext_context)) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Subtable splitting failed."); + return false; + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, "Promoting lookups to extensions if needed."); + if (!_promote_extensions_if_needed (ext_context)) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Extensions promotion failed."); + return false; + } + } + + DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs."); + if (sorted_graph.assign_spaces ()) + sorted_graph.sort_shortest_distance (); + else + sorted_graph.sort_shortest_distance_if_needed (); + } + + unsigned round = 0; + hb_vector_t<graph::overflow_record_t> overflows; + // TODO(garretrieger): select a good limit for max rounds. + while (!sorted_graph.in_error () + && graph::will_overflow (sorted_graph, &overflows) + && round < max_rounds) { + DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Overflow resolution round %u ===", round); + print_overflows (sorted_graph, overflows); + + hb_set_t priority_bumped_parents; + + if (!_try_isolating_subgraphs (overflows, sorted_graph)) + { + // Don't count space isolation towards round limit. Only increment + // round counter if space isolation made no changes. + round++; + if (!_process_overflows (overflows, priority_bumped_parents, sorted_graph)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "No resolution available :("); + break; + } + } + + sorted_graph.sort_shortest_distance (); + } + + if (sorted_graph.in_error ()) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Sorted graph in error state."); + return false; + } + + if (graph::will_overflow (sorted_graph)) + { + DEBUG_MSG (SUBSET_REPACK, nullptr, "Offset overflow resolution failed."); + return false; + } + + return true; +} + +/* + * Attempts to modify the topological sorting of the provided object graph to + * eliminate offset overflows in the links between objects of the graph. If a + * non-overflowing ordering is found the updated graph is serialized it into the + * provided serialization context. + * + * If necessary the structure of the graph may be modified in ways that do not + * affect the functionality of the graph. For example shared objects may be + * duplicated. + * + * For a detailed writeup describing how the algorithm operates see: + * docs/repacker.md + */ +template<typename T> +inline hb_blob_t* +hb_resolve_overflows (const T& packed, + hb_tag_t table_tag, + unsigned max_rounds = 20, + bool recalculate_extensions = false) { + graph_t sorted_graph (packed); + if (sorted_graph.in_error ()) + { + // Invalid graph definition. + return nullptr; + } + + if (!sorted_graph.is_fully_connected ()) + { + sorted_graph.print_orphaned_nodes (); + return nullptr; + } + + if (sorted_graph.in_error ()) + { + // Allocations failed somewhere + DEBUG_MSG (SUBSET_REPACK, nullptr, + "Graph is in error, likely due to a memory allocation error."); + return nullptr; + } + + if (!hb_resolve_graph_overflows (table_tag, max_rounds, recalculate_extensions, sorted_graph)) + return nullptr; + + return graph::serialize (sorted_graph); +} + +#endif /* HB_REPACKER_HH */ diff --git a/gfx/harfbuzz/src/hb-sanitize.hh b/gfx/harfbuzz/src/hb-sanitize.hh new file mode 100644 index 0000000000..408649c768 --- /dev/null +++ b/gfx/harfbuzz/src/hb-sanitize.hh @@ -0,0 +1,530 @@ +/* + * 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_SANITIZE_HH +#define HB_SANITIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-dispatch.hh" + + +/* + * Sanitize + * + * + * === Introduction === + * + * The sanitize machinery is at the core of our zero-cost font loading. We + * mmap() font file into memory and create a blob out of it. Font subtables + * are returned as a readonly sub-blob of the main font blob. These table + * blobs are then sanitized before use, to ensure invalid memory access does + * not happen. The toplevel sanitize API use is like, eg. to load the 'head' + * table: + * + * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (face); + * + * The blob then can be converted to a head table struct with: + * + * const head *head_table = head_blob->as<head> (); + * + * What the reference_table does is, to call hb_face_reference_table() to load + * the table blob, sanitize it and return either the sanitized blob, or empty + * blob if sanitization failed. The blob->as() function returns the null + * object of its template type argument if the blob is empty. Otherwise, it + * just casts the blob contents to the desired type. + * + * Sanitizing a blob of data with a type T works as follows (with minor + * simplification): + * + * - Cast blob content to T*, call sanitize() method of it, + * - If sanitize succeeded, return blob. + * - Otherwise, if blob is not writable, try making it writable, + * or copy if cannot be made writable in-place, + * - Call sanitize() again. Return blob if sanitize succeeded. + * - Return empty blob otherwise. + * + * + * === The sanitize() contract === + * + * The sanitize() method of each object type shall return true if it's safe to + * call other methods of the object, and %false otherwise. + * + * Note that what sanitize() checks for might align with what the specification + * describes as valid table data, but does not have to be. In particular, we + * do NOT want to be pedantic and concern ourselves with validity checks that + * are irrelevant to our use of the table. On the contrary, we want to be + * lenient with error handling and accept invalid data to the extent that it + * does not impose extra burden on us. + * + * Based on the sanitize contract, one can see that what we check for depends + * on how we use the data in other table methods. Ie. if other table methods + * assume that offsets do NOT point out of the table data block, then that's + * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On + * the other hand, if other methods do such checks themselves, then sanitize() + * does not have to bother with them (glyf/local work this way). The choice + * depends on the table structure and sanitize() performance. For example, to + * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard + * to avoid such costs during font loading. By postponing such checks to the + * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime + * cost to O(used-glyphs). As such, this is preferred. + * + * The same argument can be made re GSUB/GPOS/GDEF, but there, the table + * structure is so complicated that by checking all offsets at sanitize() time, + * we make the code much simpler in other methods, as offsets and referenced + * objects do not need to be validated at each use site. + */ + +/* This limits sanitizing time on really broken fonts. */ +#ifndef HB_SANITIZE_MAX_EDITS +#define HB_SANITIZE_MAX_EDITS 32 +#endif +#ifndef HB_SANITIZE_MAX_OPS_FACTOR +#define HB_SANITIZE_MAX_OPS_FACTOR 64 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MIN +#define HB_SANITIZE_MAX_OPS_MIN 16384 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MAX +#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF +#endif +#ifndef HB_SANITIZE_MAX_SUBTABLES +#define HB_SANITIZE_MAX_SUBTABLES 0x4000 +#endif + +struct hb_sanitize_context_t : + hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE> +{ + hb_sanitize_context_t () : + start (nullptr), end (nullptr), + length (0), + max_ops (0), max_subtables (0), + recursion_depth (0), + writable (false), edit_count (0), + blob (nullptr), + num_glyphs (65536), + num_glyphs_set (false), + lazy_some_gpos (false) {} + + const char *get_name () { return "SANITIZE"; } + template <typename T, typename F> + bool may_dispatch (const T *obj HB_UNUSED, const F *format) + { + return format->sanitize (this) && + hb_barrier (); + } + static return_t default_return_value () { return true; } + static return_t no_dispatch_return_value () { return false; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + + bool visit_subtables (unsigned count) + { + max_subtables += count; + return max_subtables < HB_SANITIZE_MAX_SUBTABLES; + } + + private: + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.sanitize (this, std::forward<Ts> (ds)...) ) + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, std::forward<Ts> (ds)...) ) + public: + template <typename T, typename ...Ts> auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) ) + + hb_sanitize_context_t (hb_blob_t *b) : hb_sanitize_context_t () + { + init (b); + + if (blob) + start_processing (); + } + + ~hb_sanitize_context_t () + { + if (blob) + end_processing (); + } + + void init (hb_blob_t *b) + { + this->blob = hb_blob_reference (b); + this->writable = false; + } + + void set_num_glyphs (unsigned int num_glyphs_) + { + num_glyphs = num_glyphs_; + num_glyphs_set = true; + } + unsigned int get_num_glyphs () { return num_glyphs; } + + void set_max_ops (int max_ops_) { max_ops = max_ops_; } + + template <typename T> + void set_object (const T *obj) + { + reset_object (); + + if (!obj) return; + + const char *obj_start = (const char *) obj; + if (unlikely (obj_start < this->start || this->end <= obj_start)) + { + this->start = this->end = nullptr; + this->length = 0; + } + else + { + this->start = obj_start; + this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ()); + this->length = this->end - this->start; + } + } + + void reset_object () + { + this->start = this->blob->data; + this->end = this->start + this->blob->length; + this->length = this->end - this->start; + assert (this->start <= this->end); /* Must not overflow. */ + } + + void start_processing () + { + reset_object (); + unsigned m; + if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR, &m))) + this->max_ops = HB_SANITIZE_MAX_OPS_MAX; + else + this->max_ops = hb_clamp (m, + (unsigned) HB_SANITIZE_MAX_OPS_MIN, + (unsigned) HB_SANITIZE_MAX_OPS_MAX); + this->edit_count = 0; + this->debug_depth = 0; + this->recursion_depth = 0; + + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + } + + void end_processing () + { + 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; + this->length = 0; + } + + unsigned get_edit_count () { return edit_count; } + + + bool check_ops(unsigned count) + { + /* Avoid underflow */ + if (unlikely (this->max_ops < 0 || count >= (unsigned) this->max_ops)) + { + this->max_ops = -1; + return false; + } + this->max_ops -= (int) count; + return true; + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool check_range (const void *base, + unsigned int len) const + { + const char *p = (const char *) base; + bool ok = (uintptr_t) (p - this->start) <= this->length && + (unsigned int) (this->end - p) >= len && + ((this->max_ops -= len) > 0); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range [%p..%p]" + " (%u bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool check_range_fast (const void *base, + unsigned int len) const + { + const char *p = (const char *) base; + bool ok = ((uintptr_t) (p - this->start) <= this->length && + (unsigned int) (this->end - p) >= len); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range_fast [%p..%p]" + " (%u bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool check_point (const void *base) const + { + const char *p = (const char *) base; + bool ok = (uintptr_t) (p - this->start) <= this->length; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_point [%p]" + " in [%p..%p] -> %s", + p, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + template <typename T> + bool check_range (const T *base, + unsigned int a, + unsigned int b) const + { + unsigned m; + return !hb_unsigned_mul_overflows (a, b, &m) && + this->check_range (base, m); + } + + template <typename T> + bool check_range (const T *base, + unsigned int a, + unsigned int b, + unsigned int c) const + { + unsigned m; + return !hb_unsigned_mul_overflows (a, b, &m) && + this->check_range (base, m, c); + } + + template <typename T> + HB_ALWAYS_INLINE + bool check_array_sized (const T *base, unsigned int len, unsigned len_size) const + { + if (len_size >= 4) + { + if (unlikely (hb_unsigned_mul_overflows (len, hb_static_size (T), &len))) + return false; + } + else + len = len * hb_static_size (T); + return this->check_range (base, len); + } + + template <typename T> + bool check_array (const T *base, unsigned int len) const + { + return this->check_range (base, len, hb_static_size (T)); + } + + template <typename T> + bool check_array (const T *base, + unsigned int a, + unsigned int b) const + { + return this->check_range (base, hb_static_size (T), a, b); + } + + bool check_start_recursion (int max_depth) + { + if (unlikely (recursion_depth >= max_depth)) return false; + return ++recursion_depth; + } + + bool end_recursion (bool result) + { + recursion_depth--; + return result; + } + + template <typename Type> +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif + bool check_struct (const Type *obj) const + { + if (sizeof (uintptr_t) == sizeof (uint32_t)) + return likely (this->check_range_fast (obj, obj->min_size)); + else + return likely (this->check_point ((const char *) obj + obj->min_size)); + } + + bool may_edit (const void *base, unsigned int len) + { + 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] (%u bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end, + this->writable ? "GRANTED" : "DENIED"); + + return this->writable; + } + + template <typename Type, typename ValueType> + bool try_set (const Type *obj, const ValueType &v) + { + if (this->may_edit (obj, hb_static_size (Type))) + { + * const_cast<Type *> (obj) = v; + return true; + } + return false; + } + + template <typename Type> + hb_blob_t *sanitize_blob (hb_blob_t *blob) + { + bool sane; + + init (blob); + + retry: + DEBUG_MSG_FUNC (SANITIZE, start, "start"); + + start_processing (); + + if (unlikely (!start)) + { + end_processing (); + return blob; + } + + Type *t = reinterpret_cast<Type *> (const_cast<char *> (start)); + + sane = t->sanitize (this); + if (sane) + { + if (edit_count) + { + DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %u edits; going for second round", edit_count); + + /* sanitize again to ensure no toe-stepping */ + edit_count = 0; + sane = t->sanitize (this); + if (edit_count) { + DEBUG_MSG_FUNC (SANITIZE, start, "requested %u edits in second round; FAILING", edit_count); + sane = false; + } + } + } + else + { + if (edit_count && !writable) { + start = hb_blob_get_data_writable (blob, nullptr); + end = start + blob->length; + + if (start) + { + writable = true; + /* ok, we made it writable by relocating. try again */ + DEBUG_MSG_FUNC (SANITIZE, start, "retry"); + goto retry; + } + } + } + + end_processing (); + + DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); + if (sane) + { + hb_blob_make_immutable (blob); + return blob; + } + else + { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + template <typename Type> + hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) + { + if (!num_glyphs_set) + set_num_glyphs (hb_face_get_glyph_count (face)); + return sanitize_blob<Type> (hb_face_reference_table (face, tableTag)); + } + + const char *start, *end; + unsigned length; + mutable int max_ops, max_subtables; + private: + int recursion_depth; + bool writable; + unsigned int edit_count; + hb_blob_t *blob; + unsigned int num_glyphs; + bool num_glyphs_set; + public: + bool lazy_some_gpos; +}; + +struct hb_sanitize_with_object_t +{ + template <typename T> + hb_sanitize_with_object_t (hb_sanitize_context_t *c, const T& obj) : c (c) + { c->set_object (obj); } + ~hb_sanitize_with_object_t () + { c->reset_object (); } + + private: + hb_sanitize_context_t *c; +}; + + +#endif /* HB_SANITIZE_HH */ diff --git a/gfx/harfbuzz/src/hb-serialize.hh b/gfx/harfbuzz/src/hb-serialize.hh new file mode 100644 index 0000000000..15eccb6a09 --- /dev/null +++ b/gfx/harfbuzz/src/hb-serialize.hh @@ -0,0 +1,788 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,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. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + * Facebook Author(s): Behdad Esfahbod + */ + +#ifndef HB_SERIALIZE_HH +#define HB_SERIALIZE_HH + +#include "hb.hh" +#include "hb-blob.hh" +#include "hb-map.hh" +#include "hb-pool.hh" + +#ifdef HB_EXPERIMENTAL_API +#include "hb-subset-repacker.h" +#endif + +/* + * Serialize + */ + +enum hb_serialize_error_t { + HB_SERIALIZE_ERROR_NONE = 0x00000000u, + HB_SERIALIZE_ERROR_OTHER = 0x00000001u, + HB_SERIALIZE_ERROR_OFFSET_OVERFLOW = 0x00000002u, + HB_SERIALIZE_ERROR_OUT_OF_ROOM = 0x00000004u, + HB_SERIALIZE_ERROR_INT_OVERFLOW = 0x00000008u, + HB_SERIALIZE_ERROR_ARRAY_OVERFLOW = 0x00000010u +}; +HB_MARK_AS_FLAG_T (hb_serialize_error_t); + +struct hb_serialize_context_t +{ + typedef unsigned objidx_t; + + enum whence_t { + Head, /* Relative to the current object head (default). */ + Tail, /* Relative to the current object tail after packed. */ + Absolute /* Absolute: from the start of the serialize buffer. */ + }; + + + + struct object_t + { + void fini () { + real_links.fini (); + virtual_links.fini (); + } + + object_t () = default; + +#ifdef HB_EXPERIMENTAL_API + object_t (const hb_object_t &o) + { + head = o.head; + tail = o.tail; + next = nullptr; + real_links.alloc (o.num_real_links, true); + for (unsigned i = 0 ; i < o.num_real_links; i++) + real_links.push (o.real_links[i]); + + virtual_links.alloc (o.num_virtual_links, true); + for (unsigned i = 0; i < o.num_virtual_links; i++) + virtual_links.push (o.virtual_links[i]); + } +#endif + + friend void swap (object_t& a, object_t& b) + { + hb_swap (a.head, b.head); + hb_swap (a.tail, b.tail); + hb_swap (a.next, b.next); + hb_swap (a.real_links, b.real_links); + hb_swap (a.virtual_links, b.virtual_links); + } + + bool operator == (const object_t &o) const + { + // Virtual links aren't considered for equality since they don't affect the functionality + // of the object. + return (tail - head == o.tail - o.head) + && (real_links.length == o.real_links.length) + && 0 == hb_memcmp (head, o.head, tail - head) + && real_links.as_bytes () == o.real_links.as_bytes (); + } + uint32_t hash () const + { + // Virtual links aren't considered for equality since they don't affect the functionality + // of the object. + return hb_bytes_t (head, hb_min (128, tail - head)).hash () ^ + real_links.as_bytes ().hash (); + } + + struct link_t + { + unsigned width: 3; + unsigned is_signed: 1; + unsigned whence: 2; + unsigned bias : 26; + unsigned position; + objidx_t objidx; + + link_t () = default; + +#ifdef HB_EXPERIMENTAL_API + link_t (const hb_link_t &o) + { + width = o.width; + is_signed = 0; + whence = 0; + position = o.position; + bias = 0; + objidx = o.objidx; + } +#endif + + HB_INTERNAL static int cmp (const void* a, const void* b) + { + int cmp = ((const link_t*)a)->position - ((const link_t*)b)->position; + if (cmp) return cmp; + + return ((const link_t*)a)->objidx - ((const link_t*)b)->objidx; + } + }; + + char *head; + char *tail; + hb_vector_t<link_t> real_links; + hb_vector_t<link_t> virtual_links; + object_t *next; + + auto all_links () const HB_AUTO_RETURN + (( hb_concat (this->real_links, this->virtual_links) )); + auto all_links_writer () HB_AUTO_RETURN + (( hb_concat (this->real_links.writer (), this->virtual_links.writer ()) )); + }; + + struct snapshot_t + { + char *head; + char *tail; + object_t *current; // Just for sanity check + unsigned num_real_links; + unsigned num_virtual_links; + hb_serialize_error_t errors; + }; + + snapshot_t snapshot () + { + return snapshot_t { + head, tail, current, + current ? current->real_links.length : 0, + current ? current->virtual_links.length : 0, + errors + }; + } + + hb_serialize_context_t (void *start_, unsigned int size) : + start ((char *) start_), + end (start + size), + current (nullptr) + { reset (); } + ~hb_serialize_context_t () { fini (); } + + void fini () + { + for (object_t *_ : ++hb_iter (packed)) _->fini (); + packed.fini (); + this->packed_map.fini (); + + while (current) + { + auto *_ = current; + current = current->next; + _->fini (); + } + } + + bool in_error () const { return bool (errors); } + + bool successful () const { return !bool (errors); } + + HB_NODISCARD bool ran_out_of_room () const { return errors & HB_SERIALIZE_ERROR_OUT_OF_ROOM; } + HB_NODISCARD bool offset_overflow () const { return errors & HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } + HB_NODISCARD bool only_offset_overflow () const { return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } + HB_NODISCARD bool only_overflow () const + { + return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW + || errors == HB_SERIALIZE_ERROR_INT_OVERFLOW + || errors == HB_SERIALIZE_ERROR_ARRAY_OVERFLOW; + } + + void reset (void *start_, unsigned int size) + { + start = (char*) start_; + end = start + size; + reset (); + current = nullptr; + } + + void reset () + { + this->errors = HB_SERIALIZE_ERROR_NONE; + this->head = this->start; + this->tail = this->end; + this->zerocopy = nullptr; + this->debug_depth = 0; + + fini (); + this->packed.push (nullptr); + this->packed_map.init (); + } + + bool check_success (bool success, + hb_serialize_error_t err_type = HB_SERIALIZE_ERROR_OTHER) + { + return successful () + && (success || err (err_type)); + } + + template <typename T1, typename T2> + bool check_equal (T1 &&v1, T2 &&v2, hb_serialize_error_t err_type) + { + if ((long long) v1 != (long long) v2) + { + return err (err_type); + } + return true; + } + + template <typename T1, typename T2> + bool check_assign (T1 &v1, T2 &&v2, hb_serialize_error_t err_type) + { return check_equal (v1 = v2, v2, err_type); } + + template <typename T> bool propagate_error (T &&obj) + { return check_success (!hb_deref (obj).in_error ()); } + + template <typename T1, typename... Ts> bool propagate_error (T1 &&o1, Ts&&... os) + { return propagate_error (std::forward<T1> (o1)) && + propagate_error (std::forward<Ts> (os)...); } + + /* To be called around main operation. */ + template <typename Type=char> + __attribute__((returns_nonnull)) + Type *start_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + + assert (!current); + return push<Type> (); + } + void end_serialize () + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, + "end [%p..%p] serialized %u bytes; %s", + this->start, this->end, + (unsigned) (this->head - this->start), + successful () ? "successful" : "UNSUCCESSFUL"); + + propagate_error (packed, packed_map); + + if (unlikely (!current)) return; + if (unlikely (in_error())) + { + // Offset overflows that occur before link resolution cannot be handled + // by repacking, so set a more general error. + if (offset_overflow ()) err (HB_SERIALIZE_ERROR_OTHER); + return; + } + + assert (!current->next); + + /* Only "pack" if there exist other objects... Otherwise, don't bother. + * Saves a move. */ + if (packed.length <= 1) + return; + + pop_pack (false); + + resolve_links (); + } + + template <typename Type = void> + __attribute__((returns_nonnull)) + Type *push () + { + if (unlikely (in_error ())) return start_embed<Type> (); + + object_t *obj = object_pool.alloc (); + if (unlikely (!obj)) + check_success (false); + else + { + obj->head = head; + obj->tail = tail; + obj->next = current; + current = obj; + } + return start_embed<Type> (); + } + void pop_discard () + { + object_t *obj = current; + if (unlikely (!obj)) return; + // Allow cleanup when we've error'd out on int overflows which don't compromise + // the serializer state. + if (unlikely (in_error() && !only_overflow ())) return; + + current = current->next; + revert (zerocopy ? zerocopy : obj->head, obj->tail); + zerocopy = nullptr; + obj->fini (); + object_pool.release (obj); + } + + /* Set share to false when an object is unlikely shareable with others + * so not worth an attempt, or a contiguous table is serialized as + * multiple consecutive objects in the reverse order so can't be shared. + */ + objidx_t pop_pack (bool share=true) + { + object_t *obj = current; + if (unlikely (!obj)) return 0; + // Allow cleanup when we've error'd out on int overflows which don't compromise + // the serializer state. + if (unlikely (in_error() && !only_overflow ())) return 0; + + current = current->next; + obj->tail = head; + obj->next = nullptr; + assert (obj->head <= obj->tail); + unsigned len = obj->tail - obj->head; + head = zerocopy ? zerocopy : obj->head; /* Rewind head. */ + bool was_zerocopy = zerocopy; + zerocopy = nullptr; + + if (!len) + { + assert (!obj->real_links.length); + assert (!obj->virtual_links.length); + return 0; + } + + objidx_t objidx; + uint32_t hash = 0; + if (share) + { + hash = hb_hash (obj); + objidx = packed_map.get_with_hash (obj, hash); + if (objidx) + { + merge_virtual_links (obj, objidx); + obj->fini (); + return objidx; + } + } + + tail -= len; + if (was_zerocopy) + assert (tail == obj->head); + else + memmove (tail, obj->head, len); + + obj->head = tail; + obj->tail = tail + len; + + packed.push (obj); + + if (unlikely (!propagate_error (packed))) + { + /* Obj wasn't successfully added to packed, so clean it up otherwise its + * links will be leaked. When we use constructor/destructors properly, we + * can remove these. */ + obj->fini (); + return 0; + } + + objidx = packed.length - 1; + + if (share) packed_map.set_with_hash (obj, hash, objidx); + propagate_error (packed_map); + + return objidx; + } + + void revert (snapshot_t snap) + { + // Overflows that happened after the snapshot will be erased by the revert. + if (unlikely (in_error () && !only_overflow ())) return; + assert (snap.current == current); + if (current) + { + current->real_links.shrink (snap.num_real_links); + current->virtual_links.shrink (snap.num_virtual_links); + } + errors = snap.errors; + revert (snap.head, snap.tail); + } + + void revert (char *snap_head, + char *snap_tail) + { + if (unlikely (in_error ())) return; + assert (snap_head <= head); + assert (tail <= snap_tail); + head = snap_head; + tail = snap_tail; + discard_stale_objects (); + } + + void discard_stale_objects () + { + if (unlikely (in_error ())) return; + while (packed.length > 1 && + packed.tail ()->head < tail) + { + packed_map.del (packed.tail ()); + assert (!packed.tail ()->next); + packed.tail ()->fini (); + packed.pop (); + } + if (packed.length > 1) + assert (packed.tail ()->head == tail); + } + + // Adds a virtual link from the current object to objidx. A virtual link is not associated with + // an actual offset field. They are solely used to enforce ordering constraints between objects. + // Adding a virtual link from object a to object b will ensure that object b is always packed after + // object a in the final serialized order. + // + // This is useful in certain situations where there needs to be a specific ordering in the + // final serialization. Such as when platform bugs require certain orderings, or to provide + // guidance to the repacker for better offset overflow resolution. + void add_virtual_link (objidx_t objidx) + { + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + + auto& link = *current->virtual_links.push (); + if (current->virtual_links.in_error ()) + err (HB_SERIALIZE_ERROR_OTHER); + + link.width = 0; + link.objidx = objidx; + link.is_signed = 0; + link.whence = 0; + link.position = 0; + link.bias = 0; + } + + template <typename T> + void add_link (T &ofs, objidx_t objidx, + whence_t whence = Head, + unsigned bias = 0) + { + if (unlikely (in_error ())) return; + + if (!objidx) + return; + + assert (current); + assert (current->head <= (const char *) &ofs); + + auto& link = *current->real_links.push (); + if (current->real_links.in_error ()) + err (HB_SERIALIZE_ERROR_OTHER); + + link.width = sizeof (T); + link.objidx = objidx; + if (unlikely (!sizeof (T))) + { + // This link is not associated with an actual offset and exists merely to enforce + // an ordering constraint. + link.is_signed = 0; + link.whence = 0; + link.position = 0; + link.bias = 0; + return; + } + + link.is_signed = std::is_signed<hb_unwrap_type (T)>::value; + link.whence = (unsigned) whence; + link.position = (const char *) &ofs - current->head; + link.bias = bias; + } + + unsigned to_bias (const void *base) const + { + if (unlikely (in_error ())) return 0; + if (!base) return 0; + assert (current); + assert (current->head <= (const char *) base); + return (const char *) base - current->head; + } + + void resolve_links () + { + if (unlikely (in_error ())) return; + + assert (!current); + assert (packed.length > 1); + + for (const object_t* parent : ++hb_iter (packed)) + for (const object_t::link_t &link : parent->real_links) + { + const object_t* child = packed[link.objidx]; + if (unlikely (!child)) { err (HB_SERIALIZE_ERROR_OTHER); return; } + unsigned offset = 0; + switch ((whence_t) link.whence) { + case Head: offset = child->head - parent->head; break; + case Tail: offset = child->head - parent->tail; break; + case Absolute: offset = (head - start) + (child->head - tail); break; + } + + assert (offset >= link.bias); + offset -= link.bias; + if (link.is_signed) + { + assert (link.width == 2 || link.width == 4); + if (link.width == 4) + assign_offset<int32_t> (parent, link, offset); + else + assign_offset<int16_t> (parent, link, offset); + } + else + { + assert (link.width == 2 || link.width == 3 || link.width == 4); + if (link.width == 4) + assign_offset<uint32_t> (parent, link, offset); + else if (link.width == 3) + assign_offset<uint32_t, 3> (parent, link, offset); + else + assign_offset<uint16_t> (parent, link, offset); + } + } + } + + unsigned int length () const + { + if (unlikely (!current)) return 0; + return this->head - current->head; + } + + void align (unsigned int alignment) + { + unsigned int l = length () % alignment; + if (l) + (void) allocate_size<void> (alignment - l); + } + + template <typename Type = void> + __attribute__((returns_nonnull)) + Type *start_embed (const Type *obj HB_UNUSED = nullptr) const + { return reinterpret_cast<Type *> (this->head); } + template <typename Type> + __attribute__((returns_nonnull)) + Type *start_embed (const Type &obj) const + { return start_embed (std::addressof (obj)); } + + bool err (hb_serialize_error_t err_type) + { + return !bool ((errors = (errors | err_type))); + } + + bool start_zerocopy (size_t size) + { + if (unlikely (in_error ())) return false; + + if (unlikely (size > INT_MAX || this->tail - this->head < ptrdiff_t (size))) + { + err (HB_SERIALIZE_ERROR_OUT_OF_ROOM); + return false; + } + + assert (!this->zerocopy); + this->zerocopy = this->head; + + assert (this->current->head == this->head); + this->current->head = this->current->tail = this->head = this->tail - size; + return true; + } + + template <typename Type> + HB_NODISCARD + Type *allocate_size (size_t size, bool clear = true) + { + if (unlikely (in_error ())) return nullptr; + + if (unlikely (size > INT_MAX || this->tail - this->head < ptrdiff_t (size))) + { + err (HB_SERIALIZE_ERROR_OUT_OF_ROOM); + return nullptr; + } + if (clear) + hb_memset (this->head, 0, size); + char *ret = this->head; + this->head += size; + return reinterpret_cast<Type *> (ret); + } + + template <typename Type> + Type *allocate_min () + { return this->allocate_size<Type> (Type::min_size); } + + template <typename Type> + HB_NODISCARD + Type *embed (const Type *obj) + { + unsigned int size = obj->get_size (); + Type *ret = this->allocate_size<Type> (size, false); + if (unlikely (!ret)) return nullptr; + hb_memcpy (ret, obj, size); + return ret; + } + template <typename Type> + HB_NODISCARD + Type *embed (const Type &obj) + { return embed (std::addressof (obj)); } + char *embed (const char *obj, unsigned size) + { + char *ret = this->allocate_size<char> (size, false); + if (unlikely (!ret)) return nullptr; + hb_memcpy (ret, obj, size); + return ret; + } + + template <typename Type, typename ...Ts> auto + _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN + (Type *, src.copy (this, std::forward<Ts> (ds)...)) + + template <typename Type> auto + _copy (const Type &src, hb_priority<0>) -> decltype (&(hb_declval<Type> () = src)) + { + Type *ret = this->allocate_size<Type> (sizeof (Type)); + if (unlikely (!ret)) return nullptr; + *ret = src; + return ret; + } + + /* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data + * instead of hb_memcpy(). */ + template <typename Type, typename ...Ts> + Type *copy (const Type &src, Ts&&... ds) + { return _copy (src, hb_prioritize, std::forward<Ts> (ds)...); } + template <typename Type, typename ...Ts> + Type *copy (const Type *src, Ts&&... ds) + { return copy (*src, std::forward<Ts> (ds)...); } + + template<typename Iterator, + hb_requires (hb_is_iterator (Iterator)), + typename ...Ts> + void copy_all (Iterator it, Ts&&... ds) + { for (decltype (*it) _ : it) copy (_, std::forward<Ts> (ds)...); } + + template <typename Type> + hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; } + + template <typename Type> + Type *extend_size (Type *obj, size_t size, bool clear = true) + { + if (unlikely (in_error ())) return nullptr; + + assert (this->start <= (char *) obj); + assert ((char *) obj <= this->head); + assert ((size_t) (this->head - (char *) obj) <= size); + if (unlikely (((char *) obj + size < (char *) obj) || + !this->allocate_size<Type> (((char *) obj) + size - this->head, clear))) return nullptr; + return reinterpret_cast<Type *> (obj); + } + template <typename Type> + Type *extend_size (Type &obj, size_t size, bool clear = true) + { return extend_size (std::addressof (obj), size, clear); } + + template <typename Type> + Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); } + template <typename Type> + Type *extend_min (Type &obj) { return extend_min (std::addressof (obj)); } + + template <typename Type, typename ...Ts> + Type *extend (Type *obj, Ts&&... ds) + { return extend_size (obj, obj->get_size (std::forward<Ts> (ds)...)); } + template <typename Type, typename ...Ts> + Type *extend (Type &obj, Ts&&... ds) + { return extend (std::addressof (obj), std::forward<Ts> (ds)...); } + + /* Output routines. */ + hb_bytes_t copy_bytes () const + { + assert (successful ()); + /* Copy both items from head side and tail side... */ + unsigned int len = (this->head - this->start) + + (this->end - this->tail); + + // If len is zero don't hb_malloc as the memory won't get properly + // cleaned up later. + if (!len) return hb_bytes_t (); + + char *p = (char *) hb_malloc (len); + if (unlikely (!p)) return hb_bytes_t (); + + hb_memcpy (p, this->start, this->head - this->start); + hb_memcpy (p + (this->head - this->start), this->tail, this->end - this->tail); + return hb_bytes_t (p, len); + } + template <typename Type> + Type *copy () const + { return reinterpret_cast<Type *> ((char *) copy_bytes ().arrayZ); } + hb_blob_t *copy_blob () const + { + hb_bytes_t b = copy_bytes (); + return hb_blob_create (b.arrayZ, b.length, + HB_MEMORY_MODE_WRITABLE, + (char *) b.arrayZ, hb_free); + } + + const hb_vector_t<object_t *>& object_graph() const + { return packed; } + + private: + template <typename T, unsigned Size = sizeof (T)> + void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset) + { + auto &off = * ((BEInt<T, Size> *) (parent->head + link.position)); + assert (0 == off); + check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW); + } + + public: + char *start, *head, *tail, *end, *zerocopy; + unsigned int debug_depth; + hb_serialize_error_t errors; + + private: + + void merge_virtual_links (const object_t* from, objidx_t to_idx) { + object_t* to = packed[to_idx]; + for (const auto& l : from->virtual_links) { + to->virtual_links.push (l); + } + } + + /* Object memory pool. */ + hb_pool_t<object_t> object_pool; + + /* Stack of currently under construction objects. */ + object_t *current; + + /* Stack of packed objects. Object 0 is always nil object. */ + hb_vector_t<object_t *> packed; + + /* Map view of packed objects. */ + hb_hashmap_t<const object_t *, objidx_t> packed_map; +}; + +#endif /* HB_SERIALIZE_HH */ diff --git a/gfx/harfbuzz/src/hb-set-digest.hh b/gfx/harfbuzz/src/hb-set-digest.hh new file mode 100644 index 0000000000..5681641baa --- /dev/null +++ b/gfx/harfbuzz/src/hb-set-digest.hh @@ -0,0 +1,217 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SET_DIGEST_HH +#define HB_SET_DIGEST_HH + +#include "hb.hh" +#include "hb-machinery.hh" + +/* + * The set-digests here implement various "filters" that support + * "approximate member query". Conceptually these are like Bloom + * Filter and Quotient Filter, however, much smaller, faster, and + * designed to fit the requirements of our uses for glyph coverage + * queries. + * + * Our filters are highly accurate if the lookup covers fairly local + * set of glyphs, but fully flooded and ineffective if coverage is + * all over the place. + * + * The way these are used is that the filter is first populated by + * a lookup's or subtable's Coverage table(s), and then when we + * want to apply the lookup or subtable to a glyph, before trying + * to apply, we ask the filter if the glyph may be covered. If it's + * not, we return early. We can also match a digest against another + * digest. + * + * We use these filters at three levels: + * - If the digest for all the glyphs in the buffer as a whole + * does not match the digest for the lookup, skip the lookup. + * - For each glyph, if it doesn't match the lookup digest, + * skip it. + * - For each glyph, if it doesn't match the subtable digest, + * skip it. + * + * The main filter we use is a combination of three bits-pattern + * filters. A bits-pattern filter checks a number of bits (5 or 6) + * of the input number (glyph-id in this case) and checks whether + * its pattern is amongst the patterns of any of the accepted values. + * The accepted patterns are represented as a "long" integer. The + * check is done using four bitwise operations only. + */ + +template <typename mask_t, unsigned int shift> +struct hb_set_digest_bits_pattern_t +{ + static constexpr unsigned mask_bytes = sizeof (mask_t); + static constexpr unsigned mask_bits = sizeof (mask_t) * 8; + static constexpr unsigned num_bits = 0 + + (mask_bytes >= 1 ? 3 : 0) + + (mask_bytes >= 2 ? 1 : 0) + + (mask_bytes >= 4 ? 1 : 0) + + (mask_bytes >= 8 ? 1 : 0) + + (mask_bytes >= 16? 1 : 0) + + 0; + + static_assert ((shift < sizeof (hb_codepoint_t) * 8), ""); + static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), ""); + + void init () { mask = 0; } + + void add (const hb_set_digest_bits_pattern_t &o) { mask |= o.mask; } + + void add (hb_codepoint_t g) { mask |= mask_for (g); } + + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + if (mask == (mask_t) -1) return false; + if ((b >> shift) - (a >> shift) >= mask_bits - 1) + { + mask = (mask_t) -1; + return false; + } + else + { + mask_t ma = mask_for (a); + mask_t mb = mask_for (b); + mask |= mb + (mb - ma) - (mb < ma); + return true; + } + } + + template <typename T> + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + for (unsigned int i = 0; i < count; i++) + { + add (*array); + array = &StructAtOffsetUnaligned<T> ((const void *) array, stride); + } + } + template <typename T> + void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); } + template <typename T> + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + add_array (array, count, stride); + return true; + } + template <typename T> + bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); } + + bool may_have (const hb_set_digest_bits_pattern_t &o) const + { return mask & o.mask; } + + bool may_have (hb_codepoint_t g) const + { return mask & mask_for (g); } + + private: + + static mask_t mask_for (hb_codepoint_t g) + { return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); } + mask_t mask; +}; + +template <typename head_t, typename tail_t> +struct hb_set_digest_combiner_t +{ + void init () + { + head.init (); + tail.init (); + } + + void add (const hb_set_digest_combiner_t &o) + { + head.add (o.head); + tail.add (o.tail); + } + + void add (hb_codepoint_t g) + { + head.add (g); + tail.add (g); + } + + bool add_range (hb_codepoint_t a, hb_codepoint_t b) + { + return (int) head.add_range (a, b) | (int) tail.add_range (a, b); + } + template <typename T> + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + head.add_array (array, count, stride); + tail.add_array (array, count, stride); + } + template <typename T> + void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); } + template <typename T> + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { + return head.add_sorted_array (array, count, stride) && + tail.add_sorted_array (array, count, stride); + } + template <typename T> + bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); } + + bool may_have (const hb_set_digest_combiner_t &o) const + { + return head.may_have (o.head) && tail.may_have (o.tail); + } + + bool may_have (hb_codepoint_t g) const + { + return head.may_have (g) && tail.may_have (g); + } + + private: + head_t head; + tail_t tail; +}; + + +/* + * hb_set_digest_t + * + * This is a combination of digests that performs "best". + * There is not much science to this: it's a result of intuition + * and testing. + */ +using hb_set_digest_t = + hb_set_digest_combiner_t + < + hb_set_digest_bits_pattern_t<unsigned long, 4>, + hb_set_digest_combiner_t + < + hb_set_digest_bits_pattern_t<unsigned long, 0>, + hb_set_digest_bits_pattern_t<unsigned long, 9> + > + > +; + + +#endif /* HB_SET_DIGEST_HH */ diff --git a/gfx/harfbuzz/src/hb-set.cc b/gfx/harfbuzz/src/hb-set.cc new file mode 100644 index 0000000000..a9386c5c91 --- /dev/null +++ b/gfx/harfbuzz/src/hb-set.cc @@ -0,0 +1,673 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb-set.hh" + + +/** + * SECTION:hb-set + * @title: hb-set + * @short_description: Objects representing a set of integers + * @include: hb.h + * + * Set objects represent a mathematical set of integer values. They are + * used in non-shaping APIs to query certain sets of characters or glyphs, + * or other integer values. + **/ + + +/** + * hb_set_create: + * + * Creates a new, initially empty set. + * + * Return value: (transfer full): The new #hb_set_t + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_create () +{ + hb_set_t *set; + + if (!(set = hb_object_create<hb_set_t> ())) + return hb_set_get_empty (); + + return set; +} + +/** + * hb_set_get_empty: + * + * Fetches the singleton empty #hb_set_t. + * + * Return value: (transfer full): The empty #hb_set_t + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_get_empty () +{ + return const_cast<hb_set_t *> (&Null (hb_set_t)); +} + +/** + * hb_set_reference: (skip) + * @set: A set + * + * Increases the reference count on a set. + * + * Return value: (transfer full): The set + * + * Since: 0.9.2 + **/ +hb_set_t * +hb_set_reference (hb_set_t *set) +{ + return hb_object_reference (set); +} + +/** + * hb_set_destroy: (skip) + * @set: A set + * + * Decreases the reference count on a set. When + * the reference count reaches zero, the set is + * destroyed, freeing all memory. + * + * Since: 0.9.2 + **/ +void +hb_set_destroy (hb_set_t *set) +{ + if (!hb_object_destroy (set)) return; + + hb_free (set); +} + +/** + * hb_set_set_user_data: (skip) + * @set: A set + * @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 set. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_set_user_data (hb_set_t *set, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (set, key, data, destroy, replace); +} + +/** + * hb_set_get_user_data: (skip) + * @set: A set + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified set. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_set_get_user_data (const hb_set_t *set, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (set, key); +} + + +/** + * hb_set_allocation_successful: + * @set: A set + * + * Tests whether memory allocation for a set was successful. + * + * Return value: `true` if allocation succeeded, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_allocation_successful (const hb_set_t *set) +{ + return !set->in_error (); +} + +/** + * hb_set_copy: + * @set: A set + * + * Allocate a copy of @set. + * + * Return value: (transfer full): Newly-allocated set. + * + * Since: 2.8.2 + **/ +hb_set_t * +hb_set_copy (const hb_set_t *set) +{ + hb_set_t *copy = hb_set_create (); + if (unlikely (copy->in_error ())) + return hb_set_get_empty (); + + copy->set (*set); + return copy; +} + +/** + * hb_set_clear: + * @set: A set + * + * Clears out the contents of a set. + * + * Since: 0.9.2 + **/ +void +hb_set_clear (hb_set_t *set) +{ + /* Immutable-safe. */ + set->clear (); +} + +/** + * hb_set_is_empty: + * @set: a set. + * + * Tests whether a set is empty (contains no elements). + * + * Return value: `true` if @set is empty + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_is_empty (const hb_set_t *set) +{ + return set->is_empty (); +} + +/** + * hb_set_has: + * @set: A set + * @codepoint: The element to query + * + * Tests whether @codepoint belongs to @set. + * + * Return value: `true` if @codepoint is in @set, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_has (const hb_set_t *set, + hb_codepoint_t codepoint) +{ + return set->has (codepoint); +} + +/** + * hb_set_add: + * @set: A set + * @codepoint: The element to add to @set + * + * Adds @codepoint to @set. + * + * Since: 0.9.2 + **/ +void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint) +{ + /* Immutable-safe. */ + set->add (codepoint); +} + +/** + * hb_set_add_sorted_array: + * @set: A set + * @sorted_codepoints: (array length=num_codepoints): Array of codepoints to add + * @num_codepoints: Length of @sorted_codepoints + * + * Adds @num_codepoints codepoints to a set at once. + * The codepoints array must be in increasing order, + * with size at least @num_codepoints. + * + * Since: 4.1.0 + */ +HB_EXTERN void +hb_set_add_sorted_array (hb_set_t *set, + const hb_codepoint_t *sorted_codepoints, + unsigned int num_codepoints) +{ + /* Immutable-safe. */ + set->add_sorted_array (sorted_codepoints, + num_codepoints, + sizeof(hb_codepoint_t)); +} + +/** + * hb_set_add_range: + * @set: A set + * @first: The first element to add to @set + * @last: The final element to add to @set + * + * Adds all of the elements from @first to @last + * (inclusive) to @set. + * + * Since: 0.9.7 + **/ +void +hb_set_add_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last) +{ + /* Immutable-safe. */ + set->add_range (first, last); +} + +/** + * hb_set_del: + * @set: A set + * @codepoint: Removes @codepoint from @set + * + * Removes @codepoint from @set. + * + * Since: 0.9.2 + **/ +void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint) +{ + /* Immutable-safe. */ + set->del (codepoint); +} + +/** + * hb_set_del_range: + * @set: A set + * @first: The first element to remove from @set + * @last: The final element to remove from @set + * + * Removes all of the elements from @first to @last + * (inclusive) from @set. + * + * If @last is #HB_SET_VALUE_INVALID, then all values + * greater than or equal to @first are removed. + * + * Since: 0.9.7 + **/ +void +hb_set_del_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last) +{ + /* Immutable-safe. */ + set->del_range (first, last); +} + +/** + * hb_set_is_equal: + * @set: A set + * @other: Another set + * + * Tests whether @set and @other are equal (contain the same + * elements). + * + * Return value: `true` if the two sets are equal, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other) +{ + return set->is_equal (*other); +} + +/** + * hb_set_hash: + * @set: A set + * + * Creates a hash representing @set. + * + * Return value: + * A hash of @set. + * + * Since: 4.4.0 + **/ +HB_EXTERN unsigned int +hb_set_hash (const hb_set_t *set) +{ + return set->hash (); +} + +/** + * hb_set_is_subset: + * @set: A set + * @larger_set: Another set + * + * Tests whether @set is a subset of @larger_set. + * + * Return value: `true` if the @set is a subset of (or equal to) @larger_set, `false` otherwise. + * + * Since: 1.8.1 + **/ +hb_bool_t +hb_set_is_subset (const hb_set_t *set, + const hb_set_t *larger_set) +{ + return set->is_subset (*larger_set); +} + +/** + * hb_set_set: + * @set: A set + * @other: Another set + * + * Makes the contents of @set equal to the contents of @other. + * + * Since: 0.9.2 + **/ +void +hb_set_set (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutable-safe. */ + set->set (*other); +} + +/** + * hb_set_union: + * @set: A set + * @other: Another set + * + * Makes @set the union of @set and @other. + * + * Since: 0.9.2 + **/ +void +hb_set_union (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutable-safe. */ + set->union_ (*other); +} + +/** + * hb_set_intersect: + * @set: A set + * @other: Another set + * + * Makes @set the intersection of @set and @other. + * + * Since: 0.9.2 + **/ +void +hb_set_intersect (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutable-safe. */ + set->intersect (*other); +} + +/** + * hb_set_subtract: + * @set: A set + * @other: Another set + * + * Subtracts the contents of @other from @set. + * + * Since: 0.9.2 + **/ +void +hb_set_subtract (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutable-safe. */ + set->subtract (*other); +} + +/** + * hb_set_symmetric_difference: + * @set: A set + * @other: Another set + * + * Makes @set the symmetric difference of @set + * and @other. + * + * Since: 0.9.2 + **/ +void +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other) +{ + /* Immutable-safe. */ + set->symmetric_difference (*other); +} + +/** + * hb_set_invert: + * @set: A set + * + * Inverts the contents of @set. + * + * Since: 3.0.0 + **/ +void +hb_set_invert (hb_set_t *set) +{ + /* Immutable-safe. */ + set->invert (); +} + +/** + * hb_set_is_inverted: + * @set: A set + * + * Returns whether the set is inverted. + * + * Return value: `true` if the set is inverted, `false` otherwise + * + * Since: 7.0.0 + **/ +hb_bool_t +hb_set_is_inverted (const hb_set_t *set) +{ + return set->is_inverted (); +} + +/** + * hb_set_get_population: + * @set: A set + * + * Returns the number of elements in the set. + * + * Return value: The population of @set + * + * Since: 0.9.7 + **/ +unsigned int +hb_set_get_population (const hb_set_t *set) +{ + return set->get_population (); +} + +/** + * hb_set_get_min: + * @set: A set + * + * Finds the smallest element in the set. + * + * Return value: minimum of @set, or #HB_SET_VALUE_INVALID if @set is empty. + * + * Since: 0.9.7 + **/ +hb_codepoint_t +hb_set_get_min (const hb_set_t *set) +{ + return set->get_min (); +} + +/** + * hb_set_get_max: + * @set: A set + * + * Finds the largest element in the set. + * + * Return value: maximum of @set, or #HB_SET_VALUE_INVALID if @set is empty. + * + * Since: 0.9.7 + **/ +hb_codepoint_t +hb_set_get_max (const hb_set_t *set) +{ + return set->get_max (); +} + +/** + * hb_set_next: + * @set: A set + * @codepoint: (inout): Input = Code point to query + * Output = Code point retrieved + * + * Fetches the next element in @set that is greater than current value of @codepoint. + * + * Set @codepoint to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a next value, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_set_next (const hb_set_t *set, + hb_codepoint_t *codepoint) +{ + return set->next (codepoint); +} + +/** + * hb_set_previous: + * @set: A set + * @codepoint: (inout): Input = Code point to query + * Output = Code point retrieved + * + * Fetches the previous element in @set that is lower than current value of @codepoint. + * + * Set @codepoint to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a previous value, `false` otherwise + * + * Since: 1.8.0 + **/ +hb_bool_t +hb_set_previous (const hb_set_t *set, + hb_codepoint_t *codepoint) +{ + return set->previous (codepoint); +} + +/** + * hb_set_next_range: + * @set: A set + * @first: (out): The first code point in the range + * @last: (inout): Input = The current last code point in the range + * Output = The last code point in the range + * + * Fetches the next consecutive range of elements in @set that + * are greater than current value of @last. + * + * Set @last to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a next range, `false` otherwise + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_set_next_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last) +{ + return set->next_range (first, last); +} + +/** + * hb_set_previous_range: + * @set: A set + * @first: (inout): Input = The current first code point in the range + * Output = The first code point in the range + * @last: (out): The last code point in the range + * + * Fetches the previous consecutive range of elements in @set that + * are greater than current value of @last. + * + * Set @first to #HB_SET_VALUE_INVALID to get started. + * + * Return value: `true` if there was a previous range, `false` otherwise + * + * Since: 1.8.0 + **/ +hb_bool_t +hb_set_previous_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last) +{ + return set->previous_range (first, last); +} + +/** + * hb_set_next_many: + * @set: A set + * @codepoint: Outputting codepoints starting after this one. + * Use #HB_SET_VALUE_INVALID to get started. + * @out: (array length=size): An array of codepoints to write to. + * @size: The maximum number of codepoints to write out. + * + * Finds the next element in @set that is greater than @codepoint. Writes out + * codepoints to @out, until either the set runs out of elements, or @size + * codepoints are written, whichever comes first. + * + * Return value: the number of values written. + * + * Since: 4.2.0 + **/ +unsigned int +hb_set_next_many (const hb_set_t *set, + hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size) +{ + return set->next_many (codepoint, out, size); +} diff --git a/gfx/harfbuzz/src/hb-set.h b/gfx/harfbuzz/src/hb-set.h new file mode 100644 index 0000000000..192abf6f63 --- /dev/null +++ b/gfx/harfbuzz/src/hb-set.h @@ -0,0 +1,203 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_SET_H +#define HB_SET_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_SET_VALUE_INVALID: + * + * Unset #hb_set_t value. + * + * Since: 0.9.21 + */ +#define HB_SET_VALUE_INVALID HB_CODEPOINT_INVALID + +/** + * hb_set_t: + * + * Data type for holding a set of integers. #hb_set_t's are + * used to gather and contain glyph IDs, Unicode code + * points, and various other collections of discrete + * values. + * + **/ +typedef struct hb_set_t hb_set_t; + + +HB_EXTERN hb_set_t * +hb_set_create (void); + +HB_EXTERN hb_set_t * +hb_set_get_empty (void); + +HB_EXTERN hb_set_t * +hb_set_reference (hb_set_t *set); + +HB_EXTERN void +hb_set_destroy (hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_set_user_data (hb_set_t *set, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_set_get_user_data (const hb_set_t *set, + hb_user_data_key_t *key); + + +/* Returns false if allocation has failed before */ +HB_EXTERN hb_bool_t +hb_set_allocation_successful (const hb_set_t *set); + +HB_EXTERN hb_set_t * +hb_set_copy (const hb_set_t *set); + +HB_EXTERN void +hb_set_clear (hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_is_empty (const hb_set_t *set); + +HB_EXTERN void +hb_set_invert (hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_is_inverted (const hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_has (const hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_add (hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_add_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + +HB_EXTERN void +hb_set_add_sorted_array (hb_set_t *set, + const hb_codepoint_t *sorted_codepoints, + unsigned int num_codepoints); + +HB_EXTERN void +hb_set_del (hb_set_t *set, + hb_codepoint_t codepoint); + +HB_EXTERN void +hb_set_del_range (hb_set_t *set, + hb_codepoint_t first, + hb_codepoint_t last); + +HB_EXTERN hb_bool_t +hb_set_is_equal (const hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN unsigned int +hb_set_hash (const hb_set_t *set); + +HB_EXTERN hb_bool_t +hb_set_is_subset (const hb_set_t *set, + const hb_set_t *larger_set); + +HB_EXTERN void +hb_set_set (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_union (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_intersect (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_subtract (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN void +hb_set_symmetric_difference (hb_set_t *set, + const hb_set_t *other); + +HB_EXTERN unsigned int +hb_set_get_population (const hb_set_t *set); + +/* Returns HB_SET_VALUE_INVALID if set empty. */ +HB_EXTERN hb_codepoint_t +hb_set_get_min (const hb_set_t *set); + +/* Returns HB_SET_VALUE_INVALID if set empty. */ +HB_EXTERN hb_codepoint_t +hb_set_get_max (const hb_set_t *set); + +/* Pass HB_SET_VALUE_INVALID in to get started. */ +HB_EXTERN hb_bool_t +hb_set_next (const hb_set_t *set, + hb_codepoint_t *codepoint); + +/* Pass HB_SET_VALUE_INVALID in to get started. */ +HB_EXTERN hb_bool_t +hb_set_previous (const hb_set_t *set, + hb_codepoint_t *codepoint); + +/* Pass HB_SET_VALUE_INVALID for first and last to get started. */ +HB_EXTERN hb_bool_t +hb_set_next_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last); + +/* Pass HB_SET_VALUE_INVALID for first and last to get started. */ +HB_EXTERN hb_bool_t +hb_set_previous_range (const hb_set_t *set, + hb_codepoint_t *first, + hb_codepoint_t *last); + +/* Pass HB_SET_VALUE_INVALID in to get started. */ +HB_EXTERN unsigned int +hb_set_next_many (const hb_set_t *set, + hb_codepoint_t codepoint, + hb_codepoint_t *out, + unsigned int size); + +HB_END_DECLS + +#endif /* HB_SET_H */ diff --git a/gfx/harfbuzz/src/hb-set.hh b/gfx/harfbuzz/src/hb-set.hh new file mode 100644 index 0000000000..ff2a170d2d --- /dev/null +++ b/gfx/harfbuzz/src/hb-set.hh @@ -0,0 +1,186 @@ +/* + * 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_SET_HH +#define HB_SET_HH + +#include "hb.hh" +#include "hb-bit-set-invertible.hh" + + +template <typename impl_t> +struct hb_sparseset_t +{ + static constexpr bool realloc_move = true; + + hb_object_header_t header; + impl_t s; + + hb_sparseset_t () { init (); } + ~hb_sparseset_t () { fini (); } + + hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); } + hb_sparseset_t (hb_sparseset_t&& other) : hb_sparseset_t () { s = std::move (other.s); } + hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; } + hb_sparseset_t& operator = (hb_sparseset_t&& other) { s = std::move (other.s); return *this; } + friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) { hb_swap (a.s, b.s); } + + hb_sparseset_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t () + { + for (auto&& item : lst) + add (item); + } + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + hb_sparseset_t (const Iterable &o) : hb_sparseset_t () + { + hb_copy (o, *this); + } + + void init () + { + hb_object_init (this); + s.init (); + } + void fini () + { + hb_object_fini (this); + s.fini (); + } + + explicit operator bool () const { return !is_empty (); } + + void err () { s.err (); } + bool in_error () const { return s.in_error (); } + + void alloc (unsigned sz) { s.alloc (sz); } + void reset () { s.reset (); } + void clear () { s.clear (); } + void invert () { s.invert (); } + bool is_inverted () const { return s.is_inverted (); } + bool is_empty () const { return s.is_empty (); } + uint32_t hash () const { return s.hash (); } + + void add (hb_codepoint_t g) { s.add (g); } + bool add_range (hb_codepoint_t a, hb_codepoint_t b) { return s.add_range (a, b); } + + template <typename T> + void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { s.add_array (array, count, stride); } + template <typename T> + void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); } + + /* Might return false if array looks unsorted. + * Used for faster rejection of corrupt data. */ + template <typename T> + bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) + { return s.add_sorted_array (array, count, stride); } + template <typename T> + bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); } + + void del (hb_codepoint_t g) { s.del (g); } + void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); } + + bool get (hb_codepoint_t g) const { return s.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_sparseset_t& operator << (hb_codepoint_t v) + { add (v); return *this; } + hb_sparseset_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 + { return s.intersects (first, last); } + + void set (const hb_sparseset_t &other) { s.set (other.s); } + + bool is_equal (const hb_sparseset_t &other) const { return s.is_equal (other.s); } + bool operator == (const hb_set_t &other) const { return is_equal (other); } + bool operator != (const hb_set_t &other) const { return !is_equal (other); } + + bool is_subset (const hb_sparseset_t &larger_set) const { return s.is_subset (larger_set.s); } + + void union_ (const hb_sparseset_t &other) { s.union_ (other.s); } + void intersect (const hb_sparseset_t &other) { s.intersect (other.s); } + void subtract (const hb_sparseset_t &other) { s.subtract (other.s); } + void symmetric_difference (const hb_sparseset_t &other) { s.symmetric_difference (other.s); } + + bool next (hb_codepoint_t *codepoint) const { return s.next (codepoint); } + bool previous (hb_codepoint_t *codepoint) const { return s.previous (codepoint); } + bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { return s.next_range (first, last); } + bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const + { return s.previous_range (first, last); } + unsigned int next_many (hb_codepoint_t codepoint, hb_codepoint_t *out, unsigned int size) const + { return s.next_many (codepoint, out, size); } + + unsigned int get_population () const { return s.get_population (); } + hb_codepoint_t get_min () const { return s.get_min (); } + hb_codepoint_t get_max () const { return s.get_max (); } + + static constexpr hb_codepoint_t INVALID = impl_t::INVALID; + + /* + * Iterator implementation. + */ + using iter_t = typename impl_t::iter_t; + iter_t iter () const { return iter_t (this->s); } + operator iter_t () const { return iter (); } +}; + +struct hb_set_t : hb_sparseset_t<hb_bit_set_invertible_t> +{ + using sparseset = hb_sparseset_t<hb_bit_set_invertible_t>; + + ~hb_set_t () = default; + hb_set_t () : sparseset () {}; + hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {}; + hb_set_t (hb_set_t&& o) : sparseset (std::move ((sparseset &) o)) {} + hb_set_t& operator = (const hb_set_t&) = default; + hb_set_t& operator = (hb_set_t&&) = default; + hb_set_t (std::initializer_list<hb_codepoint_t> lst) : sparseset (lst) {} + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + hb_set_t (const Iterable &o) : sparseset (o) {} + + hb_set_t& operator << (hb_codepoint_t v) + { sparseset::operator<< (v); return *this; } + hb_set_t& operator << (const hb_codepoint_pair_t& range) + { sparseset::operator<< (range); return *this; } +}; + +static_assert (hb_set_t::INVALID == HB_SET_VALUE_INVALID, ""); + + +#endif /* HB_SET_HH */ diff --git a/gfx/harfbuzz/src/hb-shape-plan.cc b/gfx/harfbuzz/src/hb-shape-plan.cc new file mode 100644 index 0000000000..312eeb653e --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape-plan.cc @@ -0,0 +1,581 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-shape-plan.hh" +#include "hb-shaper.hh" +#include "hb-font.hh" +#include "hb-buffer.hh" + + +#ifndef HB_NO_SHAPER + +/** + * SECTION:hb-shape-plan + * @title: hb-shape-plan + * @short_description: Object representing a shaping plan + * @include: hb.h + * + * Shape plans are an internal mechanism. Each plan contains state + * describing how HarfBuzz will shape a particular text segment, based on + * the combination of segment properties and the capabilities in the + * font face in use. + * + * Shape plans are not used for shaping directly, but can be queried to + * access certain information about how shaping will perform, given a set + * of specific input parameters (script, language, direction, features, + * etc.). + * + * Most client programs will not need to deal with shape plans directly. + **/ + + +/* + * hb_shape_plan_key_t + */ + +bool +hb_shape_plan_key_t::init (bool copy, + hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + hb_feature_t *features = nullptr; + if (copy && num_user_features && !(features = (hb_feature_t *) hb_calloc (num_user_features, sizeof (hb_feature_t)))) + goto bail; + + this->props = *props; + this->num_user_features = num_user_features; + this->user_features = copy ? features : user_features; + if (copy && num_user_features) + { + hb_memcpy (features, user_features, num_user_features * sizeof (hb_feature_t)); + /* Make start/end uniform to easier catch bugs. */ + for (unsigned int i = 0; i < num_user_features; i++) + { + if (features[0].start != HB_FEATURE_GLOBAL_START) + features[0].start = 1; + if (features[0].end != HB_FEATURE_GLOBAL_END) + features[0].end = 2; + } + } + this->shaper_func = nullptr; + this->shaper_name = nullptr; +#ifndef HB_NO_OT_SHAPE + this->ot.init (face, coords, num_coords); +#endif + + /* + * Choose shaper. + */ + +#define HB_SHAPER_PLAN(shaper) \ + HB_STMT_START { \ + if (face->data.shaper) \ + { \ + this->shaper_func = _hb_##shaper##_shape; \ + this->shaper_name = #shaper; \ + return true; \ + } \ + } HB_STMT_END + + if (unlikely (shaper_list)) + { + for (; *shaper_list; shaper_list++) + if (false) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (0 == strcmp (*shaper_list, #shaper)) \ + HB_SHAPER_PLAN (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + else + { + const HB_UNUSED hb_shaper_entry_t *shapers = _hb_shapers_get (); + for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++) + if (false) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (shapers[i].func == _hb_##shaper##_shape) \ + HB_SHAPER_PLAN (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } +#undef HB_SHAPER_PLAN + +bail: + ::hb_free (features); + return false; +} + +bool +hb_shape_plan_key_t::user_features_match (const hb_shape_plan_key_t *other) +{ + if (this->num_user_features != other->num_user_features) + return false; + for (unsigned int i = 0; i < num_user_features; i++) + { + if (this->user_features[i].tag != other->user_features[i].tag || + this->user_features[i].value != other->user_features[i].value || + (this->user_features[i].start == HB_FEATURE_GLOBAL_START && + this->user_features[i].end == HB_FEATURE_GLOBAL_END) != + (other->user_features[i].start == HB_FEATURE_GLOBAL_START && + other->user_features[i].end == HB_FEATURE_GLOBAL_END)) + return false; + } + return true; +} + +bool +hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other) +{ + return hb_segment_properties_equal (&this->props, &other->props) && + this->user_features_match (other) && +#ifndef HB_NO_OT_SHAPE + this->ot.equal (&other->ot) && +#endif + this->shaper_func == other->shaper_func; +} + + +/* + * hb_shape_plan_t + */ + + +/** + * hb_shape_plan_create: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * Constructs a shaping plan for a combination of @face, @user_features, @props, + * and @shaper_list. + * + * Return value: (transfer full): The shaping plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_create (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list) +{ + return hb_shape_plan_create2 (face, props, + user_features, num_user_features, + nullptr, 0, + shaper_list); +} + +/** + * hb_shape_plan_create2: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @coords: (array length=num_coords): The list of variation-space coordinates + * @num_coords: The number of variation-space coordinates + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * The variable-font version of #hb_shape_plan_create. + * Constructs a shaping plan for a combination of @face, @user_features, @props, + * and @shaper_list, plus the variation-space coordinates @coords. + * + * Return value: (transfer full): The shaping plan + * + * Since: 1.4.0 + **/ +hb_shape_plan_t * +hb_shape_plan_create2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr, + "face=%p num_features=%u num_coords=%u shaper_list=%p", + face, + num_user_features, + num_coords, + shaper_list); + + if (unlikely (props->direction == HB_DIRECTION_INVALID)) + return hb_shape_plan_get_empty (); + + hb_shape_plan_t *shape_plan; + + if (unlikely (!props)) + goto bail; + if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) + goto bail; + + if (unlikely (!face)) + face = hb_face_get_empty (); + hb_face_make_immutable (face); + shape_plan->face_unsafe = face; + + if (unlikely (!shape_plan->key.init (true, + face, + props, + user_features, + num_user_features, + coords, + num_coords, + shaper_list))) + goto bail2; +#ifndef HB_NO_OT_SHAPE + if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key))) + goto bail3; +#endif + + return shape_plan; + +#ifndef HB_NO_OT_SHAPE +bail3: +#endif + shape_plan->key.fini (); +bail2: + hb_free (shape_plan); +bail: + return hb_shape_plan_get_empty (); +} + +/** + * hb_shape_plan_get_empty: + * + * Fetches the singleton empty shaping plan. + * + * Return value: (transfer full): The empty shaping plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_get_empty () +{ + return const_cast<hb_shape_plan_t *> (&Null (hb_shape_plan_t)); +} + +/** + * hb_shape_plan_reference: (skip) + * @shape_plan: A shaping plan + * + * Increases the reference count on the given shaping plan. + * + * Return value: (transfer full): @shape_plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_reference (hb_shape_plan_t *shape_plan) +{ + return hb_object_reference (shape_plan); +} + +/** + * hb_shape_plan_destroy: (skip) + * @shape_plan: A shaping plan + * + * Decreases the reference count on the given shaping plan. When the + * reference count reaches zero, the shaping plan is destroyed, + * freeing all memory. + * + * Since: 0.9.7 + **/ +void +hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) +{ + if (!hb_object_destroy (shape_plan)) return; + + hb_free (shape_plan); +} + +/** + * hb_shape_plan_set_user_data: (skip) + * @shape_plan: A shaping plan + * @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 shaping plan. + * + * Return value: `true` if success, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (shape_plan, key, data, destroy, replace); +} + +/** + * hb_shape_plan_get_user_data: (skip) + * @shape_plan: A shaping plan + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified shaping plan. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.7 + **/ +void * +hb_shape_plan_get_user_data (const hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (shape_plan, key); +} + +/** + * hb_shape_plan_get_shaper: + * @shape_plan: A shaping plan + * + * Fetches the shaper from a given shaping plan. + * + * Return value: (transfer none): The shaper + * + * Since: 0.9.7 + **/ +const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan) +{ + return shape_plan->key.shaper_name; +} + + +static bool +_hb_shape_plan_execute_internal (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, + "num_features=%u shaper_func=%p, shaper_name=%s", + num_features, + shape_plan->key.shaper_func, + shape_plan->key.shaper_name); + + if (unlikely (!buffer->len)) + return true; + + assert (!hb_object_is_immutable (buffer)); + + buffer->assert_unicode (); + + if (unlikely (!hb_object_is_valid (shape_plan))) + return false; + + assert (shape_plan->face_unsafe == font->face); + assert (hb_segment_properties_equal (&shape_plan->key.props, &buffer->props)); + +#define HB_SHAPER_EXECUTE(shaper) \ + HB_STMT_START { \ + return font->data.shaper && \ + _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \ + } HB_STMT_END + + if (false) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (shape_plan->key.shaper_func == _hb_##shaper##_shape) \ + HB_SHAPER_EXECUTE (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +#undef HB_SHAPER_EXECUTE + + return false; +} +/** + * hb_shape_plan_execute: + * @shape_plan: A shaping plan + * @font: The #hb_font_t to use + * @buffer: The #hb_buffer_t to work upon + * @features: (array length=num_features): Features to enable + * @num_features: The number of features to enable + * + * Executes the given shaping plan on the specified buffer, using + * the given @font and @features. + * + * Return value: `true` if success, `false` otherwise. + * + * Since: 0.9.7 + **/ +hb_bool_t +hb_shape_plan_execute (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + bool ret = _hb_shape_plan_execute_internal (shape_plan, font, buffer, + features, num_features); + + if (ret && buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) + buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; + + return ret; +} + + +/* + * Caching + */ + +/** + * hb_shape_plan_create_cached: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * Creates a cached shaping plan suitable for reuse, for a combination + * of @face, @user_features, @props, and @shaper_list. + * + * Return value: (transfer full): The shaping plan + * + * Since: 0.9.7 + **/ +hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list) +{ + return hb_shape_plan_create_cached2 (face, props, + user_features, num_user_features, + nullptr, 0, + shaper_list); +} + +/** + * hb_shape_plan_create_cached2: + * @face: #hb_face_t to use + * @props: The #hb_segment_properties_t of the segment + * @user_features: (array length=num_user_features): The list of user-selected features + * @num_user_features: The number of user-selected features + * @coords: (array length=num_coords): The list of variation-space coordinates + * @num_coords: The number of variation-space coordinates + * @shaper_list: (array zero-terminated=1): List of shapers to try + * + * The variable-font version of #hb_shape_plan_create_cached. + * Creates a cached shaping plan suitable for reuse, for a combination + * of @face, @user_features, @props, and @shaper_list, plus the + * variation-space coordinates @coords. + * + * Return value: (transfer full): The shaping plan + * + * Since: 1.4.0 + **/ +hb_shape_plan_t * +hb_shape_plan_create_cached2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list) +{ + DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr, + "face=%p num_features=%u shaper_list=%p", + face, + num_user_features, + shaper_list); + +retry: + hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans; + + bool dont_cache = !hb_object_is_valid (face); + + if (likely (!dont_cache)) + { + hb_shape_plan_key_t key; + if (!key.init (false, + face, + props, + user_features, + num_user_features, + coords, + num_coords, + shaper_list)) + return hb_shape_plan_get_empty (); + + for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next) + if (node->shape_plan->key.equal (&key)) + { + DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache"); + return hb_shape_plan_reference (node->shape_plan); + } + } + + hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props, + user_features, num_user_features, + coords, num_coords, + shaper_list); + + if (unlikely (dont_cache)) + return shape_plan; + + hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) hb_calloc (1, sizeof (hb_face_t::plan_node_t)); + if (unlikely (!node)) + return shape_plan; + + node->shape_plan = shape_plan; + node->next = cached_plan_nodes; + + if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node))) + { + hb_shape_plan_destroy (shape_plan); + hb_free (node); + goto retry; + } + DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache"); + + return hb_shape_plan_reference (shape_plan); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-shape-plan.h b/gfx/harfbuzz/src/hb-shape-plan.h new file mode 100644 index 0000000000..aaf5cf9c45 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape-plan.h @@ -0,0 +1,122 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_SHAPE_PLAN_H +#define HB_SHAPE_PLAN_H + +#include "hb-common.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + +/** + * hb_shape_plan_t: + * + * Data type for holding a shaping plan. + * + * Shape plans contain information about how HarfBuzz will shape a + * particular text segment, based on the segment's properties and the + * capabilities in the font face in use. + * + * Shape plans can be queried about how shaping will perform, given a set + * of specific input parameters (script, language, direction, features, + * etc.). + * + **/ +typedef struct hb_shape_plan_t hb_shape_plan_t; + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_create_cached2 (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_get_empty (void); + +HB_EXTERN hb_shape_plan_t * +hb_shape_plan_reference (hb_shape_plan_t *shape_plan); + +HB_EXTERN void +hb_shape_plan_destroy (hb_shape_plan_t *shape_plan); + +HB_EXTERN hb_bool_t +hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_shape_plan_get_user_data (const hb_shape_plan_t *shape_plan, + hb_user_data_key_t *key); + + +HB_EXTERN hb_bool_t +hb_shape_plan_execute (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +HB_EXTERN const char * +hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan); + + +HB_END_DECLS + +#endif /* HB_SHAPE_PLAN_H */ diff --git a/gfx/harfbuzz/src/hb-shape-plan.hh b/gfx/harfbuzz/src/hb-shape-plan.hh new file mode 100644 index 0000000000..6fc73939b3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape-plan.hh @@ -0,0 +1,77 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SHAPE_PLAN_HH +#define HB_SHAPE_PLAN_HH + +#include "hb.hh" +#include "hb-shaper.hh" +#include "hb-ot-shape.hh" + + +struct hb_shape_plan_key_t +{ + hb_segment_properties_t props; + + const hb_feature_t *user_features; + unsigned int num_user_features; + +#ifndef HB_NO_OT_SHAPE + hb_ot_shape_plan_key_t ot; +#endif + + hb_shape_func_t *shaper_func; + const char *shaper_name; + + HB_INTERNAL bool init (bool copy, + hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const int *coords, + unsigned int num_coords, + const char * const *shaper_list); + + HB_INTERNAL void fini () { hb_free ((void *) user_features); user_features = nullptr; } + + HB_INTERNAL bool user_features_match (const hb_shape_plan_key_t *other); + + HB_INTERNAL bool equal (const hb_shape_plan_key_t *other); +}; + +struct hb_shape_plan_t +{ + ~hb_shape_plan_t () { key.fini (); } + hb_object_header_t header; + hb_face_t *face_unsafe; /* We don't carry a reference to face. */ + hb_shape_plan_key_t key; +#ifndef HB_NO_OT_SHAPE + hb_ot_shape_plan_t ot; +#endif +}; + + +#endif /* HB_SHAPE_PLAN_HH */ diff --git a/gfx/harfbuzz/src/hb-shape.cc b/gfx/harfbuzz/src/hb-shape.cc new file mode 100644 index 0000000000..844f7b9e80 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape.cc @@ -0,0 +1,445 @@ +/* + * Copyright © 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 + */ + +#include "hb.hh" + +#include "hb-shaper.hh" +#include "hb-shape-plan.hh" +#include "hb-buffer.hh" +#include "hb-font.hh" +#include "hb-machinery.hh" + + +#ifndef HB_NO_SHAPER + +/** + * SECTION:hb-shape + * @title: hb-shape + * @short_description: Conversion of text strings into positioned glyphs + * @include: hb.h + * + * Shaping is the central operation of HarfBuzz. Shaping operates on buffers, + * which are sequences of Unicode characters that use the same font and have + * the same text direction, script, and language. After shaping the buffer + * contains the output glyphs and their positions. + **/ + + +static inline void free_static_shaper_list (); + +static const char * const nil_shaper_list[] = {nullptr}; + +static struct hb_shaper_list_lazy_loader_t : hb_lazy_loader_t<const char *, + hb_shaper_list_lazy_loader_t> +{ + static const char ** create () + { + const char **shaper_list = (const char **) hb_calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *)); + if (unlikely (!shaper_list)) + return nullptr; + + const hb_shaper_entry_t *shapers = _hb_shapers_get (); + unsigned int i; + for (i = 0; i < HB_SHAPERS_COUNT; i++) + shaper_list[i] = shapers[i].name; + shaper_list[i] = nullptr; + + hb_atexit (free_static_shaper_list); + + return shaper_list; + } + static void destroy (const char **l) + { hb_free (l); } + static const char * const * get_null () + { return nil_shaper_list; } +} static_shaper_list; + +static inline +void free_static_shaper_list () +{ + static_shaper_list.free_instance (); +} + + +/** + * hb_shape_list_shapers: + * + * Retrieves the list of shapers supported by HarfBuzz. + * + * Return value: (transfer none) (array zero-terminated=1): an array of + * constant strings + * + * Since: 0.9.2 + **/ +const char ** +hb_shape_list_shapers () +{ + return static_shaper_list.get_unconst (); +} + + +/** + * hb_shape_full: + * @font: an #hb_font_t to use for shaping + * @buffer: an #hb_buffer_t to shape + * @features: (array length=num_features) (nullable): an array of user + * specified #hb_feature_t or `NULL` + * @num_features: the length of @features array + * @shaper_list: (array zero-terminated=1) (nullable): a `NULL`-terminated + * array of shapers to use or `NULL` + * + * See hb_shape() for details. If @shaper_list is not `NULL`, the specified + * shapers will be used in the given order, otherwise the default shapers list + * will be used. + * + * Return value: false if all shapers failed, true otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_shape_full (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list) +{ + if (unlikely (!buffer->len)) + return true; + + buffer->enter (); + + hb_buffer_t *text_buffer = nullptr; + if (buffer->flags & HB_BUFFER_FLAG_VERIFY) + { + text_buffer = hb_buffer_create (); + hb_buffer_append (text_buffer, buffer, 0, -1); + } + + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached2 (font->face, &buffer->props, + features, num_features, + font->coords, font->num_coords, + shaper_list); + + hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); + + if (buffer->max_ops <= 0) + buffer->shaping_failed = true; + + hb_shape_plan_destroy (shape_plan); + + if (text_buffer) + { + if (res && buffer->successful && !buffer->shaping_failed + && text_buffer->successful + && !buffer->verify (text_buffer, + font, + features, + num_features, + shaper_list)) + res = false; + hb_buffer_destroy (text_buffer); + } + + buffer->leave (); + + return res; +} + +/** + * hb_shape: + * @font: an #hb_font_t to use for shaping + * @buffer: an #hb_buffer_t to shape + * @features: (array length=num_features) (nullable): an array of user + * specified #hb_feature_t or `NULL` + * @num_features: the length of @features array + * + * Shapes @buffer using @font turning its Unicode characters content to + * positioned glyphs. If @features is not `NULL`, it will be used to control the + * features applied during shaping. If two @features have the same tag but + * overlapping ranges the value of the feature with the higher index takes + * precedence. + * + * Since: 0.9.2 + **/ +void +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + hb_shape_full (font, buffer, features, num_features, nullptr); +} + + +#ifdef HB_EXPERIMENTAL_API + +static float +buffer_advance (hb_buffer_t *buffer) +{ + float a = 0; + auto *pos = buffer->pos; + unsigned count = buffer->len; + if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction)) + for (unsigned i = 0; i < count; i++) + a += pos[i].x_advance; + else + for (unsigned i = 0; i < count; i++) + a += pos[i].y_advance; + return a; +} + +static void +reset_buffer (hb_buffer_t *buffer, + hb_array_t<const hb_glyph_info_t> text) +{ + assert (buffer->ensure (text.length)); + buffer->have_positions = false; + buffer->len = text.length; + hb_memcpy (buffer->info, text.arrayZ, text.length * sizeof (buffer->info[0])); + hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_UNICODE); +} + +/** + * hb_shape_justify: + * @font: a mutable #hb_font_t to use for shaping + * @buffer: an #hb_buffer_t to shape + * @features: (array length=num_features) (nullable): an array of user + * specified #hb_feature_t or `NULL` + * @num_features: the length of @features array + * @shaper_list: (array zero-terminated=1) (nullable): a `NULL`-terminated + * array of shapers to use or `NULL` + * @min_target_advance: Minimum advance width/height to aim for. + * @max_target_advance: Maximum advance width/height to aim for. + * @advance: (inout): Input/output advance width/height of the buffer. + * @var_tag: (out): Variation-axis tag used for justification. + * @var_value: (out): Variation-axis value used to reach target justification. + * + * See hb_shape_full() for basic details. If @shaper_list is not `NULL`, the specified + * shapers will be used in the given order, otherwise the default shapers list + * will be used. + * + * In addition, justify the shaping results such that the shaping results reach + * the target advance width/height, depending on the buffer direction. + * + * If the advance of the buffer shaped with hb_shape_full() is already known, + * put that in *advance. Otherwise set *advance to zero. + * + * This API is currently experimental and will probably change in the future. + * + * Return value: false if all shapers failed, true otherwise + * + * XSince: EXPERIMENTAL + **/ +hb_bool_t +hb_shape_justify (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list, + float min_target_advance, + float max_target_advance, + float *advance, /* IN/OUT */ + hb_tag_t *var_tag, /* OUT */ + float *var_value /* OUT */) +{ + // TODO Negative font scales? + + /* If default advance already matches target, nothing to do. Shape and return. */ + if (min_target_advance <= *advance && *advance <= max_target_advance) + { + *var_tag = HB_TAG_NONE; + *var_value = 0.0f; + return hb_shape_full (font, buffer, + features, num_features, + shaper_list); + } + + hb_face_t *face = font->face; + + /* Choose variation tag to use for justification. */ + + hb_tag_t tag = HB_TAG_NONE; + hb_ot_var_axis_info_t axis_info; + + hb_tag_t tags[] = + { + HB_TAG ('j','s','t','f'), + HB_TAG ('w','d','t','h'), + }; + for (unsigned i = 0; i < ARRAY_LENGTH (tags); i++) + if (hb_ot_var_find_axis_info (face, tags[i], &axis_info)) + { + tag = *var_tag = tags[i]; + break; + } + + /* If no suitable variation axis found, can't justify. Just shape and return. */ + if (!tag) + { + *var_tag = HB_TAG_NONE; + *var_value = 0.0f; + if (hb_shape_full (font, buffer, + features, num_features, + shaper_list)) + { + *advance = buffer_advance (buffer); + return true; + } + else + return false; + } + + /* Copy buffer text as we need it so we can shape multiple times. */ + unsigned text_len = buffer->len; + auto *text_info = (hb_glyph_info_t *) hb_malloc (text_len * sizeof (buffer->info[0])); + if (unlikely (text_len && !text_info)) + return false; + hb_memcpy (text_info, buffer->info, text_len * sizeof (buffer->info[0])); + auto text = hb_array<const hb_glyph_info_t> (text_info, text_len); + + /* If default advance was not provided to us, calculate it. */ + if (!*advance) + { + hb_font_set_variation (font, tag, axis_info.default_value); + if (!hb_shape_full (font, buffer, + features, num_features, + shaper_list)) + return false; + *advance = buffer_advance (buffer); + } + + /* If default advance already matches target, nothing to do. Shape and return. + * Do this again, in case advance was just calculated. + */ + if (min_target_advance <= *advance && *advance <= max_target_advance) + { + *var_tag = HB_TAG_NONE; + *var_value = 0.0f; + return true; + } + + /* Prepare for running the solver. */ + double a, b, ya, yb; + if (*advance < min_target_advance) + { + /* Need to expand. */ + ya = (double) *advance; + a = (double) axis_info.default_value; + b = (double) axis_info.max_value; + + /* Shape buffer for maximum expansion to use as other + * starting point for the solver. */ + hb_font_set_variation (font, tag, (float) b); + reset_buffer (buffer, text); + if (!hb_shape_full (font, buffer, + features, num_features, + shaper_list)) + return false; + yb = (double) buffer_advance (buffer); + /* If the maximum expansion is less than max target, + * there's nothing to solve for. Just return it. */ + if (yb <= (double) max_target_advance) + { + *var_value = (float) b; + *advance = (float) yb; + return true; + } + } + else + { + /* Need to shrink. */ + yb = (double) *advance; + a = (double) axis_info.min_value; + b = (double) axis_info.default_value; + + /* Shape buffer for maximum shrinkate to use as other + * starting point for the solver. */ + hb_font_set_variation (font, tag, (float) a); + reset_buffer (buffer, text); + if (!hb_shape_full (font, buffer, + features, num_features, + shaper_list)) + return false; + ya = (double) buffer_advance (buffer); + /* If the maximum shrinkate is more than min target, + * there's nothing to solve for. Just return it. */ + if (ya >= (double) min_target_advance) + { + *var_value = (float) a; + *advance = (float) ya; + return true; + } + } + + /* Run the solver to find a var axis value that hits + * the desired width. */ + + double epsilon = (b - a) / (1<<14); + bool failed = false; + + auto f = [&] (double x) + { + hb_font_set_variation (font, tag, (float) x); + reset_buffer (buffer, text); + if (unlikely (!hb_shape_full (font, buffer, + features, num_features, + shaper_list))) + { + failed = true; + return (double) min_target_advance; + } + + double w = (double) buffer_advance (buffer); + DEBUG_MSG (JUSTIFY, nullptr, "Trying '%c%c%c%c' axis parameter %f. Advance %g. Target: min %g max %g", + HB_UNTAG (tag), x, w, + (double) min_target_advance, (double) max_target_advance); + return w; + }; + + double y = 0; + double itp = solve_itp (f, + a, b, + epsilon, + (double) min_target_advance, (double) max_target_advance, + ya, yb, y); + + hb_free (text_info); + + if (failed) + return false; + + *var_value = (float) itp; + *advance = (float) y; + + return true; +} + +#endif + + +#endif diff --git a/gfx/harfbuzz/src/hb-shape.h b/gfx/harfbuzz/src/hb-shape.h new file mode 100644 index 0000000000..d4d4fdfd26 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shape.h @@ -0,0 +1,74 @@ +/* + * Copyright © 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_SHAPE_H +#define HB_SHAPE_H + +#include "hb-common.h" +#include "hb-buffer.h" +#include "hb-font.h" + +HB_BEGIN_DECLS + + +HB_EXTERN void +hb_shape (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +HB_EXTERN hb_bool_t +hb_shape_full (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list); + +HB_EXTERN hb_bool_t +hb_shape_justify (hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features, + const char * const *shaper_list, + float min_target_advance, + float max_target_advance, + float *advance, /* IN/OUT */ + hb_tag_t *var_tag, /* OUT */ + float *var_value /* OUT */); + +HB_EXTERN const char ** +hb_shape_list_shapers (void); + + +HB_END_DECLS + +#endif /* HB_SHAPE_H */ diff --git a/gfx/harfbuzz/src/hb-shaper-impl.hh b/gfx/harfbuzz/src/hb-shaper-impl.hh new file mode 100644 index 0000000000..b674fceb6a --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper-impl.hh @@ -0,0 +1,38 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SHAPER_IMPL_HH +#define HB_SHAPER_IMPL_HH + +#include "hb.hh" + +#include "hb-shaper.hh" +#include "hb-face.hh" +#include "hb-font.hh" +#include "hb-shape-plan.hh" +#include "hb-buffer.hh" + +#endif /* HB_SHAPER_IMPL_HH */ diff --git a/gfx/harfbuzz/src/hb-shaper-list.hh b/gfx/harfbuzz/src/hb-shaper-list.hh new file mode 100644 index 0000000000..f079caf4d3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper-list.hh @@ -0,0 +1,65 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SHAPER_LIST_HH +#define HB_SHAPER_LIST_HH +#endif /* HB_SHAPER_LIST_HH */ /* Dummy header guards */ + +#ifndef HB_NO_SHAPER + + +/* v--- Add new shapers in the right place here. */ + +#ifdef HAVE_WASM +/* Only picks up fonts that have a "Wasm" table. */ +HB_SHAPER_IMPLEMENT (wasm) +#endif + +#ifdef HAVE_GRAPHITE2 +/* Only picks up fonts that have a "Silf" table. */ +HB_SHAPER_IMPLEMENT (graphite2) +#endif + +#ifndef HB_NO_OT_SHAPE +HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main shaper. */ +#endif + +#ifdef HAVE_UNISCRIBE +HB_SHAPER_IMPLEMENT (uniscribe) +#endif +#ifdef HAVE_DIRECTWRITE +HB_SHAPER_IMPLEMENT (directwrite) +#endif +#ifdef HAVE_CORETEXT +HB_SHAPER_IMPLEMENT (coretext) +#endif + +#ifndef HB_NO_FALLBACK_SHAPE +HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */ +#endif + + +#endif diff --git a/gfx/harfbuzz/src/hb-shaper.cc b/gfx/harfbuzz/src/hb-shaper.cc new file mode 100644 index 0000000000..c4885cda94 --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper.cc @@ -0,0 +1,102 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-shaper.hh" +#include "hb-machinery.hh" + + +static const hb_shaper_entry_t _hb_all_shapers[] = { +#define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape}, +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +}; +#ifndef HB_NO_SHAPER +static_assert (0 != ARRAY_LENGTH_CONST (_hb_all_shapers), "No shaper enabled."); +#endif + +static inline void free_static_shapers (); + +static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<hb_shaper_entry_t, + hb_shapers_lazy_loader_t> +{ + static hb_shaper_entry_t *create () + { + char *env = getenv ("HB_SHAPER_LIST"); + if (!env || !*env) + return nullptr; + + hb_shaper_entry_t *shapers = (hb_shaper_entry_t *) hb_calloc (1, sizeof (_hb_all_shapers)); + if (unlikely (!shapers)) + return nullptr; + + hb_memcpy (shapers, _hb_all_shapers, sizeof (_hb_all_shapers)); + + /* Reorder shaper list to prefer requested shapers. */ + unsigned int i = 0; + char *end, *p = env; + for (;;) + { + end = strchr (p, ','); + if (!end) + end = p + strlen (p); + + for (unsigned int j = i; j < ARRAY_LENGTH_CONST (_hb_all_shapers); j++) + if (end - p == (int) strlen (shapers[j].name) && + 0 == strncmp (shapers[j].name, p, end - p)) + { + /* Reorder this shaper to position i */ + struct hb_shaper_entry_t t = shapers[j]; + memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i)); + shapers[i] = t; + i++; + } + + if (!*end) + break; + else + p = end + 1; + } + + hb_atexit (free_static_shapers); + + return shapers; + } + static void destroy (hb_shaper_entry_t *p) { hb_free (p); } + static const hb_shaper_entry_t *get_null () { return _hb_all_shapers; } +} static_shapers; + +static inline +void free_static_shapers () +{ + static_shapers.free_instance (); +} + +const hb_shaper_entry_t * +_hb_shapers_get () +{ + return static_shapers.get_unconst (); +} diff --git a/gfx/harfbuzz/src/hb-shaper.hh b/gfx/harfbuzz/src/hb-shaper.hh new file mode 100644 index 0000000000..b4138a324f --- /dev/null +++ b/gfx/harfbuzz/src/hb-shaper.hh @@ -0,0 +1,134 @@ +/* + * 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. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_SHAPER_HH +#define HB_SHAPER_HH + +#include "hb.hh" +#include "hb-machinery.hh" + +typedef hb_bool_t hb_shape_func_t (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features); + +#define HB_SHAPER_IMPLEMENT(name) \ + extern "C" HB_INTERNAL hb_shape_func_t _hb_##name##_shape; +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +struct hb_shaper_entry_t { + char name[16]; + hb_shape_func_t *func; +}; + +HB_INTERNAL const hb_shaper_entry_t * +_hb_shapers_get (); + + +template <typename Data, unsigned int WheresData, typename T> +struct hb_shaper_lazy_loader_t; + +#define HB_SHAPER_ORDER(Shaper) \ + HB_PASTE (HB_SHAPER_ORDER_, Shaper) +enum hb_shaper_order_t +{ + _HB_SHAPER_ORDER_ORDER_ZERO, +#define HB_SHAPER_IMPLEMENT(Shaper) \ + HB_SHAPER_ORDER (Shaper), +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + _HB_SHAPERS_COUNT_PLUS_ONE, + HB_SHAPERS_COUNT = _HB_SHAPERS_COUNT_PLUS_ONE - 1, +}; + +template <enum hb_shaper_order_t order, typename Object> struct hb_shaper_object_data_type_t; + +#define HB_SHAPER_DATA_SUCCEEDED ((void *) +1) +#define HB_SHAPER_DATA_TYPE(shaper, object) hb_##shaper##_##object##_data_t +#define HB_SHAPER_DATA_CREATE_FUNC(shaper, object) _hb_##shaper##_shaper_##object##_data_create +#define HB_SHAPER_DATA_DESTROY_FUNC(shaper, object) _hb_##shaper##_shaper_##object##_data_destroy + +#define HB_SHAPER_DATA_INSTANTIATE_SHAPERS(shaper, object) \ + \ + struct HB_SHAPER_DATA_TYPE (shaper, object); /* Type forward declaration. */ \ + extern "C" HB_INTERNAL HB_SHAPER_DATA_TYPE (shaper, object) * \ + HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (hb_##object##_t *object); \ + extern "C" HB_INTERNAL void \ + HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (HB_SHAPER_DATA_TYPE (shaper, object) *shaper##_##object); \ + \ + template <> \ + struct hb_shaper_object_data_type_t<HB_SHAPER_ORDER (shaper), hb_##object##_t> \ + { \ + typedef HB_SHAPER_DATA_TYPE(shaper, object) value; \ + }; \ + \ + template <unsigned int WheresData> \ + struct hb_shaper_lazy_loader_t<hb_##object##_t, WheresData, HB_SHAPER_DATA_TYPE(shaper, object)> \ + : hb_lazy_loader_t<HB_SHAPER_DATA_TYPE(shaper, object), \ + hb_shaper_lazy_loader_t<hb_##object##_t, \ + WheresData, \ + HB_SHAPER_DATA_TYPE(shaper, object)>, \ + hb_##object##_t, WheresData> \ + { \ + typedef HB_SHAPER_DATA_TYPE(shaper, object) Type; \ + static Type* create (hb_##object##_t *data) \ + { return HB_SHAPER_DATA_CREATE_FUNC (shaper, object) (data); } \ + static Type *get_null () { return nullptr; } \ + static void destroy (Type *p) { HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (p); } \ + }; \ + \ + static_assert (true, "") /* Require semicolon after. */ + + +template <typename Object> +struct hb_shaper_object_dataset_t +{ + void init0 (Object *parent_data) + { + this->parent_data = parent_data; +#define HB_SHAPER_IMPLEMENT(shaper) shaper.init0 (); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + void fini () + { +#define HB_SHAPER_IMPLEMENT(shaper) shaper.fini (); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + } + + Object *parent_data; /* MUST be JUST before the lazy loaders. */ +#define HB_SHAPER_IMPLEMENT(shaper) \ + hb_shaper_lazy_loader_t<Object, HB_SHAPER_ORDER(shaper), \ + typename hb_shaper_object_data_type_t<HB_SHAPER_ORDER(shaper), Object>::value \ + > shaper; +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT +}; + +#endif /* HB_SHAPER_HH */ diff --git a/gfx/harfbuzz/src/hb-static.cc b/gfx/harfbuzz/src/hb-static.cc new file mode 100644 index 0000000000..c9bd0a61bf --- /dev/null +++ b/gfx/harfbuzz/src/hb-static.cc @@ -0,0 +1,138 @@ +/* + * 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.hh" + +#include "hb-open-type.hh" +#include "hb-face.hh" + +#include "hb-aat-layout-common.hh" +#include "hb-aat-layout-feat-table.hh" +#include "hb-cff-interp-common.hh" +#include "hb-ot-layout-common.hh" +#include "hb-ot-cmap-table.hh" +#include "OT/Color/COLR/COLR.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-maxp-table.hh" + +#ifndef HB_NO_VISIBILITY +#include "hb-ot-name-language-static.hh" + +uint64_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; +/*thread_local*/ uint64_t _hb_CrapPool[(HB_NULL_POOL_SIZE + sizeof (uint64_t) - 1) / sizeof (uint64_t)] = {}; + +DEFINE_NULL_NAMESPACE_BYTES (OT, Index) = {0xFF,0xFF}; +DEFINE_NULL_NAMESPACE_BYTES (OT, VarIdx) = {0xFF,0xFF,0xFF,0xFF}; +DEFINE_NULL_NAMESPACE_BYTES (OT, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00}; +DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x01}; +DEFINE_NULL_NAMESPACE_BYTES (OT, ClipRecord) = {0x01}; +DEFINE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup) = {0x00,0x00,0x00,0x01, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00}; +DEFINE_NULL_NAMESPACE_BYTES (AAT, SettingName) = {0xFF,0xFF, 0xFF,0xFF}; +DEFINE_NULL_NAMESPACE_BYTES (AAT, Lookup) = {0xFF,0xFF}; + + +/* hb_map_t */ + +const hb_codepoint_t minus_1 = -1; +static const unsigned char static_endchar_str[] = {OpCode_endchar}; +const unsigned char *endchar_str = static_endchar_str; + +/* hb_face_t */ + +#ifndef HB_NO_BEYOND_64K +static inline unsigned +load_num_glyphs_from_loca (const hb_face_t *face) +{ + unsigned ret = 0; + + unsigned indexToLocFormat = face->table.head->indexToLocFormat; + + if (indexToLocFormat <= 1) + { + bool short_offset = 0 == indexToLocFormat; + hb_blob_t *loca_blob = face->table.loca.get_blob (); + ret = hb_max (1u, loca_blob->length / (short_offset ? 2 : 4)) - 1; + } + + return ret; +} +#endif + +static inline unsigned +load_num_glyphs_from_maxp (const hb_face_t *face) +{ + return face->table.maxp->get_num_glyphs (); +} + +unsigned int +hb_face_t::load_num_glyphs () const +{ + unsigned ret = 0; + +#ifndef HB_NO_BEYOND_64K + ret = hb_max (ret, load_num_glyphs_from_loca (this)); +#endif + + ret = hb_max (ret, load_num_glyphs_from_maxp (this)); + + num_glyphs = ret; + return ret; +} + +unsigned int +hb_face_t::load_upem () const +{ + unsigned int ret = table.head->get_upem (); + upem = ret; + return ret; +} + + +#ifndef HB_NO_VAR +bool +_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, + int *lsb) +{ + return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb); +} + +unsigned +_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical) +{ + return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical); +} +#endif + +bool +_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb) +{ + return face->table.glyf->get_leading_bearing_without_var_unscaled (gid, is_vertical, lsb); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-string-array.hh b/gfx/harfbuzz/src/hb-string-array.hh new file mode 100644 index 0000000000..e7ac119232 --- /dev/null +++ b/gfx/harfbuzz/src/hb-string-array.hh @@ -0,0 +1,85 @@ +/* + * 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_STRING_ARRAY_HH +#if 0 /* Make checks happy. */ +#define HB_STRING_ARRAY_HH +#endif + +#include "hb.hh" + +/* Based on Bruno Haible's code in Appendix B of Ulrich Drepper's dsohowto.pdf: + * https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf */ + +#define HB_STRING_ARRAY_TYPE_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr_t) +#define HB_STRING_ARRAY_POOL_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgstr) +#define HB_STRING_ARRAY_OFFS_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _msgidx) +#define HB_STRING_ARRAY_LENG_NAME HB_PASTE(HB_STRING_ARRAY_NAME, _length) + +static const union HB_STRING_ARRAY_TYPE_NAME { + struct { +/* I like to avoid storing the nul-termination byte since we don't need it, + * but C++ does not allow that. + * https://stackoverflow.com/q/28433862 + */ +#define _S(s) char HB_PASTE (str, __LINE__)[sizeof (s)]; +#include HB_STRING_ARRAY_LIST +#undef _S + } st; + char str[HB_VAR_ARRAY]; +} +HB_STRING_ARRAY_POOL_NAME = +{ + { +#define _S(s) s, +#include HB_STRING_ARRAY_LIST +#undef _S + } +}; +static const unsigned int HB_STRING_ARRAY_OFFS_NAME[] = +{ +#define _S(s) offsetof (union HB_STRING_ARRAY_TYPE_NAME, st.HB_PASTE(str, __LINE__)), +#include HB_STRING_ARRAY_LIST +#undef _S + sizeof (HB_STRING_ARRAY_TYPE_NAME) +}; + +static const unsigned int HB_STRING_ARRAY_LENG_NAME = ARRAY_LENGTH_CONST (HB_STRING_ARRAY_OFFS_NAME) - 1; + +static inline hb_bytes_t +HB_STRING_ARRAY_NAME (unsigned int i) +{ + assert (i < ARRAY_LENGTH (HB_STRING_ARRAY_OFFS_NAME) - 1); + return hb_bytes_t (HB_STRING_ARRAY_POOL_NAME.str + HB_STRING_ARRAY_OFFS_NAME[i], + HB_STRING_ARRAY_OFFS_NAME[i + 1] - HB_STRING_ARRAY_OFFS_NAME[i] - 1); +} + +#undef HB_STRING_ARRAY_TYPE_NAME +#undef HB_STRING_ARRAY_POOL_NAME +#undef HB_STRING_ARRAY_OFFS_NAME +#undef HB_STRING_ARRAY_LENG_NAME + +#endif /* HB_STRING_ARRAY_HH */ diff --git a/gfx/harfbuzz/src/hb-style.cc b/gfx/harfbuzz/src/hb-style.cc new file mode 100644 index 0000000000..bd5cb5c6be --- /dev/null +++ b/gfx/harfbuzz/src/hb-style.cc @@ -0,0 +1,134 @@ +/* + * 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" + +#ifndef HB_NO_STYLE + +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-os2-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-face.hh" + +/** + * SECTION:hb-style + * @title: hb-style + * @short_description: Font Styles + * @include: hb.h + * + * Functions for fetching style information from fonts. + **/ + +static inline float +_hb_angle_to_ratio (float a) +{ + return tanf (a * -HB_PI / 180.f); +} + +static inline float +_hb_ratio_to_angle (float r) +{ + return atanf (r) * -180.f / HB_PI; +} + +/** + * hb_style_get_value: + * @font: a #hb_font_t object. + * @style_tag: a style tag. + * + * Searches variation axes of a #hb_font_t object for a specific axis first, + * if not set, then tries to get default style values from different + * tables of the font. + * + * Returns: Corresponding axis or default value to a style tag. + * + * Since: 3.0.0 + **/ +float +hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag) +{ + if (unlikely (style_tag == HB_STYLE_TAG_SLANT_RATIO)) + return _hb_angle_to_ratio (hb_style_get_value (font, HB_STYLE_TAG_SLANT_ANGLE)); + + hb_face_t *face = font->face; + +#ifndef HB_NO_VAR + hb_ot_var_axis_info_t axis; + if (hb_ot_var_find_axis_info (face, style_tag, &axis)) + { + if (axis.axis_index < font->num_coords) return font->design_coords[axis.axis_index]; + /* If a face is variable, fvar's default_value is better than STAT records */ + return axis.default_value; + } +#endif + + if (style_tag == HB_STYLE_TAG_OPTICAL_SIZE && font->ptem) + return font->ptem; + + /* STAT */ + float value; + if (face->table.STAT->get_value (style_tag, &value)) + return value; + + switch ((unsigned) style_tag) + { + case HB_STYLE_TAG_ITALIC: + return face->table.OS2->is_italic () || face->table.head->is_italic () ? 1 : 0; + case HB_STYLE_TAG_OPTICAL_SIZE: + { + unsigned int lower, design, upper; + return face->table.OS2->v5 ().get_optical_size (&lower, &upper) + ? (float) (lower + upper) / 2.f + : hb_ot_layout_get_size_params (face, &design, nullptr, nullptr, nullptr, nullptr) + ? design / 10.f + : 12.f; + } + case HB_STYLE_TAG_SLANT_ANGLE: + { + float angle = face->table.post->table->italicAngle.to_float (); + + if (font->slant) + angle = _hb_ratio_to_angle (font->slant + _hb_angle_to_ratio (angle)); + + return angle; + } + case HB_STYLE_TAG_WIDTH: + return face->table.OS2->has_data () + ? face->table.OS2->get_width () + : (face->table.head->is_condensed () ? 75 : + face->table.head->is_expanded () ? 125 : + 100); + case HB_STYLE_TAG_WEIGHT: + return face->table.OS2->has_data () + ? face->table.OS2->usWeightClass + : (face->table.head->is_bold () ? 700 : 400); + default: + return 0; + } +} + +#endif diff --git a/gfx/harfbuzz/src/hb-style.h b/gfx/harfbuzz/src/hb-style.h new file mode 100644 index 0000000000..d17d2daa5f --- /dev/null +++ b/gfx/harfbuzz/src/hb-style.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_STYLE_H +#define HB_STYLE_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_style_tag_t: + * @HB_STYLE_TAG_ITALIC: Used to vary between non-italic and italic. + * A value of 0 can be interpreted as "Roman" (non-italic); a value of 1 can + * be interpreted as (fully) italic. + * @HB_STYLE_TAG_OPTICAL_SIZE: Used to vary design to suit different text sizes. + * Non-zero. Values can be interpreted as text size, in points. + * @HB_STYLE_TAG_SLANT_ANGLE: Used to vary between upright and slanted text. Values + * must be greater than -90 and less than +90. Values can be interpreted as + * the angle, in counter-clockwise degrees, of oblique slant from whatever the + * designer considers to be upright for that font design. Typical right-leaning + * Italic fonts have a negative slant angle (typically around -12) + * @HB_STYLE_TAG_SLANT_RATIO: same as @HB_STYLE_TAG_SLANT_ANGLE expression as ratio. + * Typical right-leaning Italic fonts have a positive slant ratio (typically around 0.2) + * @HB_STYLE_TAG_WIDTH: Used to vary width of text from narrower to wider. + * Non-zero. Values can be interpreted as a percentage of whatever the font + * designer considers “normal width” for that font design. + * @HB_STYLE_TAG_WEIGHT: Used to vary stroke thicknesses or other design details + * to give variation from lighter to blacker. Values can be interpreted in direct + * comparison to values for usWeightClass in the OS/2 table, + * or the CSS font-weight property. + * + * Defined by [OpenType Design-Variation Axis Tag Registry](https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg). + * + * Since: 3.0.0 + **/ +typedef enum +{ + HB_STYLE_TAG_ITALIC = HB_TAG ('i','t','a','l'), + HB_STYLE_TAG_OPTICAL_SIZE = HB_TAG ('o','p','s','z'), + HB_STYLE_TAG_SLANT_ANGLE = HB_TAG ('s','l','n','t'), + HB_STYLE_TAG_SLANT_RATIO = HB_TAG ('S','l','n','t'), + HB_STYLE_TAG_WIDTH = HB_TAG ('w','d','t','h'), + HB_STYLE_TAG_WEIGHT = HB_TAG ('w','g','h','t'), + + /*< private >*/ + _HB_STYLE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/ +} hb_style_tag_t; + + +HB_EXTERN float +hb_style_get_value (hb_font_t *font, hb_style_tag_t style_tag); + +HB_END_DECLS + +#endif /* HB_STYLE_H */ diff --git a/gfx/harfbuzz/src/hb-subset-accelerator.hh b/gfx/harfbuzz/src/hb-subset-accelerator.hh new file mode 100644 index 0000000000..9258383d28 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-accelerator.hh @@ -0,0 +1,141 @@ +/* + * 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 HB_SUBSET_ACCELERATOR_HH +#define HB_SUBSET_ACCELERATOR_HH + + +#include "hb.hh" + +#include "hb-map.hh" +#include "hb-multimap.hh" +#include "hb-set.hh" + +extern HB_INTERNAL hb_user_data_key_t _hb_subset_accelerator_user_data_key; + +namespace CFF { +struct cff_subset_accelerator_t; +} + +namespace OT { +struct SubtableUnicodesCache; +struct cff1_subset_accelerator_t; +struct cff2_subset_accelerator_t; +} + +struct hb_subset_accelerator_t +{ + static hb_user_data_key_t* user_data_key() + { + return &_hb_subset_accelerator_user_data_key; + } + + static hb_subset_accelerator_t* create(hb_face_t *source, + const hb_map_t& unicode_to_gid_, + const hb_set_t& unicodes_, + bool has_seac_) { + hb_subset_accelerator_t* accel = + (hb_subset_accelerator_t*) hb_calloc (1, sizeof(hb_subset_accelerator_t)); + + if (unlikely (!accel)) return accel; + + new (accel) hb_subset_accelerator_t (source, + unicode_to_gid_, + unicodes_, + has_seac_); + + return accel; + } + + static void destroy (void* p) + { + if (!p) return; + + hb_subset_accelerator_t *accel = (hb_subset_accelerator_t *) p; + + accel->~hb_subset_accelerator_t (); + + hb_free (accel); + } + + hb_subset_accelerator_t (hb_face_t *source, + const hb_map_t& unicode_to_gid_, + const hb_set_t& unicodes_, + bool has_seac_) : + unicode_to_gid(unicode_to_gid_), + unicodes(unicodes_), + cmap_cache(nullptr), + destroy_cmap_cache(nullptr), + has_seac(has_seac_), + source(hb_face_reference (source)) + { + gid_to_unicodes.alloc (unicode_to_gid.get_population ()); + for (const auto &_ : unicode_to_gid) + { + auto unicode = _.first; + auto gid = _.second; + gid_to_unicodes.add (gid, unicode); + } + } + + HB_INTERNAL ~hb_subset_accelerator_t (); + + // Generic + + mutable hb_mutex_t sanitized_table_cache_lock; + mutable hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>> sanitized_table_cache; + + hb_map_t unicode_to_gid; + hb_multimap_t gid_to_unicodes; + hb_set_t unicodes; + + // cmap + const OT::SubtableUnicodesCache* cmap_cache; + hb_destroy_func_t destroy_cmap_cache; + + // CFF + bool has_seac; + + // TODO(garretrieger): cumulative glyf checksum map + + bool in_error () const + { + return unicode_to_gid.in_error () || + gid_to_unicodes.in_error () || + unicodes.in_error () || + sanitized_table_cache.in_error (); + } + + hb_face_t *source; +#ifndef HB_NO_SUBSET_CFF + // These have to be immediately after source: + mutable hb_face_lazy_loader_t<OT::cff1_subset_accelerator_t, 1> cff1_accel; + mutable hb_face_lazy_loader_t<OT::cff2_subset_accelerator_t, 2> cff2_accel; +#endif +}; + + +#endif /* HB_SUBSET_ACCELERATOR_HH */ diff --git a/gfx/harfbuzz/src/hb-subset-cff-common.cc b/gfx/harfbuzz/src/hb-subset-cff-common.cc new file mode 100644 index 0000000000..5e4ea5fe7c --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-cff-common.cc @@ -0,0 +1,231 @@ +/* + * 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_SUBSET_CFF + +#include "hb-ot-cff-common.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-subset-cff-common.hh" + +/* Disable FDSelect format 0 for compatibility with fonttools which doesn't seem choose it. + * Rarely any/much smaller than format 3 anyway. */ +#define CFF_SERIALIZE_FDSELECT_0 0 + +using namespace CFF; + + +/* Determine an optimal FDSelect format according to a provided plan. + * + * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect + * along with a font index remapping table + */ + +bool +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, + unsigned int fdCount, + const FDSelect &src, /* IN */ + unsigned int &subset_fd_count /* OUT */, + unsigned int &subset_fdselect_size /* OUT */, + unsigned int &subset_fdselect_format /* OUT */, + hb_vector_t<code_pair_t> &fdselect_ranges /* OUT */, + hb_inc_bimap_t &fdmap /* OUT */) +{ + subset_fd_count = 0; + subset_fdselect_size = 0; + subset_fdselect_format = 0; + unsigned int num_ranges = 0; + + unsigned int subset_num_glyphs = plan->num_output_glyphs (); + if (subset_num_glyphs == 0) + return true; + + { + /* use hb_set to determine the subset of font dicts */ + hb_set_t set; + hb_codepoint_t prev_fd = CFF_UNDEF_CODE; + hb_pair_t<unsigned, hb_codepoint_t> last_range {0, 0}; + auto it = hb_iter (plan->new_to_old_gid_list); + auto _ = *it; + for (hb_codepoint_t gid = 0; gid < subset_num_glyphs; gid++) + { + hb_codepoint_t old_glyph; + if (gid == _.first) + { + old_glyph = _.second; + _ = *++it; + } + else + { + /* fonttools retains FDSelect & font dicts for missing glyphs. do the same */ + old_glyph = gid; + } + if (old_glyph >= last_range.second) + last_range = src.get_fd_range (old_glyph); + unsigned fd = last_range.first; + + if (fd != prev_fd) + { + set.add (fd); + num_ranges++; + prev_fd = fd; + fdselect_ranges.push (code_pair_t { fd, gid }); + + if (gid == old_glyph) + gid = hb_min (_.first - 1, last_range.second - 1); + } + } + + subset_fd_count = set.get_population (); + if (subset_fd_count == fdCount) + { + /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */ + fdmap.identity (fdCount); + } + else + { + /* create a fdmap */ + fdmap.reset (); + + hb_codepoint_t fd = CFF_UNDEF_CODE; + while (set.next (&fd)) + fdmap.add (fd); + if (unlikely (fdmap.get_population () != subset_fd_count)) + return false; + } + + /* update each font dict index stored as "code" in fdselect_ranges */ + for (unsigned int i = 0; i < fdselect_ranges.length; i++) + fdselect_ranges[i].code = fdmap[fdselect_ranges[i].code]; + } + + /* determine which FDSelect format is most compact */ + if (subset_fd_count > 0xFF) + { + if (unlikely (src.format != 4)) + return false; + subset_fdselect_format = 4; + subset_fdselect_size = FDSelect::min_size + FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges + HBUINT32::static_size; + } + else + { +#if CFF_SERIALIZE_FDSELECT_0 + unsigned int format0_size = FDSelect::min_size + FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs; +#endif + unsigned int format3_size = FDSelect::min_size + FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges + HBUINT16::static_size; + +#if CFF_SERIALIZE_FDSELECT_0 + if (format0_size <= format3_size) + { + // subset_fdselect_format = 0; + subset_fdselect_size = format0_size; + } + else +#endif + { + subset_fdselect_format = 3; + subset_fdselect_size = format3_size; + } + } + + return true; +} + +template <typename FDSELECT3_4> +static inline bool +serialize_fdselect_3_4 (hb_serialize_context_t *c, + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int size, + const hb_vector_t<code_pair_t> &fdselect_ranges) +{ + TRACE_SERIALIZE (this); + FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size); + if (unlikely (!p)) return_trace (false); + p->nRanges () = fdselect_ranges.length; + for (unsigned int i = 0; i < fdselect_ranges.length; i++) + { + p->ranges[i].first = fdselect_ranges[i].glyph; + p->ranges[i].fd = fdselect_ranges[i].code; + } + p->sentinel () = num_glyphs; + return_trace (true); +} + +/* Serialize a subset FDSelect format planned above. */ +bool +hb_serialize_cff_fdselect (hb_serialize_context_t *c, + const unsigned int num_glyphs, + const FDSelect &src, + unsigned int fd_count, + unsigned int fdselect_format, + unsigned int size, + const hb_vector_t<code_pair_t> &fdselect_ranges) +{ + TRACE_SERIALIZE (this); + FDSelect *p = c->allocate_min<FDSelect> (); + if (unlikely (!p)) return_trace (false); + p->format = fdselect_format; + size -= FDSelect::min_size; + + switch (fdselect_format) + { +#if CFF_SERIALIZE_FDSELECT_0 + case 0: + { + FDSelect0 *p = c->allocate_size<FDSelect0> (size); + if (unlikely (!p)) return_trace (false); + unsigned int range_index = 0; + unsigned int fd = fdselect_ranges[range_index++].code; + for (unsigned int i = 0; i < num_glyphs; i++) + { + if ((range_index < fdselect_ranges.len) && + (i >= fdselect_ranges[range_index].glyph)) + { + fd = fdselect_ranges[range_index++].code; + } + p->fds[i] = fd; + } + return_trace (true); + } +#endif /* CFF_SERIALIZE_FDSELECT_0 */ + + case 3: + return serialize_fdselect_3_4<FDSelect3> (c, num_glyphs, src, + size, fdselect_ranges); + + case 4: + return serialize_fdselect_3_4<FDSelect4> (c, num_glyphs, src, + size, fdselect_ranges); + + default: + return_trace (false); + } +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-subset-cff-common.hh b/gfx/harfbuzz/src/hb-subset-cff-common.hh new file mode 100644 index 0000000000..462e99cf8c --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-cff-common.hh @@ -0,0 +1,1183 @@ +/* + * 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_SUBSET_CFF_COMMON_HH +#define HB_SUBSET_CFF_COMMON_HH + +#include "hb.hh" + +#include "hb-subset-plan.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +/* Used for writing a temporary charstring */ +struct str_encoder_t +{ + str_encoder_t (str_buff_t &buff_) + : buff (buff_) {} + + void reset () { buff.reset (); } + + void encode_byte (unsigned char b) + { + if (likely ((signed) buff.length < buff.allocated)) + buff.arrayZ[buff.length++] = b; + else + buff.push (b); + } + + void encode_int (int v) + { + if ((-1131 <= v) && (v <= 1131)) + { + if ((-107 <= v) && (v <= 107)) + encode_byte (v + 139); + else if (v > 0) + { + v -= 108; + encode_byte ((v >> 8) + OpCode_TwoBytePosInt0); + encode_byte (v & 0xFF); + } + else + { + v = -v - 108; + encode_byte ((v >> 8) + OpCode_TwoByteNegInt0); + encode_byte (v & 0xFF); + } + } + else + { + if (unlikely (v < -32768)) + v = -32768; + else if (unlikely (v > 32767)) + v = 32767; + encode_byte (OpCode_shortint); + encode_byte ((v >> 8) & 0xFF); + encode_byte (v & 0xFF); + } + } + + // Encode number for CharString + void encode_num_cs (const number_t& n) + { + if (n.in_int_range ()) + { + encode_int (n.to_int ()); + } + else + { + int32_t v = n.to_fixed (); + encode_byte (OpCode_fixedcs); + encode_byte ((v >> 24) & 0xFF); + encode_byte ((v >> 16) & 0xFF); + encode_byte ((v >> 8) & 0xFF); + encode_byte (v & 0xFF); + } + } + + // Encode number for TopDict / Private + void encode_num_tp (const number_t& n) + { + if (n.in_int_range ()) + { + // TODO longint + encode_int (n.to_int ()); + } + else + { + // Sigh. BCD + // https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-5-nibble-definitions + double v = n.to_real (); + encode_byte (OpCode_BCD); + + // Based on: + // https://github.com/fonttools/fonttools/blob/97ed3a61cde03e17b8be36f866192fbd56f1d1a7/Lib/fontTools/misc/psCharStrings.py#L265-L294 + + char buf[16]; + /* FontTools has the following comment: + * + * # Note: 14 decimal digits seems to be the limitation for CFF real numbers + * # in macOS. However, we use 8 here to match the implementation of AFDKO. + * + * We use 8 here to match FontTools X-). + */ + + hb_locale_t clocale HB_UNUSED; + hb_locale_t oldlocale HB_UNUSED; + oldlocale = hb_uselocale (clocale = newlocale (LC_ALL_MASK, "C", NULL)); + snprintf (buf, sizeof (buf), "%.8G", v); + (void) hb_uselocale (((void) freelocale (clocale), oldlocale)); + + char *s = buf; + if (s[0] == '0' && s[1] == '.') + s++; + else if (s[0] == '-' && s[1] == '0' && s[2] == '.') + { + s[1] = '-'; + s++; + } + hb_vector_t<char> nibbles; + while (*s) + { + char c = s[0]; + s++; + + switch (c) + { + case 'E': + { + char c2 = *s; + if (c2 == '-') + { + s++; + nibbles.push (0x0C); // E- + continue; + } + if (c2 == '+') + s++; + nibbles.push (0x0B); // E + continue; + } + + case '.': case ',': // Comma for some European locales in case no uselocale available. + nibbles.push (0x0A); // . + continue; + + case '-': + nibbles.push (0x0E); // . + continue; + } + + nibbles.push (c - '0'); + } + nibbles.push (0x0F); + if (nibbles.length % 2) + nibbles.push (0x0F); + + unsigned count = nibbles.length; + for (unsigned i = 0; i < count; i += 2) + encode_byte ((nibbles[i] << 4) | nibbles[i+1]); + } + } + + void encode_op (op_code_t op) + { + if (Is_OpCode_ESC (op)) + { + encode_byte (OpCode_escape); + encode_byte (Unmake_OpCode_ESC (op)); + } + else + encode_byte (op); + } + + void copy_str (const unsigned char *str, unsigned length) + { + assert ((signed) (buff.length + length) <= buff.allocated); + hb_memcpy (buff.arrayZ + buff.length, str, length); + buff.length += length; + } + + bool in_error () const { return buff.in_error (); } + + protected: + + str_buff_t &buff; +}; + +struct cff_sub_table_info_t { + cff_sub_table_info_t () + : fd_array_link (0), + char_strings_link (0) + { + fd_select.init (); + } + + table_info_t fd_select; + objidx_t fd_array_link; + objidx_t char_strings_link; +}; + +template <typename OPSTR=op_str_t> +struct cff_top_dict_op_serializer_t : op_serializer_t +{ + bool serialize (hb_serialize_context_t *c, + const OPSTR &opstr, + const cff_sub_table_info_t &info) const + { + TRACE_SERIALIZE (this); + + switch (opstr.op) + { + case OpCode_CharStrings: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); + + case OpCode_FDArray: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute)); + + case OpCode_FDSelect: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute)); + + default: + return_trace (copy_opstr (c, opstr)); + } + return_trace (true); + } +}; + +struct cff_font_dict_op_serializer_t : op_serializer_t +{ + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + const table_info_t &privateDictInfo) const + { + TRACE_SERIALIZE (this); + + if (opstr.op == OpCode_Private) + { + /* serialize the private dict size & offset as 2-byte & 4-byte integers */ + return_trace (UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) && + Dict::serialize_link4_op (c, opstr.op, privateDictInfo.link, whence_t::Absolute)); + } + else + { + unsigned char *d = c->allocate_size<unsigned char> (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]; + //hb_memcpy (d, opstr.ptr, opstr.length); + } + return_trace (true); + } +}; + +struct flatten_param_t +{ + str_buff_t &flatStr; + bool drop_hints; + const hb_subset_plan_t *plan; +}; + +template <typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid> +struct subr_flattener_t +{ + subr_flattener_t (const ACC &acc_, + const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_) {} + + bool flatten (str_buff_vec_t &flat_charstrings) + { + unsigned count = plan->num_output_glyphs (); + if (!flat_charstrings.resize_exact (count)) + return false; + for (unsigned int i = 0; i < count; i++) + { + hb_codepoint_t glyph; + if (!plan->old_gid_for_new_gid (i, &glyph)) + { + /* add an endchar only charstring for a missing glyph if CFF1 */ + if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op); + continue; + } + const hb_ubytes_t str = (*acc.charStrings)[glyph]; + unsigned int fd = acc.fdSelect->get_fd (glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + + + ENV env (str, acc, fd, + plan->normalized_coords.arrayZ, plan->normalized_coords.length); + cs_interpreter_t<ENV, OPSET, flatten_param_t> interp (env); + flatten_param_t param = { + flat_charstrings.arrayZ[i], + (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING), + plan + }; + if (unlikely (!interp.interpret (param))) + return false; + } + return true; + } + + const ACC &acc; + const hb_subset_plan_t *plan; +}; + +struct subr_closures_t +{ + subr_closures_t (unsigned int fd_count) : global_closure (), local_closures () + { + local_closures.resize_exact (fd_count); + } + + void reset () + { + global_closure.clear(); + for (unsigned int i = 0; i < local_closures.length; i++) + local_closures[i].clear(); + } + + bool in_error () const { return local_closures.in_error (); } + hb_set_t global_closure; + hb_vector_t<hb_set_t> local_closures; +}; + +struct parsed_cs_op_t : op_str_t +{ + parsed_cs_op_t (unsigned int subr_num_ = 0) : + subr_num (subr_num_) {} + + bool is_hinting () const { return hinting_flag; } + void set_hinting () { hinting_flag = true; } + + /* The layout of this struct is designed to fit within the + * padding of op_str_t! */ + + protected: + bool hinting_flag = false; + + public: + uint16_t subr_num; +}; + +struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t> +{ + parsed_cs_str_t () : + parsed (false), + hint_dropped (false), + has_prefix_ (false), + has_calls_ (false) + { + SUPER::init (); + } + + void add_op (op_code_t op, const byte_str_ref_t& str_ref) + { + if (!is_parsed ()) + SUPER::add_op (op, str_ref); + } + + void add_call_op (op_code_t op, const byte_str_ref_t& str_ref, unsigned int subr_num) + { + if (!is_parsed ()) + { + has_calls_ = true; + + /* Pop the subroutine number. */ + values.pop (); + + SUPER::add_op (op, str_ref, {subr_num}); + } + } + + void set_prefix (const number_t &num, op_code_t op = OpCode_Invalid) + { + has_prefix_ = true; + prefix_op_ = op; + prefix_num_ = num; + } + + bool at_end (unsigned int pos) const + { + return ((pos + 1 >= values.length) /* CFF2 */ + || (values[pos + 1].op == OpCode_return)); + } + + bool is_parsed () const { return parsed; } + void set_parsed () { parsed = true; } + + bool is_hint_dropped () const { return hint_dropped; } + void set_hint_dropped () { hint_dropped = true; } + + bool is_vsindex_dropped () const { return vsindex_dropped; } + void set_vsindex_dropped () { vsindex_dropped = true; } + + bool has_prefix () const { return has_prefix_; } + op_code_t prefix_op () const { return prefix_op_; } + const number_t &prefix_num () const { return prefix_num_; } + + bool has_calls () const { return has_calls_; } + + void compact () + { + unsigned count = values.length; + if (!count) return; + auto &opstr = values.arrayZ; + unsigned j = 0; + for (unsigned i = 1; i < count; i++) + { + /* See if we can combine op j and op i. */ + bool combine = + (opstr[j].op != OpCode_callsubr && opstr[j].op != OpCode_callgsubr) && + (opstr[i].op != OpCode_callsubr && opstr[i].op != OpCode_callgsubr) && + (opstr[j].is_hinting () == opstr[i].is_hinting ()) && + (opstr[j].ptr + opstr[j].length == opstr[i].ptr) && + (opstr[j].length + opstr[i].length <= 255); + + if (combine) + { + opstr[j].length += opstr[i].length; + opstr[j].op = OpCode_Invalid; + } + else + { + opstr[++j] = opstr[i]; + } + } + values.shrink (j + 1); + } + + protected: + bool parsed : 1; + bool hint_dropped : 1; + bool vsindex_dropped : 1; + bool has_prefix_ : 1; + bool has_calls_ : 1; + op_code_t prefix_op_; + number_t prefix_num_; + + private: + typedef parsed_values_t<parsed_cs_op_t> SUPER; +}; + +struct parsed_cs_str_vec_t : hb_vector_t<parsed_cs_str_t> +{ + private: + typedef hb_vector_t<parsed_cs_str_t> SUPER; +}; + +struct cff_subset_accelerator_t +{ + static cff_subset_accelerator_t* create ( + hb_blob_t* original_blob, + const parsed_cs_str_vec_t& parsed_charstrings, + const parsed_cs_str_vec_t& parsed_global_subrs, + const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs) { + cff_subset_accelerator_t* accel = + (cff_subset_accelerator_t*) hb_malloc (sizeof(cff_subset_accelerator_t)); + if (unlikely (!accel)) return nullptr; + new (accel) cff_subset_accelerator_t (original_blob, + parsed_charstrings, + parsed_global_subrs, + parsed_local_subrs); + return accel; + } + + static void destroy (void* value) { + if (!value) return; + + cff_subset_accelerator_t* accel = (cff_subset_accelerator_t*) value; + accel->~cff_subset_accelerator_t (); + hb_free (accel); + } + + cff_subset_accelerator_t( + hb_blob_t* original_blob_, + const parsed_cs_str_vec_t& parsed_charstrings_, + const parsed_cs_str_vec_t& parsed_global_subrs_, + const hb_vector_t<parsed_cs_str_vec_t>& parsed_local_subrs_) + { + parsed_charstrings = parsed_charstrings_; + parsed_global_subrs = parsed_global_subrs_; + parsed_local_subrs = parsed_local_subrs_; + + // the parsed charstrings point to memory in the original CFF table so we must hold a reference + // to it to keep the memory valid. + original_blob = hb_blob_reference (original_blob_); + } + + ~cff_subset_accelerator_t() + { + hb_blob_destroy (original_blob); + auto *mapping = glyph_to_sid_map.get_relaxed (); + if (mapping) + { + mapping->~glyph_to_sid_map_t (); + hb_free (mapping); + } + } + + parsed_cs_str_vec_t parsed_charstrings; + parsed_cs_str_vec_t parsed_global_subrs; + hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs; + mutable hb_atomic_ptr_t<glyph_to_sid_map_t> glyph_to_sid_map; + + private: + hb_blob_t* original_blob; +}; + +struct subr_subset_param_t +{ + subr_subset_param_t (parsed_cs_str_t *parsed_charstring_, + parsed_cs_str_vec_t *parsed_global_subrs_, + parsed_cs_str_vec_t *parsed_local_subrs_, + hb_set_t *global_closure_, + hb_set_t *local_closure_, + bool drop_hints_) : + current_parsed_str (parsed_charstring_), + parsed_charstring (parsed_charstring_), + parsed_global_subrs (parsed_global_subrs_), + parsed_local_subrs (parsed_local_subrs_), + global_closure (global_closure_), + local_closure (local_closure_), + drop_hints (drop_hints_) {} + + parsed_cs_str_t *get_parsed_str_for_context (call_context_t &context) + { + switch (context.type) + { + case CSType_CharString: + return parsed_charstring; + + case CSType_LocalSubr: + if (likely (context.subr_num < parsed_local_subrs->length)) + return &(*parsed_local_subrs)[context.subr_num]; + break; + + case CSType_GlobalSubr: + if (likely (context.subr_num < parsed_global_subrs->length)) + return &(*parsed_global_subrs)[context.subr_num]; + break; + } + return nullptr; + } + + template <typename ENV> + void set_current_str (ENV &env, bool calling) + { + parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context); + if (unlikely (!parsed_str)) + { + env.set_error (); + return; + } + /* If the called subroutine is parsed partially but not completely yet, + * it must be because we are calling it recursively. + * Handle it as an error. */ + if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0))) + env.set_error (); + else + { + if (!parsed_str->is_parsed ()) + parsed_str->alloc (env.str_ref.total_size ()); + current_parsed_str = parsed_str; + } + } + + parsed_cs_str_t *current_parsed_str; + + parsed_cs_str_t *parsed_charstring; + parsed_cs_str_vec_t *parsed_global_subrs; + parsed_cs_str_vec_t *parsed_local_subrs; + hb_set_t *global_closure; + hb_set_t *local_closure; + bool drop_hints; +}; + +struct subr_remap_t : hb_inc_bimap_t +{ + void create (const hb_set_t *closure) + { + /* create a remapping of subroutine numbers from old to new. + * no optimization based on usage counts. fonttools doesn't appear doing that either. + */ + + alloc (closure->get_population ()); + for (auto old_num : *closure) + add (old_num); + + if (get_population () < 1240) + bias = 107; + else if (get_population () < 33900) + bias = 1131; + else + bias = 32768; + } + + int biased_num (unsigned int old_num) const + { + hb_codepoint_t new_num = get (old_num); + return (int)new_num - bias; + } + + protected: + int bias; +}; + +struct subr_remaps_t +{ + subr_remaps_t (unsigned int fdCount) + { + local_remaps.resize (fdCount); + } + + bool in_error() + { + return local_remaps.in_error (); + } + + void create (subr_closures_t& closures) + { + global_remap.create (&closures.global_closure); + for (unsigned int i = 0; i < local_remaps.length; i++) + local_remaps.arrayZ[i].create (&closures.local_closures[i]); + } + + subr_remap_t global_remap; + hb_vector_t<subr_remap_t> local_remaps; +}; + +template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid> +struct subr_subsetter_t +{ + subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_) + : acc (acc_), plan (plan_), closures(acc_.fdCount), + remaps(acc_.fdCount) + {} + + /* Subroutine subsetting with --no-desubroutinize runs in phases: + * + * 1. execute charstrings/subroutines to determine subroutine closures + * 2. parse out all operators and numbers + * 3. mark hint operators and operands for removal if --no-hinting + * 4. re-encode all charstrings and subroutines with new subroutine numbers + * + * Phases #1 and #2 are done at the same time in collect_subrs (). + * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required), + * because we can't tell if a number belongs to a hint op until we see the first moveto. + * + * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number + * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine. + */ + bool subset (void) + { + unsigned fd_count = acc.fdCount; + const cff_subset_accelerator_t* cff_accelerator = nullptr; + if (acc.cff_accelerator) { + cff_accelerator = acc.cff_accelerator; + fd_count = cff_accelerator->parsed_local_subrs.length; + } + + if (cff_accelerator) { + // If we are not dropping hinting then charstrings are not modified so we can + // just use a reference to the cached copies. + cached_charstrings.resize_exact (plan->num_output_glyphs ()); + parsed_global_subrs = &cff_accelerator->parsed_global_subrs; + parsed_local_subrs = &cff_accelerator->parsed_local_subrs; + } else { + parsed_charstrings.resize_exact (plan->num_output_glyphs ()); + parsed_global_subrs_storage.resize_exact (acc.globalSubrs->count); + + if (unlikely (!parsed_local_subrs_storage.resize (fd_count))) return false; + + for (unsigned int i = 0; i < acc.fdCount; i++) + { + unsigned count = acc.privateDicts[i].localSubrs->count; + parsed_local_subrs_storage[i].resize (count); + if (unlikely (parsed_local_subrs_storage[i].in_error ())) return false; + } + + parsed_global_subrs = &parsed_global_subrs_storage; + parsed_local_subrs = &parsed_local_subrs_storage; + } + + if (unlikely (remaps.in_error() + || cached_charstrings.in_error () + || parsed_charstrings.in_error () + || parsed_global_subrs->in_error () + || closures.in_error ())) { + return false; + } + + /* phase 1 & 2 */ + for (auto _ : plan->new_to_old_gid_list) + { + hb_codepoint_t new_glyph = _.first; + hb_codepoint_t old_glyph = _.second; + + const hb_ubytes_t str = (*acc.charStrings)[old_glyph]; + unsigned int fd = acc.fdSelect->get_fd (old_glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + + if (cff_accelerator) + { + // parsed string already exists in accelerator, copy it and move + // on. + if (cached_charstrings) + cached_charstrings[new_glyph] = &cff_accelerator->parsed_charstrings[old_glyph]; + else + parsed_charstrings[new_glyph] = cff_accelerator->parsed_charstrings[old_glyph]; + + continue; + } + + ENV env (str, acc, fd); + cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp (env); + + parsed_charstrings[new_glyph].alloc (str.length); + subr_subset_param_t param (&parsed_charstrings[new_glyph], + &parsed_global_subrs_storage, + &parsed_local_subrs_storage[fd], + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + + if (unlikely (!interp.interpret (param))) + return false; + + /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ + SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[new_glyph]); + + /* mark hint ops and arguments for drop */ + if ((plan->flags & HB_SUBSET_FLAGS_NO_HINTING) || plan->inprogress_accelerator) + { + subr_subset_param_t param (&parsed_charstrings[new_glyph], + &parsed_global_subrs_storage, + &parsed_local_subrs_storage[fd], + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + + drop_hints_param_t drop; + if (drop_hints_in_str (parsed_charstrings[new_glyph], param, drop)) + { + parsed_charstrings[new_glyph].set_hint_dropped (); + if (drop.vsindex_dropped) + parsed_charstrings[new_glyph].set_vsindex_dropped (); + } + } + + /* Doing this here one by one instead of compacting all at the end + * has massive peak-memory saving. + * + * The compacting both saves memory and makes further operations + * faster. + */ + parsed_charstrings[new_glyph].compact (); + } + + /* Since parsed strings were loaded from accelerator, we still need + * to compute the subroutine closures which would have normally happened during + * parsing. + * + * Or if we are dropping hinting, redo closure to get actually used subrs. + */ + if ((cff_accelerator || + (!cff_accelerator && plan->flags & HB_SUBSET_FLAGS_NO_HINTING)) && + !closure_subroutines(*parsed_global_subrs, + *parsed_local_subrs)) + return false; + + remaps.create (closures); + + populate_subset_accelerator (); + return true; + } + + bool encode_charstrings (str_buff_vec_t &buffArray, bool encode_prefix = true) const + { + unsigned num_glyphs = plan->num_output_glyphs (); + if (unlikely (!buffArray.resize_exact (num_glyphs))) + return false; + hb_codepoint_t last = 0; + for (auto _ : plan->new_to_old_gid_list) + { + hb_codepoint_t gid = _.first; + hb_codepoint_t old_glyph = _.second; + + if (endchar_op != OpCode_Invalid) + for (; last < gid; last++) + { + // Hack to point vector to static string. + auto &b = buffArray.arrayZ[last]; + b.length = 1; + b.arrayZ = const_cast<unsigned char *>(endchar_str); + } + + last++; // Skip over gid + unsigned int fd = acc.fdSelect->get_fd (old_glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + if (unlikely (!encode_str (get_parsed_charstring (gid), fd, buffArray.arrayZ[gid], encode_prefix))) + return false; + } + if (endchar_op != OpCode_Invalid) + for (; last < num_glyphs; last++) + { + // Hack to point vector to static string. + auto &b = buffArray.arrayZ[last]; + b.length = 1; + b.arrayZ = const_cast<unsigned char *>(endchar_str); + } + + return true; + } + + bool encode_subrs (const parsed_cs_str_vec_t &subrs, const subr_remap_t& remap, unsigned int fd, str_buff_vec_t &buffArray) const + { + unsigned int count = remap.get_population (); + + if (unlikely (!buffArray.resize_exact (count))) + return false; + for (unsigned int new_num = 0; new_num < count; new_num++) + { + hb_codepoint_t old_num = remap.backward (new_num); + assert (old_num != CFF_UNDEF_CODE); + + if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num]))) + return false; + } + return true; + } + + bool encode_globalsubrs (str_buff_vec_t &buffArray) + { + return encode_subrs (*parsed_global_subrs, remaps.global_remap, 0, buffArray); + } + + bool encode_localsubrs (unsigned int fd, str_buff_vec_t &buffArray) const + { + return encode_subrs ((*parsed_local_subrs)[fd], remaps.local_remaps[fd], fd, buffArray); + } + + protected: + struct drop_hints_param_t + { + drop_hints_param_t () + : seen_moveto (false), + ends_in_hint (false), + all_dropped (false), + vsindex_dropped (false) {} + + bool seen_moveto; + bool ends_in_hint; + bool all_dropped; + bool vsindex_dropped; + }; + + bool drop_hints_in_subr (parsed_cs_str_t &str, unsigned int pos, + parsed_cs_str_vec_t &subrs, unsigned int subr_num, + const subr_subset_param_t ¶m, drop_hints_param_t &drop) + { + drop.ends_in_hint = false; + bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); + + /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto), + * then this entire subroutine must be a hint. drop its call. */ + if (drop.ends_in_hint) + { + str.values[pos].set_hinting (); + /* if this subr call is at the end of the parent subr, propagate the flag + * otherwise reset the flag */ + if (!str.at_end (pos)) + drop.ends_in_hint = false; + } + else if (drop.all_dropped) + { + str.values[pos].set_hinting (); + } + + return has_hint; + } + + /* returns true if it sees a hint op before the first moveto */ + bool drop_hints_in_str (parsed_cs_str_t &str, const subr_subset_param_t ¶m, drop_hints_param_t &drop) + { + bool seen_hint = false; + + unsigned count = str.values.length; + auto *values = str.values.arrayZ; + for (unsigned int pos = 0; pos < count; pos++) + { + bool has_hint = false; + switch (values[pos].op) + { + case OpCode_callsubr: + has_hint = drop_hints_in_subr (str, pos, + *param.parsed_local_subrs, values[pos].subr_num, + param, drop); + break; + + case OpCode_callgsubr: + has_hint = drop_hints_in_subr (str, pos, + *param.parsed_global_subrs, values[pos].subr_num, + param, drop); + break; + + case OpCode_rmoveto: + case OpCode_hmoveto: + case OpCode_vmoveto: + drop.seen_moveto = true; + break; + + case OpCode_hintmask: + case OpCode_cntrmask: + if (drop.seen_moveto) + { + values[pos].set_hinting (); + break; + } + HB_FALLTHROUGH; + + case OpCode_hstemhm: + case OpCode_vstemhm: + case OpCode_hstem: + case OpCode_vstem: + has_hint = true; + values[pos].set_hinting (); + if (str.at_end (pos)) + drop.ends_in_hint = true; + break; + + case OpCode_dotsection: + values[pos].set_hinting (); + break; + + default: + /* NONE */ + break; + } + if (has_hint) + { + for (int i = pos - 1; i >= 0; i--) + { + parsed_cs_op_t &csop = values[(unsigned)i]; + if (csop.is_hinting ()) + break; + csop.set_hinting (); + if (csop.op == OpCode_vsindexcs) + drop.vsindex_dropped = true; + } + seen_hint |= has_hint; + } + } + + /* Raise all_dropped flag if all operators except return are dropped from a subr. + * It may happen even after seeing the first moveto if a subr contains + * only (usually one) hintmask operator, then calls to this subr can be dropped. + */ + drop.all_dropped = true; + for (unsigned int pos = 0; pos < count; pos++) + { + parsed_cs_op_t &csop = values[pos]; + if (csop.op == OpCode_return) + break; + if (!csop.is_hinting ()) + { + drop.all_dropped = false; + break; + } + } + + return seen_hint; + } + + bool closure_subroutines (const parsed_cs_str_vec_t& global_subrs, + const hb_vector_t<parsed_cs_str_vec_t>& local_subrs) + { + closures.reset (); + for (auto _ : plan->new_to_old_gid_list) + { + hb_codepoint_t new_glyph = _.first; + hb_codepoint_t old_glyph = _.second; + unsigned int fd = acc.fdSelect->get_fd (old_glyph); + if (unlikely (fd >= acc.fdCount)) + return false; + + // Note: const cast is safe here because the collect_subr_refs_in_str only performs a + // closure and does not modify any of the charstrings. + subr_subset_param_t param (const_cast<parsed_cs_str_t*> (&get_parsed_charstring (new_glyph)), + const_cast<parsed_cs_str_vec_t*> (&global_subrs), + const_cast<parsed_cs_str_vec_t*> (&local_subrs[fd]), + &closures.global_closure, + &closures.local_closures[fd], + plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + collect_subr_refs_in_str (get_parsed_charstring (new_glyph), param); + } + + return true; + } + + void collect_subr_refs_in_subr (unsigned int subr_num, parsed_cs_str_vec_t &subrs, + hb_set_t *closure, + const subr_subset_param_t ¶m) + { + if (closure->has (subr_num)) + return; + closure->add (subr_num); + collect_subr_refs_in_str (subrs[subr_num], param); + } + + void collect_subr_refs_in_str (const parsed_cs_str_t &str, + const subr_subset_param_t ¶m) + { + if (!str.has_calls ()) + return; + + for (auto &opstr : str.values) + { + if (!param.drop_hints || !opstr.is_hinting ()) + { + switch (opstr.op) + { + case OpCode_callsubr: + collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_local_subrs, + param.local_closure, param); + break; + + case OpCode_callgsubr: + collect_subr_refs_in_subr (opstr.subr_num, *param.parsed_global_subrs, + param.global_closure, param); + break; + + default: break; + } + } + } + } + + bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff, bool encode_prefix = true) const + { + str_encoder_t encoder (buff); + encoder.reset (); + bool hinting = !(plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, + * re-insert it at the beginning of charstreing */ + if (encode_prefix && str.has_prefix () && !hinting && str.is_hint_dropped ()) + { + encoder.encode_num_cs (str.prefix_num ()); + if (str.prefix_op () != OpCode_Invalid) + encoder.encode_op (str.prefix_op ()); + } + + unsigned size = 0; + for (auto &opstr : str.values) + { + size += opstr.length; + if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr) + size += 3; + } + if (!buff.alloc (buff.length + size, true)) + return false; + + for (auto &opstr : str.values) + { + if (hinting || !opstr.is_hinting ()) + { + switch (opstr.op) + { + case OpCode_callsubr: + encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); + encoder.copy_str (opstr.ptr, opstr.length); + break; + + case OpCode_callgsubr: + encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); + encoder.copy_str (opstr.ptr, opstr.length); + break; + + default: + encoder.copy_str (opstr.ptr, opstr.length); + break; + } + } + } + return !encoder.in_error (); + } + + void compact_parsed_subrs () const + { + for (auto &cs : parsed_global_subrs_storage) + cs.compact (); + for (auto &vec : parsed_local_subrs_storage) + for (auto &cs : vec) + cs.compact (); + } + + void populate_subset_accelerator () const + { + if (!plan->inprogress_accelerator) return; + + compact_parsed_subrs (); + + acc.cff_accelerator = + cff_subset_accelerator_t::create(acc.blob, + parsed_charstrings, + parsed_global_subrs_storage, + parsed_local_subrs_storage); + } + + const parsed_cs_str_t& get_parsed_charstring (unsigned i) const + { + if (cached_charstrings) return *(cached_charstrings[i]); + return parsed_charstrings[i]; + } + + protected: + const ACC &acc; + const hb_subset_plan_t *plan; + + subr_closures_t closures; + + hb_vector_t<const parsed_cs_str_t*> cached_charstrings; + const parsed_cs_str_vec_t* parsed_global_subrs; + const hb_vector_t<parsed_cs_str_vec_t>* parsed_local_subrs; + + subr_remaps_t remaps; + + private: + + parsed_cs_str_vec_t parsed_charstrings; + parsed_cs_str_vec_t parsed_global_subrs_storage; + hb_vector_t<parsed_cs_str_vec_t> parsed_local_subrs_storage; + typedef typename SUBRS::count_type subr_count_type; +}; + +} /* namespace CFF */ + +HB_INTERNAL bool +hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, + unsigned int fdCount, + const CFF::FDSelect &src, /* IN */ + unsigned int &subset_fd_count /* OUT */, + unsigned int &subset_fdselect_size /* OUT */, + unsigned int &subset_fdselect_format /* OUT */, + hb_vector_t<CFF::code_pair_t> &fdselect_ranges /* OUT */, + hb_inc_bimap_t &fdmap /* OUT */); + +HB_INTERNAL bool +hb_serialize_cff_fdselect (hb_serialize_context_t *c, + unsigned int num_glyphs, + const CFF::FDSelect &src, + unsigned int fd_count, + unsigned int fdselect_format, + unsigned int size, + const hb_vector_t<CFF::code_pair_t> &fdselect_ranges); + +#endif /* HB_SUBSET_CFF_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-subset-cff1.cc b/gfx/harfbuzz/src/hb-subset-cff1.cc new file mode 100644 index 0000000000..e9dd5d6427 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-cff1.cc @@ -0,0 +1,1001 @@ +/* + * 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_SUBSET_CFF + +#include "hb-open-type.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-set.h" +#include "hb-bimap.hh" +#include "hb-subset-plan.hh" +#include "hb-subset-cff-common.hh" +#include "hb-cff1-interp-cs.hh" + +using namespace CFF; + +struct remap_sid_t +{ + unsigned get_population () const { return vector.length; } + + void alloc (unsigned size) + { + map.alloc (size); + vector.alloc (size, true); + } + + bool in_error () const + { return map.in_error () || vector.in_error (); } + + unsigned int add (unsigned int sid) + { + if (is_std_str (sid) || (sid == CFF_UNDEF_SID)) + return sid; + + sid = unoffset_sid (sid); + unsigned v = next; + if (map.set (sid, v, false)) + { + vector.push (sid); + next++; + } + else + v = map.get (sid); // already exists + return offset_sid (v); + } + + unsigned int operator[] (unsigned int sid) const + { + if (is_std_str (sid) || (sid == CFF_UNDEF_SID)) + return sid; + + return offset_sid (map.get (unoffset_sid (sid))); + } + + static const unsigned int num_std_strings = 391; + + static bool is_std_str (unsigned int sid) { return sid < num_std_strings; } + static unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; } + static unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; } + unsigned next = 0; + + hb_map_t map; + hb_vector_t<unsigned> vector; +}; + +struct cff1_sub_table_info_t : cff_sub_table_info_t +{ + cff1_sub_table_info_t () + : cff_sub_table_info_t (), + encoding_link (0), + charset_link (0) + { + privateDictInfo.init (); + } + + objidx_t encoding_link; + objidx_t charset_link; + table_info_t privateDictInfo; +}; + +/* a copy of a parsed out cff1_top_dict_values_t augmented with additional operators */ +struct cff1_top_dict_values_mod_t : cff1_top_dict_values_t +{ + void init (const cff1_top_dict_values_t *base_= &Null (cff1_top_dict_values_t)) + { + SUPER::init (); + base = base_; + } + + void fini () { SUPER::fini (); } + + unsigned get_count () const { return base->get_count () + SUPER::get_count (); } + const cff1_top_dict_val_t &get_value (unsigned int i) const + { + if (i < base->get_count ()) + return (*base)[i]; + else + return SUPER::values[i - base->get_count ()]; + } + const cff1_top_dict_val_t &operator [] (unsigned int i) const { return get_value (i); } + + void reassignSIDs (const remap_sid_t& sidmap) + { + for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) + nameSIDs[i] = sidmap[base->nameSIDs[i]]; + } + + protected: + typedef cff1_top_dict_values_t SUPER; + const cff1_top_dict_values_t *base; +}; + +struct top_dict_modifiers_t +{ + top_dict_modifiers_t (const cff1_sub_table_info_t &info_, + const unsigned int (&nameSIDs_)[name_dict_values_t::ValCount]) + : info (info_), + nameSIDs (nameSIDs_) + {} + + const cff1_sub_table_info_t &info; + const unsigned int (&nameSIDs)[name_dict_values_t::ValCount]; +}; + +struct cff1_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<cff1_top_dict_val_t> +{ + bool serialize (hb_serialize_context_t *c, + const cff1_top_dict_val_t &opstr, + const top_dict_modifiers_t &mod) const + { + TRACE_SERIALIZE (this); + + op_code_t op = opstr.op; + switch (op) + { + case OpCode_charset: + if (mod.info.charset_link) + return_trace (FontDict::serialize_link4_op(c, op, mod.info.charset_link, whence_t::Absolute)); + else + goto fall_back; + + case OpCode_Encoding: + if (mod.info.encoding_link) + return_trace (FontDict::serialize_link4_op(c, op, mod.info.encoding_link, whence_t::Absolute)); + else + goto fall_back; + + case OpCode_Private: + return_trace (UnsizedByteStr::serialize_int2 (c, mod.info.privateDictInfo.size) && + Dict::serialize_link4_op (c, op, mod.info.privateDictInfo.link, whence_t::Absolute)); + + case OpCode_version: + case OpCode_Notice: + case OpCode_Copyright: + case OpCode_FullName: + case OpCode_FamilyName: + case OpCode_Weight: + case OpCode_PostScript: + case OpCode_BaseFontName: + case OpCode_FontName: + return_trace (FontDict::serialize_int2_op (c, op, mod.nameSIDs[name_dict_values_t::name_op_to_index (op)])); + + case OpCode_ROS: + { + /* for registry & ordering, reassigned SIDs are serialized + * for supplement, the original byte string is copied along with the op code */ + op_str_t supp_op; + supp_op.op = op; + if ( unlikely (!(opstr.length >= opstr.last_arg_offset + 3))) + return_trace (false); + supp_op.ptr = opstr.ptr + opstr.last_arg_offset; + supp_op.length = opstr.length - opstr.last_arg_offset; + return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::registry]) && + UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[name_dict_values_t::ordering]) && + copy_opstr (c, supp_op)); + } + fall_back: + default: + return_trace (cff_top_dict_op_serializer_t<cff1_top_dict_val_t>::serialize (c, opstr, mod.info)); + } + return_trace (true); + } + +}; + +struct cff1_font_dict_op_serializer_t : cff_font_dict_op_serializer_t +{ + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + const cff1_font_dict_values_mod_t &mod) const + { + TRACE_SERIALIZE (this); + + if (opstr.op == OpCode_FontName) + return_trace (FontDict::serialize_int2_op (c, opstr.op, mod.fontName)); + else + return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo)); + } + + private: + typedef cff_font_dict_op_serializer_t SUPER; +}; + +struct cff1_cs_opset_flatten_t : cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t> +{ + static void flush_args_and_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) + { + if (env.arg_start > 0) + flush_width (env, param); + + switch (op) + { + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + case OpCode_dotsection: + if (param.drop_hints) + { + env.clear_args (); + return; + } + HB_FALLTHROUGH; + + default: + SUPER::flush_args_and_op (op, env, param); + break; + } + } + static void flush_args (cff1_cs_interp_env_t &env, flatten_param_t& param) + { + str_encoder_t encoder (param.flatStr); + for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++) + encoder.encode_num_cs (env.eval_arg (i)); + SUPER::flush_args (env, param); + } + + static void flush_op (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) + { + str_encoder_t encoder (param.flatStr); + encoder.encode_op (op); + } + + static void flush_width (cff1_cs_interp_env_t &env, flatten_param_t& param) + { + assert (env.has_width); + str_encoder_t encoder (param.flatStr); + encoder.encode_num_cs (env.width); + } + + static void flush_hintmask (op_code_t op, cff1_cs_interp_env_t &env, flatten_param_t& param) + { + SUPER::flush_hintmask (op, env, param); + if (!param.drop_hints) + { + str_encoder_t encoder (param.flatStr); + for (unsigned int i = 0; i < env.hintmask_size; i++) + encoder.encode_byte (env.str_ref[i]); + } + } + + private: + typedef cff1_cs_opset_t<cff1_cs_opset_flatten_t, flatten_param_t> SUPER; +}; + +struct range_list_t : hb_vector_t<code_pair_t> +{ + /* replace the first glyph ID in the "glyph" field each range with a nLeft value */ + bool complete (unsigned int last_glyph) + { + hb_codepoint_t all_glyphs = 0; + unsigned count = this->length; + for (unsigned int i = count; i; i--) + { + code_pair_t &pair = arrayZ[i - 1]; + unsigned int nLeft = last_glyph - pair.glyph - 1; + all_glyphs |= nLeft; + last_glyph = pair.glyph; + pair.glyph = nLeft; + } + bool two_byte = all_glyphs >= 0x100; + return two_byte; + } +}; + +struct cff1_cs_opset_subr_subset_t : cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t> +{ + static void process_op (op_code_t op, cff1_cs_interp_env_t &env, subr_subset_param_t& param) + { + switch (op) { + + case OpCode_return: + param.current_parsed_str->add_op (op, env.str_ref); + param.current_parsed_str->set_parsed (); + env.return_from_subr (); + param.set_current_str (env, false); + break; + + case OpCode_endchar: + param.current_parsed_str->add_op (op, env.str_ref); + param.current_parsed_str->set_parsed (); + SUPER::process_op (op, env, param); + break; + + case OpCode_callsubr: + process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); + break; + + case OpCode_callgsubr: + process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); + break; + + default: + SUPER::process_op (op, env, param); + param.current_parsed_str->add_op (op, env.str_ref); + break; + } + } + + protected: + static void process_call_subr (op_code_t op, cs_type_t type, + cff1_cs_interp_env_t &env, subr_subset_param_t& param, + cff1_biased_subrs_t& subrs, hb_set_t *closure) + { + byte_str_ref_t str_ref = env.str_ref; + env.call_subr (subrs, type); + param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num); + closure->add (env.context.subr_num); + param.set_current_str (env, true); + } + + private: + typedef cff1_cs_opset_t<cff1_cs_opset_subr_subset_t, subr_subset_param_t> SUPER; +}; + +struct cff1_private_dict_op_serializer_t : op_serializer_t +{ + cff1_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_) + : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {} + + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + objidx_t subrs_link) const + { + TRACE_SERIALIZE (this); + + if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) + return_trace (true); + + if (opstr.op == OpCode_Subrs) + { + if (desubroutinize || !subrs_link) + return_trace (true); + else + return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); + } + + return_trace (copy_opstr (c, opstr)); + } + + protected: + const bool desubroutinize; + const bool drop_hints; +}; + +struct cff1_subr_subsetter_t : subr_subsetter_t<cff1_subr_subsetter_t, CFF1Subrs, const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_subr_subset_t, OpCode_endchar> +{ + cff1_subr_subsetter_t (const OT::cff1::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff1_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + { + /* insert width at the beginning of the charstring as necessary */ + if (env.has_width) + charstring.set_prefix (env.width); + + /* subroutines/charstring left on the call stack are legally left unmarked + * unmarked when a subroutine terminates with endchar. mark them. + */ + param.current_parsed_str->set_parsed (); + for (unsigned int i = 0; i < env.callStack.get_count (); i++) + { + parsed_cs_str_t *parsed_str = param.get_parsed_str_for_context (env.callStack[i]); + if (likely (parsed_str)) + parsed_str->set_parsed (); + else + env.set_error (); + } + } +}; + +namespace OT { +struct cff1_subset_plan +{ + cff1_subset_plan () + { + for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) + topDictModSIDs[i] = CFF_UNDEF_SID; + } + + void plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) + { + const Encoding *encoding = acc.encoding; + unsigned int size0, size1; + unsigned code, last_code = CFF_UNDEF_CODE - 1; + hb_vector_t<hb_codepoint_t> supp_codes; + + if (unlikely (!subset_enc_code_ranges.resize (0))) + { + plan->check_success (false); + return; + } + + supp_codes.init (); + + code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID}; + subset_enc_num_codes = plan->num_output_glyphs () - 1; + unsigned int glyph; + auto it = hb_iter (plan->new_to_old_gid_list); + if (it->first == 0) it++; + auto _ = *it; + for (glyph = 1; glyph < num_glyphs; glyph++) + { + hb_codepoint_t old_glyph; + if (glyph == _.first) + { + old_glyph = _.second; + _ = *++it; + } + else + { + /* Retain the SID for the old missing glyph ID */ + old_glyph = glyph; + } + code = acc.glyph_to_code (old_glyph, &glyph_to_sid_cache); + if (code == CFF_UNDEF_CODE) + { + subset_enc_num_codes = glyph - 1; + break; + } + + if (code != last_code + 1) + subset_enc_code_ranges.push (code_pair_t {code, glyph}); + last_code = code; + + if (encoding != &Null (Encoding)) + { + hb_codepoint_t sid = acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache); + encoding->get_supplement_codes (sid, supp_codes); + for (unsigned int i = 0; i < supp_codes.length; i++) + subset_enc_supp_codes.push (code_pair_t {supp_codes[i], sid}); + } + } + supp_codes.fini (); + + subset_enc_code_ranges.complete (glyph); + + assert (subset_enc_num_codes <= 0xFF); + size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes; + size1 = Encoding1::min_size + Encoding1_Range::static_size * subset_enc_code_ranges.length; + + if (size0 < size1) + subset_enc_format = 0; + else + subset_enc_format = 1; + } + + bool plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan) + { + unsigned int size0, size_ranges; + unsigned last_sid = CFF_UNDEF_CODE - 1; + + if (unlikely (!subset_charset_ranges.resize (0))) + { + plan->check_success (false); + return false; + } + + code_pair_t glyph_to_sid_cache {0, HB_CODEPOINT_INVALID}; + + unsigned num_glyphs = plan->num_output_glyphs (); + + if (unlikely (!subset_charset_ranges.alloc (hb_min (num_glyphs, + acc.num_charset_entries)))) + { + plan->check_success (false); + return false; + } + + glyph_to_sid_map_t *glyph_to_sid_map = acc.cff_accelerator ? + acc.cff_accelerator->glyph_to_sid_map.get_acquire () : + nullptr; + bool created_map = false; + if (!glyph_to_sid_map && acc.cff_accelerator) + { + created_map = true; + glyph_to_sid_map = acc.create_glyph_to_sid_map (); + } + + auto it = hb_iter (plan->new_to_old_gid_list); + if (it->first == 0) it++; + auto _ = *it; + bool not_is_cid = !acc.is_CID (); + bool skip = !not_is_cid && glyph_to_sid_map; + if (not_is_cid) + sidmap.alloc (num_glyphs); + for (hb_codepoint_t glyph = 1; glyph < num_glyphs; glyph++) + { + hb_codepoint_t old_glyph; + if (glyph == _.first) + { + old_glyph = _.second; + _ = *++it; + } + else + { + /* Retain the SID for the old missing glyph ID */ + old_glyph = glyph; + } + unsigned sid = glyph_to_sid_map ? + glyph_to_sid_map->arrayZ[old_glyph].code : + acc.glyph_to_sid (old_glyph, &glyph_to_sid_cache); + + if (not_is_cid) + sid = sidmap.add (sid); + + if (sid != last_sid + 1) + subset_charset_ranges.push (code_pair_t {sid, glyph}); + + if (glyph == old_glyph && skip) + { + glyph = hb_min (_.first - 1, glyph_to_sid_map->arrayZ[old_glyph].glyph); + sid += glyph - old_glyph; + } + last_sid = sid; + } + + if (created_map) + { + if ((!plan->accelerator && acc.cff_accelerator) || + !acc.cff_accelerator->glyph_to_sid_map.cmpexch (nullptr, glyph_to_sid_map)) + { + glyph_to_sid_map->~glyph_to_sid_map_t (); + hb_free (glyph_to_sid_map); + } + } + + bool two_byte = subset_charset_ranges.complete (num_glyphs); + + size0 = Charset0::get_size (plan->num_output_glyphs ()); + if (!two_byte) + size_ranges = Charset1::get_size_for_ranges (subset_charset_ranges.length); + else + size_ranges = Charset2::get_size_for_ranges (subset_charset_ranges.length); + + if (size0 < size_ranges) + subset_charset_format = 0; + else if (!two_byte) + subset_charset_format = 1; + else + subset_charset_format = 2; + + return true; + } + + bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc) + { + for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) + { + unsigned int sid = acc.topDict.nameSIDs[i]; + if (sid != CFF_UNDEF_SID) + { + topDictModSIDs[i] = sidmap.add (sid); + } + } + + if (acc.fdArray != &Null (CFF1FDArray)) + for (unsigned int i = 0; i < orig_fdcount; i++) + if (fdmap.has (i)) + (void)sidmap.add (acc.fontDicts[i].fontName); + + return true; + } + + bool create (const OT::cff1::accelerator_subset_t &acc, + hb_subset_plan_t *plan) + { + /* make sure notdef is first */ + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false; + + num_glyphs = plan->num_output_glyphs (); + orig_fdcount = acc.fdCount; + drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE; + + #ifdef HB_EXPERIMENTAL_API + min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0; + #else + min_charstrings_off_size = 0; + #endif + + subset_charset = !acc.is_predef_charset (); + if (!subset_charset) + /* check whether the subset renumbers any glyph IDs */ + for (const auto &_ : plan->new_to_old_gid_list) + { + if (_.first != _.second) + { + subset_charset = true; + break; + } + } + + subset_encoding = !acc.is_CID() && !acc.is_predef_encoding (); + + /* top dict INDEX */ + { + /* Add encoding/charset to a (copy of) top dict as necessary */ + topdict_mod.init (&acc.topDict); + bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding)); + bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset)); + if (need_to_add_enc || need_to_add_set) + { + if (need_to_add_enc) + topdict_mod.add_op (OpCode_Encoding); + if (need_to_add_set) + topdict_mod.add_op (OpCode_charset); + } + } + + /* Determine re-mapping of font index as fdmap among other info */ + if (acc.fdSelect != &Null (CFF1FDSelect)) + { + if (unlikely (!hb_plan_subset_cff_fdselect (plan, + orig_fdcount, + *acc.fdSelect, + subset_fdcount, + info.fd_select.size, + subset_fdselect_format, + subset_fdselect_ranges, + fdmap))) + return false; + } + else + fdmap.identity (1); + + /* remove unused SIDs & reassign SIDs */ + { + /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */ + if (unlikely (!collect_sids_in_dicts (acc))) + return false; + if (unlikely (sidmap.get_population () > 0x8000)) /* assumption: a dict won't reference that many strings */ + return false; + + if (subset_charset && !plan_subset_charset (acc, plan)) + return false; + + topdict_mod.reassignSIDs (sidmap); + } + + if (desubroutinize) + { + /* Flatten global & local subrs */ + subr_flattener_t<const OT::cff1::accelerator_subset_t, cff1_cs_interp_env_t, cff1_cs_opset_flatten_t, OpCode_endchar> + flattener(acc, plan); + if (!flattener.flatten (subset_charstrings)) + return false; + } + else + { + cff1_subr_subsetter_t subr_subsetter (acc, plan); + + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ + if (!subr_subsetter.subset ()) + return false; + + /* encode charstrings, global subrs, local subrs with new subroutine numbers */ + if (!subr_subsetter.encode_charstrings (subset_charstrings)) + return false; + + if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) + return false; + + /* local subrs */ + if (!subset_localsubrs.resize (orig_fdcount)) + return false; + for (unsigned int fd = 0; fd < orig_fdcount; fd++) + { + subset_localsubrs[fd].init (); + if (fdmap.has (fd)) + { + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; + } + } + } + + /* Encoding */ + if (subset_encoding) + plan_subset_encoding (acc, plan); + + /* private dicts & local subrs */ + if (!acc.is_CID ()) + fontdicts_mod.push (cff1_font_dict_values_mod_t ()); + else + { + + hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff1_font_dict_values_t &_) + { return fdmap.has (&_ - &acc.fontDicts[0]); } ) + | hb_map ([&] (const cff1_font_dict_values_t &_) + { + cff1_font_dict_values_mod_t mod; + mod.init (&_, sidmap[_.fontName]); + return mod; + }) + | hb_sink (fontdicts_mod) + ; + } + + return !plan->in_error () && + (subset_charstrings.length == plan->num_output_glyphs ()) && + (fontdicts_mod.length == subset_fdcount); + } + + cff1_top_dict_values_mod_t topdict_mod; + cff1_sub_table_info_t info; + + unsigned int num_glyphs; + unsigned int orig_fdcount = 0; + unsigned int subset_fdcount = 1; + unsigned int subset_fdselect_format = 0; + hb_vector_t<code_pair_t> subset_fdselect_ranges; + + /* font dict index remap table from fullset FDArray to subset FDArray. + * set to CFF_UNDEF_CODE if excluded from subset */ + hb_inc_bimap_t fdmap; + + str_buff_vec_t subset_charstrings; + str_buff_vec_t subset_globalsubrs; + hb_vector_t<str_buff_vec_t> subset_localsubrs; + hb_vector_t<cff1_font_dict_values_mod_t> fontdicts_mod; + + bool drop_hints = false; + + bool gid_renum; + bool subset_encoding; + uint8_t subset_enc_format; + unsigned int subset_enc_num_codes; + range_list_t subset_enc_code_ranges; + hb_vector_t<code_pair_t> subset_enc_supp_codes; + + uint8_t subset_charset_format; + range_list_t subset_charset_ranges; + bool subset_charset; + + remap_sid_t sidmap; + unsigned int topDictModSIDs[name_dict_values_t::ValCount]; + + bool desubroutinize = false; + + unsigned min_charstrings_off_size = 0; +}; +} // namespace OT + +static bool _serialize_cff1_charstrings (hb_serialize_context_t *c, + struct OT::cff1_subset_plan &plan, + const OT::cff1::accelerator_subset_t &acc) +{ + c->push<CFF1CharStrings> (); + + unsigned data_size = 0; + unsigned total_size = CFF1CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + + auto *cs = c->start_embed<CFF1CharStrings> (); + if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) { + c->pop_discard (); + return false; + } + + plan.info.char_strings_link = c->pop_pack (false); + return true; +} + +bool +OT::cff1::accelerator_subset_t::serialize (hb_serialize_context_t *c, + struct OT::cff1_subset_plan &plan) const +{ + /* push charstrings onto the object stack first which will ensure it packs as the last + object in the table. Keeping the chastrings last satisfies the requirements for patching + via IFTB. If this ordering needs to be changed in the future, charstrings should be left + at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */ + if (!_serialize_cff1_charstrings(c, plan, *this)) + return false; + + /* private dicts & local subrs */ + for (int i = (int) privateDicts.length; --i >= 0 ;) + { + if (plan.fdmap.has (i)) + { + objidx_t subrs_link = 0; + if (plan.subset_localsubrs[i].length > 0) + { + auto *dest = c->push <CFF1Subrs> (); + if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + auto *pd = c->push<PrivateDict> (); + cff1_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints); + /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ + if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + plan.fontdicts_mod[fd].privateDictInfo.size = c->length (); + plan.fontdicts_mod[fd].privateDictInfo.link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } + } + } + + if (!is_CID ()) + plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo; + + /* FDArray (FD Index) */ + if (fdArray != &Null (CFF1FDArray)) + { + auto *fda = c->push<CFF1FDArray> (); + cff1_font_dict_op_serializer_t fontSzr; + auto it = + hb_zip (+ hb_iter (plan.fontdicts_mod), + hb_iter (plan.fontdicts_mod)); + if (likely (fda->serialize (c, it, fontSzr))) + plan.info.fd_array_link = c->pop_pack (false); + else + { + c->pop_discard (); + return false; + } + } + + /* FDSelect */ + if (fdSelect != &Null (CFF1FDSelect)) + { + c->push (); + if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *fdSelect, fdCount, + plan.subset_fdselect_format, plan.info.fd_select.size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* Charset */ + if (plan.subset_charset) + { + auto *dest = c->push<Charset> (); + if (likely (dest->serialize (c, + plan.subset_charset_format, + plan.num_glyphs, + plan.subset_charset_ranges))) + plan.info.charset_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* Encoding */ + if (plan.subset_encoding) + { + auto *dest = c->push<Encoding> (); + if (likely (dest->serialize (c, + plan.subset_enc_format, + plan.subset_enc_num_codes, + plan.subset_enc_code_ranges, + plan.subset_enc_supp_codes))) + plan.info.encoding_link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* global subrs */ + { + auto *dest = c->push <CFF1Subrs> (); + if (likely (dest->serialize (c, plan.subset_globalsubrs))) + c->pop_pack (false); + else + { + c->pop_discard (); + return false; + } + } + + /* String INDEX */ + { + auto *dest = c->push<CFF1StringIndex> (); + if (likely (!plan.sidmap.in_error () && + dest->serialize (c, *stringIndex, plan.sidmap.vector))) + c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + OT::cff1 *cff = c->allocate_min<OT::cff1> (); + if (unlikely (!cff)) + return false; + + /* header */ + cff->version.major = 0x01; + cff->version.minor = 0x00; + cff->nameIndex = cff->min_size; + cff->offSize = 4; /* unused? */ + + /* name INDEX */ + if (unlikely (!c->embed (*nameIndex))) return false; + + /* top dict INDEX */ + { + /* serialize singleton TopDict */ + auto *top = c->push<TopDict> (); + cff1_top_dict_op_serializer_t topSzr; + unsigned top_size = 0; + top_dict_modifiers_t modifier (plan.info, plan.topDictModSIDs); + if (likely (top->serialize (c, plan.topdict_mod, topSzr, modifier))) + { + top_size = c->length (); + c->pop_pack (false); + } + else + { + c->pop_discard (); + return false; + } + /* serialize INDEX header for above */ + auto *dest = c->start_embed<CFF1Index> (); + return dest->serialize_header (c, hb_iter (&top_size, 1), top_size); + } +} + +bool +OT::cff1::accelerator_subset_t::subset (hb_subset_context_t *c) const +{ + cff1_subset_plan cff_plan; + + if (unlikely (!cff_plan.create (*this, c->plan))) + { + DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan."); + return false; + } + + return serialize (c->serializer, cff_plan); +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-subset-cff2.cc b/gfx/harfbuzz/src/hb-subset-cff2.cc new file mode 100644 index 0000000000..abc108e571 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-cff2.cc @@ -0,0 +1,676 @@ +/* + * 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_SUBSET_CFF + +#include "hb-open-type.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-set.h" +#include "hb-subset-plan.hh" +#include "hb-subset-cff-common.hh" +#include "hb-cff2-interp-cs.hh" + +using namespace CFF; + +struct cff2_sub_table_info_t : cff_sub_table_info_t +{ + cff2_sub_table_info_t () + : cff_sub_table_info_t (), + var_store_link (0) + {} + + objidx_t var_store_link; +}; + +struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<> +{ + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + const cff2_sub_table_info_t &info) const + { + TRACE_SERIALIZE (this); + + switch (opstr.op) + { + case OpCode_vstore: + if (info.var_store_link) + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link)); + else + return_trace (true); + + default: + return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info)); + } + } +}; + +struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> +{ + static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) + { + switch (op) + { + case OpCode_return: + case OpCode_endchar: + /* dummy opcodes in CFF2. ignore */ + break; + + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + if (param.drop_hints) + { + env.clear_args (); + return; + } + HB_FALLTHROUGH; + + default: + SUPER::flush_args_and_op (op, env, param); + break; + } + } + + static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) + { + for (unsigned int i = 0; i < env.argStack.get_count ();) + { + const blend_arg_t &arg = env.argStack[i]; + if (arg.blending ()) + { + if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues)))) + { + env.set_error (); + return; + } + flatten_blends (arg, i, env, param); + i += arg.numValues; + } + else + { + str_encoder_t encoder (param.flatStr); + encoder.encode_num_cs (arg); + i++; + } + } + SUPER::flush_args (env, param); + } + + static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) + { + /* flatten the default values */ + str_encoder_t encoder (param.flatStr); + for (unsigned int j = 0; j < arg.numValues; j++) + { + const blend_arg_t &arg1 = env.argStack[i + j]; + if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) && + (arg1.deltas.length == env.get_region_count ()))))) + { + env.set_error (); + return; + } + encoder.encode_num_cs (arg1); + } + /* flatten deltas for each value */ + for (unsigned int j = 0; j < arg.numValues; j++) + { + const blend_arg_t &arg1 = env.argStack[i + j]; + for (unsigned int k = 0; k < arg1.deltas.length; k++) + encoder.encode_num_cs (arg1.deltas[k]); + } + /* flatten the number of values followed by blend operator */ + encoder.encode_int (arg.numValues); + encoder.encode_op (OpCode_blendcs); + } + + static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) + { + switch (op) + { + case OpCode_return: + case OpCode_endchar: + return; + default: + str_encoder_t encoder (param.flatStr); + encoder.encode_op (op); + } + } + + static void flush_hintmask (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) + { + SUPER::flush_hintmask (op, env, param); + if (!param.drop_hints) + { + str_encoder_t encoder (param.flatStr); + for (unsigned int i = 0; i < env.hintmask_size; i++) + encoder.encode_byte (env.str_ref[i]); + } + } + + private: + typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER; + typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t<blend_arg_t>, flatten_param_t> CSOPSET; +}; + +struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> +{ + static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param) + { + switch (op) { + + case OpCode_return: + param.current_parsed_str->set_parsed (); + env.return_from_subr (); + param.set_current_str (env, false); + break; + + case OpCode_endchar: + param.current_parsed_str->set_parsed (); + SUPER::process_op (op, env, param); + break; + + case OpCode_callsubr: + process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); + break; + + case OpCode_callgsubr: + process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); + break; + + default: + SUPER::process_op (op, env, param); + param.current_parsed_str->add_op (op, env.str_ref); + break; + } + } + + protected: + static void process_call_subr (op_code_t op, cs_type_t type, + cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, + cff2_biased_subrs_t& subrs, hb_set_t *closure) + { + byte_str_ref_t str_ref = env.str_ref; + env.call_subr (subrs, type); + param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num); + closure->add (env.context.subr_num); + param.set_current_str (env, true); + } + + private: + typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER; +}; + +struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_subr_subset_t> +{ + cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) + : subr_subsetter_t (acc_, plan_) {} + + static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + { + /* vsindex is inserted at the beginning of the charstring as necessary */ + if (env.seen_vsindex ()) + { + number_t ivs; + ivs.set_int ((int)env.get_ivs ()); + charstring.set_prefix (ivs, OpCode_vsindexcs); + } + } +}; + +struct cff2_private_blend_encoder_param_t +{ + cff2_private_blend_encoder_param_t (hb_serialize_context_t *c, + const CFF2VariationStore *varStore, + hb_array_t<int> normalized_coords) : + c (c), varStore (varStore), normalized_coords (normalized_coords) {} + + void init () {} + + void process_blend () + { + if (!seen_blend) + { + region_count = varStore->varStore.get_region_index_count (ivs); + scalars.resize_exact (region_count); + varStore->varStore.get_region_scalars (ivs, normalized_coords.arrayZ, normalized_coords.length, + &scalars[0], region_count); + seen_blend = true; + } + } + + double blend_deltas (hb_array_t<const number_t> deltas) const + { + double v = 0; + 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; + } + + + hb_serialize_context_t *c = nullptr; + bool seen_blend = false; + unsigned ivs = 0; + unsigned region_count = 0; + hb_vector_t<float> scalars; + const CFF2VariationStore *varStore = nullptr; + hb_array_t<int> normalized_coords; +}; + +struct cff2_private_dict_blend_opset_t : dict_opset_t +{ + static void process_arg_blend (cff2_private_blend_encoder_param_t& param, + number_t &arg, + const hb_array_t<const number_t> blends, + unsigned n, unsigned i) + { + arg.set_int (round (arg.to_real () + param.blend_deltas (blends))); + } + + static void process_blend (cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param) + { + unsigned int n, k; + + param.process_blend (); + k = param.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<const number_t> blends = env.argStack.sub_array (start + n + (i * k), k); + process_arg_blend (param, 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_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param) + { + 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: + break; + case OpCode_vsindexdict: + env.process_vsindex (); + param.ivs = env.get_ivs (); + env.clear_args (); + return; + case OpCode_blenddict: + process_blend (env, param); + return; + + default: + dict_opset_t::process_op (op, env); + if (!env.argStack.is_empty ()) return; + break; + } + + if (unlikely (env.in_error ())) return; + + // Write args then op + + str_buff_t str; + str_encoder_t encoder (str); + + unsigned count = env.argStack.get_count (); + for (unsigned i = 0; i < count; i++) + encoder.encode_num_tp (env.argStack[i]); + + encoder.encode_op (op); + + auto bytes = str.as_bytes (); + param.c->embed (&bytes, bytes.length); + + env.clear_args (); + } +}; + +struct cff2_private_dict_op_serializer_t : op_serializer_t +{ + cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_, + const CFF::CFF2VariationStore* varStore_, + hb_array_t<int> normalized_coords_) + : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_), + varStore (varStore_), normalized_coords (normalized_coords_) {} + + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + objidx_t subrs_link) const + { + TRACE_SERIALIZE (this); + + if (drop_hints && dict_opset_t::is_hint_op (opstr.op)) + return_trace (true); + + if (opstr.op == OpCode_Subrs) + { + if (desubroutinize || !subrs_link) + return_trace (true); + else + return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link)); + } + + if (pinned) + { + // Reinterpret opstr and process blends. + cff2_priv_dict_interp_env_t env {hb_ubytes_t (opstr.ptr, opstr.length)}; + cff2_private_blend_encoder_param_t param (c, varStore, normalized_coords); + dict_interpreter_t<cff2_private_dict_blend_opset_t, cff2_private_blend_encoder_param_t, cff2_priv_dict_interp_env_t> interp (env); + return_trace (interp.interpret (param)); + } + + return_trace (copy_opstr (c, opstr)); + } + + protected: + const bool desubroutinize; + const bool drop_hints; + const bool pinned; + const CFF::CFF2VariationStore* varStore; + hb_array_t<int> normalized_coords; +}; + + +namespace OT { +struct cff2_subset_plan +{ + bool create (const OT::cff2::accelerator_subset_t &acc, + hb_subset_plan_t *plan) + { + /* make sure notdef is first */ + hb_codepoint_t old_glyph; + if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false; + + num_glyphs = plan->num_output_glyphs (); + orig_fdcount = acc.fdArray->count; + + drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + pinned = (bool) plan->normalized_coords; + desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE || + pinned; // For instancing we need this path + + #ifdef HB_EXPERIMENTAL_API + min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0; + #else + min_charstrings_off_size = 0; + #endif + + if (desubroutinize) + { + /* Flatten global & local subrs */ + subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t> + flattener(acc, plan); + if (!flattener.flatten (subset_charstrings)) + return false; + } + else + { + cff2_subr_subsetter_t subr_subsetter (acc, plan); + + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ + if (!subr_subsetter.subset ()) + return false; + + /* encode charstrings, global subrs, local subrs with new subroutine numbers */ + if (!subr_subsetter.encode_charstrings (subset_charstrings, !pinned)) + return false; + + if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) + return false; + + /* local subrs */ + if (!subset_localsubrs.resize (orig_fdcount)) + return false; + for (unsigned int fd = 0; fd < orig_fdcount; fd++) + { + subset_localsubrs[fd].init (); + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; + } + } + + /* FDSelect */ + if (acc.fdSelect != &Null (CFF2FDSelect)) + { + if (unlikely (!hb_plan_subset_cff_fdselect (plan, + orig_fdcount, + *(const FDSelect *)acc.fdSelect, + subset_fdcount, + subset_fdselect_size, + subset_fdselect_format, + subset_fdselect_ranges, + fdmap))) + return false; + } + else + fdmap.identity (1); + + return true; + } + + cff2_sub_table_info_t info; + + unsigned int num_glyphs; + unsigned int orig_fdcount = 0; + unsigned int subset_fdcount = 1; + unsigned int subset_fdselect_size = 0; + unsigned int subset_fdselect_format = 0; + bool pinned = false; + hb_vector_t<code_pair_t> subset_fdselect_ranges; + + hb_inc_bimap_t fdmap; + + str_buff_vec_t subset_charstrings; + str_buff_vec_t subset_globalsubrs; + hb_vector_t<str_buff_vec_t> subset_localsubrs; + + bool drop_hints = false; + bool desubroutinize = false; + + unsigned min_charstrings_off_size = 0; +}; +} // namespace OT + +static bool _serialize_cff2_charstrings (hb_serialize_context_t *c, + cff2_subset_plan &plan, + const OT::cff2::accelerator_subset_t &acc) +{ + c->push (); + + unsigned data_size = 0; + unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size); + if (unlikely (!c->start_zerocopy (total_size))) + return false; + + auto *cs = c->start_embed<CFF2CharStrings> (); + if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size))) + { + c->pop_discard (); + return false; + } + + plan.info.char_strings_link = c->pop_pack (false); + return true; +} + +bool +OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c, + struct cff2_subset_plan &plan, + hb_array_t<int> normalized_coords) const +{ + /* push charstrings onto the object stack first which will ensure it packs as the last + object in the table. Keeping the chastrings last satisfies the requirements for patching + via IFTB. If this ordering needs to be changed in the future, charstrings should be left + at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */ + if (!_serialize_cff2_charstrings(c, plan, *this)) + return false; + + /* private dicts & local subrs */ + hb_vector_t<table_info_t> private_dict_infos; + if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false; + + for (int i = (int)privateDicts.length; --i >= 0 ;) + { + if (plan.fdmap.has (i)) + { + objidx_t subrs_link = 0; + + if (plan.subset_localsubrs[i].length > 0) + { + auto *dest = c->push <CFF2Subrs> (); + if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (false); + else + { + c->pop_discard (); + return false; + } + } + auto *pd = c->push<PrivateDict> (); + cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned, + varStore, normalized_coords); + if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link))) + { + unsigned fd = plan.fdmap[i]; + private_dict_infos[fd].size = c->length (); + private_dict_infos[fd].link = c->pop_pack (); + } + else + { + c->pop_discard (); + return false; + } + } + } + + /* FDSelect */ + if (fdSelect != &Null (CFF2FDSelect)) + { + c->push (); + if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect, + plan.orig_fdcount, + plan.subset_fdselect_format, plan.subset_fdselect_size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else + { + c->pop_discard (); + return false; + } + } + + /* FDArray (FD Index) */ + { + auto *fda = c->push<CFF2FDArray> (); + cff_font_dict_op_serializer_t fontSzr; + auto it = + + hb_zip (+ hb_iter (fontDicts) + | hb_filter ([&] (const cff2_font_dict_values_t &_) + { return plan.fdmap.has (&_ - &fontDicts[0]); }), + hb_iter (private_dict_infos)) + ; + if (unlikely (!fda->serialize (c, it, fontSzr))) + { + c->pop_discard (); + return false; + } + plan.info.fd_array_link = c->pop_pack (false); + } + + /* variation store */ + if (varStore != &Null (CFF2VariationStore) && + !plan.pinned) + { + auto *dest = c->push<CFF2VariationStore> (); + if (unlikely (!dest->serialize (c, varStore))) + { + c->pop_discard (); + return false; + } + plan.info.var_store_link = c->pop_pack (false); + } + + OT::cff2 *cff2 = c->allocate_min<OT::cff2> (); + if (unlikely (!cff2)) return false; + + /* header */ + cff2->version.major = 0x02; + cff2->version.minor = 0x00; + cff2->topDict = OT::cff2::static_size; + + /* top dict */ + { + TopDict &dict = cff2 + cff2->topDict; + cff2_top_dict_op_serializer_t topSzr; + if (unlikely (!dict.serialize (c, topDict, topSzr, plan.info))) return false; + cff2->topDictSize = c->head - (const char *)&dict; + } + + /* global subrs */ + { + auto *dest = c->start_embed <CFF2Subrs> (); + return dest->serialize (c, plan.subset_globalsubrs); + } +} + +bool +OT::cff2::accelerator_subset_t::subset (hb_subset_context_t *c) const +{ + cff2_subset_plan cff2_plan; + + if (unlikely (!cff2_plan.create (*this, c->plan))) return false; + return serialize (c->serializer, cff2_plan, + c->plan->normalized_coords.as_array ()); +} + +#endif diff --git a/gfx/harfbuzz/src/hb-subset-input.cc b/gfx/harfbuzz/src/hb-subset-input.cc new file mode 100644 index 0000000000..1e0a89a630 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-input.cc @@ -0,0 +1,677 @@ +/* + * 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, Rod Sheeter, Behdad Esfahbod + */ + +#include "hb-subset.hh" +#include "hb-set.hh" +#include "hb-utf.hh" + + +hb_subset_input_t::hb_subset_input_t () +{ + for (auto& set : sets_iter ()) + set = hb::shared_ptr<hb_set_t> (hb_set_create ()); + + if (in_error ()) + return; + + flags = HB_SUBSET_FLAGS_DEFAULT; + + hb_set_add_range (sets.name_ids, 0, 6); + hb_set_add (sets.name_languages, 0x0409); + + hb_tag_t default_drop_tables[] = { + // Layout disabled by default + HB_TAG ('m', 'o', 'r', 'x'), + HB_TAG ('m', 'o', 'r', 't'), + HB_TAG ('k', 'e', 'r', 'x'), + HB_TAG ('k', 'e', 'r', 'n'), + + // Copied from fontTools: + HB_TAG ('B', 'A', 'S', 'E'), + HB_TAG ('J', 'S', 'T', 'F'), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('E', 'B', 'D', 'T'), + HB_TAG ('E', 'B', 'L', 'C'), + HB_TAG ('E', 'B', 'S', 'C'), + HB_TAG ('S', 'V', 'G', ' '), + HB_TAG ('P', 'C', 'L', 'T'), + HB_TAG ('L', 'T', 'S', 'H'), + // Graphite tables + HB_TAG ('F', 'e', 'a', 't'), + HB_TAG ('G', 'l', 'a', 't'), + HB_TAG ('G', 'l', 'o', 'c'), + HB_TAG ('S', 'i', 'l', 'f'), + HB_TAG ('S', 'i', 'l', 'l'), + }; + sets.drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables)); + + hb_tag_t default_no_subset_tables[] = { + HB_TAG ('g', 'a', 's', 'p'), + HB_TAG ('f', 'p', 'g', 'm'), + HB_TAG ('p', 'r', 'e', 'p'), + HB_TAG ('V', 'D', 'M', 'X'), + HB_TAG ('D', 'S', 'I', 'G'), + }; + sets.no_subset_tables->add_array (default_no_subset_tables, + ARRAY_LENGTH (default_no_subset_tables)); + + //copied from _layout_features_groups in fonttools + hb_tag_t default_layout_features[] = { + // default shaper + // common + HB_TAG ('r', 'v', 'r', 'n'), + HB_TAG ('c', 'c', 'm', 'p'), + HB_TAG ('l', 'i', 'g', 'a'), + HB_TAG ('l', 'o', 'c', 'l'), + HB_TAG ('m', 'a', 'r', 'k'), + HB_TAG ('m', 'k', 'm', 'k'), + HB_TAG ('r', 'l', 'i', 'g'), + + //fractions + HB_TAG ('f', 'r', 'a', 'c'), + HB_TAG ('n', 'u', 'm', 'r'), + HB_TAG ('d', 'n', 'o', 'm'), + + //horizontal + HB_TAG ('c', 'a', 'l', 't'), + HB_TAG ('c', 'l', 'i', 'g'), + HB_TAG ('c', 'u', 'r', 's'), + HB_TAG ('k', 'e', 'r', 'n'), + HB_TAG ('r', 'c', 'l', 't'), + + //vertical + HB_TAG ('v', 'a', 'l', 't'), + HB_TAG ('v', 'e', 'r', 't'), + HB_TAG ('v', 'k', 'r', 'n'), + HB_TAG ('v', 'p', 'a', 'l'), + HB_TAG ('v', 'r', 't', '2'), + + //ltr + HB_TAG ('l', 't', 'r', 'a'), + HB_TAG ('l', 't', 'r', 'm'), + + //rtl + HB_TAG ('r', 't', 'l', 'a'), + HB_TAG ('r', 't', 'l', 'm'), + + //random + HB_TAG ('r', 'a', 'n', 'd'), + + //justify + HB_TAG ('j', 'a', 'l', 't'), // HarfBuzz doesn't use; others might + + //East Asian spacing + HB_TAG ('c', 'h', 'w', 's'), + HB_TAG ('v', 'c', 'h', 'w'), + HB_TAG ('h', 'a', 'l', 't'), + HB_TAG ('v', 'h', 'a', 'l'), + + //private + HB_TAG ('H', 'a', 'r', 'f'), + HB_TAG ('H', 'A', 'R', 'F'), + HB_TAG ('B', 'u', 'z', 'z'), + HB_TAG ('B', 'U', 'Z', 'Z'), + + //shapers + + //arabic + HB_TAG ('i', 'n', 'i', 't'), + HB_TAG ('m', 'e', 'd', 'i'), + HB_TAG ('f', 'i', 'n', 'a'), + HB_TAG ('i', 's', 'o', 'l'), + HB_TAG ('m', 'e', 'd', '2'), + HB_TAG ('f', 'i', 'n', '2'), + HB_TAG ('f', 'i', 'n', '3'), + HB_TAG ('c', 's', 'w', 'h'), + HB_TAG ('m', 's', 'e', 't'), + HB_TAG ('s', 't', 'c', 'h'), + + //hangul + HB_TAG ('l', 'j', 'm', 'o'), + HB_TAG ('v', 'j', 'm', 'o'), + HB_TAG ('t', 'j', 'm', 'o'), + + //tibetan + HB_TAG ('a', 'b', 'v', 's'), + HB_TAG ('b', 'l', 'w', 's'), + HB_TAG ('a', 'b', 'v', 'm'), + HB_TAG ('b', 'l', 'w', 'm'), + + //indic + HB_TAG ('n', 'u', 'k', 't'), + HB_TAG ('a', 'k', 'h', 'n'), + HB_TAG ('r', 'p', 'h', 'f'), + HB_TAG ('r', 'k', 'r', 'f'), + HB_TAG ('p', 'r', 'e', 'f'), + HB_TAG ('b', 'l', 'w', 'f'), + HB_TAG ('h', 'a', 'l', 'f'), + HB_TAG ('a', 'b', 'v', 'f'), + HB_TAG ('p', 's', 't', 'f'), + HB_TAG ('c', 'f', 'a', 'r'), + HB_TAG ('v', 'a', 't', 'u'), + HB_TAG ('c', 'j', 'c', 't'), + HB_TAG ('i', 'n', 'i', 't'), + HB_TAG ('p', 'r', 'e', 's'), + HB_TAG ('a', 'b', 'v', 's'), + HB_TAG ('b', 'l', 'w', 's'), + HB_TAG ('p', 's', 't', 's'), + HB_TAG ('h', 'a', 'l', 'n'), + HB_TAG ('d', 'i', 's', 't'), + HB_TAG ('a', 'b', 'v', 'm'), + HB_TAG ('b', 'l', 'w', 'm'), + }; + + sets.layout_features->add_array (default_layout_features, ARRAY_LENGTH (default_layout_features)); + + sets.layout_scripts->invert (); // Default to all scripts. +} + +/** + * hb_subset_input_create_or_fail: + * + * Creates a new subset input object. + * + * Return value: (transfer full): New subset input, or `NULL` if failed. Destroy + * with hb_subset_input_destroy(). + * + * Since: 1.8.0 + **/ +hb_subset_input_t * +hb_subset_input_create_or_fail (void) +{ + hb_subset_input_t *input = hb_object_create<hb_subset_input_t>(); + + if (unlikely (!input)) + return nullptr; + + if (input->in_error ()) + { + hb_subset_input_destroy (input); + return nullptr; + } + + return input; +} + +/** + * hb_subset_input_reference: (skip) + * @input: a #hb_subset_input_t object. + * + * Increases the reference count on @input. + * + * Return value: @input. + * + * Since: 1.8.0 + **/ +hb_subset_input_t * +hb_subset_input_reference (hb_subset_input_t *input) +{ + return hb_object_reference (input); +} + +/** + * hb_subset_input_destroy: + * @input: a #hb_subset_input_t object. + * + * Decreases the reference count on @input, and if it reaches zero, destroys + * @input, freeing all memory. + * + * Since: 1.8.0 + **/ +void +hb_subset_input_destroy (hb_subset_input_t *input) +{ + if (!hb_object_destroy (input)) return; + + hb_free (input); +} + +/** + * hb_subset_input_unicode_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of Unicode code points to retain, the caller should modify the + * set as needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of Unicode code + * points. + * + * Since: 1.8.0 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_unicode_set (hb_subset_input_t *input) +{ + return input->sets.unicodes; +} + +/** + * hb_subset_input_glyph_set: + * @input: a #hb_subset_input_t object. + * + * Gets the set of glyph IDs to retain, the caller should modify the set as + * needed. + * + * Return value: (transfer none): pointer to the #hb_set_t of glyph IDs. + * + * Since: 1.8.0 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_glyph_set (hb_subset_input_t *input) +{ + return input->sets.glyphs; +} + +/** + * hb_subset_input_set: + * @input: a #hb_subset_input_t object. + * @set_type: a #hb_subset_sets_t set type. + * + * Gets the set of the specified type. + * + * Return value: (transfer none): pointer to the #hb_set_t of the specified type. + * + * Since: 2.9.1 + **/ +HB_EXTERN hb_set_t * +hb_subset_input_set (hb_subset_input_t *input, hb_subset_sets_t set_type) +{ + return input->sets_iter () [set_type]; +} + +/** + * hb_subset_input_get_flags: + * @input: a #hb_subset_input_t object. + * + * Gets all of the subsetting flags in the input object. + * + * Return value: the subsetting flags bit field. + * + * Since: 2.9.0 + **/ +HB_EXTERN hb_subset_flags_t +hb_subset_input_get_flags (hb_subset_input_t *input) +{ + return (hb_subset_flags_t) input->flags; +} + +/** + * hb_subset_input_set_flags: + * @input: a #hb_subset_input_t object. + * @value: bit field of flags + * + * Sets all of the flags in the input object to the values specified by the bit + * field. + * + * Since: 2.9.0 + **/ +HB_EXTERN void +hb_subset_input_set_flags (hb_subset_input_t *input, + unsigned value) +{ + input->flags = (hb_subset_flags_t) value; +} + +/** + * hb_subset_input_set_user_data: (skip) + * @input: a #hb_subset_input_t 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 subset input object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 2.9.0 + **/ +hb_bool_t +hb_subset_input_set_user_data (hb_subset_input_t *input, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (input, key, data, destroy, replace); +} + +/** + * hb_subset_input_get_user_data: (skip) + * @input: a #hb_subset_input_t object. + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified subset input object. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 2.9.0 + **/ +void * +hb_subset_input_get_user_data (const hb_subset_input_t *input, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (input, key); +} + +/** + * hb_subset_input_keep_everything: + * @input: a #hb_subset_input_t object + * + * Configure input object to keep everything in the font face. + * That is, all Unicodes, glyphs, names, layout items, + * glyph names, etc. + * + * The input can be tailored afterwards by the caller. + * + * Since: 7.0.0 + */ +void +hb_subset_input_keep_everything (hb_subset_input_t *input) +{ + const hb_subset_sets_t indices[] = {HB_SUBSET_SETS_UNICODE, + HB_SUBSET_SETS_GLYPH_INDEX, + HB_SUBSET_SETS_NAME_ID, + HB_SUBSET_SETS_NAME_LANG_ID, + HB_SUBSET_SETS_LAYOUT_FEATURE_TAG, + HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG}; + + for (auto idx : hb_iter (indices)) + { + hb_set_t *set = hb_subset_input_set (input, idx); + hb_set_clear (set); + hb_set_invert (set); + } + + // Don't drop any tables + hb_set_clear (hb_subset_input_set (input, HB_SUBSET_SETS_DROP_TABLE_TAG)); + + hb_subset_input_set_flags (input, + HB_SUBSET_FLAGS_NOTDEF_OUTLINE | + HB_SUBSET_FLAGS_GLYPH_NAMES | + HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES | + HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED); +} + +#ifndef HB_NO_VAR +/** + * hb_subset_input_pin_axis_to_default: (skip) + * @input: a #hb_subset_input_t object. + * @face: a #hb_face_t object. + * @axis_tag: Tag of the axis to be pinned + * + * Pin an axis to its default location in the given subset input object. + * + * All axes in a font must be pinned. Additionally, `CFF2` table, if present, + * will be de-subroutinized. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 6.0.0 + **/ +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag) +{ + hb_ot_var_axis_info_t axis_info; + if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) + return false; + + float default_val = axis_info.default_value; + return input->axes_location.set (axis_tag, Triple (default_val, default_val, default_val)); +} + +/** + * hb_subset_input_pin_axis_location: (skip) + * @input: a #hb_subset_input_t object. + * @face: a #hb_face_t object. + * @axis_tag: Tag of the axis to be pinned + * @axis_value: Location on the axis to be pinned at + * + * Pin an axis to a fixed location in the given subset input object. + * + * All axes in a font must be pinned. Additionally, `CFF2` table, if present, + * will be de-subroutinized. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 6.0.0 + **/ +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_location (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag, + float axis_value) +{ + hb_ot_var_axis_info_t axis_info; + if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) + return false; + + float val = hb_clamp(axis_value, axis_info.min_value, axis_info.max_value); + return input->axes_location.set (axis_tag, Triple (val, val, val)); +} + +#ifdef HB_EXPERIMENTAL_API +/** + * hb_subset_input_set_axis_range: (skip) + * @input: a #hb_subset_input_t object. + * @face: a #hb_face_t object. + * @axis_tag: Tag of the axis + * @axis_min_value: Minimum value of the axis variation range to set + * @axis_max_value: Maximum value of the axis variation range to set + * @axis_def_value: Default value of the axis variation range to set, in case of + * null, it'll be determined automatically + * + * Restricting the range of variation on an axis in the given subset input object. + * New min/default/max values will be clamped if they're not within the fvar axis range. + * If the new default value is null: + * If the fvar axis default value is within the new range, then new default + * value is the same as original default value. + * If the fvar axis default value is not within the new range, the new default + * value will be changed to the new min or max value, whichever is closer to the fvar + * axis default. + * + * Note: input min value can not be bigger than input max value. If the input + * default value is not within the new min/max range, it'll be clamped. + * Note: currently it supports gvar and cvar tables only. + * + * Return value: `true` if success, `false` otherwise + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_bool_t +hb_subset_input_set_axis_range (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag, + float axis_min_value, + float axis_max_value, + float *axis_def_value /* IN, maybe NULL */) +{ + if (axis_min_value > axis_max_value) + return false; + + hb_ot_var_axis_info_t axis_info; + if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) + return false; + + float new_min_val = hb_clamp(axis_min_value, axis_info.min_value, axis_info.max_value); + float new_max_val = hb_clamp(axis_max_value, axis_info.min_value, axis_info.max_value); + float new_default_val = axis_def_value ? *axis_def_value : axis_info.default_value; + new_default_val = hb_clamp(new_default_val, new_min_val, new_max_val); + return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val)); +} +#endif +#endif + +/** + * hb_subset_preprocess: + * @source: a #hb_face_t object. + * + * Preprocesses the face and attaches data that will be needed by the + * subsetter. Future subsetting operations can then use the precomputed data + * to speed up the subsetting operation. + * + * See [subset-preprocessing](https://github.com/harfbuzz/harfbuzz/blob/main/docs/subset-preprocessing.md) + * for more information. + * + * Note: the preprocessed face may contain sub-blobs that reference the memory + * backing the source #hb_face_t. Therefore in the case that this memory is not + * owned by the source face you will need to ensure that memory lives + * as long as the returned #hb_face_t. + * + * Returns: a new #hb_face_t. + * + * Since: 6.0.0 + **/ + +HB_EXTERN hb_face_t * +hb_subset_preprocess (hb_face_t *source) +{ + hb_subset_input_t* input = hb_subset_input_create_or_fail (); + if (!input) + return hb_face_reference (source); + + hb_subset_input_keep_everything (input); + + input->attach_accelerator_data = true; + + // Always use long loca in the preprocessed version. This allows + // us to store the glyph bytes unpadded which allows the future subset + // operation to run faster by skipping the trim padding step. + input->force_long_loca = true; + + hb_face_t* new_source = hb_subset_or_fail (source, input); + hb_subset_input_destroy (input); + + if (!new_source) { + DEBUG_MSG (SUBSET, nullptr, "Preprocessing failed due to subset failure."); + return hb_face_reference (source); + } + + return new_source; +} + +/** + * hb_subset_input_old_to_new_glyph_mapping: + * @input: a #hb_subset_input_t object. + * + * Returns a map which can be used to provide an explicit mapping from old to new glyph + * id's in the produced subset. The caller should populate the map as desired. + * If this map is left empty then glyph ids will be automatically mapped to new + * values by the subsetter. If populated, the mapping must be unique. That + * is no two original glyph ids can be mapped to the same new id. + * Additionally, if a mapping is provided then the retain gids option cannot + * be enabled. + * + * Any glyphs that are retained in the subset which are not specified + * in this mapping will be assigned glyph ids after the highest glyph + * id in the mapping. + * + * Note: this will accept and apply non-monotonic mappings, however this + * may result in unsorted Coverage tables. Such fonts may not work for all + * use cases (for example ots will reject unsorted coverage tables). So it's + * recommended, if possible, to supply a monotonic mapping. + * + * Return value: (transfer none): pointer to the #hb_map_t of the custom glyphs ID map. + * + * Since: 7.3.0 + **/ +HB_EXTERN hb_map_t* +hb_subset_input_old_to_new_glyph_mapping (hb_subset_input_t *input) +{ + return &input->glyph_map; +} + +#ifdef HB_EXPERIMENTAL_API +/** + * hb_subset_input_override_name_table: + * @input: a #hb_subset_input_t object. + * @name_id: name_id of a nameRecord + * @platform_id: platform ID of a nameRecord + * @encoding_id: encoding ID of a nameRecord + * @language_id: language ID of a nameRecord + * @name_str: pointer to name string new value or null to indicate should remove + * @str_len: the size of @name_str, or -1 if it is `NULL`-terminated + * + * Override the name string of the NameRecord identified by name_id, + * platform_id, encoding_id and language_id. If a record with that name_id + * doesn't exist, create it and insert to the name table. + * + * Note: for mac platform, we only support name_str with all ascii characters, + * name_str with non-ascii characters will be ignored. + * + * XSince: EXPERIMENTAL + **/ +HB_EXTERN hb_bool_t +hb_subset_input_override_name_table (hb_subset_input_t *input, + hb_ot_name_id_t name_id, + unsigned platform_id, + unsigned encoding_id, + unsigned language_id, + const char *name_str, + int str_len /* -1 means nul-terminated */) +{ + if (!name_str) + { + str_len = 0; + } + else if (str_len == -1) + { + str_len = strlen (name_str); + } + + hb_bytes_t name_bytes (nullptr, 0); + if (str_len) + { + if (platform_id == 1) + { + const uint8_t *src = reinterpret_cast<const uint8_t*> (name_str); + const uint8_t *src_end = src + str_len; + + hb_codepoint_t unicode; + const hb_codepoint_t replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT; + while (src < src_end) + { + src = hb_utf8_t::next (src, src_end, &unicode, replacement); + if (unicode >= 0x0080u) + { + printf ("Non-ascii character detected, ignored...This API supports acsii characters only for mac platform\n"); + return false; + } + } + } + char *override_name = (char *) hb_malloc (str_len); + if (unlikely (!override_name)) return false; + + hb_memcpy (override_name, name_str, str_len); + name_bytes = hb_bytes_t (override_name, str_len); + } + input->name_table_overrides.set (hb_ot_name_record_ids_t (platform_id, encoding_id, language_id, name_id), name_bytes); + return true; +} + +#endif diff --git a/gfx/harfbuzz/src/hb-subset-input.hh b/gfx/harfbuzz/src/hb-subset-input.hh new file mode 100644 index 0000000000..6ae311e613 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-input.hh @@ -0,0 +1,155 @@ +/* + * 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, Roderick Sheeter + */ + +#ifndef HB_SUBSET_INPUT_HH +#define HB_SUBSET_INPUT_HH + + +#include "hb.hh" + +#include "hb-subset.h" +#include "hb-map.hh" +#include "hb-set.hh" +#include "hb-cplusplus.hh" +#include "hb-font.hh" +#include "hb-subset-instancer-solver.hh" + +struct hb_ot_name_record_ids_t +{ + hb_ot_name_record_ids_t () = default; + hb_ot_name_record_ids_t (unsigned platform_id_, + unsigned encoding_id_, + unsigned language_id_, + unsigned name_id_) + :platform_id (platform_id_), + encoding_id (encoding_id_), + language_id (language_id_), + name_id (name_id_) {} + + bool operator != (const hb_ot_name_record_ids_t o) const + { return !(*this == o); } + + inline bool operator == (const hb_ot_name_record_ids_t& o) const + { + return platform_id == o.platform_id && + encoding_id == o.encoding_id && + language_id == o.language_id && + name_id == o.name_id; + } + + inline uint32_t hash () const + { + uint32_t current = 0; + current = current * 31 + hb_hash (platform_id); + current = current * 31 + hb_hash (encoding_id); + current = current * 31 + hb_hash (language_id); + current = current * 31 + hb_hash (name_id); + return current; + } + + unsigned platform_id; + unsigned encoding_id; + unsigned language_id; + unsigned name_id; +}; + +typedef struct hb_ot_name_record_ids_t hb_ot_name_record_ids_t; + + +HB_MARK_AS_FLAG_T (hb_subset_flags_t); + +struct hb_subset_input_t +{ + HB_INTERNAL hb_subset_input_t (); + + ~hb_subset_input_t () + { + sets.~sets_t (); + +#ifdef HB_EXPERIMENTAL_API + for (auto _ : name_table_overrides.values ()) + _.fini (); +#endif + } + + hb_object_header_t header; + + struct sets_t { + hb::shared_ptr<hb_set_t> glyphs; + hb::shared_ptr<hb_set_t> unicodes; + hb::shared_ptr<hb_set_t> no_subset_tables; + hb::shared_ptr<hb_set_t> drop_tables; + hb::shared_ptr<hb_set_t> name_ids; + hb::shared_ptr<hb_set_t> name_languages; + hb::shared_ptr<hb_set_t> layout_features; + hb::shared_ptr<hb_set_t> layout_scripts; + }; + + union { + sets_t sets; + hb::shared_ptr<hb_set_t> set_ptrs[sizeof (sets_t) / sizeof (hb_set_t*)]; + }; + + unsigned flags; + bool attach_accelerator_data = false; + + // If set loca format will always be the long version. + bool force_long_loca = false; + + hb_hashmap_t<hb_tag_t, Triple> axes_location; + hb_map_t glyph_map; +#ifdef HB_EXPERIMENTAL_API + hb_hashmap_t<hb_ot_name_record_ids_t, hb_bytes_t> name_table_overrides; +#endif + + inline unsigned num_sets () const + { + return sizeof (set_ptrs) / sizeof (hb_set_t*); + } + + inline hb_array_t<hb::shared_ptr<hb_set_t>> sets_iter () + { + return hb_array (set_ptrs); + } + + bool in_error () const + { + for (unsigned i = 0; i < num_sets (); i++) + { + if (unlikely (set_ptrs[i]->in_error ())) + return true; + } + + return axes_location.in_error () +#ifdef HB_EXPERIMENTAL_API + || name_table_overrides.in_error () +#endif + ; + } +}; + + +#endif /* HB_SUBSET_INPUT_HH */ diff --git a/gfx/harfbuzz/src/hb-subset-instancer-solver.cc b/gfx/harfbuzz/src/hb-subset-instancer-solver.cc new file mode 100644 index 0000000000..4876bc4379 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-instancer-solver.cc @@ -0,0 +1,431 @@ +/* + * Copyright © 2023 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. + */ + +#include "hb-subset-instancer-solver.hh" + +/* This file is a straight port of the following: + * + * https://github.com/fonttools/fonttools/blob/f73220816264fc383b8a75f2146e8d69e455d398/Lib/fontTools/varLib/instancer/solver.py + * + * Where that file returns None for a triple, we return Triple{}. + * This should be safe. + */ + +constexpr static float EPSILON = 1.f / (1 << 14); +constexpr static float MAX_F2DOT14 = float (0x7FFF) / (1 << 14); + +static inline Triple _reverse_negate(const Triple &v) +{ return {-v.maximum, -v.middle, -v.minimum}; } + + +static inline float supportScalar (float coord, const Triple &tent) +{ + /* Copied from VarRegionAxis::evaluate() */ + float start = tent.minimum, peak = tent.middle, end = tent.maximum; + + if (unlikely (start > peak || peak > end)) + return 1.; + if (unlikely (start < 0 && end > 0 && peak != 0)) + return 1.; + + if (peak == 0 || coord == peak) + return 1.; + + if (coord <= start || end <= coord) + return 0.; + + /* Interpolate */ + if (coord < peak) + return (coord - start) / (peak - start); + else + return (end - coord) / (end - peak); +} + +static inline result_t +_solve (Triple tent, Triple axisLimit, bool negative = false) +{ + float axisMin = axisLimit.minimum; + float axisDef = axisLimit.middle; + float axisMax = axisLimit.maximum; + float lower = tent.minimum; + float peak = tent.middle; + float upper = tent.maximum; + + // Mirror the problem such that axisDef <= peak + if (axisDef > peak) + { + result_t vec = _solve (_reverse_negate (tent), + _reverse_negate (axisLimit), + !negative); + + for (auto &p : vec) + p = hb_pair (p.first, _reverse_negate (p.second)); + + return vec; + } + // axisDef <= peak + + /* case 1: The whole deltaset falls outside the new limit; we can drop it + * + * peak + * 1.........................................o.......... + * / \ + * / \ + * / \ + * / \ + * 0---|-----------|----------|-------- o o----1 + * axisMin axisDef axisMax lower upper + */ + if (axisMax <= lower && axisMax < peak) + return result_t{}; // No overlap + + /* case 2: Only the peak and outermost bound fall outside the new limit; + * we keep the deltaset, update peak and outermost bound and scale deltas + * by the scalar value for the restricted axis at the new limit, and solve + * recursively. + * + * |peak + * 1...............................|.o.......... + * |/ \ + * / \ + * /| \ + * / | \ + * 0--------------------------- o | o----1 + * lower | upper + * | + * axisMax + * + * Convert to: + * + * 1............................................ + * | + * o peak + * /| + * /x| + * 0--------------------------- o o upper ----1 + * lower | + * | + * axisMax + */ + if (axisMax < peak) + { + float mult = supportScalar (axisMax, tent); + tent = Triple{lower, axisMax, axisMax}; + + result_t vec = _solve (tent, axisLimit); + + for (auto &p : vec) + p = hb_pair (p.first * mult, p.second); + + return vec; + } + + // lower <= axisDef <= peak <= axisMax + + float gain = supportScalar (axisDef, tent); + result_t out {hb_pair (gain, Triple{})}; + + // First, the positive side + + // outGain is the scalar of axisMax at the tent. + float outGain = supportScalar (axisMax, tent); + + /* Case 3a: Gain is more than outGain. The tent down-slope crosses + * the axis into negative. We have to split it into multiples. + * + * | peak | + * 1...................|.o.....|.............. + * |/x\_ | + * gain................+....+_.|.............. + * /| |y\| + * ................../.|....|..+_......outGain + * / | | | \ + * 0---|-----------o | | | o----------1 + * axisMin lower | | | upper + * | | | + * axisDef | axisMax + * | + * crossing + */ + if (gain >= outGain) + { + // Note that this is the branch taken if both gain and outGain are 0. + + // Crossing point on the axis. + float crossing = peak + (1 - gain) * (upper - peak); + + Triple loc{hb_max (lower, axisDef), peak, crossing}; + float scalar = 1.f; + + // The part before the crossing point. + out.push (hb_pair (scalar - gain, loc)); + + /* The part after the crossing point may use one or two tents, + * depending on whether upper is before axisMax or not, in one + * case we need to keep it down to eternity. + * + * Case 3a1, similar to case 1neg; just one tent needed, as in + * the drawing above. + */ + if (upper >= axisMax) + { + Triple loc {crossing, axisMax, axisMax}; + float scalar = outGain; + + out.push (hb_pair (scalar - gain, loc)); + } + + /* Case 3a2: Similar to case 2neg; two tents needed, to keep + * down to eternity. + * + * | peak | + * 1...................|.o................|... + * |/ \_ | + * gain................+....+_............|... + * /| | \xxxxxxxxxxy| + * / | | \_xxxxxyyyy| + * / | | \xxyyyyyy| + * 0---|-----------o | | o-------|--1 + * axisMin lower | | upper | + * | | | + * axisDef | axisMax + * | + * crossing + */ + else + { + // A tent's peak cannot fall on axis default. Nudge it. + if (upper == axisDef) + upper += EPSILON; + + // Downslope. + Triple loc1 {crossing, upper, axisMax}; + float scalar1 = 0.f; + + // Eternity justify. + Triple loc2 {upper, axisMax, axisMax}; + float scalar2 = 0.f; + + out.push (hb_pair (scalar1 - gain, loc1)); + out.push (hb_pair (scalar2 - gain, loc2)); + } + } + + else + { + // Special-case if peak is at axisMax. + if (axisMax == peak) + upper = peak; + + /* Case 3: + * we keep deltas as is and only scale the axis upper to achieve + * the desired new tent if feasible. + * + * peak + * 1.....................o.................... + * / \_| + * ..................../....+_.........outGain + * / | \ + * gain..............+......|..+_............. + * /| | | \ + * 0---|-----------o | | | o----------1 + * axisMin lower| | | upper + * | | newUpper + * axisDef axisMax + */ + float newUpper = peak + (1 - gain) * (upper - peak); + assert (axisMax <= newUpper); // Because outGain > gain + if (newUpper <= axisDef + (axisMax - axisDef) * 2) + { + upper = newUpper; + if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper) + { + // we clamp +2.0 to the max F2Dot14 (~1.99994) for convenience + upper = axisDef + (axisMax - axisDef) * MAX_F2DOT14; + assert (peak < upper); + } + + Triple loc {hb_max (axisDef, lower), peak, upper}; + float scalar = 1.f; + + out.push (hb_pair (scalar - gain, loc)); + } + + /* Case 4: New limit doesn't fit; we need to chop into two tents, + * because the shape of a triangle with part of one side cut off + * cannot be represented as a triangle itself. + * + * | peak | + * 1.........|......o.|.................... + * ..........|...../x\|.............outGain + * | |xxy|\_ + * | /xxxy| \_ + * | |xxxxy| \_ + * | /xxxxy| \_ + * 0---|-----|-oxxxxxx| o----------1 + * axisMin | lower | upper + * | | + * axisDef axisMax + */ + else + { + Triple loc1 {hb_max (axisDef, lower), peak, axisMax}; + float scalar1 = 1.f; + + Triple loc2 {peak, axisMax, axisMax}; + float scalar2 = outGain; + + out.push (hb_pair (scalar1 - gain, loc1)); + // Don't add a dirac delta! + if (peak < axisMax) + out.push (hb_pair (scalar2 - gain, loc2)); + } + } + + /* Now, the negative side + * + * Case 1neg: Lower extends beyond axisMin: we chop. Simple. + * + * | |peak + * 1..................|...|.o................. + * | |/ \ + * gain...............|...+...\............... + * |x_/| \ + * |/ | \ + * _/| | \ + * 0---------------o | | o----------1 + * lower | | upper + * | | + * axisMin axisDef + */ + if (lower <= axisMin) + { + Triple loc {axisMin, axisMin, axisDef}; + float scalar = supportScalar (axisMin, tent); + + out.push (hb_pair (scalar - gain, loc)); + } + + /* Case 2neg: Lower is betwen axisMin and axisDef: we add two + * tents to keep it down all the way to eternity. + * + * | |peak + * 1...|...............|.o................. + * | |/ \ + * gain|...............+...\............... + * |yxxxxxxxxxxxxx/| \ + * |yyyyyyxxxxxxx/ | \ + * |yyyyyyyyyyyx/ | \ + * 0---|-----------o | o----------1 + * axisMin lower | upper + * | + * axisDef + */ + else + { + // A tent's peak cannot fall on axis default. Nudge it. + if (lower == axisDef) + lower -= EPSILON; + + // Downslope. + Triple loc1 {axisMin, lower, axisDef}; + float scalar1 = 0.f; + + // Eternity justify. + Triple loc2 {axisMin, axisMin, lower}; + float scalar2 = 0.f; + + out.push (hb_pair (scalar1 - gain, loc1)); + out.push (hb_pair (scalar2 - gain, loc2)); + } + + return out; +} + +static inline TripleDistances _reverse_triple_distances (const TripleDistances &v) +{ return TripleDistances (v.positive, v.negative); } + +float renormalizeValue (float v, const Triple &triple, + const TripleDistances &triple_distances, bool extrapolate) +{ + float lower = triple.minimum, def = triple.middle, upper = triple.maximum; + assert (lower <= def && def <= upper); + + if (!extrapolate) + v = hb_max (hb_min (v, upper), lower); + + if (v == def) + return 0.f; + + if (def < 0.f) + return -renormalizeValue (-v, _reverse_negate (triple), + _reverse_triple_distances (triple_distances), extrapolate); + + /* default >= 0 and v != default */ + if (v > def) + return (v - def) / (upper - def); + + /* v < def */ + if (lower >= 0.f) + return (v - def) / (def - lower); + + /* lower < 0 and v < default */ + float total_distance = triple_distances.negative * (-lower) + triple_distances.positive * def; + + float v_distance; + if (v >= 0.f) + v_distance = (def - v) * triple_distances.positive; + else + v_distance = (-v) * triple_distances.negative + triple_distances.positive * def; + + return (-v_distance) /total_distance; +} + +result_t +rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances) +{ + assert (-1.f <= axisLimit.minimum && axisLimit.minimum <= axisLimit.middle && axisLimit.middle <= axisLimit.maximum && axisLimit.maximum <= +1.f); + assert (-2.f <= tent.minimum && tent.minimum <= tent.middle && tent.middle <= tent.maximum && tent.maximum <= +2.f); + assert (tent.middle != 0.f); + + result_t sols = _solve (tent, axisLimit); + + auto n = [&axisLimit, &axis_triple_distances] (float v) { return renormalizeValue (v, axisLimit, axis_triple_distances); }; + + result_t out; + for (auto &p : sols) + { + if (!p.first) continue; + if (p.second == Triple{}) + { + out.push (p); + continue; + } + Triple t = p.second; + out.push (hb_pair (p.first, + Triple{n (t.minimum), n (t.middle), n (t.maximum)})); + } + + return out; +} diff --git a/gfx/harfbuzz/src/hb-subset-instancer-solver.hh b/gfx/harfbuzz/src/hb-subset-instancer-solver.hh new file mode 100644 index 0000000000..563fccbb59 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-instancer-solver.hh @@ -0,0 +1,112 @@ +/* + * Copyright © 2023 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_SUBSET_INSTANCER_SOLVER_HH +#define HB_SUBSET_INSTANCER_SOLVER_HH + +#include "hb.hh" + +/* pre-normalized distances */ +struct TripleDistances +{ + TripleDistances (): negative (1.f), positive (1.f) {} + TripleDistances (float neg_, float pos_): negative (neg_), positive (pos_) {} + TripleDistances (float min, float default_, float max) + { + negative = default_ - min; + positive = max - default_; + } + + float negative; + float positive; +}; + +struct Triple { + + Triple () : + minimum (0.f), middle (0.f), maximum (0.f) {} + + Triple (float minimum_, float middle_, float maximum_) : + minimum (minimum_), middle (middle_), maximum (maximum_) {} + + bool operator == (const Triple &o) const + { + return minimum == o.minimum && + middle == o.middle && + maximum == o.maximum; + } + + bool operator != (const Triple o) const + { return !(*this == o); } + + bool is_point () const + { return minimum == middle && middle == maximum; } + + bool contains (float point) const + { return minimum <= point && point <= maximum; } + + /* from hb_array_t hash ()*/ + uint32_t hash () const + { + uint32_t current = /*cbf29ce4*/0x84222325; + current = current ^ hb_hash (minimum); + current = current * 16777619; + + current = current ^ hb_hash (middle); + current = current * 16777619; + + current = current ^ hb_hash (maximum); + current = current * 16777619; + return current; + } + + + float minimum; + float middle; + float maximum; +}; + +using result_item_t = hb_pair_t<float, Triple>; +using result_t = hb_vector_t<result_item_t>; + +/* renormalize a normalized value v to the range of an axis, + * considering the prenormalized distances as well as the new axis limits. + * Ported from fonttools */ +HB_INTERNAL float renormalizeValue (float v, const Triple &triple, + const TripleDistances &triple_distances, + bool extrapolate = true); +/* Given a tuple (lower,peak,upper) "tent" and new axis limits + * (axisMin,axisDefault,axisMax), solves how to represent the tent + * under the new axis configuration. All values are in normalized + * -1,0,+1 coordinate system. Tent values can be outside this range. + * + * Return value: a list of tuples. Each tuple is of the form + * (scalar,tent), where scalar is a multipler to multiply any + * delta-sets by, and tent is a new tent for that output delta-set. + * If tent value is Triple{}, that is a special deltaset that should + * be always-enabled (called "gain"). + */ +HB_INTERNAL result_t rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distances); + +#endif /* HB_SUBSET_INSTANCER_SOLVER_HH */ diff --git a/gfx/harfbuzz/src/hb-subset-plan-member-list.hh b/gfx/harfbuzz/src/hb-subset-plan-member-list.hh new file mode 100644 index 0000000000..71da80e387 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-plan-member-list.hh @@ -0,0 +1,149 @@ +/* + * Copyright © 2018 Google, Inc. + * Copyright © 2023 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): Garret Rieger, Roderick Sheeter + */ + +#ifndef HB_SUBSET_PLAN_MEMBER_LIST_HH +#define HB_SUBSET_PLAN_MEMBER_LIST_HH +#endif /* HB_SUBSET_PLAN_MEMBER_LIST_HH */ /* Dummy header guards */ + +#define E(x, y) x, y + +// For each cp that we'd like to retain maps to the corresponding gid. +HB_SUBSET_PLAN_MEMBER (hb_set_t, unicodes) +HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t<hb_codepoint_pair_t>, unicode_to_new_gid_list) + +HB_SUBSET_PLAN_MEMBER (hb_sorted_vector_t<hb_codepoint_pair_t>, new_to_old_gid_list) + +// name_ids we would like to retain +HB_SUBSET_PLAN_MEMBER (hb_set_t, name_ids) + +// name_languages we would like to retain +HB_SUBSET_PLAN_MEMBER (hb_set_t, name_languages) + +//layout features which will be preserved +HB_SUBSET_PLAN_MEMBER (hb_set_t, layout_features) + +// layout scripts which will be preserved. +HB_SUBSET_PLAN_MEMBER (hb_set_t, layout_scripts) + +//glyph ids requested to retain +HB_SUBSET_PLAN_MEMBER (hb_set_t, glyphs_requested) + +// Tables which should not be processed, just pass them through. +HB_SUBSET_PLAN_MEMBER (hb_set_t, no_subset_tables) + +// Tables which should be dropped. +HB_SUBSET_PLAN_MEMBER (hb_set_t, drop_tables) + +// Old -> New glyph id mapping +HB_SUBSET_PLAN_MEMBER (hb_map_t, glyph_map_gsub) + +HB_SUBSET_PLAN_MEMBER (hb_set_t, _glyphset) +HB_SUBSET_PLAN_MEMBER (hb_set_t, _glyphset_gsub) +HB_SUBSET_PLAN_MEMBER (hb_set_t, _glyphset_mathed) +HB_SUBSET_PLAN_MEMBER (hb_set_t, _glyphset_colred) + +//active lookups we'd like to retain +HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_lookups) +HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_lookups) + +//use_mark_sets mapping: old->new +HB_SUBSET_PLAN_MEMBER (hb_map_t, used_mark_sets_map) + +//active langsys we'd like to retain +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb::unique_ptr<hb_set_t>>), gsub_langsys) +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb::unique_ptr<hb_set_t>>), gpos_langsys) + +//active features after removing redundant langsys and prune_features +HB_SUBSET_PLAN_MEMBER (hb_map_t, gsub_features) +HB_SUBSET_PLAN_MEMBER (hb_map_t, gpos_features) + +//active feature variation records/condition index with variations +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb::shared_ptr<hb_set_t>>), gsub_feature_record_cond_idx_map) +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb::shared_ptr<hb_set_t>>), gpos_feature_record_cond_idx_map) + +//feature index-> address of substituation feature table mapping with +//variations +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, const OT::Feature*>), gsub_feature_substitutes_map) +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, const OT::Feature*>), gpos_feature_substitutes_map) + +// old feature_indexes set, used to reinstate the old features +HB_SUBSET_PLAN_MEMBER (hb_set_t, gsub_old_features) +HB_SUBSET_PLAN_MEMBER (hb_set_t, gpos_old_features) + +//feature_index->pair of (address of old feature, feature tag), used for inserting a catch all record +//if necessary +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb_pair_t E(<const void*, const void*>)>), gsub_old_feature_idx_tag_map) +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb_pair_t E(<const void*, const void*>)>), gpos_old_feature_idx_tag_map) + +//active layers/palettes we'd like to retain +HB_SUBSET_PLAN_MEMBER (hb_map_t, colrv1_layers) +HB_SUBSET_PLAN_MEMBER (hb_map_t, colr_palettes) + +//Old layout item variation index -> (New varidx, delta) mapping +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<unsigned, hb_pair_t E(<unsigned, int>)>), layout_variation_idx_delta_map) + +//gdef varstore retained varidx mapping +HB_SUBSET_PLAN_MEMBER (hb_vector_t<hb_inc_bimap_t>, gdef_varstore_inner_maps) + +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, hb::unique_ptr<hb_blob_t>>), sanitized_table_cache) + +//normalized axes range map +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, Triple>), axes_location) +HB_SUBSET_PLAN_MEMBER (hb_vector_t<int>, normalized_coords) + +//user specified axes range map +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, Triple>), user_axes_location) +//axis->TripleDistances map (distances in the pre-normalized space) +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_tag_t, TripleDistances>), axes_triple_distances) + +//retained old axis index -> new axis index mapping in fvar axis array +HB_SUBSET_PLAN_MEMBER (hb_map_t, axes_index_map) + +//axis_index->axis_tag mapping in fvar axis array +HB_SUBSET_PLAN_MEMBER (hb_map_t, axes_old_index_tag_map) +//vector of retained axis tags in the order of axes given in the 'fvar' table +HB_SUBSET_PLAN_MEMBER (hb_vector_t<hb_tag_t>, axis_tags) + +//hmtx metrics map: new gid->(advance, lsb) +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, hb_pair_t E(<unsigned, int>)>), hmtx_map) +//vmtx metrics map: new gid->(advance, lsb) +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, hb_pair_t E(<unsigned, int>)>), vmtx_map) +//boundsWidth map: new gid->boundsWidth, boundWidth=xMax - xMin +HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_width_vec) +//boundsHeight map: new gid->boundsHeight, boundsHeight=yMax - yMin +HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_height_vec) + +//map: new_gid -> contour points vector +HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, contour_point_vector_t>), new_gid_contour_points_map) + +#ifdef HB_EXPERIMENTAL_API +// name table overrides map: hb_ot_name_record_ids_t-> name string new value or +// None to indicate should remove +HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<hb_ot_name_record_ids_t, hb_bytes_t>), name_table_overrides) +#endif + +#undef E diff --git a/gfx/harfbuzz/src/hb-subset-plan.cc b/gfx/harfbuzz/src/hb-subset-plan.cc new file mode 100644 index 0000000000..5786223196 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-plan.cc @@ -0,0 +1,1416 @@ +/* + * 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, Roderick Sheeter + */ + +#include "hb-subset-plan.hh" +#include "hb-subset-accelerator.hh" +#include "hb-map.hh" +#include "hb-multimap.hh" +#include "hb-set.hh" + +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "OT/Color/COLR/COLR.hh" +#include "OT/Color/COLR/colrv1-closure.hh" +#include "OT/Color/CPAL/CPAL.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-ot-math-table.hh" + +using OT::Layout::GSUB; +using OT::Layout::GPOS; + + +hb_subset_accelerator_t::~hb_subset_accelerator_t () +{ + if (cmap_cache && destroy_cmap_cache) + destroy_cmap_cache ((void*) cmap_cache); + +#ifndef HB_NO_SUBSET_CFF + cff1_accel.fini (); + cff2_accel.fini (); +#endif + hb_face_destroy (source); +} + + +typedef hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> script_langsys_map; +#ifndef HB_NO_SUBSET_CFF +static inline bool +_add_cff_seac_components (const OT::cff1::accelerator_subset_t &cff, + hb_codepoint_t gid, + hb_set_t *gids_to_retain) +{ + hb_codepoint_t base_gid, accent_gid; + if (cff.get_seac_components (gid, &base_gid, &accent_gid)) + { + gids_to_retain->add (base_gid); + gids_to_retain->add (accent_gid); + return true; + } + return false; +} +#endif + +static void +_remap_palette_indexes (const hb_set_t *palette_indexes, + hb_map_t *mapping /* OUT */) +{ + unsigned new_idx = 0; + for (unsigned palette_index : palette_indexes->iter ()) + { + if (palette_index == 0xFFFF) + { + mapping->set (palette_index, palette_index); + continue; + } + mapping->set (palette_index, new_idx); + new_idx++; + } +} + +static void +_remap_indexes (const hb_set_t *indexes, + hb_map_t *mapping /* OUT */) +{ + for (auto _ : + hb_enumerate (indexes->iter ())) + mapping->set (_.second, _.first); + +} + +#ifndef HB_NO_SUBSET_LAYOUT + +/* + * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates. + * Returns true if anything was removed (not including duplicates). + */ +static bool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */ + const hb_set_t* filter) +{ + hb_vector_t<hb_tag_t> out; + out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator. + + bool removed = false; + hb_set_t visited; + + for (hb_tag_t tag : *tags) + { + if (!tag) continue; + if (visited.has (tag)) continue; + + if (!filter->has (tag)) + { + removed = true; + continue; + } + + visited.add (tag); + out.push (tag); + } + + // The collect function needs a null element to signal end of the array. + out.push (HB_TAG_NONE); + + hb_swap (out, *tags); + return removed; +} + +template <typename T> +static void _collect_layout_indices (hb_subset_plan_t *plan, + const T& table, + hb_set_t *lookup_indices, /* OUT */ + hb_set_t *feature_indices, /* OUT */ + hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */ + hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, /* OUT */ + hb_set_t& catch_all_record_feature_idxes, /* OUT */ + hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map /* OUT */) +{ + unsigned num_features = table.get_feature_count (); + hb_vector_t<hb_tag_t> features; + if (!plan->check_success (features.resize (num_features))) return; + table.get_feature_tags (0, &num_features, features.arrayZ); + bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features); + + unsigned num_scripts = table.get_script_count (); + hb_vector_t<hb_tag_t> scripts; + if (!plan->check_success (scripts.resize (num_scripts))) return; + table.get_script_tags (0, &num_scripts, scripts.arrayZ); + bool retain_all_scripts = !_filter_tag_list (&scripts, &plan->layout_scripts); + + if (!plan->check_success (!features.in_error ()) || !features + || !plan->check_success (!scripts.in_error ()) || !scripts) + return; + + hb_ot_layout_collect_features (plan->source, + T::tableTag, + retain_all_scripts ? nullptr : scripts.arrayZ, + nullptr, + retain_all_features ? nullptr : features.arrayZ, + feature_indices); + +#ifndef HB_NO_VAR + // collect feature substitutes with variations + if (!plan->user_axes_location.is_empty ()) + { + hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map; + OT::hb_collect_feature_substitutes_with_var_context_t c = + { + &plan->axes_old_index_tag_map, + &plan->axes_location, + feature_record_cond_idx_map, + feature_substitutes_map, + catch_all_record_feature_idxes, + feature_indices, + false, + false, + false, + 0, + &conditionset_map + }; + table.collect_feature_substitutes_with_variations (&c); + } +#endif + + for (unsigned feature_index : *feature_indices) + { + const OT::Feature* f = &(table.get_feature (feature_index)); + const OT::Feature **p = nullptr; + if (feature_substitutes_map->has (feature_index, &p)) + f = *p; + + f->add_lookup_indexes_to (lookup_indices); + } + +#ifndef HB_NO_VAR + if (catch_all_record_feature_idxes) + { + for (unsigned feature_index : catch_all_record_feature_idxes) + { + const OT::Feature& f = table.get_feature (feature_index); + f.add_lookup_indexes_to (lookup_indices); + const void *tag = reinterpret_cast<const void*> (&(table.get_feature_list ().get_tag (feature_index))); + catch_all_record_idx_feature_map.set (feature_index, hb_pair (&f, tag)); + } + } + + // If all axes are pinned then all feature variations will be dropped so there's no need + // to collect lookups from them. + if (!plan->all_axes_pinned) + table.feature_variation_collect_lookups (feature_indices, + plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map, + lookup_indices); +#endif +} + + +static inline void +_GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g, + const hb_map_t *lookup_indices, + const hb_set_t *feature_indices, + const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, + hb_map_t *duplicate_feature_map /* OUT */) +{ + if (feature_indices->is_empty ()) return; + hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features; + //find out duplicate features after subset + for (unsigned i : feature_indices->iter ()) + { + hb_tag_t t = g.get_feature_tag (i); + if (t == HB_MAP_VALUE_INVALID) continue; + if (!unique_features.has (t)) + { + if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) + return; + if (unique_features.has (t)) + unique_features.get (t)->add (i); + duplicate_feature_map->set (i, i); + continue; + } + + bool found = false; + + hb_set_t* same_tag_features = unique_features.get (t); + for (unsigned other_f_index : same_tag_features->iter ()) + { + const OT::Feature* f = &(g.get_feature (i)); + const OT::Feature **p = nullptr; + if (feature_substitutes_map->has (i, &p)) + f = *p; + + const OT::Feature* other_f = &(g.get_feature (other_f_index)); + if (feature_substitutes_map->has (other_f_index, &p)) + other_f = *p; + + auto f_iter = + + hb_iter (f->lookupIndex) + | hb_filter (lookup_indices) + ; + + auto other_f_iter = + + hb_iter (other_f->lookupIndex) + | hb_filter (lookup_indices) + ; + + bool is_equal = true; + for (; f_iter && other_f_iter; f_iter++, other_f_iter++) + { + unsigned a = *f_iter; + unsigned b = *other_f_iter; + if (a != b) { is_equal = false; break; } + } + + if (is_equal == false || f_iter || other_f_iter) continue; + + found = true; + duplicate_feature_map->set (i, other_f_index); + break; + } + + if (found == false) + { + same_tag_features->add (i); + duplicate_feature_map->set (i, i); + } + } +} + +template <typename T> +static inline void +_closure_glyphs_lookups_features (hb_subset_plan_t *plan, + hb_set_t *gids_to_retain, + hb_map_t *lookups, + hb_map_t *features, + script_langsys_map *langsys_map, + hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, + hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, + hb_set_t &catch_all_record_feature_idxes, + hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map) +{ + hb_blob_ptr_t<T> table = plan->source_table<T> (); + hb_tag_t table_tag = table->tableTag; + hb_set_t lookup_indices, feature_indices; + _collect_layout_indices<T> (plan, + *table, + &lookup_indices, + &feature_indices, + feature_record_cond_idx_map, + feature_substitutes_map, + catch_all_record_feature_idxes, + catch_all_record_idx_feature_map); + + if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE)) + hb_ot_layout_lookups_substitute_closure (plan->source, + &lookup_indices, + gids_to_retain); + table->closure_lookups (plan->source, + gids_to_retain, + &lookup_indices); + _remap_indexes (&lookup_indices, lookups); + + // prune features + table->prune_features (lookups, + plan->user_axes_location.is_empty () ? nullptr : feature_record_cond_idx_map, + feature_substitutes_map, + &feature_indices); + hb_map_t duplicate_feature_map; + _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map); + + feature_indices.clear (); + table->prune_langsys (&duplicate_feature_map, &plan->layout_scripts, langsys_map, &feature_indices); + _remap_indexes (&feature_indices, features); + + table.destroy (); +} + +#endif + +#ifndef HB_NO_VAR +static inline void +_generate_varstore_inner_maps (const hb_set_t& varidx_set, + unsigned subtable_count, + hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */) +{ + if (varidx_set.is_empty () || subtable_count == 0) return; + + if (unlikely (!inner_maps.resize (subtable_count))) return; + for (unsigned idx : varidx_set) + { + uint16_t major = idx >> 16; + uint16_t minor = idx & 0xFFFF; + + if (major >= subtable_count) + continue; + inner_maps[major].add (minor); + } +} + +static inline hb_font_t* +_get_hb_font_with_variations (const hb_subset_plan_t *plan) +{ + hb_font_t *font = hb_font_create (plan->source); + + hb_vector_t<hb_variation_t> vars; + if (!vars.alloc (plan->user_axes_location.get_population ())) { + 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; +} + +static inline void +_collect_layout_variation_indices (hb_subset_plan_t* plan) +{ + hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); + hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> (); + + if (!gdef->has_data ()) + { + gdef.destroy (); + gpos.destroy (); + return; + } + + hb_set_t varidx_set; + OT::hb_collect_variation_indices_context_t c (&varidx_set, + &plan->_glyphset_gsub, + &plan->gpos_lookups); + gdef->collect_variation_indices (&c); + + if (hb_ot_layout_has_positioning (plan->source)) + gpos->collect_variation_indices (&c); + + gdef->remap_layout_variation_indices (&varidx_set, + plan->normalized_coords, + !plan->pinned_at_default, + plan->all_axes_pinned, + &plan->layout_variation_idx_delta_map); + + unsigned subtable_count = gdef->has_var_store () ? gdef->get_var_store ().get_sub_table_count () : 0; + _generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps); + + gdef.destroy (); + gpos.destroy (); +} +#endif + +static inline void +_cmap_closure (hb_face_t *face, + const hb_set_t *unicodes, + hb_set_t *glyphset) +{ + OT::cmap::accelerator_t cmap (face); + cmap.table->closure_glyphs (unicodes, glyphset); +} + +static void _colr_closure (hb_face_t *face, + hb_map_t *layers_map, + hb_map_t *palettes_map, + hb_set_t *glyphs_colred) +{ + OT::COLR::accelerator_t colr (face); + if (!colr.is_valid ()) return; + + hb_set_t palette_indices, layer_indices; + // Collect all glyphs referenced by COLRv0 + hb_set_t glyphset_colrv0; + for (hb_codepoint_t gid : *glyphs_colred) + colr.closure_glyphs (gid, &glyphset_colrv0); + + glyphs_colred->union_ (glyphset_colrv0); + + //closure for COLRv1 + colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices); + + colr.closure_V0palette_indices (glyphs_colred, &palette_indices); + _remap_indexes (&layer_indices, layers_map); + _remap_palette_indexes (&palette_indices, palettes_map); +} + +static inline void +_math_closure (hb_subset_plan_t *plan, + hb_set_t *glyphset) +{ + hb_blob_ptr_t<OT::MATH> math = plan->source_table<OT::MATH> (); + if (math->has_data ()) + math->closure_glyphs (glyphset); + math.destroy (); +} + +static inline void +_remap_used_mark_sets (hb_subset_plan_t *plan, + hb_map_t& used_mark_sets_map) +{ + hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); + + if (!gdef->has_data () || !gdef->has_mark_glyph_sets ()) + { + gdef.destroy (); + return; + } + + hb_set_t used_mark_sets; + gdef->get_mark_glyph_sets ().collect_used_mark_sets (plan->_glyphset_gsub, used_mark_sets); + gdef.destroy (); + + _remap_indexes (&used_mark_sets, &used_mark_sets_map); +} + +static inline void +_remove_invalid_gids (hb_set_t *glyphs, + unsigned int num_glyphs) +{ + glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID); +} + +static void +_populate_unicodes_to_retain (const hb_set_t *unicodes, + const hb_set_t *glyphs, + hb_subset_plan_t *plan) +{ + OT::cmap::accelerator_t cmap (plan->source); + unsigned size_threshold = plan->source->get_num_glyphs (); + if (glyphs->is_empty () && unicodes->get_population () < size_threshold) + { + + const hb_map_t* unicode_to_gid = nullptr; + if (plan->accelerator) + unicode_to_gid = &plan->accelerator->unicode_to_gid; + + // This is approach to collection is faster, but can only be used if glyphs + // are not being explicitly added to the subset and the input unicodes set is + // not excessively large (eg. an inverted set). + plan->unicode_to_new_gid_list.alloc (unicodes->get_population ()); + if (!unicode_to_gid) { + for (hb_codepoint_t cp : *unicodes) + { + hb_codepoint_t gid; + if (!cmap.get_nominal_glyph (cp, &gid)) + { + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); + continue; + } + + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + } else { + // Use in memory unicode to gid map it's faster then looking up from + // the map. This code is mostly duplicated from above to avoid doing + // conditionals on the presence of the unicode_to_gid map each + // iteration. + for (hb_codepoint_t cp : *unicodes) + { + hb_codepoint_t gid = unicode_to_gid->get (cp); + if (gid == HB_MAP_VALUE_INVALID) + { + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp); + continue; + } + + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + } + } + else + { + // This approach is slower, but can handle adding in glyphs to the subset and will match + // them with cmap entries. + + hb_map_t unicode_glyphid_map_storage; + hb_set_t cmap_unicodes_storage; + const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage; + const hb_set_t* cmap_unicodes = &cmap_unicodes_storage; + + if (!plan->accelerator) { + cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage); + plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population () + + glyphs->get_population (), + cmap_unicodes->get_population ())); + } else { + unicode_glyphid_map = &plan->accelerator->unicode_to_gid; + cmap_unicodes = &plan->accelerator->unicodes; + } + + if (plan->accelerator && + unicodes->get_population () < cmap_unicodes->get_population () && + glyphs->get_population () < cmap_unicodes->get_population ()) + { + plan->codepoint_to_glyph->alloc (unicodes->get_population () + glyphs->get_population ()); + + auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes; + for (hb_codepoint_t gid : *glyphs) + { + auto unicodes = gid_to_unicodes.get (gid); + + for (hb_codepoint_t cp : unicodes) + { + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + } + for (hb_codepoint_t cp : *unicodes) + { + /* Don't double-add entry. */ + if (plan->codepoint_to_glyph->has (cp)) + continue; + + hb_codepoint_t *gid; + if (!unicode_glyphid_map->has(cp, &gid)) + continue; + + plan->codepoint_to_glyph->set (cp, *gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, *gid)); + } + plan->unicode_to_new_gid_list.qsort (); + } + else + { + plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ()); + hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID; + for (; cmap_unicodes->next_range (&first, &last); ) + { + for (unsigned cp = first; cp <= last; cp++) + { + hb_codepoint_t gid = (*unicode_glyphid_map)[cp]; + if (!unicodes->has (cp) && !glyphs->has (gid)) + continue; + + plan->codepoint_to_glyph->set (cp, gid); + plan->unicode_to_new_gid_list.push (hb_pair (cp, gid)); + } + } + } + + /* Add gids which where requested, but not mapped in cmap */ + unsigned num_glyphs = plan->source->get_num_glyphs (); + hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID; + for (; glyphs->next_range (&first, &last); ) + { + if (first >= num_glyphs) + break; + if (last >= num_glyphs) + last = num_glyphs - 1; + plan->_glyphset_gsub.add_range (first, last); + } + } + + auto &arr = plan->unicode_to_new_gid_list; + if (arr.length) + { + plan->unicodes.add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ)); + plan->_glyphset_gsub.add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ)); + } +} + +#ifndef HB_COMPOSITE_OPERATIONS_PER_GLYPH +#define HB_COMPOSITE_OPERATIONS_PER_GLYPH 64 +#endif + +static unsigned +_glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf, + hb_codepoint_t gid, + hb_set_t *gids_to_retain, + int operation_count, + unsigned depth = 0) +{ + /* Check if is already visited */ + if (gids_to_retain->has (gid)) return operation_count; + + gids_to_retain->add (gid); + + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count; + if (unlikely (--operation_count < 0)) return operation_count; + + auto glyph = glyf.glyph_for_gid (gid); + + for (auto &item : glyph.get_composite_iterator ()) + operation_count = + _glyf_add_gid_and_children (glyf, + item.get_gid (), + gids_to_retain, + operation_count, + depth); + +#ifndef HB_NO_VAR_COMPOSITES + for (auto &item : glyph.get_var_composite_iterator ()) + { + operation_count = + _glyf_add_gid_and_children (glyf, + item.get_gid (), + gids_to_retain, + operation_count, + depth); + } +#endif + + return operation_count; +} + +static void +_nameid_closure (hb_subset_plan_t* plan, + hb_set_t* drop_tables) +{ +#ifndef HB_NO_STYLE + plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids); +#endif +#ifndef HB_NO_VAR + if (!plan->all_axes_pinned) + plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->axes_old_index_tag_map, &plan->name_ids); +#endif +#ifndef HB_NO_COLOR + if (!drop_tables->has (HB_OT_TAG_CPAL)) + plan->source->table.CPAL->collect_name_ids (&plan->colr_palettes, &plan->name_ids); +#endif + +#ifndef HB_NO_SUBSET_LAYOUT + if (!drop_tables->has (HB_OT_TAG_GPOS)) + { + hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> (); + gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids); + gpos.destroy (); + } + if (!drop_tables->has (HB_OT_TAG_GSUB)) + { + hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> (); + gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids); + gsub.destroy (); + } +#endif +} + +static void +_populate_gids_to_retain (hb_subset_plan_t* plan, + hb_set_t* drop_tables) +{ + OT::glyf_accelerator_t glyf (plan->source); +#ifndef HB_NO_SUBSET_CFF + // Note: we cannot use inprogress_accelerator here, since it has not been + // created yet. So in case of preprocessed-face (and otherwise), we do an + // extra sanitize pass here, which is not ideal. + OT::cff1::accelerator_subset_t stack_cff (plan->accelerator ? nullptr : plan->source); + const OT::cff1::accelerator_subset_t *cff (plan->accelerator ? plan->accelerator->cff1_accel.get () : &stack_cff); +#endif + + plan->_glyphset_gsub.add (0); // Not-def + + _cmap_closure (plan->source, &plan->unicodes, &plan->_glyphset_gsub); + +#ifndef HB_NO_SUBSET_LAYOUT + if (!drop_tables->has (HB_OT_TAG_GSUB)) + // closure all glyphs/lookups/features needed for GSUB substitutions. + _closure_glyphs_lookups_features<GSUB> ( + plan, + &plan->_glyphset_gsub, + &plan->gsub_lookups, + &plan->gsub_features, + &plan->gsub_langsys, + &plan->gsub_feature_record_cond_idx_map, + &plan->gsub_feature_substitutes_map, + plan->gsub_old_features, + plan->gsub_old_feature_idx_tag_map); + + if (!drop_tables->has (HB_OT_TAG_GPOS)) + _closure_glyphs_lookups_features<GPOS> ( + plan, + &plan->_glyphset_gsub, + &plan->gpos_lookups, + &plan->gpos_features, + &plan->gpos_langsys, + &plan->gpos_feature_record_cond_idx_map, + &plan->gpos_feature_substitutes_map, + plan->gpos_old_features, + plan->gpos_old_feature_idx_tag_map); +#endif + _remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ()); + + plan->_glyphset_mathed = plan->_glyphset_gsub; + if (!drop_tables->has (HB_OT_TAG_MATH)) + { + _math_closure (plan, &plan->_glyphset_mathed); + _remove_invalid_gids (&plan->_glyphset_mathed, plan->source->get_num_glyphs ()); + } + + hb_set_t cur_glyphset = plan->_glyphset_mathed; + if (!drop_tables->has (HB_OT_TAG_COLR)) + { + _colr_closure (plan->source, &plan->colrv1_layers, &plan->colr_palettes, &cur_glyphset); + _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ()); + } + + plan->_glyphset_colred = cur_glyphset; + + _nameid_closure (plan, drop_tables); + /* Populate a full set of glyphs to retain by adding all referenced + * composite glyphs. */ + if (glyf.has_data ()) + for (hb_codepoint_t gid : cur_glyphset) + _glyf_add_gid_and_children (glyf, gid, &plan->_glyphset, + cur_glyphset.get_population () * HB_COMPOSITE_OPERATIONS_PER_GLYPH); + else + plan->_glyphset.union_ (cur_glyphset); +#ifndef HB_NO_SUBSET_CFF + if (!plan->accelerator || plan->accelerator->has_seac) + { + bool has_seac = false; + if (cff->is_valid ()) + for (hb_codepoint_t gid : cur_glyphset) + if (_add_cff_seac_components (*cff, gid, &plan->_glyphset)) + has_seac = true; + plan->has_seac = has_seac; + } +#endif + + _remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ()); + +#ifndef HB_NO_VAR + if (!drop_tables->has (HB_OT_TAG_GDEF)) + _collect_layout_variation_indices (plan); +#endif +} + +static void +_create_glyph_map_gsub (const hb_set_t* glyph_set_gsub, + const hb_map_t* glyph_map, + hb_map_t* out) +{ + out->alloc (glyph_set_gsub->get_population ()); + + hb_iter (glyph_set_gsub) + | hb_map ([&] (hb_codepoint_t gid) { + return hb_codepoint_pair_t (gid, glyph_map->get (gid)); + }) + | hb_sink (out) + ; +} + +static bool +_create_old_gid_to_new_gid_map (const hb_face_t *face, + bool retain_gids, + const hb_set_t *all_gids_to_retain, + const hb_map_t *requested_glyph_map, + hb_map_t *glyph_map, /* OUT */ + hb_map_t *reverse_glyph_map, /* OUT */ + hb_sorted_vector_t<hb_codepoint_pair_t> *new_to_old_gid_list /* OUT */, + unsigned int *num_glyphs /* OUT */) +{ + unsigned pop = all_gids_to_retain->get_population (); + reverse_glyph_map->alloc (pop); + glyph_map->alloc (pop); + new_to_old_gid_list->alloc (pop); + + if (*requested_glyph_map) + { + hb_set_t new_gids(requested_glyph_map->values()); + if (new_gids.get_population() != requested_glyph_map->get_population()) + { + DEBUG_MSG (SUBSET, nullptr, "The provided custom glyph mapping is not unique."); + return false; + } + + if (retain_gids) + { + DEBUG_MSG (SUBSET, nullptr, + "HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if " + "a custom glyph mapping has been provided."); + return false; + } + + hb_codepoint_t max_glyph = 0; + hb_set_t remaining; + for (auto old_gid : all_gids_to_retain->iter ()) + { + if (old_gid == 0) { + new_to_old_gid_list->push (hb_pair<hb_codepoint_t, hb_codepoint_t> (0u, 0u)); + continue; + } + + hb_codepoint_t* new_gid; + if (!requested_glyph_map->has (old_gid, &new_gid)) + { + remaining.add(old_gid); + continue; + } + + if (*new_gid > max_glyph) + max_glyph = *new_gid; + new_to_old_gid_list->push (hb_pair (*new_gid, old_gid)); + } + new_to_old_gid_list->qsort (); + + // Anything that wasn't mapped by the requested mapping should + // be placed after the requested mapping. + for (auto old_gid : remaining) + new_to_old_gid_list->push (hb_pair (++max_glyph, old_gid)); + + *num_glyphs = max_glyph + 1; + } + else if (!retain_gids) + { + + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0) + | hb_sink (new_to_old_gid_list) + ; + *num_glyphs = new_to_old_gid_list->length; + } + else + { + + hb_iter (all_gids_to_retain) + | hb_map ([] (hb_codepoint_t _) { + return hb_codepoint_pair_t (_, _); + }) + | hb_sink (new_to_old_gid_list) + ; + + hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID; + hb_set_previous (all_gids_to_retain, &max_glyph); + + *num_glyphs = max_glyph + 1; + } + + reverse_glyph_map->alloc (reverse_glyph_map->get_population () + new_to_old_gid_list->length); + + hb_iter (new_to_old_gid_list) + | hb_sink (reverse_glyph_map) + ; + glyph_map->alloc (glyph_map->get_population () + new_to_old_gid_list->length); + + hb_iter (new_to_old_gid_list) + | hb_map (&hb_codepoint_pair_t::reverse) + | hb_sink (glyph_map) + ; + + return true; +} + +#ifndef HB_NO_VAR +static void +_normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan) +{ + if (plan->user_axes_location.is_empty ()) + return; + + hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes (); + plan->normalized_coords.resize (axes.length); + + bool has_avar = face->table.avar->has_data (); + const OT::SegmentMaps *seg_maps = nullptr; + unsigned avar_axis_count = 0; + if (has_avar) + { + seg_maps = face->table.avar->get_segment_maps (); + avar_axis_count = face->table.avar->get_axis_count(); + } + + bool axis_not_pinned = false; + unsigned old_axis_idx = 0, new_axis_idx = 0; + for (const auto& axis : axes) + { + hb_tag_t axis_tag = axis.get_axis_tag (); + plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag); + + if (!plan->user_axes_location.has (axis_tag) || + !plan->user_axes_location.get (axis_tag).is_point ()) + { + axis_not_pinned = true; + plan->axes_index_map.set (old_axis_idx, new_axis_idx); + plan->axis_tags.push (axis_tag); + new_axis_idx++; + } + + Triple *axis_range; + if (plan->user_axes_location.has (axis_tag, &axis_range)) + { + plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ()); + + int normalized_min = axis.normalize_axis_value (axis_range->minimum); + int normalized_default = axis.normalize_axis_value (axis_range->middle); + int normalized_max = axis.normalize_axis_value (axis_range->maximum); + + if (has_avar && old_axis_idx < avar_axis_count) + { + normalized_min = seg_maps->map (normalized_min); + normalized_default = seg_maps->map (normalized_default); + normalized_max = seg_maps->map (normalized_max); + } + plan->axes_location.set (axis_tag, Triple (static_cast<float> (normalized_min / 16384.f), + static_cast<float> (normalized_default / 16384.f), + static_cast<float> (normalized_max / 16384.f))); + + if (normalized_default != 0) + plan->pinned_at_default = false; + + plan->normalized_coords[old_axis_idx] = normalized_default; + } + + old_axis_idx++; + + if (has_avar && old_axis_idx < avar_axis_count) + seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps); + } + plan->all_axes_pinned = !axis_not_pinned; +} + +static void +_update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan) +{ + if (!plan->normalized_coords) return; + OT::cff2::accelerator_t cff2 (plan->source); + if (!cff2.is_valid ()) return; + + hb_font_t *font = nullptr; + if (unlikely (!plan->check_success (font = _get_hb_font_with_variations (plan)))) + { + hb_font_destroy (font); + return; + } + + hb_glyph_extents_t extents = {0x7FFF, -0x7FFF}; + OT::hmtx_accelerator_t _hmtx (plan->source); + float *hvar_store_cache = nullptr; + if (_hmtx.has_data () && _hmtx.var_table.get_length ()) + hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache (); + + OT::vmtx_accelerator_t _vmtx (plan->source); + float *vvar_store_cache = nullptr; + if (_vmtx.has_data () && _vmtx.var_table.get_length ()) + vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache (); + + for (auto p : *plan->glyph_map) + { + hb_codepoint_t old_gid = p.first; + hb_codepoint_t new_gid = p.second; + if (!cff2.get_extents (font, old_gid, &extents)) continue; + bool has_bounds_info = true; + if (extents.x_bearing == 0 && extents.width == 0 && + extents.height == 0 && extents.y_bearing == 0) + has_bounds_info = false; + + if (has_bounds_info) + { + plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, extents.x_bearing); + plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, extents.x_bearing + extents.width); + plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, extents.y_bearing); + plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, extents.y_bearing + extents.height); + } + + if (_hmtx.has_data ()) + { + int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid); + if (_hmtx.var_table.get_length ()) + hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords, + hvar_store_cache)); + int lsb = extents.x_bearing; + if (!has_bounds_info) + { + if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) + continue; + } + plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb)); + plan->bounds_width_vec[new_gid] = extents.width; + } + + if (_vmtx.has_data ()) + { + int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid); + if (_vmtx.var_table.get_length ()) + vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords, + vvar_store_cache)); + + int tsb = extents.y_bearing; + if (!has_bounds_info) + { + if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb)) + continue; + } + plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb)); + plan->bounds_height_vec[new_gid] = extents.height; + } + } + hb_font_destroy (font); + if (hvar_store_cache) + _hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache); + if (vvar_store_cache) + _vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache); +} + +static bool +_get_instance_glyphs_contour_points (hb_subset_plan_t *plan) +{ + /* contour_points vector only needed for updating gvar table (infer delta) + * during partial instancing */ + if (plan->user_axes_location.is_empty () || plan->all_axes_pinned) + return true; + + OT::glyf_accelerator_t glyf (plan->source); + + for (auto &_ : plan->new_to_old_gid_list) + { + hb_codepoint_t new_gid = _.first; + contour_point_vector_t all_points; + if (new_gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) + { + if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) + return false; + continue; + } + + hb_codepoint_t old_gid = _.second; + if (unlikely (!glyf.glyph_for_gid (old_gid).get_all_points_without_var (plan->source, all_points))) + return false; + if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) + return false; + } + return true; +} +#endif + +hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, + const hb_subset_input_t *input) +{ + successful = true; + flags = input->flags; + + unicode_to_new_gid_list.init (); + + name_ids = *input->sets.name_ids; + name_languages = *input->sets.name_languages; + layout_features = *input->sets.layout_features; + layout_scripts = *input->sets.layout_scripts; + glyphs_requested = *input->sets.glyphs; + drop_tables = *input->sets.drop_tables; + no_subset_tables = *input->sets.no_subset_tables; + source = hb_face_reference (face); + dest = hb_face_builder_create (); + + codepoint_to_glyph = hb_map_create (); + glyph_map = hb_map_create (); + reverse_glyph_map = hb_map_create (); + + gsub_insert_catch_all_feature_variation_rec = false; + gpos_insert_catch_all_feature_variation_rec = false; + gdef_varstore_inner_maps.init (); + + user_axes_location = input->axes_location; + all_axes_pinned = false; + pinned_at_default = true; + has_gdef_varstore = false; + +#ifdef HB_EXPERIMENTAL_API + for (auto _ : input->name_table_overrides) + { + hb_bytes_t name_bytes = _.second; + unsigned len = name_bytes.length; + char *name_str = (char *) hb_malloc (len); + if (unlikely (!check_success (name_str))) + break; + + hb_memcpy (name_str, name_bytes.arrayZ, len); + name_table_overrides.set (_.first, hb_bytes_t (name_str, len)); + } +#endif + + void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key()); + + attach_accelerator_data = input->attach_accelerator_data; + force_long_loca = input->force_long_loca; +#ifdef HB_EXPERIMENTAL_API + force_long_loca = force_long_loca || (flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); +#endif + + if (accel) + accelerator = (hb_subset_accelerator_t*) accel; + + if (unlikely (in_error ())) + return; + +#ifndef HB_NO_VAR + _normalize_axes_location (face, this); +#endif + + _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this); + + _populate_gids_to_retain (this, input->sets.drop_tables); + if (unlikely (in_error ())) + return; + + if (!check_success(_create_old_gid_to_new_gid_map( + face, + input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS, + &_glyphset, + &input->glyph_map, + glyph_map, + reverse_glyph_map, + &new_to_old_gid_list, + &_num_output_glyphs))) { + return; + } + + _create_glyph_map_gsub ( + &_glyphset_gsub, + glyph_map, + &glyph_map_gsub); + + // Now that we have old to new gid map update the unicode to new gid list. + for (unsigned i = 0; i < unicode_to_new_gid_list.length; i++) + { + // Use raw array access for performance. + unicode_to_new_gid_list.arrayZ[i].second = + glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second); + } + + bounds_width_vec.resize (_num_output_glyphs, false); + for (auto &v : bounds_width_vec) + v = 0xFFFFFFFF; + bounds_height_vec.resize (_num_output_glyphs, false); + for (auto &v : bounds_height_vec) + v = 0xFFFFFFFF; + + if (!drop_tables.has (HB_OT_TAG_GDEF)) + _remap_used_mark_sets (this, used_mark_sets_map); + + if (unlikely (in_error ())) + return; + +#ifndef HB_NO_VAR + _update_instance_metrics_map_from_cff2 (this); + if (!check_success (_get_instance_glyphs_contour_points (this))) + return; +#endif + + if (attach_accelerator_data) + { + inprogress_accelerator = + hb_subset_accelerator_t::create (source, + *codepoint_to_glyph, + unicodes, + has_seac); + + check_success (inprogress_accelerator); + } + +#define HB_SUBSET_PLAN_MEMBER(Type, Name) check_success (!Name.in_error ()); +#include "hb-subset-plan-member-list.hh" +#undef HB_SUBSET_PLAN_MEMBER +} + +hb_subset_plan_t::~hb_subset_plan_t() +{ + hb_face_destroy (dest); + + hb_map_destroy (codepoint_to_glyph); + hb_map_destroy (glyph_map); + hb_map_destroy (reverse_glyph_map); +#ifndef HB_NO_SUBSET_CFF + cff1_accel.fini (); + cff2_accel.fini (); +#endif + hb_face_destroy (source); + +#ifdef HB_EXPERIMENTAL_API + for (auto _ : name_table_overrides.iter_ref ()) + _.second.fini (); +#endif + + if (inprogress_accelerator) + hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator); +} + + +/** + * hb_subset_plan_create_or_fail: + * @face: font face to create the plan for. + * @input: a #hb_subset_input_t input. + * + * Computes a plan for subsetting the supplied face according + * to a provided input. The plan describes + * which tables and glyphs should be retained. + * + * Return value: (transfer full): New subset plan. Destroy with + * hb_subset_plan_destroy(). If there is a failure creating the plan + * nullptr will be returned. + * + * Since: 4.0.0 + **/ +hb_subset_plan_t * +hb_subset_plan_create_or_fail (hb_face_t *face, + const hb_subset_input_t *input) +{ + hb_subset_plan_t *plan; + if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> (face, input)))) + return nullptr; + + if (unlikely (plan->in_error ())) + { + hb_subset_plan_destroy (plan); + return nullptr; + } + + return plan; +} + +/** + * hb_subset_plan_destroy: + * @plan: a #hb_subset_plan_t + * + * Decreases the reference count on @plan, and if it reaches zero, destroys + * @plan, freeing all memory. + * + * Since: 4.0.0 + **/ +void +hb_subset_plan_destroy (hb_subset_plan_t *plan) +{ + if (!hb_object_destroy (plan)) return; + + hb_free (plan); +} + +/** + * hb_subset_plan_old_to_new_glyph_mapping: + * @plan: a subsetting plan. + * + * Returns the mapping between glyphs in the original font to glyphs in the + * subset that will be produced by @plan + * + * Return value: (transfer none): + * A pointer to the #hb_map_t of the mapping. + * + * Since: 4.0.0 + **/ +hb_map_t * +hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan) +{ + return plan->glyph_map; +} + +/** + * hb_subset_plan_new_to_old_glyph_mapping: + * @plan: a subsetting plan. + * + * Returns the mapping between glyphs in the subset that will be produced by + * @plan and the glyph in the original font. + * + * Return value: (transfer none): + * A pointer to the #hb_map_t of the mapping. + * + * Since: 4.0.0 + **/ +hb_map_t * +hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan) +{ + return plan->reverse_glyph_map; +} + +/** + * hb_subset_plan_unicode_to_old_glyph_mapping: + * @plan: a subsetting plan. + * + * Returns the mapping between codepoints in the original font and the + * associated glyph id in the original font. + * + * Return value: (transfer none): + * A pointer to the #hb_map_t of the mapping. + * + * Since: 4.0.0 + **/ +hb_map_t * +hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan) +{ + return plan->codepoint_to_glyph; +} + +/** + * hb_subset_plan_reference: (skip) + * @plan: a #hb_subset_plan_t object. + * + * Increases the reference count on @plan. + * + * Return value: @plan. + * + * Since: 4.0.0 + **/ +hb_subset_plan_t * +hb_subset_plan_reference (hb_subset_plan_t *plan) +{ + return hb_object_reference (plan); +} + +/** + * hb_subset_plan_set_user_data: (skip) + * @plan: a #hb_subset_plan_t 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 subset plan object. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 4.0.0 + **/ +hb_bool_t +hb_subset_plan_set_user_data (hb_subset_plan_t *plan, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (plan, key, data, destroy, replace); +} + +/** + * hb_subset_plan_get_user_data: (skip) + * @plan: a #hb_subset_plan_t object. + * @key: The user-data key to query + * + * Fetches the user data associated with the specified key, + * attached to the specified subset plan object. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 4.0.0 + **/ +void * +hb_subset_plan_get_user_data (const hb_subset_plan_t *plan, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (plan, key); +} diff --git a/gfx/harfbuzz/src/hb-subset-plan.hh b/gfx/harfbuzz/src/hb-subset-plan.hh new file mode 100644 index 0000000000..1f19a58c1e --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-plan.hh @@ -0,0 +1,279 @@ +/* + * 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, Roderick Sheeter + */ + +#ifndef HB_SUBSET_PLAN_HH +#define HB_SUBSET_PLAN_HH + +#include "hb.hh" + +#include "hb-subset.h" +#include "hb-subset-input.hh" +#include "hb-subset-accelerator.hh" + +#include "hb-map.hh" +#include "hb-bimap.hh" +#include "hb-set.hh" + +namespace OT { +struct Feature; +} + +struct head_maxp_info_t +{ + head_maxp_info_t () + :xMin (0x7FFF), xMax (-0x7FFF), yMin (0x7FFF), yMax (-0x7FFF), + maxPoints (0), maxContours (0), + maxCompositePoints (0), + maxCompositeContours (0), + maxComponentElements (0), + maxComponentDepth (0), + allXMinIsLsb (true) {} + + int xMin; + int xMax; + int yMin; + int yMax; + unsigned maxPoints; + unsigned maxContours; + unsigned maxCompositePoints; + unsigned maxCompositeContours; + unsigned maxComponentElements; + unsigned maxComponentDepth; + bool allXMinIsLsb; +}; + +typedef struct head_maxp_info_t head_maxp_info_t; + +struct contour_point_t +{ + void init (float x_ = 0.f, float y_ = 0.f, bool is_end_point_ = false) + { flag = 0; x = x_; y = y_; is_end_point = is_end_point_; } + + void transform (const float (&matrix)[4]) + { + float x_ = x * matrix[0] + y * matrix[2]; + y = x * matrix[1] + y * matrix[3]; + x = x_; + } + HB_ALWAYS_INLINE + void translate (const contour_point_t &p) { x += p.x; y += p.y; } + + + float x; + float y; + uint8_t flag; + bool is_end_point; +}; + +struct contour_point_vector_t : hb_vector_t<contour_point_t> +{ + void extend (const hb_array_t<contour_point_t> &a) + { + unsigned int old_len = length; + if (unlikely (!resize (old_len + a.length, false))) + return; + auto arrayZ = this->arrayZ + old_len; + unsigned count = a.length; + hb_memcpy (arrayZ, a.arrayZ, count * sizeof (arrayZ[0])); + } +}; + +namespace OT { + struct cff1_subset_accelerator_t; + struct cff2_subset_accelerator_t; +} + +struct hb_subset_plan_t +{ + HB_INTERNAL hb_subset_plan_t (hb_face_t *, + const hb_subset_input_t *input); + + HB_INTERNAL ~hb_subset_plan_t(); + + hb_object_header_t header; + + bool successful; + unsigned flags; + bool attach_accelerator_data = false; + bool force_long_loca = false; + + // The glyph subset + hb_map_t *codepoint_to_glyph; // Needs to be heap-allocated + + // Old -> New glyph id mapping + hb_map_t *glyph_map; // Needs to be heap-allocated + hb_map_t *reverse_glyph_map; // Needs to be heap-allocated + + // Plan is only good for a specific source/dest so keep them with it + hb_face_t *source; +#ifndef HB_NO_SUBSET_CFF + // These have to be immediately after source: + hb_face_lazy_loader_t<OT::cff1_subset_accelerator_t, 1> cff1_accel; + hb_face_lazy_loader_t<OT::cff2_subset_accelerator_t, 2> cff2_accel; +#endif + + hb_face_t *dest; + + unsigned int _num_output_glyphs; + + bool all_axes_pinned; + bool pinned_at_default; + bool has_seac; + + // whether to insert a catch-all FeatureVariationRecord + bool gsub_insert_catch_all_feature_variation_rec; + bool gpos_insert_catch_all_feature_variation_rec; + + // whether GDEF VarStore is retained + mutable bool has_gdef_varstore; + +#define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name; +#include "hb-subset-plan-member-list.hh" +#undef HB_SUBSET_PLAN_MEMBER + + //recalculated head/maxp table info after instancing + mutable head_maxp_info_t head_maxp_info; + + const hb_subset_accelerator_t* accelerator; + hb_subset_accelerator_t* inprogress_accelerator; + + public: + + template<typename T> + struct source_table_loader + { + hb_blob_ptr_t<T> operator () (hb_subset_plan_t *plan) + { + hb_lock_t lock (plan->accelerator ? &plan->accelerator->sanitized_table_cache_lock : nullptr); + + auto *cache = plan->accelerator ? &plan->accelerator->sanitized_table_cache : &plan->sanitized_table_cache; + if (cache + && !cache->in_error () + && cache->has (+T::tableTag)) { + return hb_blob_reference (cache->get (+T::tableTag).get ()); + } + + hb::unique_ptr<hb_blob_t> table_blob {hb_sanitize_context_t ().reference_table<T> (plan->source)}; + hb_blob_t* ret = hb_blob_reference (table_blob.get ()); + + if (likely (cache)) + cache->set (+T::tableTag, std::move (table_blob)); + + return ret; + } + }; + + template<typename T> + auto source_table() HB_AUTO_RETURN (source_table_loader<T> {} (this)) + + bool in_error () const { return !successful; } + + bool check_success(bool success) + { + successful = (successful && success); + return successful; + } + + /* + * The set of input glyph ids which will be retained in the subset. + * Does NOT include ids kept due to retain_gids. You probably want to use + * glyph_map/reverse_glyph_map. + */ + inline const hb_set_t * + glyphset () const + { + return &_glyphset; + } + + /* + * The set of input glyph ids which will be retained in the subset. + */ + inline const hb_set_t * + glyphset_gsub () const + { + return &_glyphset_gsub; + } + + /* + * The total number of output glyphs in the final subset. + */ + inline unsigned int + num_output_glyphs () const + { + return _num_output_glyphs; + } + + inline bool new_gid_for_codepoint (hb_codepoint_t codepoint, + hb_codepoint_t *new_gid) const + { + hb_codepoint_t old_gid = codepoint_to_glyph->get (codepoint); + if (old_gid == HB_MAP_VALUE_INVALID) + return false; + + return new_gid_for_old_gid (old_gid, new_gid); + } + + inline bool new_gid_for_old_gid (hb_codepoint_t old_gid, + hb_codepoint_t *new_gid) const + { + hb_codepoint_t gid = glyph_map->get (old_gid); + if (gid == HB_MAP_VALUE_INVALID) + return false; + + *new_gid = gid; + return true; + } + + inline bool old_gid_for_new_gid (hb_codepoint_t new_gid, + hb_codepoint_t *old_gid) const + { + hb_codepoint_t gid = reverse_glyph_map->get (new_gid); + if (gid == HB_MAP_VALUE_INVALID) + return false; + + *old_gid = gid; + return true; + } + + inline bool + add_table (hb_tag_t tag, + hb_blob_t *contents) + { + if (HB_DEBUG_SUBSET) + { + hb_blob_t *source_blob = source->reference_table (tag); + DEBUG_MSG(SUBSET, nullptr, "add table %c%c%c%c, dest %u bytes, source %u bytes", + HB_UNTAG(tag), + hb_blob_get_length (contents), + hb_blob_get_length (source_blob)); + hb_blob_destroy (source_blob); + } + return hb_face_builder_add_table (dest, tag, contents); + } +}; + + +#endif /* HB_SUBSET_PLAN_HH */ diff --git a/gfx/harfbuzz/src/hb-subset-repacker.cc b/gfx/harfbuzz/src/hb-subset-repacker.cc new file mode 100644 index 0000000000..6a29b35be7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-repacker.cc @@ -0,0 +1,58 @@ +/* + * 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. + * + */ +#include "hb-repacker.hh" + +#ifdef HB_EXPERIMENTAL_API + +/** + * hb_subset_repack_or_fail: + * @table_tag: tag of the table being packed, needed to allow table specific optimizations. + * @hb_objects: raw array of struct hb_object_t, which provides + * object graph info + * @num_hb_objs: number of hb_object_t in the hb_objects array. + * + * Given the input object graph info, repack a table to eliminate + * offset overflows. A nullptr is returned if the repacking attempt fails. + * Table specific optimizations (eg. extension promotion in GSUB/GPOS) may be performed. + * Passing HB_TAG_NONE will disable table specific optimizations. + * + * XSince: EXPERIMENTAL + **/ +hb_blob_t* hb_subset_repack_or_fail (hb_tag_t table_tag, + hb_object_t* hb_objects, + unsigned num_hb_objs) +{ + hb_vector_t<const hb_object_t *> packed; + packed.alloc (num_hb_objs + 1); + packed.push (nullptr); + for (unsigned i = 0 ; i < num_hb_objs ; i++) + packed.push (&(hb_objects[i])); + + return hb_resolve_overflows (packed, + table_tag, + 20, + true); +} +#endif diff --git a/gfx/harfbuzz/src/hb-subset-repacker.h b/gfx/harfbuzz/src/hb-subset-repacker.h new file mode 100644 index 0000000000..245cf60765 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset-repacker.h @@ -0,0 +1,81 @@ +/* + * 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. + * + */ + +#ifndef HB_SUBSET_REPACKER_H +#define HB_SUBSET_REPACKER_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +/* + * struct hb_link_t + * width: offsetSize in bytes + * position: position of the offset field in bytes + * from beginning of subtable + * objidx: index of subtable + */ +struct hb_link_t +{ + unsigned width; + unsigned position; + unsigned objidx; +}; + +typedef struct hb_link_t hb_link_t; + +/* + * struct hb_object_t + * head: start of object data + * tail: end of object data + * num_real_links: num of offset field in the object + * real_links: pointer to array of offset info + * num_virtual_links: num of objects that must be packed + * after current object in the final serialized order + * virtual_links: array of virtual link info + */ +struct hb_object_t +{ + char *head; + char *tail; + unsigned num_real_links; + hb_link_t *real_links; + unsigned num_virtual_links; + hb_link_t *virtual_links; +}; + +typedef struct hb_object_t hb_object_t; + +HB_EXTERN hb_blob_t* +hb_subset_repack_or_fail (hb_tag_t table_tag, + hb_object_t* hb_objects, + unsigned num_hb_objs); + +#endif + +HB_END_DECLS + +#endif /* HB_SUBSET_REPACKER_H */ diff --git a/gfx/harfbuzz/src/hb-subset.cc b/gfx/harfbuzz/src/hb-subset.cc new file mode 100644 index 0000000000..06e77dd8eb --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset.cc @@ -0,0 +1,701 @@ +/* + * 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, Rod Sheeter, Behdad Esfahbod + */ + +#include "hb.hh" +#include "hb-open-type.hh" + +#include "hb-subset.hh" + +#include "hb-open-file.hh" +#include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-hdmx-table.hh" +#include "hb-ot-head-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-maxp-table.hh" +#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 "hb-ot-os2-table.hh" +#include "hb-ot-post-table.hh" +#include "hb-ot-post-table-v2subset.hh" +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-ot-vorg-table.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-layout-gsub-table.hh" +#include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-var-avar-table.hh" +#include "hb-ot-var-cvar-table.hh" +#include "hb-ot-var-fvar-table.hh" +#include "hb-ot-var-gvar-table.hh" +#include "hb-ot-var-hvar-table.hh" +#include "hb-ot-var-mvar-table.hh" +#include "hb-ot-math-table.hh" +#include "hb-ot-stat-table.hh" +#include "hb-repacker.hh" +#include "hb-subset-accelerator.hh" + +using OT::Layout::GSUB; +using OT::Layout::GPOS; + + +#ifndef HB_NO_SUBSET_CFF +template<> +struct hb_subset_plan_t::source_table_loader<const OT::cff1> +{ + auto operator () (hb_subset_plan_t *plan) + HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff1_accel : + plan->inprogress_accelerator ? plan->inprogress_accelerator->cff1_accel : + plan->cff1_accel) +}; +template<> +struct hb_subset_plan_t::source_table_loader<const OT::cff2> +{ + auto operator () (hb_subset_plan_t *plan) + HB_AUTO_RETURN (plan->accelerator ? plan->accelerator->cff2_accel : + plan->inprogress_accelerator ? plan->inprogress_accelerator->cff2_accel : + plan->cff2_accel) +}; +#endif + + +/** + * SECTION:hb-subset + * @title: hb-subset + * @short_description: Subsets font files. + * @include: hb-subset.h + * + * Subsetting reduces the codepoint coverage of font files and removes all data + * that is no longer needed. A subset input describes the desired subset. The input is + * provided along with a font to the subsetting operation. Output is a new font file + * containing only the data specified in the input. + * + * Currently most outline and bitmap tables are supported: glyf, CFF, CFF2, sbix, + * COLR, and CBDT/CBLC. This also includes fonts with variable outlines via OpenType + * variations. Notably EBDT/EBLC and SVG are not supported. Layout subsetting is supported + * only for OpenType Layout tables (GSUB, GPOS, GDEF). Notably subsetting of graphite or AAT tables + * is not yet supported. + * + * Fonts with graphite or AAT tables may still be subsetted but will likely need to use the + * retain glyph ids option and configure the subset to pass through the layout tables untouched. + */ + + +hb_user_data_key_t _hb_subset_accelerator_user_data_key = {}; + + +/* + * The list of tables in the open type spec. Used to check for tables that may need handling + * if we are unable to list the tables in a face. + */ +static hb_tag_t known_tables[] { + HB_TAG ('a', 'v', 'a', 'r'), + HB_OT_TAG_BASE, + HB_OT_TAG_CBDT, + HB_OT_TAG_CBLC, + HB_OT_TAG_CFF1, + HB_OT_TAG_CFF2, + HB_OT_TAG_cmap, + HB_OT_TAG_COLR, + HB_OT_TAG_CPAL, + HB_TAG ('c', 'v', 'a', 'r'), + HB_TAG ('c', 'v', 't', ' '), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('E', 'B', 'D', 'T'), + HB_TAG ('E', 'B', 'L', 'C'), + HB_TAG ('E', 'B', 'S', 'C'), + HB_TAG ('f', 'p', 'g', 'm'), + HB_TAG ('f', 'v', 'a', 'r'), + HB_TAG ('g', 'a', 's', 'p'), + HB_OT_TAG_GDEF, + HB_OT_TAG_glyf, + HB_OT_TAG_GPOS, + HB_OT_TAG_GSUB, + HB_OT_TAG_gvar, + HB_OT_TAG_hdmx, + HB_OT_TAG_head, + HB_OT_TAG_hhea, + HB_OT_TAG_hmtx, + HB_OT_TAG_HVAR, + HB_OT_TAG_JSTF, + HB_TAG ('k', 'e', 'r', 'n'), + HB_OT_TAG_loca, + HB_TAG ('L', 'T', 'S', 'H'), + HB_OT_TAG_MATH, + HB_OT_TAG_maxp, + HB_TAG ('M', 'E', 'R', 'G'), + HB_TAG ('m', 'e', 't', 'a'), + HB_TAG ('M', 'V', 'A', 'R'), + HB_TAG ('P', 'C', 'L', 'T'), + HB_OT_TAG_post, + HB_TAG ('p', 'r', 'e', 'p'), + HB_OT_TAG_sbix, + HB_TAG ('S', 'T', 'A', 'T'), + HB_TAG ('S', 'V', 'G', ' '), + HB_TAG ('V', 'D', 'M', 'X'), + HB_OT_TAG_vhea, + HB_OT_TAG_vmtx, + HB_OT_TAG_VORG, + HB_OT_TAG_VVAR, + HB_OT_TAG_name, + HB_OT_TAG_OS2 +}; + +static bool _table_is_empty (const hb_face_t *face, hb_tag_t tag) +{ + hb_blob_t* blob = hb_face_reference_table (face, tag); + bool result = (blob == hb_blob_get_empty ()); + hb_blob_destroy (blob); + return result; +} + +static unsigned int +_get_table_tags (const hb_subset_plan_t* plan, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) +{ + unsigned num_tables = hb_face_get_table_tags (plan->source, 0, nullptr, nullptr); + if (num_tables) + return hb_face_get_table_tags (plan->source, start_offset, table_count, table_tags); + + // If face has 0 tables associated with it, assume that it was built from + // hb_face_create_tables and thus is unable to list its tables. Fallback to + // checking each table type we can handle for existence instead. + auto it = + hb_concat ( + + hb_array (known_tables) + | hb_filter ([&] (hb_tag_t tag) { + return !_table_is_empty (plan->source, tag) && !plan->no_subset_tables.has (tag); + }) + | hb_map ([] (hb_tag_t tag) -> hb_tag_t { return tag; }), + + plan->no_subset_tables.iter () + | hb_filter([&] (hb_tag_t tag) { + return !_table_is_empty (plan->source, tag); + })); + + it += start_offset; + + unsigned num_written = 0; + while (bool (it) && num_written < *table_count) + table_tags[num_written++] = *it++; + + *table_count = num_written; + return num_written; +} + + +static unsigned +_plan_estimate_subset_table_size (hb_subset_plan_t *plan, + unsigned table_len, + hb_tag_t table_tag) +{ + unsigned src_glyphs = plan->source->get_num_glyphs (); + unsigned dst_glyphs = plan->glyphset ()->get_population (); + + unsigned bulk = 8192; + /* Tables that we want to allocate same space as the source table. For GSUB/GPOS it's + * because those are expensive to subset, so giving them more room is fine. */ + bool same_size = table_tag == HB_OT_TAG_GSUB || + table_tag == HB_OT_TAG_GPOS || + table_tag == HB_OT_TAG_name; + + if (plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS) + { + if (table_tag == HB_OT_TAG_CFF1) + { + /* Add some extra room for the CFF charset. */ + bulk += src_glyphs * 16; + } + else if (table_tag == HB_OT_TAG_CFF2) + { + /* Just extra CharString offsets. */ + bulk += src_glyphs * 4; + } + } + + if (unlikely (!src_glyphs) || same_size) + return bulk + table_len; + + return bulk + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs)); +} + +/* + * Repack the serialization buffer if any offset overflows exist. + */ +static hb_blob_t* +_repack (hb_tag_t tag, const hb_serialize_context_t& c) +{ + if (!c.offset_overflow ()) + return c.copy_blob (); + + hb_blob_t* result = hb_resolve_overflows (c.object_graph (), tag); + + if (unlikely (!result)) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c offset overflow resolution failed.", + HB_UNTAG (tag)); + return nullptr; + } + + return result; +} + +template<typename TableType> +static +bool +_try_subset (const TableType *table, + hb_vector_t<char>* buf, + hb_subset_context_t* c /* OUT */) +{ + c->serializer->start_serialize (); + if (c->serializer->in_error ()) return false; + + bool needed = table->subset (c); + if (!c->serializer->ran_out_of_room ()) + { + c->serializer->end_serialize (); + return needed; + } + + unsigned buf_size = buf->allocated; + buf_size = buf_size * 2 + 16; + + + + + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", + HB_UNTAG (c->table_tag), buf_size); + + if (unlikely (buf_size > c->source_blob->length * 16 || + !buf->alloc (buf_size, true))) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", + HB_UNTAG (c->table_tag), buf_size); + return needed; + } + + c->serializer->reset (buf->arrayZ, buf->allocated); + return _try_subset (table, buf, c); +} + +template <typename T> +static auto _do_destroy (T &t, hb_priority<1>) HB_RETURN (void, t.destroy ()) + +template <typename T> +static void _do_destroy (T &t, hb_priority<0>) {} + +template<typename TableType> +static bool +_subset (hb_subset_plan_t *plan, hb_vector_t<char> &buf) +{ + auto &&source_blob = plan->source_table<TableType> (); + auto *table = source_blob.get (); + + hb_tag_t tag = TableType::tableTag; + hb_blob_t *blob = source_blob.get_blob(); + if (unlikely (!blob || !blob->data)) + { + DEBUG_MSG (SUBSET, nullptr, + "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag)); + _do_destroy (source_blob, hb_prioritize); + return false; + } + + unsigned buf_size = _plan_estimate_subset_table_size (plan, blob->length, TableType::tableTag); + DEBUG_MSG (SUBSET, nullptr, + "OT::%c%c%c%c initial estimated table size: %u bytes.", HB_UNTAG (tag), buf_size); + if (unlikely (!buf.alloc (buf_size))) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to allocate %u bytes.", HB_UNTAG (tag), buf_size); + _do_destroy (source_blob, hb_prioritize); + return false; + } + + bool needed = false; + hb_serialize_context_t serializer (buf.arrayZ, buf.allocated); + { + hb_subset_context_t c (blob, plan, &serializer, tag); + needed = _try_subset (table, &buf, &c); + } + _do_destroy (source_blob, hb_prioritize); + + if (serializer.in_error () && !serializer.only_offset_overflow ()) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset FAILED!", HB_UNTAG (tag)); + return false; + } + + if (!needed) + { + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag)); + return true; + } + + bool result = false; + hb_blob_t *dest_blob = _repack (tag, serializer); + if (dest_blob) + { + DEBUG_MSG (SUBSET, nullptr, + "OT::%c%c%c%c final subset table size: %u bytes.", + HB_UNTAG (tag), dest_blob->length); + result = plan->add_table (tag, dest_blob); + hb_blob_destroy (dest_blob); + } + + DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset %s", + HB_UNTAG (tag), result ? "success" : "FAILED!"); + return result; +} + +static bool +_is_table_present (hb_face_t *source, hb_tag_t tag) +{ + + if (!hb_face_get_table_tags (source, 0, nullptr, nullptr)) { + // If face has 0 tables associated with it, assume that it was built from + // hb_face_create_tables and thus is unable to list its tables. Fallback to + // checking if the blob associated with tag is empty. + return !_table_is_empty (source, tag); + } + + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + while (((void) hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) + if (table_tags[i] == tag) + return true; + offset += num_tables; + } + return false; +} + +static bool +_should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) +{ + if (plan->drop_tables.has (tag)) + return true; + + switch (tag) + { + case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */ + return plan->all_axes_pinned || (plan->flags & HB_SUBSET_FLAGS_NO_HINTING); + + case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */ + case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */ + case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */ + case HB_TAG ('h','d','m','x'): /* hint table, fallthrough */ + case HB_TAG ('V','D','M','X'): /* hint table, fallthrough */ + return plan->flags & HB_SUBSET_FLAGS_NO_HINTING; + +#ifdef HB_NO_SUBSET_LAYOUT + // Drop Layout Tables if requested. + case HB_OT_TAG_GDEF: + case HB_OT_TAG_GPOS: + case HB_OT_TAG_GSUB: + case HB_TAG ('m','o','r','x'): + case HB_TAG ('m','o','r','t'): + case HB_TAG ('k','e','r','x'): + case HB_TAG ('k','e','r','n'): + return true; +#endif + + case HB_TAG ('a','v','a','r'): + case HB_TAG ('f','v','a','r'): + case HB_TAG ('g','v','a','r'): + case HB_OT_TAG_HVAR: + case HB_OT_TAG_VVAR: + case HB_TAG ('M','V','A','R'): + return plan->all_axes_pinned; + + default: + return false; + } +} + +static bool +_passthrough (hb_subset_plan_t *plan, hb_tag_t tag) +{ + hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); + bool result = plan->add_table (tag, source_table); + hb_blob_destroy (source_table); + return result; +} + +static bool +_dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag, + const hb_set_t &subsetted_tags, + const hb_set_t &pending_subset_tags) +{ + switch (tag) + { + case HB_OT_TAG_hmtx: + case HB_OT_TAG_vmtx: + case HB_OT_TAG_maxp: + case HB_OT_TAG_OS2: + return !plan->normalized_coords || !pending_subset_tags.has (HB_OT_TAG_glyf); + case HB_OT_TAG_GPOS: + return plan->all_axes_pinned || !pending_subset_tags.has (HB_OT_TAG_GDEF); + default: + return true; + } +} + +static bool +_subset_table (hb_subset_plan_t *plan, + hb_vector_t<char> &buf, + hb_tag_t tag) +{ + if (plan->no_subset_tables.has (tag)) { + return _passthrough (plan, tag); + } + + DEBUG_MSG (SUBSET, nullptr, "subset %c%c%c%c", HB_UNTAG (tag)); + switch (tag) + { + case HB_OT_TAG_glyf: return _subset<const OT::glyf> (plan, buf); + case HB_OT_TAG_hdmx: return _subset<const OT::hdmx> (plan, buf); + case HB_OT_TAG_name: return _subset<const OT::name> (plan, buf); + case HB_OT_TAG_head: + if (_is_table_present (plan->source, HB_OT_TAG_glyf) && !_should_drop_table (plan, HB_OT_TAG_glyf)) + return true; /* skip head, handled by glyf */ + return _subset<const OT::head> (plan, buf); + case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */ + case HB_OT_TAG_hmtx: return _subset<const OT::hmtx> (plan, buf); + case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */ + case HB_OT_TAG_vmtx: return _subset<const OT::vmtx> (plan, buf); + case HB_OT_TAG_maxp: return _subset<const OT::maxp> (plan, buf); + case HB_OT_TAG_sbix: return _subset<const OT::sbix> (plan, buf); + case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */ + case HB_OT_TAG_cmap: return _subset<const OT::cmap> (plan, buf); + case HB_OT_TAG_OS2 : return _subset<const OT::OS2 > (plan, buf); + case HB_OT_TAG_post: return _subset<const OT::post> (plan, buf); + case HB_OT_TAG_COLR: return _subset<const OT::COLR> (plan, buf); + case HB_OT_TAG_CPAL: return _subset<const OT::CPAL> (plan, buf); + case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan, buf); + case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ + case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan, buf); + +#ifndef HB_NO_SUBSET_CFF + case HB_OT_TAG_CFF1: return _subset<const OT::cff1> (plan, buf); + case HB_OT_TAG_CFF2: return _subset<const OT::cff2> (plan, buf); + case HB_OT_TAG_VORG: return _subset<const OT::VORG> (plan, buf); +#endif + +#ifndef HB_NO_SUBSET_LAYOUT + case HB_OT_TAG_GDEF: return _subset<const OT::GDEF> (plan, buf); + case HB_OT_TAG_GSUB: return _subset<const GSUB> (plan, buf); + case HB_OT_TAG_GPOS: return _subset<const GPOS> (plan, buf); + case HB_OT_TAG_gvar: return _subset<const OT::gvar> (plan, buf); + case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan, buf); + case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan, buf); +#endif + +#ifndef HB_NO_VAR + case HB_OT_TAG_fvar: + if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); + return _subset<const OT::fvar> (plan, buf); + case HB_OT_TAG_avar: + if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); + return _subset<const OT::avar> (plan, buf); + case HB_OT_TAG_cvar: + if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); + return _subset<const OT::cvar> (plan, buf); + case HB_OT_TAG_MVAR: + if (plan->user_axes_location.is_empty ()) return _passthrough (plan, tag); + return _subset<const OT::MVAR> (plan, buf); +#endif + + case HB_OT_TAG_STAT: + if (!plan->user_axes_location.is_empty ()) return _subset<const OT::STAT> (plan, buf); + else return _passthrough (plan, tag); + + case HB_TAG ('c', 'v', 't', ' '): +#ifndef HB_NO_VAR + if (_is_table_present (plan->source, HB_OT_TAG_cvar) && + plan->normalized_coords && !plan->pinned_at_default) + { + auto &cvar = *plan->source->table.cvar; + return OT::cvar::add_cvt_and_apply_deltas (plan, cvar.get_tuple_var_data (), &cvar); + } +#endif + return _passthrough (plan, tag); + default: + if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) + return _passthrough (plan, tag); + + // Drop table + return true; + } +} + +static void _attach_accelerator_data (hb_subset_plan_t* plan, + hb_face_t* face /* IN/OUT */) +{ + if (!plan->inprogress_accelerator) return; + + // Transfer the accelerator from the plan to us. + hb_subset_accelerator_t* accel = plan->inprogress_accelerator; + plan->inprogress_accelerator = nullptr; + + if (accel->in_error ()) + { + hb_subset_accelerator_t::destroy (accel); + return; + } + + // Populate caches that need access to the final tables. + hb_blob_ptr_t<OT::cmap> cmap_ptr (hb_sanitize_context_t ().reference_table<OT::cmap> (face)); + accel->cmap_cache = OT::cmap::create_filled_cache (cmap_ptr); + accel->destroy_cmap_cache = OT::SubtableUnicodesCache::destroy; + + if (!hb_face_set_user_data(face, + hb_subset_accelerator_t::user_data_key(), + accel, + hb_subset_accelerator_t::destroy, + true)) + hb_subset_accelerator_t::destroy (accel); +} + +/** + * hb_subset_or_fail: + * @source: font face data to be subset. + * @input: input to use for the subsetting. + * + * Subsets a font according to provided input. Returns nullptr + * if the subset operation fails. + * + * Since: 2.9.0 + **/ +hb_face_t * +hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input) +{ + if (unlikely (!input || !source)) return hb_face_get_empty (); + + hb_subset_plan_t *plan = hb_subset_plan_create_or_fail (source, input); + if (unlikely (!plan)) { + return nullptr; + } + + hb_face_t * result = hb_subset_plan_execute_or_fail (plan); + hb_subset_plan_destroy (plan); + return result; +} + + +/** + * hb_subset_plan_execute_or_fail: + * @plan: a subsetting plan. + * + * Executes the provided subsetting @plan. + * + * Return value: + * on success returns a reference to generated font subset. If the subsetting operation fails + * returns nullptr. + * + * Since: 4.0.0 + **/ +hb_face_t * +hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) +{ + if (unlikely (!plan || plan->in_error ())) { + return nullptr; + } + + hb_tag_t table_tags[32]; + unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); + + hb_set_t subsetted_tags, pending_subset_tags; + while (((void) _get_table_tags (plan, offset, &num_tables, table_tags), num_tables)) + { + for (unsigned i = 0; i < num_tables; ++i) + { + hb_tag_t tag = table_tags[i]; + if (_should_drop_table (plan, tag)) continue; + pending_subset_tags.add (tag); + } + + offset += num_tables; + } + + bool success = true; + + { + // Grouping to deallocate buf before calling hb_face_reference (plan->dest). + + hb_vector_t<char> buf; + buf.alloc (8192 - 16); + + while (!pending_subset_tags.is_empty ()) + { + if (subsetted_tags.in_error () + || pending_subset_tags.in_error ()) { + success = false; + goto end; + } + + bool made_changes = false; + for (hb_tag_t tag : pending_subset_tags) + { + if (!_dependencies_satisfied (plan, tag, + subsetted_tags, + pending_subset_tags)) + { + // delayed subsetting for some tables since they might have dependency on other tables + // in some cases: e.g: during instantiating glyf tables, hmetrics/vmetrics are updated + // and saved in subset plan, hmtx/vmtx subsetting need to use these updated metrics values + continue; + } + + pending_subset_tags.del (tag); + subsetted_tags.add (tag); + made_changes = true; + + success = _subset_table (plan, buf, tag); + if (unlikely (!success)) goto end; + } + + if (!made_changes) + { + DEBUG_MSG (SUBSET, nullptr, "Table dependencies unable to be satisfied. Subset failed."); + success = false; + goto end; + } + } + } + + if (success && plan->attach_accelerator_data) { + _attach_accelerator_data (plan, plan->dest); + } + +end: + return success ? hb_face_reference (plan->dest) : nullptr; +} diff --git a/gfx/harfbuzz/src/hb-subset.h b/gfx/harfbuzz/src/hb-subset.h new file mode 100644 index 0000000000..d79e7f762a --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset.h @@ -0,0 +1,247 @@ +/* + * 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): Rod Sheeter + */ + +#ifndef HB_SUBSET_H +#define HB_SUBSET_H + +#include "hb.h" +#include "hb-ot.h" + +HB_BEGIN_DECLS + +/** + * hb_subset_input_t: + * + * Things that change based on the input. Characters to keep, etc. + */ + +typedef struct hb_subset_input_t hb_subset_input_t; + +/** + * hb_subset_plan_t: + * + * Contains information about how the subset operation will be executed. + * Such as mappings from the old glyph ids to the new ones in the subset. + */ + +typedef struct hb_subset_plan_t hb_subset_plan_t; + +/** + * hb_subset_flags_t: + * @HB_SUBSET_FLAGS_DEFAULT: all flags at their default value of false. + * @HB_SUBSET_FLAGS_NO_HINTING: If set hinting instructions will be dropped in + * the produced subset. Otherwise hinting instructions will be retained. + * @HB_SUBSET_FLAGS_RETAIN_GIDS: If set glyph indices will not be modified in + * the produced subset. If glyphs are dropped their indices will be retained + * as an empty glyph. + * @HB_SUBSET_FLAGS_DESUBROUTINIZE: If set and subsetting a CFF font the + * subsetter will attempt to remove subroutines from the CFF glyphs. + * @HB_SUBSET_FLAGS_NAME_LEGACY: If set non-unicode name records will be + * retained in the subset. + * @HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG: If set the subsetter will set the + * OVERLAP_SIMPLE flag on each simple glyph. + * @HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED: If set the subsetter will not + * drop unrecognized tables and instead pass them through untouched. + * @HB_SUBSET_FLAGS_NOTDEF_OUTLINE: If set the notdef glyph outline will be + * retained in the final subset. + * @HB_SUBSET_FLAGS_GLYPH_NAMES: If set the PS glyph names will be retained + * in the final subset. + * @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in + * OS/2 will not be recalculated. + * @HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE: If set don't perform glyph closure on layout + * substitution rules (GSUB). Since: 7.2.0. + * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset + * to allow it to be used with incremental font transfer IFTB patches. Primarily, + * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL + * + * List of boolean properties that can be configured on the subset input. + * + * Since: 2.9.0 + **/ +typedef enum { /*< flags >*/ + HB_SUBSET_FLAGS_DEFAULT = 0x00000000u, + HB_SUBSET_FLAGS_NO_HINTING = 0x00000001u, + HB_SUBSET_FLAGS_RETAIN_GIDS = 0x00000002u, + HB_SUBSET_FLAGS_DESUBROUTINIZE = 0x00000004u, + HB_SUBSET_FLAGS_NAME_LEGACY = 0x00000008u, + HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG = 0x00000010u, + HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED = 0x00000020u, + HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u, + HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u, + HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u, + HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u, +#ifdef HB_EXPERIMENTAL_API + HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00000400u, +#endif +} hb_subset_flags_t; + +/** + * hb_subset_sets_t: + * @HB_SUBSET_SETS_GLYPH_INDEX: the set of glyph indexes to retain in the subset. + * @HB_SUBSET_SETS_UNICODE: the set of unicode codepoints to retain in the subset. + * @HB_SUBSET_SETS_NO_SUBSET_TABLE_TAG: the set of table tags which specifies tables that should not be + * subsetted. + * @HB_SUBSET_SETS_DROP_TABLE_TAG: the set of table tags which specifies tables which will be dropped + * in the subset. + * @HB_SUBSET_SETS_NAME_ID: the set of name ids that will be retained. + * @HB_SUBSET_SETS_NAME_LANG_ID: the set of name lang ids that will be retained. + * @HB_SUBSET_SETS_LAYOUT_FEATURE_TAG: the set of layout feature tags that will be retained + * in the subset. + * @HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG: the set of layout script tags that will be retained + * in the subset. Defaults to all tags. Since: 5.0.0 + * + * List of sets that can be configured on the subset input. + * + * Since: 2.9.1 + **/ +typedef enum { + HB_SUBSET_SETS_GLYPH_INDEX = 0, + HB_SUBSET_SETS_UNICODE, + HB_SUBSET_SETS_NO_SUBSET_TABLE_TAG, + HB_SUBSET_SETS_DROP_TABLE_TAG, + HB_SUBSET_SETS_NAME_ID, + HB_SUBSET_SETS_NAME_LANG_ID, + HB_SUBSET_SETS_LAYOUT_FEATURE_TAG, + HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG, +} hb_subset_sets_t; + +HB_EXTERN hb_subset_input_t * +hb_subset_input_create_or_fail (void); + +HB_EXTERN hb_subset_input_t * +hb_subset_input_reference (hb_subset_input_t *input); + +HB_EXTERN void +hb_subset_input_destroy (hb_subset_input_t *input); + +HB_EXTERN hb_bool_t +hb_subset_input_set_user_data (hb_subset_input_t *input, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_subset_input_get_user_data (const hb_subset_input_t *input, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_subset_input_keep_everything (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_unicode_set (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_glyph_set (hb_subset_input_t *input); + +HB_EXTERN hb_set_t * +hb_subset_input_set (hb_subset_input_t *input, hb_subset_sets_t set_type); + +HB_EXTERN hb_map_t* +hb_subset_input_old_to_new_glyph_mapping (hb_subset_input_t *input); + +HB_EXTERN hb_subset_flags_t +hb_subset_input_get_flags (hb_subset_input_t *input); + +HB_EXTERN void +hb_subset_input_set_flags (hb_subset_input_t *input, + unsigned value); + +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag); + +HB_EXTERN hb_bool_t +hb_subset_input_pin_axis_location (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag, + float axis_value); + +#ifdef HB_EXPERIMENTAL_API +HB_EXTERN hb_bool_t +hb_subset_input_set_axis_range (hb_subset_input_t *input, + hb_face_t *face, + hb_tag_t axis_tag, + float axis_min_value, + float axis_max_value, + float *axis_def_value); + +HB_EXTERN hb_bool_t +hb_subset_input_override_name_table (hb_subset_input_t *input, + hb_ot_name_id_t name_id, + unsigned platform_id, + unsigned encoding_id, + unsigned language_id, + const char *name_str, + int str_len); + +#endif + +HB_EXTERN hb_face_t * +hb_subset_preprocess (hb_face_t *source); + +HB_EXTERN hb_face_t * +hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input); + +HB_EXTERN hb_face_t * +hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan); + +HB_EXTERN hb_subset_plan_t * +hb_subset_plan_create_or_fail (hb_face_t *face, + const hb_subset_input_t *input); + +HB_EXTERN void +hb_subset_plan_destroy (hb_subset_plan_t *plan); + +HB_EXTERN hb_map_t * +hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan); + +HB_EXTERN hb_map_t * +hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan); + +HB_EXTERN hb_map_t * +hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan); + + +HB_EXTERN hb_subset_plan_t * +hb_subset_plan_reference (hb_subset_plan_t *plan); + +HB_EXTERN hb_bool_t +hb_subset_plan_set_user_data (hb_subset_plan_t *plan, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_subset_plan_get_user_data (const hb_subset_plan_t *plan, + hb_user_data_key_t *key); + + +HB_END_DECLS + +#endif /* HB_SUBSET_H */ diff --git a/gfx/harfbuzz/src/hb-subset.hh b/gfx/harfbuzz/src/hb-subset.hh new file mode 100644 index 0000000000..4f192aae40 --- /dev/null +++ b/gfx/harfbuzz/src/hb-subset.hh @@ -0,0 +1,74 @@ +/* + * 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, Roderick Sheeter + */ + +#ifndef HB_SUBSET_HH +#define HB_SUBSET_HH + + +#include "hb.hh" + +#include "hb-subset.h" + +#include "hb-machinery.hh" +#include "hb-serialize.hh" +#include "hb-subset-input.hh" +#include "hb-subset-plan.hh" + +struct hb_subset_context_t : + hb_dispatch_context_t<hb_subset_context_t, bool, HB_DEBUG_SUBSET> +{ + const char *get_name () { return "SUBSET"; } + static return_t default_return_value () { return true; } + + private: + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN + ( obj.subset (this, std::forward<Ts> (ds)...) ) + template <typename T, typename ...Ts> auto + _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN + ( obj.dispatch (this, std::forward<Ts> (ds)...) ) + public: + template <typename T, typename ...Ts> auto + dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN + ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) ) + + hb_blob_t *source_blob; + hb_subset_plan_t *plan; + hb_serialize_context_t *serializer; + hb_tag_t table_tag; + + hb_subset_context_t (hb_blob_t *source_blob_, + hb_subset_plan_t *plan_, + hb_serialize_context_t *serializer_, + hb_tag_t table_tag_) : + source_blob (source_blob_), + plan (plan_), + serializer (serializer_), + table_tag (table_tag_) {} +}; + + +#endif /* HB_SUBSET_HH */ diff --git a/gfx/harfbuzz/src/hb-ucd-table.hh b/gfx/harfbuzz/src/hb-ucd-table.hh new file mode 100644 index 0000000000..8d3807a80f --- /dev/null +++ b/gfx/harfbuzz/src/hb-ucd-table.hh @@ -0,0 +1,5637 @@ +/* == Start of generated table == */ +/* + * The following table is generated by running: + * + * ./gen-ucd-table.py ucd.nounihan.grouped.xml + * + * on file with this description: Unicode 15.1.0 + */ + +#ifndef HB_UCD_TABLE_HH +#define HB_UCD_TABLE_HH + +#include "hb.hh" + +static const hb_script_t +_hb_ucd_sc_map[165] = +{ + HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, + HB_SCRIPT_UNKNOWN, HB_SCRIPT_ARABIC, + HB_SCRIPT_ARMENIAN, HB_SCRIPT_BENGALI, + HB_SCRIPT_CYRILLIC, HB_SCRIPT_DEVANAGARI, + HB_SCRIPT_GEORGIAN, HB_SCRIPT_GREEK, + HB_SCRIPT_GUJARATI, HB_SCRIPT_GURMUKHI, + HB_SCRIPT_HANGUL, HB_SCRIPT_HAN, + HB_SCRIPT_HEBREW, HB_SCRIPT_HIRAGANA, + HB_SCRIPT_KANNADA, HB_SCRIPT_KATAKANA, + HB_SCRIPT_LAO, HB_SCRIPT_LATIN, + HB_SCRIPT_MALAYALAM, HB_SCRIPT_ORIYA, + HB_SCRIPT_TAMIL, HB_SCRIPT_TELUGU, + HB_SCRIPT_THAI, HB_SCRIPT_TIBETAN, + HB_SCRIPT_BOPOMOFO, HB_SCRIPT_BRAILLE, + HB_SCRIPT_CANADIAN_SYLLABICS, HB_SCRIPT_CHEROKEE, + HB_SCRIPT_ETHIOPIC, HB_SCRIPT_KHMER, + HB_SCRIPT_MONGOLIAN, HB_SCRIPT_MYANMAR, + HB_SCRIPT_OGHAM, HB_SCRIPT_RUNIC, + HB_SCRIPT_SINHALA, HB_SCRIPT_SYRIAC, + HB_SCRIPT_THAANA, HB_SCRIPT_YI, + HB_SCRIPT_DESERET, HB_SCRIPT_GOTHIC, + HB_SCRIPT_OLD_ITALIC, HB_SCRIPT_BUHID, + HB_SCRIPT_HANUNOO, HB_SCRIPT_TAGALOG, + HB_SCRIPT_TAGBANWA, HB_SCRIPT_CYPRIOT, + HB_SCRIPT_LIMBU, HB_SCRIPT_LINEAR_B, + HB_SCRIPT_OSMANYA, HB_SCRIPT_SHAVIAN, + HB_SCRIPT_TAI_LE, HB_SCRIPT_UGARITIC, + HB_SCRIPT_BUGINESE, HB_SCRIPT_COPTIC, + HB_SCRIPT_GLAGOLITIC, HB_SCRIPT_KHAROSHTHI, + HB_SCRIPT_NEW_TAI_LUE, HB_SCRIPT_OLD_PERSIAN, + HB_SCRIPT_SYLOTI_NAGRI, HB_SCRIPT_TIFINAGH, + HB_SCRIPT_BALINESE, HB_SCRIPT_CUNEIFORM, + HB_SCRIPT_NKO, HB_SCRIPT_PHAGS_PA, + HB_SCRIPT_PHOENICIAN, HB_SCRIPT_CARIAN, + HB_SCRIPT_CHAM, HB_SCRIPT_KAYAH_LI, + HB_SCRIPT_LEPCHA, HB_SCRIPT_LYCIAN, + HB_SCRIPT_LYDIAN, HB_SCRIPT_OL_CHIKI, + HB_SCRIPT_REJANG, HB_SCRIPT_SAURASHTRA, + HB_SCRIPT_SUNDANESE, HB_SCRIPT_VAI, + HB_SCRIPT_AVESTAN, HB_SCRIPT_BAMUM, + HB_SCRIPT_EGYPTIAN_HIEROGLYPHS, HB_SCRIPT_IMPERIAL_ARAMAIC, + HB_SCRIPT_INSCRIPTIONAL_PAHLAVI, HB_SCRIPT_INSCRIPTIONAL_PARTHIAN, + HB_SCRIPT_JAVANESE, HB_SCRIPT_KAITHI, + HB_SCRIPT_LISU, HB_SCRIPT_MEETEI_MAYEK, + HB_SCRIPT_OLD_SOUTH_ARABIAN, HB_SCRIPT_OLD_TURKIC, + HB_SCRIPT_SAMARITAN, HB_SCRIPT_TAI_THAM, + HB_SCRIPT_TAI_VIET, HB_SCRIPT_BATAK, + HB_SCRIPT_BRAHMI, HB_SCRIPT_MANDAIC, + HB_SCRIPT_CHAKMA, HB_SCRIPT_MEROITIC_CURSIVE, + HB_SCRIPT_MEROITIC_HIEROGLYPHS, HB_SCRIPT_MIAO, + HB_SCRIPT_SHARADA, HB_SCRIPT_SORA_SOMPENG, + HB_SCRIPT_TAKRI, HB_SCRIPT_BASSA_VAH, + HB_SCRIPT_CAUCASIAN_ALBANIAN, HB_SCRIPT_DUPLOYAN, + HB_SCRIPT_ELBASAN, HB_SCRIPT_GRANTHA, + HB_SCRIPT_KHOJKI, HB_SCRIPT_KHUDAWADI, + HB_SCRIPT_LINEAR_A, HB_SCRIPT_MAHAJANI, + HB_SCRIPT_MANICHAEAN, HB_SCRIPT_MENDE_KIKAKUI, + HB_SCRIPT_MODI, HB_SCRIPT_MRO, + HB_SCRIPT_NABATAEAN, HB_SCRIPT_OLD_NORTH_ARABIAN, + HB_SCRIPT_OLD_PERMIC, HB_SCRIPT_PAHAWH_HMONG, + HB_SCRIPT_PALMYRENE, HB_SCRIPT_PAU_CIN_HAU, + HB_SCRIPT_PSALTER_PAHLAVI, HB_SCRIPT_SIDDHAM, + HB_SCRIPT_TIRHUTA, HB_SCRIPT_WARANG_CITI, + HB_SCRIPT_AHOM, HB_SCRIPT_ANATOLIAN_HIEROGLYPHS, + HB_SCRIPT_HATRAN, HB_SCRIPT_MULTANI, + HB_SCRIPT_OLD_HUNGARIAN, HB_SCRIPT_SIGNWRITING, + HB_SCRIPT_ADLAM, HB_SCRIPT_BHAIKSUKI, + HB_SCRIPT_MARCHEN, HB_SCRIPT_OSAGE, + HB_SCRIPT_TANGUT, HB_SCRIPT_NEWA, + HB_SCRIPT_MASARAM_GONDI, HB_SCRIPT_NUSHU, + HB_SCRIPT_SOYOMBO, HB_SCRIPT_ZANABAZAR_SQUARE, + HB_SCRIPT_DOGRA, HB_SCRIPT_GUNJALA_GONDI, + HB_SCRIPT_HANIFI_ROHINGYA, HB_SCRIPT_MAKASAR, + HB_SCRIPT_MEDEFAIDRIN, HB_SCRIPT_OLD_SOGDIAN, + HB_SCRIPT_SOGDIAN, HB_SCRIPT_ELYMAIC, + HB_SCRIPT_NANDINAGARI, HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, + HB_SCRIPT_WANCHO, HB_SCRIPT_CHORASMIAN, + HB_SCRIPT_DIVES_AKURU, HB_SCRIPT_KHITAN_SMALL_SCRIPT, + HB_SCRIPT_YEZIDI, HB_SCRIPT_CYPRO_MINOAN, + HB_SCRIPT_OLD_UYGHUR, HB_SCRIPT_TANGSA, + HB_SCRIPT_TOTO, HB_SCRIPT_VITHKUQI, + HB_SCRIPT_MATH, HB_SCRIPT_KAWI, + HB_SCRIPT_NAG_MUNDARI, +}; +static const uint16_t +_hb_ucd_dm1_p0_map[825] = +{ + 0x003Bu, 0x004Bu, 0x0060u, 0x00B4u, 0x00B7u, 0x00C5u, 0x02B9u, 0x0300u, + 0x0301u, 0x0313u, 0x0385u, 0x0386u, 0x0388u, 0x0389u, 0x038Au, 0x038Cu, + 0x038Eu, 0x038Fu, 0x0390u, 0x03A9u, 0x03ACu, 0x03ADu, 0x03AEu, 0x03AFu, + 0x03B0u, 0x03B9u, 0x03CCu, 0x03CDu, 0x03CEu, 0x2002u, 0x2003u, 0x3008u, + 0x3009u, 0x349Eu, 0x34B9u, 0x34BBu, 0x34DFu, 0x3515u, 0x36EEu, 0x36FCu, + 0x3781u, 0x382Fu, 0x3862u, 0x387Cu, 0x38C7u, 0x38E3u, 0x391Cu, 0x393Au, + 0x3A2Eu, 0x3A6Cu, 0x3AE4u, 0x3B08u, 0x3B19u, 0x3B49u, 0x3B9Du, 0x3C18u, + 0x3C4Eu, 0x3D33u, 0x3D96u, 0x3EACu, 0x3EB8u, 0x3F1Bu, 0x3FFCu, 0x4008u, + 0x4018u, 0x4039u, 0x4046u, 0x4096u, 0x40E3u, 0x412Fu, 0x4202u, 0x4227u, + 0x42A0u, 0x4301u, 0x4334u, 0x4359u, 0x43D5u, 0x43D9u, 0x440Bu, 0x446Bu, + 0x452Bu, 0x455Du, 0x4561u, 0x456Bu, 0x45D7u, 0x45F9u, 0x4635u, 0x46BEu, + 0x46C7u, 0x4995u, 0x49E6u, 0x4A6Eu, 0x4A76u, 0x4AB2u, 0x4B33u, 0x4BCEu, + 0x4CCEu, 0x4CEDu, 0x4CF8u, 0x4D56u, 0x4E0Du, 0x4E26u, 0x4E32u, 0x4E38u, + 0x4E39u, 0x4E3Du, 0x4E41u, 0x4E82u, 0x4E86u, 0x4EAEu, 0x4EC0u, 0x4ECCu, + 0x4EE4u, 0x4F60u, 0x4F80u, 0x4F86u, 0x4F8Bu, 0x4FAEu, 0x4FBBu, 0x4FBFu, + 0x5002u, 0x502Bu, 0x507Au, 0x5099u, 0x50CFu, 0x50DAu, 0x50E7u, 0x5140u, + 0x5145u, 0x514Du, 0x5154u, 0x5164u, 0x5167u, 0x5168u, 0x5169u, 0x516Du, + 0x5177u, 0x5180u, 0x518Du, 0x5192u, 0x5195u, 0x5197u, 0x51A4u, 0x51ACu, + 0x51B5u, 0x51B7u, 0x51C9u, 0x51CCu, 0x51DCu, 0x51DEu, 0x51F5u, 0x5203u, + 0x5207u, 0x5217u, 0x5229u, 0x523Au, 0x523Bu, 0x5246u, 0x5272u, 0x5277u, + 0x5289u, 0x529Bu, 0x52A3u, 0x52B3u, 0x52C7u, 0x52C9u, 0x52D2u, 0x52DEu, + 0x52E4u, 0x52F5u, 0x52FAu, 0x5305u, 0x5306u, 0x5317u, 0x533Fu, 0x5349u, + 0x5351u, 0x535Au, 0x5373u, 0x5375u, 0x537Du, 0x537Fu, 0x53C3u, 0x53CAu, + 0x53DFu, 0x53E5u, 0x53EBu, 0x53F1u, 0x5406u, 0x540Fu, 0x541Du, 0x5438u, + 0x5442u, 0x5448u, 0x5468u, 0x549Eu, 0x54A2u, 0x54BDu, 0x54F6u, 0x5510u, + 0x5553u, 0x5555u, 0x5563u, 0x5584u, 0x5587u, 0x5599u, 0x559Du, 0x55ABu, + 0x55B3u, 0x55C0u, 0x55C2u, 0x55E2u, 0x5606u, 0x5651u, 0x5668u, 0x5674u, + 0x56F9u, 0x5716u, 0x5717u, 0x578Bu, 0x57CEu, 0x57F4u, 0x580Du, 0x5831u, + 0x5832u, 0x5840u, 0x585Au, 0x585Eu, 0x58A8u, 0x58ACu, 0x58B3u, 0x58D8u, + 0x58DFu, 0x58EEu, 0x58F2u, 0x58F7u, 0x5906u, 0x591Au, 0x5922u, 0x5944u, + 0x5948u, 0x5951u, 0x5954u, 0x5962u, 0x5973u, 0x59D8u, 0x59ECu, 0x5A1Bu, + 0x5A27u, 0x5A62u, 0x5A66u, 0x5AB5u, 0x5B08u, 0x5B28u, 0x5B3Eu, 0x5B85u, + 0x5BC3u, 0x5BD8u, 0x5BE7u, 0x5BEEu, 0x5BF3u, 0x5BFFu, 0x5C06u, 0x5C22u, + 0x5C3Fu, 0x5C60u, 0x5C62u, 0x5C64u, 0x5C65u, 0x5C6Eu, 0x5C8Du, 0x5CC0u, + 0x5D19u, 0x5D43u, 0x5D50u, 0x5D6Bu, 0x5D6Eu, 0x5D7Cu, 0x5DB2u, 0x5DBAu, + 0x5DE1u, 0x5DE2u, 0x5DFDu, 0x5E28u, 0x5E3Du, 0x5E69u, 0x5E74u, 0x5EA6u, + 0x5EB0u, 0x5EB3u, 0x5EB6u, 0x5EC9u, 0x5ECAu, 0x5ED2u, 0x5ED3u, 0x5ED9u, + 0x5EECu, 0x5EFEu, 0x5F04u, 0x5F22u, 0x5F53u, 0x5F62u, 0x5F69u, 0x5F6Bu, + 0x5F8Bu, 0x5F9Au, 0x5FA9u, 0x5FADu, 0x5FCDu, 0x5FD7u, 0x5FF5u, 0x5FF9u, + 0x6012u, 0x601Cu, 0x6075u, 0x6081u, 0x6094u, 0x60C7u, 0x60D8u, 0x60E1u, + 0x6108u, 0x6144u, 0x6148u, 0x614Cu, 0x614Eu, 0x6160u, 0x6168u, 0x617Au, + 0x618Eu, 0x6190u, 0x61A4u, 0x61AFu, 0x61B2u, 0x61DEu, 0x61F2u, 0x61F6u, + 0x6200u, 0x6210u, 0x621Bu, 0x622Eu, 0x6234u, 0x625Du, 0x62B1u, 0x62C9u, + 0x62CFu, 0x62D3u, 0x62D4u, 0x62FCu, 0x62FEu, 0x633Du, 0x6350u, 0x6368u, + 0x637Bu, 0x6383u, 0x63A0u, 0x63A9u, 0x63C4u, 0x63C5u, 0x63E4u, 0x641Cu, + 0x6422u, 0x6452u, 0x6469u, 0x6477u, 0x647Eu, 0x649Au, 0x649Du, 0x64C4u, + 0x654Fu, 0x6556u, 0x656Cu, 0x6578u, 0x6599u, 0x65C5u, 0x65E2u, 0x65E3u, + 0x6613u, 0x6649u, 0x6674u, 0x6688u, 0x6691u, 0x669Cu, 0x66B4u, 0x66C6u, + 0x66F4u, 0x66F8u, 0x6700u, 0x6717u, 0x671Bu, 0x6721u, 0x674Eu, 0x6753u, + 0x6756u, 0x675Eu, 0x677Bu, 0x6785u, 0x6797u, 0x67F3u, 0x67FAu, 0x6817u, + 0x681Fu, 0x6852u, 0x6881u, 0x6885u, 0x688Eu, 0x68A8u, 0x6914u, 0x6942u, + 0x69A3u, 0x69EAu, 0x6A02u, 0x6A13u, 0x6AA8u, 0x6AD3u, 0x6ADBu, 0x6B04u, + 0x6B21u, 0x6B54u, 0x6B72u, 0x6B77u, 0x6B79u, 0x6B9Fu, 0x6BAEu, 0x6BBAu, + 0x6BBBu, 0x6C4Eu, 0x6C67u, 0x6C88u, 0x6CBFu, 0x6CCCu, 0x6CCDu, 0x6CE5u, + 0x6D16u, 0x6D1Bu, 0x6D1Eu, 0x6D34u, 0x6D3Eu, 0x6D41u, 0x6D69u, 0x6D6Au, + 0x6D77u, 0x6D78u, 0x6D85u, 0x6DCBu, 0x6DDAu, 0x6DEAu, 0x6DF9u, 0x6E1Au, + 0x6E2Fu, 0x6E6Eu, 0x6E9Cu, 0x6EBAu, 0x6EC7u, 0x6ECBu, 0x6ED1u, 0x6EDBu, + 0x6F0Fu, 0x6F22u, 0x6F23u, 0x6F6Eu, 0x6FC6u, 0x6FEBu, 0x6FFEu, 0x701Bu, + 0x701Eu, 0x7039u, 0x704Au, 0x7070u, 0x7077u, 0x707Du, 0x7099u, 0x70ADu, + 0x70C8u, 0x70D9u, 0x7145u, 0x7149u, 0x716Eu, 0x719Cu, 0x71CEu, 0x71D0u, + 0x7210u, 0x721Bu, 0x7228u, 0x722Bu, 0x7235u, 0x7250u, 0x7262u, 0x7280u, + 0x7295u, 0x72AFu, 0x72C0u, 0x72FCu, 0x732Au, 0x7375u, 0x737Au, 0x7387u, + 0x738Bu, 0x73A5u, 0x73B2u, 0x73DEu, 0x7406u, 0x7409u, 0x7422u, 0x7447u, + 0x745Cu, 0x7469u, 0x7471u, 0x7485u, 0x7489u, 0x7498u, 0x74CAu, 0x7506u, + 0x7524u, 0x753Bu, 0x753Eu, 0x7559u, 0x7565u, 0x7570u, 0x75E2u, 0x7610u, + 0x761Du, 0x761Fu, 0x7642u, 0x7669u, 0x76CAu, 0x76DBu, 0x76E7u, 0x76F4u, + 0x7701u, 0x771Eu, 0x771Fu, 0x7740u, 0x774Au, 0x778Bu, 0x77A7u, 0x784Eu, + 0x786Bu, 0x788Cu, 0x7891u, 0x78CAu, 0x78CCu, 0x78FBu, 0x792Au, 0x793Cu, + 0x793Eu, 0x7948u, 0x7949u, 0x7950u, 0x7956u, 0x795Du, 0x795Eu, 0x7965u, + 0x797Fu, 0x798Du, 0x798Eu, 0x798Fu, 0x79AEu, 0x79CAu, 0x79EBu, 0x7A1Cu, + 0x7A40u, 0x7A4Au, 0x7A4Fu, 0x7A81u, 0x7AB1u, 0x7ACBu, 0x7AEEu, 0x7B20u, + 0x7BC0u, 0x7BC6u, 0x7BC9u, 0x7C3Eu, 0x7C60u, 0x7C7Bu, 0x7C92u, 0x7CBEu, + 0x7CD2u, 0x7CD6u, 0x7CE3u, 0x7CE7u, 0x7CE8u, 0x7D00u, 0x7D10u, 0x7D22u, + 0x7D2Fu, 0x7D5Bu, 0x7D63u, 0x7DA0u, 0x7DBEu, 0x7DC7u, 0x7DF4u, 0x7E02u, + 0x7E09u, 0x7E37u, 0x7E41u, 0x7E45u, 0x7F3Eu, 0x7F72u, 0x7F79u, 0x7F7Au, + 0x7F85u, 0x7F95u, 0x7F9Au, 0x7FBDu, 0x7FFAu, 0x8001u, 0x8005u, 0x8046u, + 0x8060u, 0x806Fu, 0x8070u, 0x807Eu, 0x808Bu, 0x80ADu, 0x80B2u, 0x8103u, + 0x813Eu, 0x81D8u, 0x81E8u, 0x81EDu, 0x8201u, 0x8204u, 0x8218u, 0x826Fu, + 0x8279u, 0x828Bu, 0x8291u, 0x829Du, 0x82B1u, 0x82B3u, 0x82BDu, 0x82E5u, + 0x82E6u, 0x831Du, 0x8323u, 0x8336u, 0x8352u, 0x8353u, 0x8363u, 0x83ADu, + 0x83BDu, 0x83C9u, 0x83CAu, 0x83CCu, 0x83DCu, 0x83E7u, 0x83EFu, 0x83F1u, + 0x843Du, 0x8449u, 0x8457u, 0x84EEu, 0x84F1u, 0x84F3u, 0x84FCu, 0x8516u, + 0x8564u, 0x85CDu, 0x85FAu, 0x8606u, 0x8612u, 0x862Du, 0x863Fu, 0x8650u, + 0x865Cu, 0x8667u, 0x8669u, 0x8688u, 0x86A9u, 0x86E2u, 0x870Eu, 0x8728u, + 0x876Bu, 0x8779u, 0x8786u, 0x87BAu, 0x87E1u, 0x8801u, 0x881Fu, 0x884Cu, + 0x8860u, 0x8863u, 0x88C2u, 0x88CFu, 0x88D7u, 0x88DEu, 0x88E1u, 0x88F8u, + 0x88FAu, 0x8910u, 0x8941u, 0x8964u, 0x8986u, 0x898Bu, 0x8996u, 0x8AA0u, + 0x8AAAu, 0x8ABFu, 0x8ACBu, 0x8AD2u, 0x8AD6u, 0x8AEDu, 0x8AF8u, 0x8AFEu, + 0x8B01u, 0x8B39u, 0x8B58u, 0x8B80u, 0x8B8Au, 0x8C48u, 0x8C55u, 0x8CABu, + 0x8CC1u, 0x8CC2u, 0x8CC8u, 0x8CD3u, 0x8D08u, 0x8D1Bu, 0x8D77u, 0x8DBCu, + 0x8DCBu, 0x8DEFu, 0x8DF0u, 0x8ECAu, 0x8ED4u, 0x8F26u, 0x8F2Au, 0x8F38u, + 0x8F3Bu, 0x8F62u, 0x8F9Eu, 0x8FB0u, 0x8FB6u, 0x9023u, 0x9038u, 0x9072u, + 0x907Cu, 0x908Fu, 0x9094u, 0x90CEu, 0x90DEu, 0x90F1u, 0x90FDu, 0x9111u, + 0x911Bu, 0x916Au, 0x9199u, 0x91B4u, 0x91CCu, 0x91CFu, 0x91D1u, 0x9234u, + 0x9238u, 0x9276u, 0x927Cu, 0x92D7u, 0x92D8u, 0x9304u, 0x934Au, 0x93F9u, + 0x9415u, 0x958Bu, 0x95ADu, 0x95B7u, 0x962Eu, 0x964Bu, 0x964Du, 0x9675u, + 0x9678u, 0x967Cu, 0x9686u, 0x96A3u, 0x96B7u, 0x96B8u, 0x96C3u, 0x96E2u, + 0x96E3u, 0x96F6u, 0x96F7u, 0x9723u, 0x9732u, 0x9748u, 0x9756u, 0x97DBu, + 0x97E0u, 0x97FFu, 0x980Bu, 0x9818u, 0x9829u, 0x983Bu, 0x985Eu, 0x98E2u, + 0x98EFu, 0x98FCu, 0x9928u, 0x9929u, 0x99A7u, 0x99C2u, 0x99F1u, 0x99FEu, + 0x9A6Au, 0x9B12u, 0x9B6Fu, 0x9C40u, 0x9C57u, 0x9CFDu, 0x9D67u, 0x9DB4u, + 0x9DFAu, 0x9E1Eu, 0x9E7Fu, 0x9E97u, 0x9E9Fu, 0x9EBBu, 0x9ECEu, 0x9EF9u, + 0x9EFEu, 0x9F05u, 0x9F0Fu, 0x9F16u, 0x9F3Bu, 0x9F43u, 0x9F8Du, 0x9F8Eu, + 0x9F9Cu, +}; +static const uint16_t +_hb_ucd_dm1_p2_map[110] = +{ + 0x0122u, 0x051Cu, 0x0525u, 0x054Bu, 0x063Au, 0x0804u, 0x08DEu, 0x0A2Cu, + 0x0B63u, 0x14E4u, 0x16A8u, 0x16EAu, 0x19C8u, 0x1B18u, 0x1D0Bu, 0x1DE4u, + 0x1DE6u, 0x2183u, 0x219Fu, 0x2331u, 0x26D4u, 0x2844u, 0x284Au, 0x2B0Cu, + 0x2BF1u, 0x300Au, 0x32B8u, 0x335Fu, 0x3393u, 0x339Cu, 0x33C3u, 0x33D5u, + 0x346Du, 0x36A3u, 0x38A7u, 0x3A8Du, 0x3AFAu, 0x3CBCu, 0x3D1Eu, 0x3ED1u, + 0x3F5Eu, 0x3F8Eu, 0x4263u, 0x42EEu, 0x43ABu, 0x4608u, 0x4735u, 0x4814u, + 0x4C36u, 0x4C92u, 0x4FA1u, 0x4FB8u, 0x5044u, 0x50F2u, 0x50F3u, 0x5119u, + 0x5133u, 0x5249u, 0x541Du, 0x5626u, 0x569Au, 0x56C5u, 0x597Cu, 0x5AA7u, + 0x5BABu, 0x5C80u, 0x5CD0u, 0x5F86u, 0x61DAu, 0x6228u, 0x6247u, 0x62D9u, + 0x633Eu, 0x64DAu, 0x6523u, 0x65A8u, 0x67A7u, 0x67B5u, 0x6B3Cu, 0x6C36u, + 0x6CD5u, 0x6D6Bu, 0x6F2Cu, 0x6FB1u, 0x70D2u, 0x73CAu, 0x7667u, 0x78AEu, + 0x7966u, 0x7CA8u, 0x7ED3u, 0x7F2Fu, 0x85D2u, 0x85EDu, 0x872Eu, 0x8BFAu, + 0x8D77u, 0x9145u, 0x91DFu, 0x921Au, 0x940Au, 0x9496u, 0x95B6u, 0x9B30u, + 0xA0CEu, 0xA105u, 0xA20Eu, 0xA291u, 0xA392u, 0xA600u, +}; +static const uint32_t +_hb_ucd_dm2_u32_map[638] = +{ + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Cu, 0x0338u, 0x226Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Du, 0x0338u, 0x2260u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x003Eu, 0x0338u, 0x226Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0300u, 0x00C0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0301u, 0x00C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0302u, 0x00C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0303u, 0x00C3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0304u, 0x0100u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0306u, 0x0102u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0307u, 0x0226u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0308u, 0x00C4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0309u, 0x1EA2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Au, 0x00C5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Cu, 0x01CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x030Fu, 0x0200u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0311u, 0x0202u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0323u, 0x1EA0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0325u, 0x1E00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0041u, 0x0328u, 0x0104u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0307u, 0x1E02u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0323u, 0x1E04u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0042u, 0x0331u, 0x1E06u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0301u, 0x0106u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0302u, 0x0108u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0307u, 0x010Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x030Cu, 0x010Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0043u, 0x0327u, 0x00C7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0307u, 0x1E0Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x030Cu, 0x010Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0323u, 0x1E0Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0327u, 0x1E10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x032Du, 0x1E12u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0044u, 0x0331u, 0x1E0Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0300u, 0x00C8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0301u, 0x00C9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0302u, 0x00CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0303u, 0x1EBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0304u, 0x0112u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0306u, 0x0114u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0307u, 0x0116u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0308u, 0x00CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0309u, 0x1EBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Cu, 0x011Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x030Fu, 0x0204u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0311u, 0x0206u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0323u, 0x1EB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0327u, 0x0228u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0328u, 0x0118u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x032Du, 0x1E18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0045u, 0x0330u, 0x1E1Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0046u, 0x0307u, 0x1E1Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0301u, 0x01F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0302u, 0x011Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0304u, 0x1E20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0306u, 0x011Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0307u, 0x0120u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x030Cu, 0x01E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0047u, 0x0327u, 0x0122u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0302u, 0x0124u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0307u, 0x1E22u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0308u, 0x1E26u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x030Cu, 0x021Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0323u, 0x1E24u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x0327u, 0x1E28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0048u, 0x032Eu, 0x1E2Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0300u, 0x00CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0301u, 0x00CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0302u, 0x00CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0303u, 0x0128u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0304u, 0x012Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0306u, 0x012Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0307u, 0x0130u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0308u, 0x00CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0309u, 0x1EC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Cu, 0x01CFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x030Fu, 0x0208u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0311u, 0x020Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0323u, 0x1ECAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0328u, 0x012Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0049u, 0x0330u, 0x1E2Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Au, 0x0302u, 0x0134u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0301u, 0x1E30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x030Cu, 0x01E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0323u, 0x1E32u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0327u, 0x0136u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Bu, 0x0331u, 0x1E34u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0301u, 0x0139u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x030Cu, 0x013Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0323u, 0x1E36u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0327u, 0x013Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x032Du, 0x1E3Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Cu, 0x0331u, 0x1E3Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0301u, 0x1E3Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0307u, 0x1E40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Du, 0x0323u, 0x1E42u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0300u, 0x01F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0301u, 0x0143u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0303u, 0x00D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0307u, 0x1E44u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x030Cu, 0x0147u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0323u, 0x1E46u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0327u, 0x0145u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x032Du, 0x1E4Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Eu, 0x0331u, 0x1E48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0300u, 0x00D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0301u, 0x00D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0302u, 0x00D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0303u, 0x00D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0304u, 0x014Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0306u, 0x014Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0307u, 0x022Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0308u, 0x00D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0309u, 0x1ECEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Bu, 0x0150u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Cu, 0x01D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x030Fu, 0x020Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0311u, 0x020Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x031Bu, 0x01A0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0323u, 0x1ECCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x004Fu, 0x0328u, 0x01EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0301u, 0x1E54u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0050u, 0x0307u, 0x1E56u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0301u, 0x0154u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0307u, 0x1E58u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Cu, 0x0158u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x030Fu, 0x0210u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0311u, 0x0212u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0323u, 0x1E5Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0327u, 0x0156u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0052u, 0x0331u, 0x1E5Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0301u, 0x015Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0302u, 0x015Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0307u, 0x1E60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x030Cu, 0x0160u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0323u, 0x1E62u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0326u, 0x0218u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0053u, 0x0327u, 0x015Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0307u, 0x1E6Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x030Cu, 0x0164u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0323u, 0x1E6Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0326u, 0x021Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0327u, 0x0162u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x032Du, 0x1E70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0054u, 0x0331u, 0x1E6Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0300u, 0x00D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0301u, 0x00DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0302u, 0x00DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0303u, 0x0168u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0304u, 0x016Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0306u, 0x016Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0308u, 0x00DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0309u, 0x1EE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Au, 0x016Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Bu, 0x0170u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Cu, 0x01D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x030Fu, 0x0214u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0311u, 0x0216u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x031Bu, 0x01AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0323u, 0x1EE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0324u, 0x1E72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0328u, 0x0172u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x032Du, 0x1E76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0055u, 0x0330u, 0x1E74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0303u, 0x1E7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0056u, 0x0323u, 0x1E7Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0300u, 0x1E80u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0301u, 0x1E82u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0302u, 0x0174u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0307u, 0x1E86u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0308u, 0x1E84u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0057u, 0x0323u, 0x1E88u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0307u, 0x1E8Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0058u, 0x0308u, 0x1E8Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0300u, 0x1EF2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0301u, 0x00DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0302u, 0x0176u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0303u, 0x1EF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0304u, 0x0232u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0307u, 0x1E8Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0308u, 0x0178u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0309u, 0x1EF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0059u, 0x0323u, 0x1EF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0301u, 0x0179u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0302u, 0x1E90u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0307u, 0x017Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x030Cu, 0x017Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0323u, 0x1E92u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x005Au, 0x0331u, 0x1E94u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0300u, 0x00E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0301u, 0x00E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0302u, 0x00E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0303u, 0x00E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0304u, 0x0101u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0306u, 0x0103u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0307u, 0x0227u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0308u, 0x00E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0309u, 0x1EA3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Au, 0x00E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Cu, 0x01CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x030Fu, 0x0201u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0311u, 0x0203u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0323u, 0x1EA1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0325u, 0x1E01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0061u, 0x0328u, 0x0105u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0307u, 0x1E03u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0323u, 0x1E05u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0062u, 0x0331u, 0x1E07u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0301u, 0x0107u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0302u, 0x0109u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0307u, 0x010Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x030Cu, 0x010Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0063u, 0x0327u, 0x00E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0307u, 0x1E0Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x030Cu, 0x010Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0323u, 0x1E0Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0327u, 0x1E11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x032Du, 0x1E13u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0064u, 0x0331u, 0x1E0Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0300u, 0x00E8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0301u, 0x00E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0302u, 0x00EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0303u, 0x1EBDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0304u, 0x0113u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0306u, 0x0115u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0307u, 0x0117u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0308u, 0x00EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0309u, 0x1EBBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Cu, 0x011Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x030Fu, 0x0205u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0311u, 0x0207u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0323u, 0x1EB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0327u, 0x0229u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0328u, 0x0119u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x032Du, 0x1E19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0065u, 0x0330u, 0x1E1Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0066u, 0x0307u, 0x1E1Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0301u, 0x01F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0302u, 0x011Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0304u, 0x1E21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0306u, 0x011Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0307u, 0x0121u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x030Cu, 0x01E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0067u, 0x0327u, 0x0123u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0302u, 0x0125u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0307u, 0x1E23u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0308u, 0x1E27u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x030Cu, 0x021Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0323u, 0x1E25u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0327u, 0x1E29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x032Eu, 0x1E2Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0068u, 0x0331u, 0x1E96u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0300u, 0x00ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0301u, 0x00EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0302u, 0x00EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0303u, 0x0129u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0304u, 0x012Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0306u, 0x012Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0308u, 0x00EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0309u, 0x1EC9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Cu, 0x01D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x030Fu, 0x0209u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0311u, 0x020Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0323u, 0x1ECBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0328u, 0x012Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0069u, 0x0330u, 0x1E2Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x0302u, 0x0135u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Au, 0x030Cu, 0x01F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0301u, 0x1E31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x030Cu, 0x01E9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0323u, 0x1E33u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0327u, 0x0137u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Bu, 0x0331u, 0x1E35u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0301u, 0x013Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x030Cu, 0x013Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0323u, 0x1E37u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0327u, 0x013Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x032Du, 0x1E3Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Cu, 0x0331u, 0x1E3Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0301u, 0x1E3Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0307u, 0x1E41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Du, 0x0323u, 0x1E43u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0300u, 0x01F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0301u, 0x0144u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0303u, 0x00F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0307u, 0x1E45u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x030Cu, 0x0148u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0323u, 0x1E47u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0327u, 0x0146u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x032Du, 0x1E4Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Eu, 0x0331u, 0x1E49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0300u, 0x00F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0301u, 0x00F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0302u, 0x00F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0303u, 0x00F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0304u, 0x014Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0306u, 0x014Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0307u, 0x022Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0308u, 0x00F6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0309u, 0x1ECFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Bu, 0x0151u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Cu, 0x01D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x030Fu, 0x020Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0311u, 0x020Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x031Bu, 0x01A1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0323u, 0x1ECDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x006Fu, 0x0328u, 0x01EBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0301u, 0x1E55u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0070u, 0x0307u, 0x1E57u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0301u, 0x0155u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0307u, 0x1E59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Cu, 0x0159u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x030Fu, 0x0211u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0311u, 0x0213u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0323u, 0x1E5Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0327u, 0x0157u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0072u, 0x0331u, 0x1E5Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0301u, 0x015Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0302u, 0x015Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0307u, 0x1E61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x030Cu, 0x0161u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0323u, 0x1E63u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0326u, 0x0219u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0073u, 0x0327u, 0x015Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0307u, 0x1E6Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0308u, 0x1E97u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x030Cu, 0x0165u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0323u, 0x1E6Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0326u, 0x021Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0327u, 0x0163u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x032Du, 0x1E71u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0074u, 0x0331u, 0x1E6Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0300u, 0x00F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0301u, 0x00FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0302u, 0x00FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0303u, 0x0169u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0304u, 0x016Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0306u, 0x016Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0308u, 0x00FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0309u, 0x1EE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Au, 0x016Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Bu, 0x0171u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Cu, 0x01D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x030Fu, 0x0215u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0311u, 0x0217u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x031Bu, 0x01B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0323u, 0x1EE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0324u, 0x1E73u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0328u, 0x0173u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x032Du, 0x1E77u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0075u, 0x0330u, 0x1E75u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0303u, 0x1E7Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0076u, 0x0323u, 0x1E7Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0300u, 0x1E81u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0301u, 0x1E83u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0302u, 0x0175u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0307u, 0x1E87u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0308u, 0x1E85u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x030Au, 0x1E98u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0077u, 0x0323u, 0x1E89u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0307u, 0x1E8Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0078u, 0x0308u, 0x1E8Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0300u, 0x1EF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0301u, 0x00FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0302u, 0x0177u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0303u, 0x1EF9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0304u, 0x0233u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0307u, 0x1E8Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0308u, 0x00FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0309u, 0x1EF7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x030Au, 0x1E99u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0079u, 0x0323u, 0x1EF5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0301u, 0x017Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0302u, 0x1E91u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0307u, 0x017Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x030Cu, 0x017Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0323u, 0x1E93u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x007Au, 0x0331u, 0x1E95u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0300u, 0x1FEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0301u, 0x0385u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00A8u, 0x0342u, 0x1FC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0300u, 0x1EA6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0301u, 0x1EA4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0303u, 0x1EAAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C2u, 0x0309u, 0x1EA8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C4u, 0x0304u, 0x01DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C5u, 0x0301u, 0x01FAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0301u, 0x01FCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C6u, 0x0304u, 0x01E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00C7u, 0x0301u, 0x1E08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0300u, 0x1EC0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0301u, 0x1EBEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0303u, 0x1EC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CAu, 0x0309u, 0x1EC2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00CFu, 0x0301u, 0x1E2Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0300u, 0x1ED2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0301u, 0x1ED0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0303u, 0x1ED6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D4u, 0x0309u, 0x1ED4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0301u, 0x1E4Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0304u, 0x022Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D5u, 0x0308u, 0x1E4Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D6u, 0x0304u, 0x022Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00D8u, 0x0301u, 0x01FEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0300u, 0x01DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0301u, 0x01D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x0304u, 0x01D5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00DCu, 0x030Cu, 0x01D9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0300u, 0x1EA7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0301u, 0x1EA5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0303u, 0x1EABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E2u, 0x0309u, 0x1EA9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E4u, 0x0304u, 0x01DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E5u, 0x0301u, 0x01FBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0301u, 0x01FDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E6u, 0x0304u, 0x01E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00E7u, 0x0301u, 0x1E09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0300u, 0x1EC1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0301u, 0x1EBFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0303u, 0x1EC5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EAu, 0x0309u, 0x1EC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00EFu, 0x0301u, 0x1E2Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0300u, 0x1ED3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0301u, 0x1ED1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0303u, 0x1ED7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F4u, 0x0309u, 0x1ED5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0301u, 0x1E4Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0304u, 0x022Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F5u, 0x0308u, 0x1E4Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F6u, 0x0304u, 0x022Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00F8u, 0x0301u, 0x01FFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0300u, 0x01DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0301u, 0x01D8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x0304u, 0x01D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x00FCu, 0x030Cu, 0x01DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0300u, 0x1EB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0301u, 0x1EAEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0303u, 0x1EB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0102u, 0x0309u, 0x1EB2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0300u, 0x1EB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0301u, 0x1EAFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0303u, 0x1EB5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0103u, 0x0309u, 0x1EB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0300u, 0x1E14u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0112u, 0x0301u, 0x1E16u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0300u, 0x1E15u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0113u, 0x0301u, 0x1E17u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0300u, 0x1E50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Cu, 0x0301u, 0x1E52u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0300u, 0x1E51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x014Du, 0x0301u, 0x1E53u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Au, 0x0307u, 0x1E64u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x015Bu, 0x0307u, 0x1E65u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0160u, 0x0307u, 0x1E66u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0161u, 0x0307u, 0x1E67u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0168u, 0x0301u, 0x1E78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0169u, 0x0301u, 0x1E79u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Au, 0x0308u, 0x1E7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x016Bu, 0x0308u, 0x1E7Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x017Fu, 0x0307u, 0x1E9Bu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0300u, 0x1EDCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0301u, 0x1EDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0303u, 0x1EE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0309u, 0x1EDEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A0u, 0x0323u, 0x1EE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0300u, 0x1EDDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0301u, 0x1EDBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0303u, 0x1EE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0309u, 0x1EDFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01A1u, 0x0323u, 0x1EE3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0300u, 0x1EEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0301u, 0x1EE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0303u, 0x1EEEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0309u, 0x1EECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01AFu, 0x0323u, 0x1EF0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0300u, 0x1EEBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0301u, 0x1EE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0303u, 0x1EEFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0309u, 0x1EEDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B0u, 0x0323u, 0x1EF1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01B7u, 0x030Cu, 0x01EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EAu, 0x0304u, 0x01ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x01EBu, 0x0304u, 0x01EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0226u, 0x0304u, 0x01E0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0227u, 0x0304u, 0x01E1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0228u, 0x0306u, 0x1E1Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0229u, 0x0306u, 0x1E1Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Eu, 0x0304u, 0x0230u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x022Fu, 0x0304u, 0x0231u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0292u, 0x030Cu, 0x01EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0308u, 0x0301u, 0x0000u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0300u, 0x1FBAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0301u, 0x0386u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0304u, 0x1FB9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0306u, 0x1FB8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0313u, 0x1F08u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0314u, 0x1F09u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0391u, 0x0345u, 0x1FBCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0300u, 0x1FC8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0301u, 0x0388u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0313u, 0x1F18u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0395u, 0x0314u, 0x1F19u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0300u, 0x1FCAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0301u, 0x0389u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0313u, 0x1F28u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0314u, 0x1F29u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0397u, 0x0345u, 0x1FCCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0300u, 0x1FDAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0301u, 0x038Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0304u, 0x1FD9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0306u, 0x1FD8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0308u, 0x03AAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0313u, 0x1F38u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0399u, 0x0314u, 0x1F39u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0300u, 0x1FF8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0301u, 0x038Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0313u, 0x1F48u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x039Fu, 0x0314u, 0x1F49u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A1u, 0x0314u, 0x1FECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0300u, 0x1FEAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0301u, 0x038Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0304u, 0x1FE9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0306u, 0x1FE8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0308u, 0x03ABu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A5u, 0x0314u, 0x1F59u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0300u, 0x1FFAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0301u, 0x038Fu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0313u, 0x1F68u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0314u, 0x1F69u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03A9u, 0x0345u, 0x1FFCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03ACu, 0x0345u, 0x1FB4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03AEu, 0x0345u, 0x1FC4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0300u, 0x1F70u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0301u, 0x03ACu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0304u, 0x1FB1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0306u, 0x1FB0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0313u, 0x1F00u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0314u, 0x1F01u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0342u, 0x1FB6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B1u, 0x0345u, 0x1FB3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0300u, 0x1F72u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0301u, 0x03ADu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0313u, 0x1F10u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B5u, 0x0314u, 0x1F11u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0300u, 0x1F74u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0301u, 0x03AEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0313u, 0x1F20u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0314u, 0x1F21u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0342u, 0x1FC6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B7u, 0x0345u, 0x1FC3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0300u, 0x1F76u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0301u, 0x03AFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0304u, 0x1FD1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0306u, 0x1FD0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0308u, 0x03CAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0313u, 0x1F30u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0314u, 0x1F31u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03B9u, 0x0342u, 0x1FD6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0300u, 0x1F78u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0301u, 0x03CCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0313u, 0x1F40u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03BFu, 0x0314u, 0x1F41u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0313u, 0x1FE4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C1u, 0x0314u, 0x1FE5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0300u, 0x1F7Au), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0301u, 0x03CDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0304u, 0x1FE1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0306u, 0x1FE0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0308u, 0x03CBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0313u, 0x1F50u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0314u, 0x1F51u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C5u, 0x0342u, 0x1FE6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0300u, 0x1F7Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0301u, 0x03CEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0313u, 0x1F60u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0314u, 0x1F61u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0342u, 0x1FF6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03C9u, 0x0345u, 0x1FF3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0300u, 0x1FD2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0301u, 0x0390u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CAu, 0x0342u, 0x1FD7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0300u, 0x1FE2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0301u, 0x03B0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CBu, 0x0342u, 0x1FE7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03CEu, 0x0345u, 0x1FF4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0301u, 0x03D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x03D2u, 0x0308u, 0x03D4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0406u, 0x0308u, 0x0407u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0306u, 0x04D0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0410u, 0x0308u, 0x04D2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0413u, 0x0301u, 0x0403u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0300u, 0x0400u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0306u, 0x04D6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0415u, 0x0308u, 0x0401u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0306u, 0x04C1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0416u, 0x0308u, 0x04DCu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0417u, 0x0308u, 0x04DEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0300u, 0x040Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0304u, 0x04E2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0306u, 0x0419u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0418u, 0x0308u, 0x04E4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Au, 0x0301u, 0x040Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x041Eu, 0x0308u, 0x04E6u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0304u, 0x04EEu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0306u, 0x040Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x0308u, 0x04F0u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0423u, 0x030Bu, 0x04F2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0427u, 0x0308u, 0x04F4u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Bu, 0x0308u, 0x04F8u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x042Du, 0x0308u, 0x04ECu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0306u, 0x04D1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0430u, 0x0308u, 0x04D3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0433u, 0x0301u, 0x0453u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0300u, 0x0450u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0306u, 0x04D7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0435u, 0x0308u, 0x0451u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0306u, 0x04C2u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0436u, 0x0308u, 0x04DDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0437u, 0x0308u, 0x04DFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0300u, 0x045Du), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0304u, 0x04E3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0306u, 0x0439u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0438u, 0x0308u, 0x04E5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Au, 0x0301u, 0x045Cu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x043Eu, 0x0308u, 0x04E7u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0304u, 0x04EFu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0306u, 0x045Eu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x0308u, 0x04F1u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0443u, 0x030Bu, 0x04F3u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0447u, 0x0308u, 0x04F5u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Bu, 0x0308u, 0x04F9u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x044Du, 0x0308u, 0x04EDu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0456u, 0x0308u, 0x0457u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0474u, 0x030Fu, 0x0476u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x0475u, 0x030Fu, 0x0477u), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D8u, 0x0308u, 0x04DAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04D9u, 0x0308u, 0x04DBu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E8u, 0x0308u, 0x04EAu), + HB_CODEPOINT_ENCODE3_11_7_14 (0x04E9u, 0x0308u, 0x04EBu), +}; +static const uint64_t +_hb_ucd_dm2_u64_map[388] = +{ + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05B8u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D0u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D1u, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D2u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D3u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D4u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05B9u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D5u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D8u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05B4u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05D9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DBu, 0x05BFu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05DCu, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05DEu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E0u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E1u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E3u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E4u, 0x05BFu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E6u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E7u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E8u, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05E9u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x05EAu, 0x05BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x05F2u, 0x05B7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0653u, 0x0622u), HB_CODEPOINT_ENCODE3 (0x0627u, 0x0654u, 0x0623u), + HB_CODEPOINT_ENCODE3 (0x0627u, 0x0655u, 0x0625u), HB_CODEPOINT_ENCODE3 (0x0648u, 0x0654u, 0x0624u), + HB_CODEPOINT_ENCODE3 (0x064Au, 0x0654u, 0x0626u), HB_CODEPOINT_ENCODE3 (0x06C1u, 0x0654u, 0x06C2u), + HB_CODEPOINT_ENCODE3 (0x06D2u, 0x0654u, 0x06D3u), HB_CODEPOINT_ENCODE3 (0x06D5u, 0x0654u, 0x06C0u), + HB_CODEPOINT_ENCODE3 (0x0915u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0916u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0917u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x091Cu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0921u, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0922u, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0928u, 0x093Cu, 0x0929u), HB_CODEPOINT_ENCODE3 (0x092Bu, 0x093Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x092Fu, 0x093Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0930u, 0x093Cu, 0x0931u), + HB_CODEPOINT_ENCODE3 (0x0933u, 0x093Cu, 0x0934u), HB_CODEPOINT_ENCODE3 (0x09A1u, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09A2u, 0x09BCu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x09AFu, 0x09BCu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09BEu, 0x09CBu), HB_CODEPOINT_ENCODE3 (0x09C7u, 0x09D7u, 0x09CCu), + HB_CODEPOINT_ENCODE3 (0x0A16u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A17u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A1Cu, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A2Bu, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0A32u, 0x0A3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0A38u, 0x0A3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B21u, 0x0B3Cu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0B22u, 0x0B3Cu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B3Eu, 0x0B4Bu), HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B56u, 0x0B48u), + HB_CODEPOINT_ENCODE3 (0x0B47u, 0x0B57u, 0x0B4Cu), HB_CODEPOINT_ENCODE3 (0x0B92u, 0x0BD7u, 0x0B94u), + HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BBEu, 0x0BCAu), HB_CODEPOINT_ENCODE3 (0x0BC6u, 0x0BD7u, 0x0BCCu), + HB_CODEPOINT_ENCODE3 (0x0BC7u, 0x0BBEu, 0x0BCBu), HB_CODEPOINT_ENCODE3 (0x0C46u, 0x0C56u, 0x0C48u), + HB_CODEPOINT_ENCODE3 (0x0CBFu, 0x0CD5u, 0x0CC0u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CC2u, 0x0CCAu), + HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD5u, 0x0CC7u), HB_CODEPOINT_ENCODE3 (0x0CC6u, 0x0CD6u, 0x0CC8u), + HB_CODEPOINT_ENCODE3 (0x0CCAu, 0x0CD5u, 0x0CCBu), HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D3Eu, 0x0D4Au), + HB_CODEPOINT_ENCODE3 (0x0D46u, 0x0D57u, 0x0D4Cu), HB_CODEPOINT_ENCODE3 (0x0D47u, 0x0D3Eu, 0x0D4Bu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCAu, 0x0DDAu), HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DCFu, 0x0DDCu), + HB_CODEPOINT_ENCODE3 (0x0DD9u, 0x0DDFu, 0x0DDEu), HB_CODEPOINT_ENCODE3 (0x0DDCu, 0x0DCAu, 0x0DDDu), + HB_CODEPOINT_ENCODE3 (0x0F40u, 0x0FB5u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F42u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F4Cu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F51u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F56u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F5Bu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F72u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F74u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F71u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F90u, 0x0FB5u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0F92u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0F9Cu, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FA1u, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FA6u, 0x0FB7u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FABu, 0x0FB7u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x0FB2u, 0x0F80u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x0FB3u, 0x0F80u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1025u, 0x102Eu, 0x1026u), + HB_CODEPOINT_ENCODE3 (0x1B05u, 0x1B35u, 0x1B06u), HB_CODEPOINT_ENCODE3 (0x1B07u, 0x1B35u, 0x1B08u), + HB_CODEPOINT_ENCODE3 (0x1B09u, 0x1B35u, 0x1B0Au), HB_CODEPOINT_ENCODE3 (0x1B0Bu, 0x1B35u, 0x1B0Cu), + HB_CODEPOINT_ENCODE3 (0x1B0Du, 0x1B35u, 0x1B0Eu), HB_CODEPOINT_ENCODE3 (0x1B11u, 0x1B35u, 0x1B12u), + HB_CODEPOINT_ENCODE3 (0x1B3Au, 0x1B35u, 0x1B3Bu), HB_CODEPOINT_ENCODE3 (0x1B3Cu, 0x1B35u, 0x1B3Du), + HB_CODEPOINT_ENCODE3 (0x1B3Eu, 0x1B35u, 0x1B40u), HB_CODEPOINT_ENCODE3 (0x1B3Fu, 0x1B35u, 0x1B41u), + HB_CODEPOINT_ENCODE3 (0x1B42u, 0x1B35u, 0x1B43u), HB_CODEPOINT_ENCODE3 (0x1E36u, 0x0304u, 0x1E38u), + HB_CODEPOINT_ENCODE3 (0x1E37u, 0x0304u, 0x1E39u), HB_CODEPOINT_ENCODE3 (0x1E5Au, 0x0304u, 0x1E5Cu), + HB_CODEPOINT_ENCODE3 (0x1E5Bu, 0x0304u, 0x1E5Du), HB_CODEPOINT_ENCODE3 (0x1E62u, 0x0307u, 0x1E68u), + HB_CODEPOINT_ENCODE3 (0x1E63u, 0x0307u, 0x1E69u), HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0302u, 0x1EACu), + HB_CODEPOINT_ENCODE3 (0x1EA0u, 0x0306u, 0x1EB6u), HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0302u, 0x1EADu), + HB_CODEPOINT_ENCODE3 (0x1EA1u, 0x0306u, 0x1EB7u), HB_CODEPOINT_ENCODE3 (0x1EB8u, 0x0302u, 0x1EC6u), + HB_CODEPOINT_ENCODE3 (0x1EB9u, 0x0302u, 0x1EC7u), HB_CODEPOINT_ENCODE3 (0x1ECCu, 0x0302u, 0x1ED8u), + HB_CODEPOINT_ENCODE3 (0x1ECDu, 0x0302u, 0x1ED9u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0300u, 0x1F02u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0301u, 0x1F04u), HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0342u, 0x1F06u), + HB_CODEPOINT_ENCODE3 (0x1F00u, 0x0345u, 0x1F80u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0300u, 0x1F03u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0301u, 0x1F05u), HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0342u, 0x1F07u), + HB_CODEPOINT_ENCODE3 (0x1F01u, 0x0345u, 0x1F81u), HB_CODEPOINT_ENCODE3 (0x1F02u, 0x0345u, 0x1F82u), + HB_CODEPOINT_ENCODE3 (0x1F03u, 0x0345u, 0x1F83u), HB_CODEPOINT_ENCODE3 (0x1F04u, 0x0345u, 0x1F84u), + HB_CODEPOINT_ENCODE3 (0x1F05u, 0x0345u, 0x1F85u), HB_CODEPOINT_ENCODE3 (0x1F06u, 0x0345u, 0x1F86u), + HB_CODEPOINT_ENCODE3 (0x1F07u, 0x0345u, 0x1F87u), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0300u, 0x1F0Au), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0301u, 0x1F0Cu), HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0342u, 0x1F0Eu), + HB_CODEPOINT_ENCODE3 (0x1F08u, 0x0345u, 0x1F88u), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0300u, 0x1F0Bu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0301u, 0x1F0Du), HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0342u, 0x1F0Fu), + HB_CODEPOINT_ENCODE3 (0x1F09u, 0x0345u, 0x1F89u), HB_CODEPOINT_ENCODE3 (0x1F0Au, 0x0345u, 0x1F8Au), + HB_CODEPOINT_ENCODE3 (0x1F0Bu, 0x0345u, 0x1F8Bu), HB_CODEPOINT_ENCODE3 (0x1F0Cu, 0x0345u, 0x1F8Cu), + HB_CODEPOINT_ENCODE3 (0x1F0Du, 0x0345u, 0x1F8Du), HB_CODEPOINT_ENCODE3 (0x1F0Eu, 0x0345u, 0x1F8Eu), + HB_CODEPOINT_ENCODE3 (0x1F0Fu, 0x0345u, 0x1F8Fu), HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0300u, 0x1F12u), + HB_CODEPOINT_ENCODE3 (0x1F10u, 0x0301u, 0x1F14u), HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0300u, 0x1F13u), + HB_CODEPOINT_ENCODE3 (0x1F11u, 0x0301u, 0x1F15u), HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0300u, 0x1F1Au), + HB_CODEPOINT_ENCODE3 (0x1F18u, 0x0301u, 0x1F1Cu), HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0300u, 0x1F1Bu), + HB_CODEPOINT_ENCODE3 (0x1F19u, 0x0301u, 0x1F1Du), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0300u, 0x1F22u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0301u, 0x1F24u), HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0342u, 0x1F26u), + HB_CODEPOINT_ENCODE3 (0x1F20u, 0x0345u, 0x1F90u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0300u, 0x1F23u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0301u, 0x1F25u), HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0342u, 0x1F27u), + HB_CODEPOINT_ENCODE3 (0x1F21u, 0x0345u, 0x1F91u), HB_CODEPOINT_ENCODE3 (0x1F22u, 0x0345u, 0x1F92u), + HB_CODEPOINT_ENCODE3 (0x1F23u, 0x0345u, 0x1F93u), HB_CODEPOINT_ENCODE3 (0x1F24u, 0x0345u, 0x1F94u), + HB_CODEPOINT_ENCODE3 (0x1F25u, 0x0345u, 0x1F95u), HB_CODEPOINT_ENCODE3 (0x1F26u, 0x0345u, 0x1F96u), + HB_CODEPOINT_ENCODE3 (0x1F27u, 0x0345u, 0x1F97u), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0300u, 0x1F2Au), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0301u, 0x1F2Cu), HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0342u, 0x1F2Eu), + HB_CODEPOINT_ENCODE3 (0x1F28u, 0x0345u, 0x1F98u), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0300u, 0x1F2Bu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0301u, 0x1F2Du), HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0342u, 0x1F2Fu), + HB_CODEPOINT_ENCODE3 (0x1F29u, 0x0345u, 0x1F99u), HB_CODEPOINT_ENCODE3 (0x1F2Au, 0x0345u, 0x1F9Au), + HB_CODEPOINT_ENCODE3 (0x1F2Bu, 0x0345u, 0x1F9Bu), HB_CODEPOINT_ENCODE3 (0x1F2Cu, 0x0345u, 0x1F9Cu), + HB_CODEPOINT_ENCODE3 (0x1F2Du, 0x0345u, 0x1F9Du), HB_CODEPOINT_ENCODE3 (0x1F2Eu, 0x0345u, 0x1F9Eu), + HB_CODEPOINT_ENCODE3 (0x1F2Fu, 0x0345u, 0x1F9Fu), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0300u, 0x1F32u), + HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0301u, 0x1F34u), HB_CODEPOINT_ENCODE3 (0x1F30u, 0x0342u, 0x1F36u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0300u, 0x1F33u), HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0301u, 0x1F35u), + HB_CODEPOINT_ENCODE3 (0x1F31u, 0x0342u, 0x1F37u), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0300u, 0x1F3Au), + HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0301u, 0x1F3Cu), HB_CODEPOINT_ENCODE3 (0x1F38u, 0x0342u, 0x1F3Eu), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0300u, 0x1F3Bu), HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0301u, 0x1F3Du), + HB_CODEPOINT_ENCODE3 (0x1F39u, 0x0342u, 0x1F3Fu), HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0300u, 0x1F42u), + HB_CODEPOINT_ENCODE3 (0x1F40u, 0x0301u, 0x1F44u), HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0300u, 0x1F43u), + HB_CODEPOINT_ENCODE3 (0x1F41u, 0x0301u, 0x1F45u), HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0300u, 0x1F4Au), + HB_CODEPOINT_ENCODE3 (0x1F48u, 0x0301u, 0x1F4Cu), HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0300u, 0x1F4Bu), + HB_CODEPOINT_ENCODE3 (0x1F49u, 0x0301u, 0x1F4Du), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0300u, 0x1F52u), + HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0301u, 0x1F54u), HB_CODEPOINT_ENCODE3 (0x1F50u, 0x0342u, 0x1F56u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0300u, 0x1F53u), HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0301u, 0x1F55u), + HB_CODEPOINT_ENCODE3 (0x1F51u, 0x0342u, 0x1F57u), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0300u, 0x1F5Bu), + HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0301u, 0x1F5Du), HB_CODEPOINT_ENCODE3 (0x1F59u, 0x0342u, 0x1F5Fu), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0300u, 0x1F62u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0301u, 0x1F64u), + HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0342u, 0x1F66u), HB_CODEPOINT_ENCODE3 (0x1F60u, 0x0345u, 0x1FA0u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0300u, 0x1F63u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0301u, 0x1F65u), + HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0342u, 0x1F67u), HB_CODEPOINT_ENCODE3 (0x1F61u, 0x0345u, 0x1FA1u), + HB_CODEPOINT_ENCODE3 (0x1F62u, 0x0345u, 0x1FA2u), HB_CODEPOINT_ENCODE3 (0x1F63u, 0x0345u, 0x1FA3u), + HB_CODEPOINT_ENCODE3 (0x1F64u, 0x0345u, 0x1FA4u), HB_CODEPOINT_ENCODE3 (0x1F65u, 0x0345u, 0x1FA5u), + HB_CODEPOINT_ENCODE3 (0x1F66u, 0x0345u, 0x1FA6u), HB_CODEPOINT_ENCODE3 (0x1F67u, 0x0345u, 0x1FA7u), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0300u, 0x1F6Au), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0301u, 0x1F6Cu), + HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0342u, 0x1F6Eu), HB_CODEPOINT_ENCODE3 (0x1F68u, 0x0345u, 0x1FA8u), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0300u, 0x1F6Bu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0301u, 0x1F6Du), + HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0342u, 0x1F6Fu), HB_CODEPOINT_ENCODE3 (0x1F69u, 0x0345u, 0x1FA9u), + HB_CODEPOINT_ENCODE3 (0x1F6Au, 0x0345u, 0x1FAAu), HB_CODEPOINT_ENCODE3 (0x1F6Bu, 0x0345u, 0x1FABu), + HB_CODEPOINT_ENCODE3 (0x1F6Cu, 0x0345u, 0x1FACu), HB_CODEPOINT_ENCODE3 (0x1F6Du, 0x0345u, 0x1FADu), + HB_CODEPOINT_ENCODE3 (0x1F6Eu, 0x0345u, 0x1FAEu), HB_CODEPOINT_ENCODE3 (0x1F6Fu, 0x0345u, 0x1FAFu), + HB_CODEPOINT_ENCODE3 (0x1F70u, 0x0345u, 0x1FB2u), HB_CODEPOINT_ENCODE3 (0x1F74u, 0x0345u, 0x1FC2u), + HB_CODEPOINT_ENCODE3 (0x1F7Cu, 0x0345u, 0x1FF2u), HB_CODEPOINT_ENCODE3 (0x1FB6u, 0x0345u, 0x1FB7u), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0300u, 0x1FCDu), HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0301u, 0x1FCEu), + HB_CODEPOINT_ENCODE3 (0x1FBFu, 0x0342u, 0x1FCFu), HB_CODEPOINT_ENCODE3 (0x1FC6u, 0x0345u, 0x1FC7u), + HB_CODEPOINT_ENCODE3 (0x1FF6u, 0x0345u, 0x1FF7u), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0300u, 0x1FDDu), + HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0301u, 0x1FDEu), HB_CODEPOINT_ENCODE3 (0x1FFEu, 0x0342u, 0x1FDFu), + HB_CODEPOINT_ENCODE3 (0x2190u, 0x0338u, 0x219Au), HB_CODEPOINT_ENCODE3 (0x2192u, 0x0338u, 0x219Bu), + HB_CODEPOINT_ENCODE3 (0x2194u, 0x0338u, 0x21AEu), HB_CODEPOINT_ENCODE3 (0x21D0u, 0x0338u, 0x21CDu), + HB_CODEPOINT_ENCODE3 (0x21D2u, 0x0338u, 0x21CFu), HB_CODEPOINT_ENCODE3 (0x21D4u, 0x0338u, 0x21CEu), + HB_CODEPOINT_ENCODE3 (0x2203u, 0x0338u, 0x2204u), HB_CODEPOINT_ENCODE3 (0x2208u, 0x0338u, 0x2209u), + HB_CODEPOINT_ENCODE3 (0x220Bu, 0x0338u, 0x220Cu), HB_CODEPOINT_ENCODE3 (0x2223u, 0x0338u, 0x2224u), + HB_CODEPOINT_ENCODE3 (0x2225u, 0x0338u, 0x2226u), HB_CODEPOINT_ENCODE3 (0x223Cu, 0x0338u, 0x2241u), + HB_CODEPOINT_ENCODE3 (0x2243u, 0x0338u, 0x2244u), HB_CODEPOINT_ENCODE3 (0x2245u, 0x0338u, 0x2247u), + HB_CODEPOINT_ENCODE3 (0x2248u, 0x0338u, 0x2249u), HB_CODEPOINT_ENCODE3 (0x224Du, 0x0338u, 0x226Du), + HB_CODEPOINT_ENCODE3 (0x2261u, 0x0338u, 0x2262u), HB_CODEPOINT_ENCODE3 (0x2264u, 0x0338u, 0x2270u), + HB_CODEPOINT_ENCODE3 (0x2265u, 0x0338u, 0x2271u), HB_CODEPOINT_ENCODE3 (0x2272u, 0x0338u, 0x2274u), + HB_CODEPOINT_ENCODE3 (0x2273u, 0x0338u, 0x2275u), HB_CODEPOINT_ENCODE3 (0x2276u, 0x0338u, 0x2278u), + HB_CODEPOINT_ENCODE3 (0x2277u, 0x0338u, 0x2279u), HB_CODEPOINT_ENCODE3 (0x227Au, 0x0338u, 0x2280u), + HB_CODEPOINT_ENCODE3 (0x227Bu, 0x0338u, 0x2281u), HB_CODEPOINT_ENCODE3 (0x227Cu, 0x0338u, 0x22E0u), + HB_CODEPOINT_ENCODE3 (0x227Du, 0x0338u, 0x22E1u), HB_CODEPOINT_ENCODE3 (0x2282u, 0x0338u, 0x2284u), + HB_CODEPOINT_ENCODE3 (0x2283u, 0x0338u, 0x2285u), HB_CODEPOINT_ENCODE3 (0x2286u, 0x0338u, 0x2288u), + HB_CODEPOINT_ENCODE3 (0x2287u, 0x0338u, 0x2289u), HB_CODEPOINT_ENCODE3 (0x2291u, 0x0338u, 0x22E2u), + HB_CODEPOINT_ENCODE3 (0x2292u, 0x0338u, 0x22E3u), HB_CODEPOINT_ENCODE3 (0x22A2u, 0x0338u, 0x22ACu), + HB_CODEPOINT_ENCODE3 (0x22A8u, 0x0338u, 0x22ADu), HB_CODEPOINT_ENCODE3 (0x22A9u, 0x0338u, 0x22AEu), + HB_CODEPOINT_ENCODE3 (0x22ABu, 0x0338u, 0x22AFu), HB_CODEPOINT_ENCODE3 (0x22B2u, 0x0338u, 0x22EAu), + HB_CODEPOINT_ENCODE3 (0x22B3u, 0x0338u, 0x22EBu), HB_CODEPOINT_ENCODE3 (0x22B4u, 0x0338u, 0x22ECu), + HB_CODEPOINT_ENCODE3 (0x22B5u, 0x0338u, 0x22EDu), HB_CODEPOINT_ENCODE3 (0x2ADDu, 0x0338u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x3046u, 0x3099u, 0x3094u), HB_CODEPOINT_ENCODE3 (0x304Bu, 0x3099u, 0x304Cu), + HB_CODEPOINT_ENCODE3 (0x304Du, 0x3099u, 0x304Eu), HB_CODEPOINT_ENCODE3 (0x304Fu, 0x3099u, 0x3050u), + HB_CODEPOINT_ENCODE3 (0x3051u, 0x3099u, 0x3052u), HB_CODEPOINT_ENCODE3 (0x3053u, 0x3099u, 0x3054u), + HB_CODEPOINT_ENCODE3 (0x3055u, 0x3099u, 0x3056u), HB_CODEPOINT_ENCODE3 (0x3057u, 0x3099u, 0x3058u), + HB_CODEPOINT_ENCODE3 (0x3059u, 0x3099u, 0x305Au), HB_CODEPOINT_ENCODE3 (0x305Bu, 0x3099u, 0x305Cu), + HB_CODEPOINT_ENCODE3 (0x305Du, 0x3099u, 0x305Eu), HB_CODEPOINT_ENCODE3 (0x305Fu, 0x3099u, 0x3060u), + HB_CODEPOINT_ENCODE3 (0x3061u, 0x3099u, 0x3062u), HB_CODEPOINT_ENCODE3 (0x3064u, 0x3099u, 0x3065u), + HB_CODEPOINT_ENCODE3 (0x3066u, 0x3099u, 0x3067u), HB_CODEPOINT_ENCODE3 (0x3068u, 0x3099u, 0x3069u), + HB_CODEPOINT_ENCODE3 (0x306Fu, 0x3099u, 0x3070u), HB_CODEPOINT_ENCODE3 (0x306Fu, 0x309Au, 0x3071u), + HB_CODEPOINT_ENCODE3 (0x3072u, 0x3099u, 0x3073u), HB_CODEPOINT_ENCODE3 (0x3072u, 0x309Au, 0x3074u), + HB_CODEPOINT_ENCODE3 (0x3075u, 0x3099u, 0x3076u), HB_CODEPOINT_ENCODE3 (0x3075u, 0x309Au, 0x3077u), + HB_CODEPOINT_ENCODE3 (0x3078u, 0x3099u, 0x3079u), HB_CODEPOINT_ENCODE3 (0x3078u, 0x309Au, 0x307Au), + HB_CODEPOINT_ENCODE3 (0x307Bu, 0x3099u, 0x307Cu), HB_CODEPOINT_ENCODE3 (0x307Bu, 0x309Au, 0x307Du), + HB_CODEPOINT_ENCODE3 (0x309Du, 0x3099u, 0x309Eu), HB_CODEPOINT_ENCODE3 (0x30A6u, 0x3099u, 0x30F4u), + HB_CODEPOINT_ENCODE3 (0x30ABu, 0x3099u, 0x30ACu), HB_CODEPOINT_ENCODE3 (0x30ADu, 0x3099u, 0x30AEu), + HB_CODEPOINT_ENCODE3 (0x30AFu, 0x3099u, 0x30B0u), HB_CODEPOINT_ENCODE3 (0x30B1u, 0x3099u, 0x30B2u), + HB_CODEPOINT_ENCODE3 (0x30B3u, 0x3099u, 0x30B4u), HB_CODEPOINT_ENCODE3 (0x30B5u, 0x3099u, 0x30B6u), + HB_CODEPOINT_ENCODE3 (0x30B7u, 0x3099u, 0x30B8u), HB_CODEPOINT_ENCODE3 (0x30B9u, 0x3099u, 0x30BAu), + HB_CODEPOINT_ENCODE3 (0x30BBu, 0x3099u, 0x30BCu), HB_CODEPOINT_ENCODE3 (0x30BDu, 0x3099u, 0x30BEu), + HB_CODEPOINT_ENCODE3 (0x30BFu, 0x3099u, 0x30C0u), HB_CODEPOINT_ENCODE3 (0x30C1u, 0x3099u, 0x30C2u), + HB_CODEPOINT_ENCODE3 (0x30C4u, 0x3099u, 0x30C5u), HB_CODEPOINT_ENCODE3 (0x30C6u, 0x3099u, 0x30C7u), + HB_CODEPOINT_ENCODE3 (0x30C8u, 0x3099u, 0x30C9u), HB_CODEPOINT_ENCODE3 (0x30CFu, 0x3099u, 0x30D0u), + HB_CODEPOINT_ENCODE3 (0x30CFu, 0x309Au, 0x30D1u), HB_CODEPOINT_ENCODE3 (0x30D2u, 0x3099u, 0x30D3u), + HB_CODEPOINT_ENCODE3 (0x30D2u, 0x309Au, 0x30D4u), HB_CODEPOINT_ENCODE3 (0x30D5u, 0x3099u, 0x30D6u), + HB_CODEPOINT_ENCODE3 (0x30D5u, 0x309Au, 0x30D7u), HB_CODEPOINT_ENCODE3 (0x30D8u, 0x3099u, 0x30D9u), + HB_CODEPOINT_ENCODE3 (0x30D8u, 0x309Au, 0x30DAu), HB_CODEPOINT_ENCODE3 (0x30DBu, 0x3099u, 0x30DCu), + HB_CODEPOINT_ENCODE3 (0x30DBu, 0x309Au, 0x30DDu), HB_CODEPOINT_ENCODE3 (0x30EFu, 0x3099u, 0x30F7u), + HB_CODEPOINT_ENCODE3 (0x30F0u, 0x3099u, 0x30F8u), HB_CODEPOINT_ENCODE3 (0x30F1u, 0x3099u, 0x30F9u), + HB_CODEPOINT_ENCODE3 (0x30F2u, 0x3099u, 0x30FAu), HB_CODEPOINT_ENCODE3 (0x30FDu, 0x3099u, 0x30FEu), + HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C1u, 0x0000u), HB_CODEPOINT_ENCODE3 (0xFB49u, 0x05C2u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x11099u, 0x110BAu, 0x1109Au),HB_CODEPOINT_ENCODE3 (0x1109Bu, 0x110BAu, 0x1109Cu), + HB_CODEPOINT_ENCODE3 (0x110A5u, 0x110BAu, 0x110ABu),HB_CODEPOINT_ENCODE3 (0x11131u, 0x11127u, 0x1112Eu), + HB_CODEPOINT_ENCODE3 (0x11132u, 0x11127u, 0x1112Fu),HB_CODEPOINT_ENCODE3 (0x11347u, 0x1133Eu, 0x1134Bu), + HB_CODEPOINT_ENCODE3 (0x11347u, 0x11357u, 0x1134Cu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114B0u, 0x114BCu), + HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BAu, 0x114BBu),HB_CODEPOINT_ENCODE3 (0x114B9u, 0x114BDu, 0x114BEu), + HB_CODEPOINT_ENCODE3 (0x115B8u, 0x115AFu, 0x115BAu),HB_CODEPOINT_ENCODE3 (0x115B9u, 0x115AFu, 0x115BBu), + HB_CODEPOINT_ENCODE3 (0x11935u, 0x11930u, 0x11938u), HB_CODEPOINT_ENCODE3 (0x1D157u, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D158u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Eu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D16Fu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D170u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D171u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D15Fu, 0x1D172u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1B9u, 0x1D165u, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BAu, 0x1D165u, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BBu, 0x1D16Fu, 0x0000u), + HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Eu, 0x0000u), HB_CODEPOINT_ENCODE3 (0x1D1BCu, 0x1D16Fu, 0x0000u), +}; + +#ifndef HB_OPTIMIZE_SIZE + +static const uint8_t +_hb_ucd_u8[17884] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 22, 22, 22, 22, 24, 7, 7, + 25, 26, 22, 22, 22, 27, 28, 29, 22, 30, 31, 32, 33, 34, 35, 36, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 22, 42, + 7, 7, 43, 7, 44, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 45, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 46, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 47, + 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, 34, 35, 36, 37, 38, 39, 34, 34, 34, 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, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, + 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, + 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, + 91, 34, 34, 34, 34, 34, 34, 34, 34, 92, 34, 34, 93, 94, 95, 96, + 97, 98, 99,100,101,102,103,104, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,105, + 106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, + 107,107, 34, 34,108,109,110,111, 34, 34,112,113,114,115,116,117, + 118,119,120,121,122,123,124,125,126,127,128,129, 34, 34,130,131, + 132,133,134,135,136,137,138,139,140,141,142,122,143,144,145,146, + 147,148,149,150,151,152,153,122,154,155,122,156,157,158,159,122, + 160,161,162,163,164,165,166,122,167,168,169,170,122,171,172,173, + 34, 34, 34, 34, 34, 34, 34,174,175, 34,176,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,177, + 34, 34, 34, 34, 34, 34, 34, 34,178,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122, 34, 34, 34, 34,179,122,122,122, + 34, 34, 34, 34,180,181,182,183,122,122,122,122,184,185,186,187, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,188, + 34, 34, 34, 34, 34, 34, 34, 34, 34,189,190,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,191, + 34, 34,192, 34, 34,193,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,194,195,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,196,197, + 69,198,199,200,201,202,203,122,204,205,206,207,208,209,210,211, + 69, 69, 69, 69,212,213,122,122,122,122,122,122,122,122,214,122, + 215,216,217,122,122,218,122,122,122,219,122,122,122,122,122,220, + 34,221,222,122,122,122,122,122,223,224,225,122,226,227,122,122, + 228,229,230,231,232,122, 69,233, 69, 69, 69, 69, 69,234,235,236, + 237,238, 69, 69,239,240, 69,241,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,242, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,243, 34, + 244, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,245, 34, 34, + 34, 34, 34, 34, 34, 34, 34,246, 34, 34, 34, 34,247,122,122,122, + 34, 34, 34, 34,248,122,122,122,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34,249, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34,250,122,122,122,122,122,122,122,122, + 251,122,252,253,122,122,122,122,122,122,122,122,122,122,122,122, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,254, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,255, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, + 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, + 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, + 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, + 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, + 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, + 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, + 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, + 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, + 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, + 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 44, 43, 43, 43, 43, + 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, + 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, + 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, + 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, + 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, + 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, + 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, + 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, + 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, + 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, + 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, + 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, + 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, + 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, + 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, + 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, + 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, + 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, + 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, + 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, + 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, + 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, + 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, + 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, + 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, + 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, + 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, + 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, + 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, + 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, + 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, + 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, + 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, + 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, + 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, + 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, + 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, + 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, + 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, + 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, + 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, + 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, + 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, + 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, + 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, + 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, + 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, + 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, + 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, + 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, + 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, + 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, + 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, + 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, + 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, + 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, + 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, + 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, + 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, + 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, + 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 44, + 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 64, + 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, + 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, + 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, + 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 16, 16, 16, 16,110, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, + 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, + 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, + 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, + 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, + 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, + 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, + 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, + 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, + 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, + 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, + 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, + 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, + 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, + 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, + 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, + 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, + 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, + 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, + 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, + 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, + 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, + 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, + 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 92, 44, 44, 27, 27, 27, 27, 27, 27, 67, 67, + 67, 67, 67, 67, 67, 27, 27, 27, 67, 67, 67, 26, 67, 67, 67, 67, + 26, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, + 67, 67, 67, 67, 67, 67, 67, 26, 67, 67, 67, 67, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, + 8, 8,129,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,129,148,148,148,148,148,148,148,148,148,148,147, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,144, 26, 8, 8,144, 67, 67, 67, 44, 67, 67, 67, 67, 67, + 67, 67, 67, 55, 67, 67, 67, 67, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,140, 67, 67,138, 34,149, + 43, 32, 44, 44, 93, 2, 99, 2, 16, 16, 16,150, 44, 44,150, 44, + 36, 36, 36, 36, 44, 44, 44, 52, 64, 44, 44, 44, 44, 44, 44, 57, + 36, 36, 36, 61, 44, 44, 44, 44, 36, 36, 36, 61, 36, 36, 36, 61, + 2,121,121, 2,125,126,121, 2, 2, 2, 2, 6, 2,108,121, 2, + 121, 4, 4, 4, 4, 2, 2, 88, 2, 2, 2, 2, 2,120, 2, 2, + 108,151, 2, 2, 2, 2, 2, 2, 67, 2,152,148,148,148,153, 44, + 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 44, 44, 44, 44, 44, 1, 2,154,155, 4, 4, 4, 4, + 4, 67, 4, 4, 4, 4,156,157,158,105,105,105,105, 43, 43, 86, + 159, 40, 40, 67,105,160, 63, 67, 36, 36, 36, 61, 57,161,162, 69, + 36, 36, 36, 36, 36, 63, 40, 69, 44, 44, 62, 36, 36, 36, 36, 36, + 67, 27, 27, 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 44, 55, + 67, 67, 67, 67, 67, 67, 67, 92, 27, 27, 27, 27, 27, 67, 67, 67, + 67, 67, 67, 67, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 83, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,164, 2, + 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, + 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, + 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, + 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, + 32, 32, 11, 11, 34,110, 44, 44, 32,150,150, 32, 32, 44, 44, 44, + 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, + 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, + 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, + 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, + 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, + 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, + 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, + 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, + 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, + 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, + 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, + 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, + 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, + 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, + 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, + 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, + 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, + 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, + 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, + 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, + 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, + 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, + 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, + 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, + 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, + 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, + 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, + 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, + 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, + 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, + 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, + 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, + 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, + 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, + 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, + 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, + 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, + 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, + 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, + 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, + 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, + 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, + 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, + 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, + 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, + 27, 27, 27, 27, 27, 27, 27,100, 36, 36, 36, 36, 36, 57,184, 44, + 36, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 43, + 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, + 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, + 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, + 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, + 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, + 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, + 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, + 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, + 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, + 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, + 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, + 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, + 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, + 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 94, 86, 43, 43, 43, 43, + 86, 43, 85, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 93, 71, + 86, 87, 43, 43, 85, 85, 86, 87, 85, 43, 36, 72, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 94, 86, 43, 43, 44, 86, 86, 43, 87, + 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, + 86, 87, 43, 43, 43, 85, 87, 87, 60, 2, 61, 44, 44, 44, 44, 44, + 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 87, 86, + 43, 43, 43, 87, 63, 44, 44, 44, 86, 43, 43, 87, 43, 43, 44, 44, + 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, + 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, + 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, + 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, + 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, + 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, + 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, + 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, + 43, 43, 43, 80, 43, 43, 43, 87, 63, 2, 2, 44, 44, 44, 44, 44, + 2, 36, 36, 36, 36, 36, 36, 36, 44, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 89, 43, 43, 43, 85, 43, 87, 80, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 62, 36, 36, 70, 43, 43, 80, 44, 80, 43, 57, + 43, 43, 43, 70, 44, 44, 44, 44, 36, 36, 36, 62, 61, 36, 36, 36, + 36, 36, 36, 36, 36, 86, 86, 90, 43, 89, 87, 87, 61, 44, 44, 44, + 36, 70, 85,107, 64, 44, 44, 44, 43, 94, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 43, 43, 80, 44, 86, 85, 60, 2, 2, 2, 2, 2, 2, + 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, + 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, + 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, + 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, + 43, 43, 43, 44, 44, 44, 44, 44, 43, 43, 60, 44, 44, 44, 44, 44, + 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 62, 36, 27, 27, 27, 30, 2, 64, 44, 44, + 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, + 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, + 86, 44, 44, 44, 44, 44, 44, 44, 40, 40, 52, 40, 40, 40, 52, 81, + 36, 61, 44, 44, 44, 44, 44, 44, 44, 61, 44, 44, 44, 44, 44, 44, + 36, 61, 62, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, + 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 80, 44, 44, 44, 44, + 67, 67, 67, 92, 55, 67, 67, 67, 67, 67,186, 87, 43, 67,186, 86, + 86,187, 65, 65, 65, 84, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, + 67, 67, 67, 67, 67, 43, 43, 67, 67, 43, 76, 44, 44, 44, 44, 44, + 27, 27, 44, 44, 44, 44, 44, 44, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,110, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 47, 11, 44, 47, 48, 47, 48, 11, 47, 11, + 11, 11, 11, 16, 16,150,150, 16, 16, 16,150, 16, 16, 16, 16, 16, + 16, 16, 11, 48, 11, 47, 48, 11, 11, 11, 47, 11, 11, 11, 47, 16, + 16, 16, 16, 16, 11, 48, 11, 47, 11, 11, 47, 47, 44, 11, 11, 11, + 47, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 44, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 44, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 43, 43, 43, 76, 67, 50, 43, 43, + 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, + 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, + 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, + 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, + 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, + 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, + 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, + 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, + 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, + 36, 36, 36, 61, 36, 36, 62, 61, 36, 36, 61,179, 27, 27, 27, 27, + 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,163, 27, + 188, 27,100, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,163, + 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, + 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, + 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, + 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, + 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, + 8, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 44, 44, + 55, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, 27, 27, 91, 67, + 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, + 67, 92, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 92, 44, 44, 44, + 67, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 25, 41, 41, + 67, 67, 67, 67, 44, 44, 67, 67, 67, 67, 67, 92, 44, 55, 67, 67, + 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 67, 55, + 67, 67, 67, 44, 44, 44, 44, 67, 67, 92, 67, 67, 67, 67, 67, 67, + 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, + 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, + 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, + 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, + 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, + 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 24, 7, 7, 6, + 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, + 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, + 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 10, 21, + 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, + 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, + 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, + 1, 2, 15, 6, 18, 6, 23, 2, 12, 11, 9, 26, 26, 9, 26, 5, + 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, + 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, + 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, + 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, + 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, + 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, + 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, + 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 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, 17, 18, 19, 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, 20, + 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 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, 35, 0, 36, 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, + 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, + 0, 0, 40, 41, 42, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, + 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, + 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, + 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, + 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, + 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, + 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, + 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, + 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 75, 76, 0, 77, 78, 0, + 0, 79, 80, 0, 81, 62, 0, 82, 83, 0, 0, 84, 85, 86, 0, 0, + 0, 87, 0, 88, 0, 0, 51, 89, 51, 0, 90, 0, 91, 0, 0, 0, + 80, 0, 0, 0, 92, 93, 0, 94, 95, 96, 97, 0, 0, 0, 0, 0, + 51, 0, 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, 0,100, 0, 0, + 0, 0, 0,101,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,103, + 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 0, + 0,107, 0, 0, 0, 0, 0, 0,108, 0,109, 0,102, 0, 0, 0, + 0, 0,110,111, 0, 0, 0, 0, 0, 0, 0,112, 0, 0, 0, 0, + 0, 0, 0,113, 0,114, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, + 0, 13, 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, + 21, 0, 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, + 0, 27, 0, 0, 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, + 33, 0, 0, 35, 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, + 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, + 42, 0, 0, 0, 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, + 47, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, + 0, 51, 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, + 0, 56, 0, 0, 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, + 0, 0, 0, 61, 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, + 0, 0, 0, 67, 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, + 0, 0, 77, 78, 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, + 0, 81, 0, 0, 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, + 84, 0, 85, 0, 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, + 0, 0, 0, 88, 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, + 0, 0, 33, 0, 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, + 0, 0, 93, 0, 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, + 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 0,100,101, 93, 0, 0, + 102, 0, 0, 0, 84, 0, 0,103, 0, 0, 0,104,105, 0, 0,106, + 107, 0, 0, 0, 0, 0, 0,108, 0, 0,109, 0, 0, 0, 0,110, + 33, 0,111,112,113, 35, 0, 0,114, 0, 0, 0,115, 0, 0, 0, + 0, 0, 0,116, 0, 0,117, 0, 0, 0, 0,118, 88, 0, 0, 0, + 0, 0, 57, 0, 0, 0, 0, 52,119, 0, 0, 0, 0,120, 0, 0, + 121, 0, 0, 0, 0,119, 0, 0,122, 0, 0, 0, 0, 0, 0,123, + 0, 0, 0,124, 0, 0, 0,125, 0,126, 0, 0, 0, 0,127,128, + 129, 0,130, 0,131, 0, 0, 0,132,133,134, 0, 77, 0, 0, 0, + 0, 0, 35, 0, 0, 0,135, 0, 0, 0,136, 0, 0,137, 0, 0, + 138, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, + 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, + 0, 0, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, + 54, 21, 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, + 0, 0, 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, + 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, + 0, 0, 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, + 0, 77, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, + 0, 80, 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, + 0, 0, 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, + 1, 52, 15, 86, 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, + 1, 0, 0, 0, 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, + 0, 78, 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, + 21, 1, 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, + 81, 99,100, 4, 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, + 0, 0, 0, 61, 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, + 0, 0, 0, 38, 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, + 61, 0, 0, 0, 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, + 0, 0, 0,107, 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, + 0, 0, 0,108, 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, + 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, + 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, + 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, + 0, 38, 1, 58, 1, 58, 0, 0, 63, 89, 0, 0,115, 0, 0, 0, + 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, + 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, + 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, 0,117, + 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 38, 50, + 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, + 0, 0, 0, 1, 0, 0, 0,123, 4,122, 0, 0, 0, 1,124, 0, + 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, + 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, + 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, + 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, + 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, + 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, + 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, + 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, + 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, + 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, + 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, + 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, + 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, + 230,220,220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, + 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, + 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, + 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, + 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, + 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, + 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, + 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, + 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, + 0, 0, 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0, + 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, + 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, + 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, + 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, + 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, + 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, + 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, + 230,230, 7, 0, 16, 17, 17, 17, 17, 17, 17, 33, 17, 17, 17, 19, + 17, 17, 17, 17, 20,101, 17,113,129,169, 17, 27, 28, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,237, 0, 1, 2, 2, 0, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 6, 7, 8, 9, 0, 0, 0, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, + 0, 0, 21, 22, 0, 0, 0, 0, 23, 24, 25, 26, 0, 27, 0, 28, + 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, 0, 33, 34, 35, 36, 0, + 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, + 0, 0, 0, 0, 1, 2, 40, 41, 0, 1, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 0, 0, 3, 4, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, + 0, 0, 7, 1, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, + 0, 0, 10, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 11, 12, + 0, 13, 0, 14, 15, 16, 0, 0, 0, 0, 0, 1, 17, 18, 0, 19, + 7, 1, 0, 0, 0, 20, 20, 7, 20, 20, 20, 20, 20, 20, 20, 8, + 21, 0, 22, 0, 7, 23, 24, 0, 20, 20, 25, 0, 0, 0, 26, 27, + 1, 7, 20, 20, 20, 20, 20, 1, 28, 29, 30, 31, 0, 0, 20, 0, + 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 20, 20, + 20, 1, 0, 0, 8, 21, 32, 4, 0, 10, 0, 33, 7, 20, 20, 20, + 0, 0, 0, 0, 8, 34, 34, 35, 36, 34, 37, 0, 38, 1, 20, 20, + 0, 0, 39, 0, 1, 1, 0, 8, 21, 1, 20, 0, 0, 0, 1, 0, + 0, 40, 1, 1, 0, 0, 8, 21, 0, 1, 0, 1, 0, 1, 0, 0, + 0, 0, 26, 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 7, 20, 41, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 21, 0, 42, 43, 44, 0, 45, + 0, 8, 21, 0, 0, 0, 0, 0, 0, 0, 0, 46, 7, 1, 10, 1, + 0, 0, 0, 1, 20, 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 26, 34, 9, 0, 0, 20, 20, 1, 20, 20, 0, 0, 0, 0, 0, + 0, 0, 26, 21, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 9, 10, 11, 11, 11, 11, 12, 13, 13, 13, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 13, 22, 13, 13, 13, 13, 23, 24, 24, 25, 26, 13, 13, + 13, 27, 28, 29, 13, 30, 31, 32, 33, 34, 35, 36, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 13, 42, 7, 7, 43, 7, + 44, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 45, 0, 0, 1, + 2, 2, 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, 32, 33, 34, 35, 36, 37, 37, 37, 37, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 2, 2, 53, 54, 55, 56, + 57, 58, 59, 59, 59, 59, 60, 59, 59, 59, 59, 59, 59, 59, 61, 61, + 59, 59, 59, 59, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 59, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 79, 70, 70, 70, 70, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 81, 82, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 95, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 70, 70, 97, 98, 99,100,101,101,102,103,104,105,106,107,108,109, + 110,111, 96,112,113,114,115,116,117,118,119,119,120,121,122,123, + 124,125,126,127,128,129,130,131,132, 96,133,134,135,136,137,138, + 139,140,141,142,143, 96,144,145, 96,146,147,148,149, 96,150,151, + 152,153,154,155,156, 96,157,158,159,160, 96,161,162,163,164,164, + 164,164,164,164,164,165,166,164,167, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,168,169,169, + 169,169,169,169,169,169,170, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96,171,171,171,171,172, 96, 96, 96,173,173, + 173,173,174,175,176,177, 96, 96, 96, 96,178,179,180,181,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,183,182,182, + 182,182,182,182,184,184,184,185,186, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,187,188,189, + 190,191,191,192, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96,193,194, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,195,196, 59,197, + 198,199,200,201,202, 96,203,204,205, 59, 59,206, 59,207,208,208, + 208,208,208,209, 96, 96, 96, 96, 96, 96, 96, 96,210, 96,211,212, + 213, 96, 96,214, 96, 96, 96,215, 96, 96, 96, 96, 96,216,217,218, + 219, 96, 96, 96, 96, 96,220,221,222, 96,223,224, 96, 96,225,226, + 59,227,228, 96, 59, 59, 59, 59, 59, 59, 59,229,230,231,232,233, + 59, 59,234,235, 59,236, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,237, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,238, 70,239, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,240, 70, 70, 70, 70, + 70, 70, 70, 70, 70,241, 70, 70, 70, 70,242, 96, 96, 96, 70, 70, + 70, 70,243, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, + 70, 70, 70, 70,244, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70,245, 96, 96, 96, 96, 96, 96, 96, 96,246, 96, + 247,248, 0, 1, 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 0, 0, + 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 0, 19, 0, + 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, + 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, + 9, 9, 0, 9, 9, 9, 2, 2, 9, 9, 9, 9, 0, 9, 2, 2, + 2, 2, 9, 0, 9, 0, 9, 9, 9, 2, 9, 2, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, + 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, + 2, 2, 2, 2, 2, 2, 14, 14, 14, 2, 2, 2, 2, 14, 14, 14, + 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, + 37, 37, 37, 37, 2, 37, 37, 37, 37, 2, 2, 37, 37, 37, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 2, 2, 90, 90, + 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 2, 2, 95, 2, 37, 37, 37, 2, 2, 2, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, + 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, + 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, + 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, + 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, + 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, + 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, + 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, + 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, + 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, + 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, + 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, + 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, + 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, + 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, + 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, + 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, + 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, + 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, + 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, + 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, + 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, + 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, + 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, + 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, 2, 2, 23, 23, + 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, + 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, + 2, 2, 2, 16, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 2, 16, + 16, 16, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 2, 20, 20, 20, 2, 20, 20, 20, 20, 20, 20, 2, 2, + 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, 36, + 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, + 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, 2, + 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, + 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 24, 24, 24, 24, 2, 2, 2, 2, 2, 18, + 18, 2, 18, 2, 18, 18, 18, 18, 18, 2, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, + 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, + 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, 25, + 25, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 2, 25, 25, 25, 25, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 2, 2, 2, 2, 33, 33, + 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 2, 2, 2, 2, 8, 2, 2, 8, 8, + 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 30, 30, 30, 30, 2, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, + 30, 30, 30, 2, 2, 2, 30, 30, 2, 2, 2, 2, 2, 2, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 28, 28, + 28, 28, 28, 28, 28, 28, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 2, 2, 2, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 2, 46, 46, 46, 2, 46, 46, 2, 2, 2, 2, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 2, 2, 31, 31, + 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, 2, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, 28, + 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 2, 2, 2, 2, 48, 2, + 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 2, 2, 2, 2, 58, 58, + 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, + 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 2, 2, 54, 54, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 2, 91, 91, + 91, 91, 91, 2, 2, 91, 91, 91, 2, 2, 2, 2, 2, 2, 91, 91, + 91, 91, 91, 91, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 2, 2, 62, 62, + 62, 62, 62, 62, 62, 2, 76, 76, 76, 76, 76, 76, 76, 76, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 2, 2, 2, 2, 2, 2, + 2, 2, 93, 93, 93, 93, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2, + 2, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, + 73, 73, 73, 73, 73, 73, 6, 2, 2, 2, 2, 2, 2, 2, 8, 8, + 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 19, 19, + 19, 19, 19, 19, 9, 9, 9, 9, 9, 6, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 19, 19, 19, 19, 19, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, 9, 9, 2, 2, 2, 9, + 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, + 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 2, 2, + 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 19, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, + 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 56, 56, + 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, + 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, + 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, + 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, + 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, + 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, + 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 0, 0, + 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 12, 12, + 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, + 39, 39, 39, 39, 39, 2, 86, 86, 86, 86, 86, 86, 86, 86, 77, 77, + 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 2, 2, 2, 2, 79, 79, + 79, 79, 79, 79, 79, 79, 0, 0, 19, 19, 19, 19, 19, 19, 0, 0, + 0, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 19, 19, + 2, 19, 2, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 2, 2, 2, + 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, + 60, 60, 60, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 65, 65, + 65, 65, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, + 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, + 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 74, 74, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 74, 12, 12, + 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, + 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 68, 68, 68, 68, 68, 2, 68, 68, 68, 68, 68, 68, 2, 2, 68, 68, + 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 2, 2, 2, 2, 2, 2, 2, 2, 92, 92, 92, 92, 92, 87, 87, + 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 30, + 30, 30, 30, 30, 30, 2, 19, 19, 19, 0, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 87, 87, + 87, 87, 87, 87, 2, 2, 87, 87, 2, 2, 2, 2, 2, 2, 12, 12, + 12, 12, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 13, 13, + 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, + 2, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 2, 14, 14, 14, 14, 14, 2, 14, 2, 14, 14, + 2, 14, 14, 2, 14, 14, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, + 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 3, 3, + 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, + 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, + 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, + 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, + 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, + 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, + 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, + 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, + 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, + 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, + 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, + 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, + 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, + 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, + 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, + 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,110,110, + 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, + 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, + 19, 19, 19, 19, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81,120,120, + 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116, + 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,128,128, + 128,128,128,128,128,128,128,128,128, 2,128,128, 2, 2, 2, 2, + 2,128,128,128,128,128, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 2, 2, 2, 66, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 97, 97, + 97, 97, 97, 97, 97, 97, 2, 2, 2, 2, 97, 97, 97, 97, 2, 2, + 97, 97, 97, 97, 97, 97, 57, 57, 57, 57, 2, 57, 57, 2, 2, 2, + 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 2, 57, + 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 57, 2, + 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88,117,117, + 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112, + 112,112,112,112,112, 2, 2, 2, 2,112,112,112,112,112, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 2, 2, 2, 78, + 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 2, 2, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 82, 2, 2, 2, 2, 2,122,122,122,122,122,122,122,122,122,122, + 2, 2, 2, 2, 2, 2, 2,122,122,122,122, 2, 2, 2, 2,122, + 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 2, + 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,130,130,130,130, + 130, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144,144, + 144,144,144,144,144,144,144,144, 2, 2, 2, 2, 2, 2,156,156, + 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,147,147, + 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148, + 2, 2, 2, 2, 2, 2,158,158,158,158,158,158,158,158,158,158, + 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153, + 153,153, 2, 2, 2, 2,149,149,149,149,149,149,149,149,149,149, + 149,149,149,149,149, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, + 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, 2, + 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, + 85, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 85, 2, 2,101,101, + 101,101,101,101,101,101,101, 2, 2, 2, 2, 2, 2, 2,101,101, + 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 2, 96, 96,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111, 2,100,100,100,100,100,100,100,100, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,108,108, + 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108, + 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, 2, + 129,129,129,129, 2,129,129,129,129,129,129,129,129,129,129,129, + 129,129,129,129, 2,129,129,129, 2, 2, 2, 2, 2, 2,109,109, + 109,109,109,109,109,109,109,109,109, 2, 2, 2, 2, 2,109,109, + 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107,107, + 107,107,107, 2, 2,107,107, 2, 2,107,107,107,107,107,107,107, + 107,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, 2, + 107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, 2, + 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2, + 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,107,107, + 107,107,107, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, + 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, + 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, + 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, + 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, + 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, + 102,102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2,126,126, + 126,126,126,126,126,126,126,126,126, 2, 2,126,126,126,126,126, + 126,126, 2, 2, 2, 2,126,126,126,126,126,126,126, 2,142,142, + 142,142,142,142,142,142,142,142,142,142, 2, 2, 2, 2,125,125, + 125,125,125,125,125,125,125,125,125, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, + 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154,154, + 154,154,154,154,154,154,154,154,154,154,154,154, 2,154,154, 2, + 2,154,154,154,154,154,154,154, 2, 2, 2, 2, 2, 2,150,150, + 150,150,150,150,150,150, 2, 2,150,150,150,150,150,150,150,150, + 150,150,150, 2, 2, 2,141,141,141,141,141,141,141,141,140,140, + 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2,121,121, + 121,121,121,121,121,121,121, 2, 2, 2, 2, 2, 2, 2, 7, 7, + 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133, 2, + 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133, + 133,133,133,133, 2, 2,133,133,133,133,133, 2, 2, 2,134,134, + 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134, 2,134, + 134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138,138, + 138,138,138,138,138, 2,138,138, 2,138,138,138,138,138,138,138, + 138,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138,138, + 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, 2, + 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, + 143,143,143,143,143, 2,143,143, 2,143,143,143,143,143,143, 2, + 2, 2, 2, 2, 2, 2,143,143, 2, 2, 2, 2, 2, 2,145,145, + 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, + 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, + 163, 2, 2, 2,163,163,163,163, 2, 2, 2, 2, 2, 2, 86, 2, + 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, + 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, + 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2,127,127, + 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 79, 2, + 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115, + 115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159,159, + 159,159,159,159,159,159,159,159,159,159,159,159,159, 2,159,159, + 2, 2, 2, 2, 2, 2,103,103,103,103,103,103,103,103,103,103, + 103,103,103,103, 2, 2,119,119,119,119,119,119,119,119,119,119, + 119,119,119,119, 2, 2,119,119, 2,119,119,119,119,119, 2, 2, + 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146, + 146, 2, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, + 13, 13,155, 2, 2, 2,136,136,136,136,136,136,136,136,155,155, + 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2,136, 2, + 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 17, 17, 17, 17, 17, + 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 15, 15, 15, 15, 17, 17, + 17, 2, 2, 2, 2, 2, 2, 2, 15, 2, 2, 2, 2, 2, 15, 15, + 15, 2, 2, 17, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17,139,139, + 139,139,139,139,139,139,139,139,139,139, 2, 2, 2, 2,105,105, + 105,105,105,105,105,105,105,105,105, 2, 2, 2, 2, 2,105,105, + 105,105,105, 2, 2, 2,105, 2, 2, 2, 2, 2, 2, 2,105,105, + 2, 2,105,105,105,105, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 2, 2, + 0, 2, 2, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0,131,131,131,131,131,131,131,131,131,131, + 131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2,131, + 131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, 56, + 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56, + 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, 6, + 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151,151, + 151,151,151,151,151,151,151,151,151,151,151, 2, 2, 2,151,151, + 151,151,151,151, 2, 2,151,151, 2, 2, 2, 2,151,151,160,160, + 160,160,160,160,160,160,160,160,160,160,160,160,160, 2,152,152, + 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,164,164, + 164,164,164,164,164,164,164,164, 2, 2, 2, 2, 2, 2, 30, 30, + 30, 30, 2, 30, 30, 2,113,113,113,113,113,113,113,113,113,113, + 113,113,113, 2, 2,113,113,113,113,113,113,113,113, 2,132,132, + 132,132,132,132,132,132,132,132,132,132, 2, 2, 2, 2,132,132, + 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, + 3, 2, 3, 2, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, 2, + 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, 3, + 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, + 3, 3, 2, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 2, 2, + 2, 0, 0, 0, 0, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, + 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, + 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, + 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 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, 43, 44, + 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, + 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 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, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, + 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, + 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0, + 110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 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,117, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 118,119,120,121, 0,122,123,124,125,126, 0,127, 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,128,129,130,131, + 132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147, + 148,149,150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160, + 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 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, + 165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, + 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,169,170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176, + 177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192, + 193,194,195,196,197,198,199,200,201,202,203,204,205,206, 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, +}; +static const uint16_t +_hb_ucd_u16[9344] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 48, 72, 73, 48, 48, 74, 32, 75, 32, + 76, 48, 48, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 83, 84, 91, 92, 93, 94, 95, 96, 97, 84, 98, 99, 100, 88, 101, + 102, 83, 84, 103, 104, 105, 88, 106, 107, 108, 109, 110, 111, 112, 94, 113, + 114, 115, 84, 116, 117, 118, 88, 119, 120, 115, 84, 121, 122, 123, 88, 124, + 125, 115, 48, 126, 127, 128, 88, 129, 130, 131, 48, 132, 133, 134, 94, 135, + 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, + 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, + 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, + 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 280, 209, 281, 209, 209, 209, 209, 282, + 209, 283, 279, 284, 209, 285, 286, 209, 209, 209, 287, 140, 288, 140, 271, 271, + 271, 289, 209, 209, 209, 209, 290, 271, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 291, 292, 209, 209, 293, 209, 209, 209, 209, 209, 209, 294, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 271, 297, 209, 209, 298, 279, 299, 279, + 300, 301, 279, 279, 279, 302, 279, 303, 209, 209, 209, 279, 304, 209, 209, 305, + 209, 306, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 307, 308, + 13, 13, 13, 13, 13, 13, 309, 310, 11, 11, 311, 48, 48, 48, 312, 313, + 48, 314, 315, 315, 315, 315, 32, 32, 316, 317, 318, 319, 320, 321, 140, 140, + 209, 322, 209, 209, 209, 209, 209, 323, 209, 209, 209, 209, 209, 324, 140, 209, + 325, 326, 327, 328, 136, 48, 48, 48, 48, 329, 178, 48, 48, 48, 48, 330, + 331, 48, 48, 136, 48, 48, 48, 48, 200, 332, 48, 48, 209, 209, 333, 48, + 209, 334, 335, 209, 336, 337, 209, 209, 335, 209, 209, 337, 209, 209, 209, 209, + 48, 48, 48, 48, 209, 209, 209, 209, 48, 338, 48, 48, 48, 48, 48, 48, + 151, 209, 209, 209, 287, 48, 48, 229, 339, 48, 340, 140, 13, 13, 341, 342, + 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, + 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, + 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, + 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, + 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 315, 11, 11, 385, 386, 11, + 11, 11, 11, 11, 48, 48, 387, 192, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, + 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, + 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, + 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, + 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, + 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, + 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, + 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, + 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 140, 140, 140, 140, + 48, 48, 48, 314, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, + 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, + 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, + 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, + 48, 48, 475, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 271, 476, + 48, 48, 477, 478, 140, 140, 140, 479, 48, 464, 480, 48, 62, 481, 140, 48, + 482, 140, 140, 48, 483, 140, 48, 314, 484, 48, 48, 485, 486, 457, 487, 488, + 222, 48, 48, 489, 490, 48, 196, 192, 491, 48, 492, 493, 494, 48, 48, 495, + 222, 48, 48, 496, 497, 498, 499, 500, 48, 97, 501, 502, 503, 140, 140, 140, + 504, 505, 506, 48, 48, 507, 508, 192, 509, 83, 84, 510, 511, 512, 513, 514, + 48, 48, 48, 515, 516, 517, 478, 140, 48, 48, 48, 518, 519, 192, 140, 140, + 48, 48, 520, 521, 522, 523, 140, 140, 48, 48, 48, 524, 525, 192, 526, 140, + 48, 48, 527, 528, 192, 140, 140, 140, 48, 173, 529, 530, 314, 140, 140, 140, + 48, 48, 501, 531, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 532, + 533, 534, 48, 535, 536, 192, 140, 140, 140, 140, 537, 48, 48, 538, 539, 140, + 540, 48, 48, 541, 542, 543, 48, 48, 544, 545, 546, 48, 48, 48, 48, 196, + 547, 140, 140, 140, 140, 140, 140, 140, 84, 48, 520, 548, 549, 148, 175, 550, + 48, 551, 552, 553, 140, 140, 140, 140, 554, 48, 48, 555, 556, 192, 557, 48, + 558, 559, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 560, + 561, 115, 48, 562, 563, 192, 140, 140, 140, 140, 140, 100, 271, 564, 565, 566, + 48, 207, 140, 140, 140, 140, 140, 140, 272, 272, 272, 272, 272, 272, 567, 568, + 48, 48, 48, 48, 388, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 569, + 48, 48, 48, 570, 571, 572, 140, 140, 48, 48, 48, 48, 314, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 573, + 48, 48, 48, 574, 575, 576, 577, 578, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 271, 579, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 580, 581, 582, 582, 583, 584, 140, 140, 140, 140, 585, 586, + 48, 48, 48, 48, 48, 48, 48, 440, 48, 48, 48, 48, 48, 199, 140, 140, + 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 587, + 48, 48, 588, 589, 140, 590, 591, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 592, 593, 140, 140, 140, 140, 140, + 32, 32, 594, 32, 595, 209, 209, 209, 209, 209, 209, 209, 323, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 596, 209, 209, 209, 597, 598, + 599, 209, 600, 209, 209, 209, 288, 140, 209, 209, 209, 209, 601, 140, 140, 140, + 140, 140, 140, 140, 271, 602, 271, 602, 209, 209, 209, 209, 209, 287, 271, 461, + 9, 603, 11, 604, 605, 606, 241, 9, 607, 608, 609, 610, 611, 9, 603, 11, + 612, 613, 11, 614, 615, 616, 617, 9, 618, 11, 9, 603, 11, 604, 605, 11, + 241, 9, 607, 617, 9, 618, 11, 9, 603, 11, 619, 9, 620, 621, 622, 623, + 11, 624, 9, 625, 626, 627, 628, 11, 629, 9, 630, 11, 631, 632, 632, 632, + 32, 32, 32, 633, 32, 32, 634, 635, 636, 637, 45, 140, 140, 140, 140, 140, + 638, 639, 640, 140, 140, 140, 140, 140, 641, 642, 643, 27, 27, 27, 644, 140, + 645, 140, 140, 140, 140, 140, 140, 140, 48, 48, 151, 646, 647, 140, 140, 140, + 140, 48, 648, 140, 48, 48, 649, 650, 140, 140, 140, 140, 140, 48, 651, 192, + 140, 140, 140, 140, 140, 140, 652, 200, 48, 48, 48, 48, 653, 595, 140, 140, + 9, 9, 607, 11, 654, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 499, + 271, 271, 655, 656, 140, 140, 140, 140, 499, 271, 657, 658, 140, 140, 140, 140, + 659, 48, 660, 661, 662, 663, 664, 665, 666, 206, 667, 206, 140, 140, 140, 668, + 209, 209, 669, 209, 209, 209, 209, 209, 209, 323, 334, 670, 670, 670, 209, 324, + 671, 209, 209, 209, 209, 209, 209, 209, 209, 209, 672, 140, 140, 140, 673, 209, + 674, 209, 209, 669, 675, 676, 324, 140, 209, 209, 209, 209, 209, 209, 209, 677, + 209, 209, 209, 209, 209, 678, 426, 426, 209, 209, 209, 209, 209, 209, 209, 679, + 209, 209, 209, 209, 209, 176, 669, 427, 669, 209, 209, 209, 680, 176, 209, 209, + 680, 209, 672, 676, 140, 140, 140, 140, 209, 209, 209, 209, 209, 323, 672, 426, + 675, 209, 209, 681, 682, 669, 675, 675, 209, 683, 209, 209, 288, 140, 140, 192, + 48, 48, 48, 48, 48, 48, 140, 140, 48, 48, 48, 207, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 478, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, + 48, 204, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 71, 48, 48, 48, + 48, 48, 48, 140, 140, 140, 140, 140, 684, 140, 570, 570, 570, 570, 570, 570, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, + 391, 391, 391, 391, 391, 391, 391, 685, 391, 391, 391, 391, 391, 391, 391, 686, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, + 0, 0, 0, 0, 0, 4, 0, 4, 2, 2, 5, 2, 2, 2, 5, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 6, 0, 0, 0, 0, 7, 8, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 11, + 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 16, 17, 14, 14, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 20, 21, 21, 21, 22, 20, 21, 21, 21, 21, + 21, 23, 24, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 27, 28, 26, + 29, 30, 31, 32, 31, 31, 31, 31, 33, 34, 35, 31, 31, 31, 36, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 29, 31, 31, 31, 31, + 37, 38, 37, 37, 37, 37, 37, 37, 37, 39, 31, 31, 31, 31, 31, 31, + 40, 40, 40, 40, 40, 40, 41, 26, 42, 42, 42, 42, 42, 42, 42, 43, + 44, 44, 44, 44, 44, 45, 44, 46, 47, 47, 47, 48, 37, 49, 31, 31, + 31, 50, 51, 31, 31, 31, 31, 31, 31, 31, 31, 31, 52, 31, 31, 31, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 53, 55, 53, 53, 53, + 56, 57, 58, 59, 59, 60, 61, 62, 57, 63, 64, 65, 66, 59, 59, 67, + 68, 69, 70, 71, 71, 72, 73, 74, 69, 75, 76, 77, 78, 71, 79, 26, + 80, 81, 82, 83, 83, 84, 85, 86, 81, 87, 88, 26, 89, 83, 90, 91, + 92, 93, 94, 95, 95, 96, 97, 98, 93, 99, 100, 101, 102, 95, 95, 26, + 103, 104, 105, 106, 107, 104, 108, 109, 104, 105, 110, 26, 111, 108, 108, 112, + 113, 114, 115, 113, 113, 115, 113, 116, 114, 117, 118, 119, 120, 113, 121, 113, + 122, 123, 124, 122, 122, 124, 125, 126, 123, 127, 128, 128, 129, 122, 130, 26, + 131, 132, 133, 131, 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, + 136, 137, 138, 139, 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, + 146, 147, 147, 147, 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, + 150, 151, 152, 152, 153, 152, 152, 154, 155, 156, 152, 157, 26, 26, 26, 26, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 158, 158, 158, 160, 159, 158, + 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, 26, 26, 26, 26, + 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, + 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, + 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, + 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 172, 171, 170, 170, 170, 170, + 170, 171, 170, 170, 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 170, 170, + 170, 170, 171, 170, 170, 170, 170, 170, 170, 170, 170, 173, 170, 170, 170, 174, + 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 179, 179, 179, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 181, 183, + 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, + 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, + 197, 198, 198, 199, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 200, + 198, 198, 198, 198, 198, 201, 178, 178, 178, 178, 178, 178, 178, 178, 202, 26, + 203, 203, 203, 204, 203, 205, 203, 205, 206, 203, 207, 207, 207, 208, 209, 26, + 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 194, 194, 194, 194, + 214, 214, 214, 215, 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, + 216, 219, 216, 219, 216, 220, 9, 9, 9, 221, 26, 26, 26, 26, 26, 26, + 222, 222, 222, 222, 222, 222, 222, 222, 222, 223, 222, 222, 222, 222, 222, 224, + 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, 226, 226, 226, 226, 227, 228, + 229, 229, 229, 229, 229, 229, 229, 230, 229, 231, 232, 232, 232, 232, 232, 232, + 18, 233, 165, 165, 165, 165, 165, 234, 225, 26, 235, 9, 236, 237, 238, 239, + 2, 2, 2, 2, 240, 241, 2, 2, 2, 2, 2, 242, 243, 244, 2, 245, + 2, 2, 2, 2, 2, 2, 2, 246, 9, 9, 9, 9, 9, 9, 9, 9, + 14, 14, 247, 247, 14, 14, 14, 14, 247, 247, 14, 248, 14, 14, 14, 247, + 14, 14, 14, 14, 14, 14, 249, 14, 249, 14, 250, 251, 14, 14, 252, 253, + 0, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 256, 257, + 0, 258, 2, 259, 0, 0, 0, 0, 260, 26, 9, 9, 9, 9, 261, 26, + 0, 0, 0, 0, 262, 263, 4, 0, 0, 264, 0, 0, 2, 2, 2, 2, + 2, 265, 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, 258, 26, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, + 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, + 0, 0, 269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 2, 2, 2, 2, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 271, 272, + 165, 165, 165, 165, 166, 167, 273, 273, 273, 273, 273, 273, 273, 274, 275, 274, + 170, 170, 172, 26, 172, 172, 172, 172, 172, 172, 172, 172, 18, 18, 18, 18, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276, 26, 26, 26, 26, + 277, 277, 277, 278, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 279, 26, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 26, 26, 26, 0, 0, + 281, 0, 0, 0, 282, 283, 0, 284, 285, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 287, 288, 289, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 291, + 292, 293, 293, 293, 293, 293, 294, 169, 169, 169, 169, 169, 169, 169, 169, 169, + 169, 295, 0, 0, 293, 293, 293, 293, 0, 0, 0, 0, 296, 297, 290, 290, + 169, 169, 169, 295, 0, 0, 0, 0, 0, 0, 0, 0, 169, 169, 169, 298, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 290, 290, 290, 290, 290, 299, + 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 290, 0, 0, 0, 0, 0, + 277, 277, 277, 277, 277, 277, 277, 277, 0, 0, 0, 0, 0, 0, 0, 0, + 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, + 300, 301, 300, 300, 300, 300, 300, 300, 302, 26, 303, 303, 303, 303, 303, 303, + 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, + 304, 304, 304, 304, 304, 305, 26, 26, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 26, + 0, 0, 0, 0, 307, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 308, 2, 2, 2, 2, 2, 2, 2, 309, 310, 311, 26, 26, 312, 2, + 313, 313, 313, 313, 313, 314, 0, 315, 316, 316, 316, 316, 316, 316, 316, 26, + 317, 317, 317, 317, 317, 317, 317, 317, 318, 319, 317, 320, 53, 53, 53, 53, + 321, 321, 321, 321, 321, 322, 323, 323, 323, 323, 324, 325, 169, 169, 169, 326, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 328, 327, 329, 164, 164, 164, 330, + 331, 331, 331, 331, 331, 331, 332, 26, 331, 333, 331, 334, 164, 164, 164, 164, + 335, 335, 335, 335, 335, 335, 335, 335, 336, 26, 26, 337, 338, 338, 339, 26, + 340, 340, 340, 26, 172, 172, 2, 2, 2, 2, 2, 341, 342, 343, 176, 176, + 176, 176, 176, 176, 176, 176, 176, 176, 338, 338, 338, 338, 338, 344, 338, 345, + 169, 169, 169, 169, 346, 26, 169, 169, 295, 347, 169, 169, 169, 169, 169, 346, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 348, 26, 26, 26, 26, + 349, 26, 350, 351, 25, 25, 352, 353, 354, 25, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 355, 26, 356, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 357, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 358, 31, 31, 31, 31, 31, 31, 359, 26, 26, 26, 26, 31, 31, + 9, 9, 0, 315, 9, 360, 0, 0, 0, 0, 361, 0, 258, 296, 362, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 363, + 364, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 365, 290, 289, 290, + 290, 290, 290, 366, 169, 169, 169, 295, 367, 367, 367, 368, 258, 258, 26, 369, + 370, 371, 370, 370, 372, 370, 370, 373, 370, 374, 370, 374, 26, 26, 26, 26, + 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 370, 375, + 376, 0, 0, 0, 0, 0, 377, 0, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 253, 0, 378, 379, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 380, + 381, 381, 381, 382, 383, 383, 383, 383, 383, 383, 384, 26, 385, 0, 0, 296, + 386, 386, 386, 386, 387, 388, 389, 389, 389, 390, 391, 391, 391, 391, 391, 392, + 393, 393, 393, 394, 395, 395, 395, 395, 396, 395, 397, 26, 26, 26, 26, 26, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, 399, 399, 399, 399, 399, 399, + 400, 400, 400, 401, 400, 402, 403, 403, 403, 403, 404, 403, 403, 403, 403, 404, + 405, 405, 405, 405, 405, 26, 406, 406, 406, 406, 406, 406, 407, 408, 409, 410, + 409, 410, 411, 409, 412, 409, 412, 413, 26, 26, 26, 26, 26, 26, 26, 26, + 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, 414, + 414, 414, 414, 414, 414, 414, 415, 26, 414, 414, 416, 26, 414, 26, 26, 26, + 417, 2, 2, 2, 2, 2, 418, 309, 26, 26, 26, 26, 26, 26, 26, 26, + 419, 420, 421, 421, 421, 421, 422, 423, 424, 424, 425, 424, 426, 426, 426, 426, + 427, 427, 427, 428, 429, 427, 26, 26, 26, 26, 26, 26, 430, 430, 431, 432, + 433, 433, 433, 434, 435, 435, 435, 436, 26, 26, 26, 26, 26, 26, 26, 26, + 437, 437, 437, 437, 438, 438, 438, 439, 438, 438, 440, 438, 438, 438, 438, 438, + 441, 442, 443, 444, 445, 445, 446, 447, 445, 448, 445, 448, 449, 449, 449, 449, + 450, 450, 450, 450, 26, 26, 26, 26, 451, 451, 451, 451, 452, 453, 452, 26, + 454, 454, 454, 454, 454, 454, 455, 456, 457, 457, 458, 457, 459, 459, 460, 459, + 461, 461, 462, 463, 26, 464, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 465, 465, 465, 465, 465, 465, 465, 465, 465, 466, 26, 26, 26, 26, 26, 26, + 467, 467, 467, 467, 467, 467, 468, 26, 467, 467, 467, 467, 467, 467, 468, 469, + 470, 470, 470, 470, 470, 26, 470, 471, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 31, 31, 50, + 472, 472, 472, 472, 472, 473, 474, 26, 26, 26, 26, 26, 26, 26, 26, 475, + 476, 476, 476, 476, 476, 26, 477, 477, 477, 477, 477, 478, 26, 26, 479, 479, + 479, 480, 26, 26, 26, 26, 481, 481, 481, 482, 26, 26, 483, 483, 484, 26, + 485, 485, 485, 485, 485, 485, 485, 485, 485, 486, 487, 485, 485, 485, 486, 488, + 489, 489, 489, 489, 489, 489, 489, 489, 490, 491, 492, 492, 492, 493, 492, 494, + 495, 495, 495, 495, 495, 495, 496, 495, 495, 26, 497, 497, 497, 497, 498, 26, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 500, 137, 501, 26, + 502, 502, 503, 502, 502, 502, 502, 502, 504, 26, 26, 26, 26, 26, 26, 26, + 505, 506, 507, 508, 507, 509, 510, 510, 510, 510, 510, 510, 510, 511, 510, 512, + 513, 514, 515, 516, 516, 517, 518, 519, 514, 520, 521, 522, 523, 524, 524, 26, + 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 526, 527, 26, 26, 26, + 528, 528, 528, 528, 528, 528, 528, 528, 528, 26, 528, 529, 26, 26, 26, 26, + 530, 530, 530, 530, 530, 530, 531, 530, 530, 530, 530, 531, 26, 26, 26, 26, + 532, 532, 532, 532, 532, 532, 532, 532, 533, 26, 532, 534, 198, 535, 26, 26, + 536, 536, 536, 536, 536, 536, 536, 537, 536, 537, 26, 26, 26, 26, 26, 26, + 538, 538, 538, 539, 538, 540, 538, 538, 541, 26, 26, 26, 26, 26, 26, 26, + 542, 542, 542, 542, 542, 542, 542, 543, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 545, 546, + 547, 548, 549, 550, 550, 550, 551, 552, 547, 26, 550, 553, 26, 26, 26, 26, + 26, 26, 26, 26, 554, 555, 554, 554, 554, 554, 554, 555, 556, 26, 26, 26, + 557, 557, 557, 557, 557, 557, 557, 557, 557, 26, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 559, 26, 178, 178, 560, 560, 560, 560, 560, 560, 560, 561, + 53, 562, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 563, 564, 563, 563, 563, 563, 565, 563, 566, 26, 563, 563, 563, 567, 568, 568, + 568, 568, 569, 568, 568, 570, 571, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 572, 573, 574, 574, 574, 574, 572, 575, 574, 26, 574, 576, 577, 578, 579, 579, + 579, 580, 581, 582, 579, 583, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 584, 584, 584, 585, + 586, 586, 587, 586, 586, 586, 586, 588, 586, 586, 586, 589, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 590, 26, 108, 108, 108, 108, 108, 108, 591, 592, + 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, + 593, 593, 593, 594, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 595, 596, 26, + 593, 593, 593, 593, 593, 593, 593, 593, 597, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 599, 26, + 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, + 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 601, 26, 26, 26, 26, 26, + 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 602, 602, 602, 602, 602, 602, 603, 26, 26, 26, 26, 26, 26, 26, + 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, 306, + 306, 306, 306, 306, 306, 306, 306, 604, 605, 605, 605, 606, 605, 607, 608, 608, + 608, 608, 608, 608, 608, 608, 608, 609, 608, 610, 611, 611, 611, 612, 612, 26, + 613, 613, 613, 613, 613, 613, 613, 613, 614, 26, 613, 615, 615, 613, 613, 616, + 613, 613, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 617, 617, 617, 617, 617, 617, 617, 617, + 617, 617, 617, 618, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 619, 619, 619, 619, 619, 619, 619, 619, 619, 620, 619, 619, 619, 619, 619, 619, + 619, 621, 619, 619, 26, 26, 26, 26, 26, 26, 26, 26, 622, 26, 348, 26, + 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, + 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 623, 26, + 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, + 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 625, 26, 26, 26, 26, 26, + 623, 626, 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, 627, 628, + 629, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 630, 26, 631, 26, 26, 26, 632, 26, 633, 26, 634, 634, + 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, + 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 635, + 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, 637, 636, 638, + 636, 639, 636, 640, 296, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 9, 9, 9, 9, 9, 641, 9, 9, 221, 26, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 296, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276, 26, + 0, 0, 0, 0, 258, 364, 0, 0, 0, 0, 0, 0, 642, 643, 0, 644, + 645, 646, 0, 0, 0, 647, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, + 14, 14, 14, 14, 14, 14, 14, 14, 247, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 296, 26, 0, 0, 296, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 26, 0, 0, 0, 260, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, + 0, 0, 0, 255, 648, 649, 0, 650, 651, 0, 0, 0, 0, 0, 0, 0, + 269, 652, 255, 255, 0, 0, 0, 653, 654, 655, 656, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 276, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, + 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, 657, + 657, 658, 26, 659, 660, 657, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 2, 2, 2, 349, 661, 309, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 662, 270, 270, 663, 664, 665, 18, 18, 18, 18, 18, 18, 18, 666, 26, 26, + 26, 667, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 668, 668, 668, 668, 668, 669, 668, 670, 668, 671, 26, 26, 26, 26, 26, 26, + 26, 26, 672, 672, 672, 673, 26, 26, 674, 674, 674, 674, 674, 674, 674, 675, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 676, 676, 676, 676, 676, 677, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 172, 678, 170, 172, + 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, 679, + 679, 679, 679, 679, 679, 679, 679, 679, 680, 679, 681, 26, 26, 26, 26, 26, + 682, 682, 682, 682, 682, 682, 682, 682, 682, 683, 682, 684, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 364, 0, + 0, 0, 0, 0, 0, 0, 378, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 364, 0, 0, 0, 0, 0, 0, 276, 26, 26, 26, 26, 26, 26, 26, 26, + 685, 31, 31, 31, 686, 687, 688, 689, 690, 691, 686, 692, 686, 688, 688, 693, + 31, 694, 31, 695, 696, 694, 31, 695, 26, 26, 26, 26, 26, 26, 51, 26, + 0, 0, 0, 0, 0, 296, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 296, 26, 0, 258, 364, 0, 364, 0, 364, 0, 0, 0, 276, 26, + 0, 0, 0, 0, 0, 276, 26, 26, 26, 26, 26, 26, 697, 0, 0, 0, + 698, 26, 0, 0, 0, 0, 0, 296, 0, 260, 315, 26, 276, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 699, 0, 378, 0, 378, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 700, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 315, 0, 296, 260, 26, + 0, 296, 0, 0, 0, 0, 0, 0, 0, 26, 0, 315, 0, 0, 0, 0, + 0, 26, 0, 0, 0, 276, 315, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 296, 26, 0, 276, 0, 378, + 0, 260, 0, 0, 0, 0, 0, 269, 276, 697, 0, 296, 0, 260, 0, 260, + 0, 0, 361, 0, 0, 0, 0, 0, 0, 266, 26, 26, 26, 26, 0, 315, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 26, 26, 26, 26, + 277, 277, 277, 277, 277, 277, 277, 348, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 280, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 348, 26, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 701, 26, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 280, 26, 26, 26, 26, + 277, 277, 277, 280, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 702, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 703, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, + 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, + 1124,1125,1126,1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, + 0,1219,1226,1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, + 943,1128, 944,1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, + 0, 0, 973,1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, + 991,1176, 993,1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199, + 1007, 0, 0, 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, + 0, 0, 0,1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231, + 1049,1235, 0, 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258, + 1069,1255,1077,1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275, + 1089,1276,1096,1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0, + 1053,1239, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093, + 1280, 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, 949,1134,1010, + 1195,1050,1236,1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347, + 1418,1419,1323,1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424, + 1202, 0, 0, 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, + 950,1135, 951,1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238, + 1061,1247,1062,1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, + 0, 0, 0, 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232, + 1422,1423,1113,1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461, + 1514, 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,1446,1458,1468,1476,1480,1486, + 1517, 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,1489,1503,1494,1500,1508, 0, + 0, 0, 0,1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1526,1528, 0,1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0, + 1548,1550, 0,1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, + 0, 0, 0, 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1529,1551, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1523,1545,1524,1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, + 0, 0,1533,1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560, + 1540,1562,1541,1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, + 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, + 0,1623, 0, 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, + 0, 0, 0,1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, + 0, 0, 0, 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0, + 1646, 0, 0, 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, + 0, 0, 0,1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, + 0, 0,1661, 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, + 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, + 0, 0,1670, 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, + 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0, + 1684,1685, 0,1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 953,1138, 955,1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, + 966,1151, 967,1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, + 989,1174, 995,1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356, + 1017,1203,1019,1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214, + 1029,1215,1030,1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363, + 1382,1384,1383,1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251, + 1068,1254,1070,1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266, + 1078,1265,1095,1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287, + 1101,1288,1102,1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302, + 1119,1308,1122,1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, + 952,1137, 947,1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375, + 1370,1374,1373,1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353, + 1325,1352,1328,1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234, + 1331,1358,1330,1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403, + 1397,1402,1399,1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412, + 1409,1414,1109,1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0, + 1471,1472,1701,1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721, + 1477,1478,1729,1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0, + 1481,1482,1737,1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757, + 1490,1491,1765,1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776, + 1495,1496,1777,1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0, + 1504,1505,1785,1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793, + 1509,1510,1794,1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814, + 1467, 21,1475, 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0, + 1704,1708,1709,1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728, + 1740,1744,1745,1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764, + 1797,1801,1802,1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821, + 1470,1469,1822,1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, + 0,1315,1823,1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828, + 1488,1487,1513, 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833, + 1502,1501,1516, 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, + 0, 0,1824,1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, + 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, + 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1845, 0, 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, + 937, 0,1850, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938, + 1851,1852, 0, 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0, + 1857,1858, 0, 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870, + 1859,1860,1865,1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0, + 1879, 0,1880, 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0, + 1887, 0,1888, 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0, + 1892,1893, 0,1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, + 0, 0, 0, 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0, + 1906, 0,1907, 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0, + 1914, 0,1915, 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0, + 1919,1920, 0,1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, + 0, 0, 0, 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, + 710, 385, 724, 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, + 663, 676, 688, 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, + 810, 275, 462, 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, + 368, 414, 481, 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, + 811, 701, 233, 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, + 594, 766, 167, 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, + 313, 496, 518, 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, + 424, 732, 428, 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, + 193, 245, 297, 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, + 337, 366, 459, 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, + 683, 697, 292, 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, + 608, 752, 778, 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, + 479, 523, 655, 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, + 791, 136, 340, 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, + 377, 391, 406, 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, + 659, 772, 805, 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, + 153, 288, 346, 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, + 227, 0, 379, 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, + 661, 0, 703, 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, + 118, 127, 130, 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, + 335, 369, 375, 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, + 549, 550, 554, 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, + 690, 695, 705, 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, + 102, 145, 134, 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, + 250, 254, 294, 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, + 370, 379, 388, 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, + 493, 507, 512, 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, + 591, 597, 607, 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, + 709, 717, 728, 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, + 847, 857, 55, 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575, + 1576,1577,1579,1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0, + 1593,1594, 0,1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950, + 1951,1952,1953,1954,1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959, + 1961, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 106, 104, 107, 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, + 132, 137, 827, 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, + 157, 158, 159, 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, + 181, 182, 182, 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, + 197, 199, 200, 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, + 153, 234, 221, 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, + 836, 837, 247, 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, + 261, 839, 262, 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, + 278, 281, 282, 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, + 298, 845, 845, 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, + 316, 48, 47, 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, + 335, 336, 338, 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, + 358, 356, 49, 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, + 52, 51, 140, 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, + 396, 402, 404, 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, + 860, 418, 57, 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, + 437, 441, 438, 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, + 866, 867, 461, 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, + 483, 485, 486, 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, + 507, 508, 511, 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, + 528, 880, 879, 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, + 541, 69, 885, 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, + 71, 890, 570, 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, + 596, 75, 895, 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, + 853, 77, 615, 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, + 626, 628, 164, 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, + 638, 643, 644, 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, + 909, 910, 84, 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, + 677, 678, 86, 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, + 702, 709, 711, 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, + 918, 919, 739, 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, + 764, 922, 91, 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, + 789, 928, 792, 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, + 930, 99, 931, 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[196] = +{ + 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, + 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, + 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, + 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, + 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, + 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, + 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, + 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, + -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, + -1, 0, 1, -1, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[6808+(((_hb_ucd_u8[1312+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[8800+(((_hb_ucd_u8[8244+(((_hb_ucd_u8[7784+(((_hb_ucd_u8[7432+(((_hb_ucd_u8[7186+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9548+(((_hb_ucd_u8[9428+(((_hb_ucd_b4(9300+_hb_ucd_u8,u>>2>>3>>3))<<3)+((u>>2>>3)&7u))])<<3)+((u>>2)&7u))])<<2)+((u)&3u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[11070+(((_hb_ucd_u16[2048+(((_hb_ucd_u8[10334+(((_hb_ucd_u8[9884+(u>>3>>4>>4)])<<4)+((u>>3>>4)&15u))])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[6032+(((_hb_ucd_u8[17084+(((_hb_ucd_u8[16702+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + + +#elif !defined(HB_NO_UCD_UNASSIGNED) + +static const uint8_t +_hb_ucd_u8[14752] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 11, 12, 13, 13, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 22, 22, 22, 22, 24, 7, 7, + 25, 26, 22, 22, 22, 27, 28, 29, 22, 30, 31, 32, 33, 34, 35, 36, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, 7, 41, 22, 42, + 7, 7, 43, 7, 44, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 45, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 46, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 47, + 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, 34, 35, 36, 37, 38, 39, 34, 34, 34, 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, 64, 65, 66, 67, 68, 69, 70, 71, 69, 72, 73, + 69, 69, 64, 74, 64, 64, 75, 76, 77, 78, 79, 80, 81, 82, 69, 83, + 84, 85, 86, 87, 88, 89, 69, 69, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 90, 34, 34, 34, 34, + 91, 34, 34, 34, 34, 34, 34, 34, 34, 92, 34, 34, 93, 94, 95, 96, + 97, 98, 99,100,101,102,103,104, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,105, + 106,106,106,106,106,106,106,106,106,106,106,106,106,106,106,106, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107, + 107,107, 34, 34,108,109,110,111, 34, 34,112,113,114,115,116,117, + 118,119,120,121,122,123,124,125,126,127,128,129, 34, 34,130,131, + 132,133,134,135,136,137,138,139,140,141,142,122,143,144,145,146, + 147,148,149,150,151,152,153,122,154,155,122,156,157,158,159,122, + 160,161,162,163,164,165,166,122,167,168,169,170,122,171,172,173, + 34, 34, 34, 34, 34, 34, 34,174,175, 34,176,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,177, + 34, 34, 34, 34, 34, 34, 34, 34,178,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122, 34, 34, 34, 34,179,122,122,122, + 34, 34, 34, 34,180,181,182,183,122,122,122,122,184,185,186,187, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,188, + 34, 34, 34, 34, 34, 34, 34, 34, 34,189,190,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,122,191, + 34, 34,192, 34, 34,193,122,122,122,122,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,194,195,122,122,122,122,122,122, + 122,122,122,122,122,122,122,122,122,122,122,122,122,122,196,197, + 69,198,199,200,201,202,203,122,204,205,206,207,208,209,210,211, + 69, 69, 69, 69,212,213,122,122,122,122,122,122,122,122,214,122, + 215,216,217,122,122,218,122,122,122,219,122,122,122,122,122,220, + 34,221,222,122,122,122,122,122,223,224,225,122,226,227,122,122, + 228,229,230,231,232,122, 69,233, 69, 69, 69, 69, 69,234,235,236, + 237,238, 69, 69,239,240, 69,241,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,242, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,243, 34, + 244, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,245, 34, 34, + 34, 34, 34, 34, 34, 34, 34,246, 34, 34, 34, 34,247,122,122,122, + 34, 34, 34, 34,248,122,122,122,122,122,122,122,122,122,122,122, + 34, 34, 34, 34, 34, 34,249, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34,250,122,122,122,122,122,122,122,122, + 251,122,252,253,122,122,122,122,122,122,122,122,122,122,122,122, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,254, + 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,255, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 44, 45, 16, 10, + 44, 44, 41, 46, 11, 47, 47, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 48, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 49, 34, 32, 34, 11, + 32, 50, 43, 43, 51, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 47, 52, 2, 2, 2, + 16, 16, 16, 16, 53, 54, 55, 56, 57, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 43, 43, 43, 58, 59, 60, 43, 59, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 62, + 36, 63, 64, 44, 44, 44, 44, 44, 65, 65, 65, 8, 9, 66, 2, 67, + 43, 43, 43, 43, 43, 60, 68, 2, 69, 36, 36, 36, 36, 70, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 71, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 72, 43, 43, 43, 73, 50, 43, 43, 74, 75, 76, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 77, 78, 2, 2, 2, 2, 2, 2, 2, 79, + 70, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 80, 62, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 71, 44, 44, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, + 43, 43, 40, 21, 2, 81, 57, 20, 36, 36, 36, 43, 43, 75, 43, 43, + 43, 43, 75, 43, 75, 43, 43, 44, 2, 2, 2, 2, 2, 2, 2, 64, + 36, 36, 36, 36, 70, 43, 44, 64, 36, 36, 36, 36, 36, 61, 44, 44, + 36, 36, 36, 36, 82, 36, 36, 61, 65, 44, 44, 44, 43, 43, 43, 43, + 36, 36, 36, 36, 83, 43, 43, 43, 43, 84, 43, 43, 43, 43, 43, 43, + 43, 85, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 85, 71, 86, + 87, 43, 43, 43, 85, 86, 87, 86, 70, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 88, 36, 36, 36, 36, 36, 36, 36, + 70, 86, 62, 36, 36, 36, 61, 62, 61, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 36, 36, 36, 61, 61, 44, 36, 36, 44, 71, 86, + 87, 43, 80, 89, 90, 89, 87, 61, 44, 44, 44, 89, 44, 44, 36, 62, + 36, 43, 44, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 56, 63, 80, + 57, 85, 62, 36, 36, 61, 44, 62, 61, 36, 62, 61, 36, 44, 80, 86, + 87, 80, 44, 57, 80, 57, 43, 44, 57, 44, 44, 44, 62, 36, 61, 61, + 44, 44, 44, 7, 7, 7, 7, 7, 43, 36, 70, 64, 44, 44, 44, 44, + 57, 85, 62, 36, 36, 36, 36, 62, 36, 62, 36, 36, 36, 36, 36, 36, + 61, 36, 62, 36, 36, 44, 71, 86, 87, 43, 43, 57, 85, 89, 87, 44, + 61, 44, 44, 44, 44, 44, 44, 44, 66, 44, 44, 44, 62, 43, 43, 43, + 57, 86, 62, 36, 36, 36, 61, 62, 61, 36, 62, 36, 36, 44, 71, 87, + 87, 43, 80, 89, 90, 89, 87, 44, 44, 44, 57, 85, 44, 44, 36, 62, + 78, 27, 27, 27, 44, 44, 44, 44, 44, 71, 62, 36, 36, 61, 44, 36, + 61, 36, 36, 44, 62, 61, 61, 36, 44, 62, 61, 44, 36, 61, 44, 36, + 36, 36, 36, 36, 36, 44, 44, 86, 85, 90, 44, 86, 90, 86, 87, 44, + 61, 44, 44, 89, 44, 44, 44, 44, 27, 91, 67, 67, 56, 92, 44, 44, + 85, 86, 71, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 44, 71, 43, 85, 86, 90, 43, 80, 43, 43, 44, + 44, 44, 57, 80, 36, 61, 62, 44, 44, 44, 44, 93, 27, 27, 27, 91, + 70, 86, 72, 36, 36, 36, 61, 36, 36, 36, 62, 36, 36, 44, 71, 87, + 86, 86, 90, 85, 90, 86, 43, 44, 44, 44, 89, 90, 44, 44, 62, 61, + 62, 94, 44, 44, 44, 44, 44, 44, 43, 86, 36, 36, 36, 36, 61, 36, + 36, 36, 36, 36, 36, 70, 71, 86, 87, 43, 80, 86, 90, 86, 87, 77, + 44, 44, 36, 94, 27, 27, 27, 95, 27, 27, 27, 27, 91, 36, 36, 36, + 57, 86, 62, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, 36, 36, 36, + 36, 62, 36, 36, 36, 36, 62, 44, 36, 36, 36, 61, 44, 80, 44, 89, + 86, 43, 80, 80, 86, 86, 86, 86, 44, 86, 64, 44, 44, 44, 44, 44, + 62, 36, 36, 36, 36, 36, 36, 36, 70, 36, 43, 43, 43, 80, 44, 96, + 36, 36, 36, 75, 43, 43, 43, 60, 7, 7, 7, 7, 7, 2, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 61, 36, 36, 61, 36, 36, + 36, 36, 62, 62, 36, 36, 36, 36, 70, 36, 43, 43, 43, 43, 71, 44, + 36, 36, 61, 81, 43, 43, 43, 80, 7, 7, 7, 7, 7, 44, 36, 36, + 77, 67, 2, 2, 2, 2, 2, 2, 2, 97, 97, 67, 43, 67, 67, 67, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 86, + 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44, + 57, 43, 43, 43, 43, 43, 43, 85, 43, 43, 60, 43, 36, 36, 70, 43, + 43, 43, 43, 43, 57, 43, 43, 43, 43, 43, 43, 43, 43, 43, 80, 67, + 67, 67, 67, 76, 67, 67, 92, 67, 2, 2, 97, 67, 21, 64, 44, 44, + 36, 36, 36, 36, 36, 94, 87, 43, 85, 43, 43, 43, 87, 85, 87, 71, + 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 86, 43, 36, 36, 43, + 71, 86, 98, 94, 86, 86, 86, 36, 70, 43, 71, 36, 36, 36, 36, 36, + 36, 85, 87, 85, 86, 86, 87, 94, 7, 7, 7, 7, 7, 86, 87, 67, + 11, 11, 11, 48, 44, 44, 48, 44, 16, 16, 16, 16, 16, 53, 45, 16, + 36, 36, 36, 36, 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, + 61, 36, 36, 44, 36, 36, 36, 61, 61, 36, 36, 44, 36, 36, 36, 36, + 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 57, 43, + 2, 2, 2, 2, 99, 27, 27, 27, 27, 27, 27, 27, 27, 27,100, 44, + 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44, + 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72, + 102, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,103,104, 44, + 36, 36, 36, 36, 36, 63, 2,105,106, 36, 36, 36, 61, 44, 44, 44, + 36, 43, 85, 44, 44, 44, 44, 62, 36, 43,107, 64, 44, 44, 44, 44, + 36, 43, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 61, 36, + 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 87, 43, 43, 43, 86, + 86, 86, 86, 85, 87, 43, 43, 43, 43, 43, 2, 88, 2, 66, 70, 44, + 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44, + 2, 2, 2,108, 2, 59, 43, 84, 36, 83, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 61, 44, 44, 44, 36, 36, 70, 71, 36, 36, 36, 36, + 36, 36, 36, 36, 70, 61, 44, 44, 36, 36, 36, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 61, 43, 85, 86, 87, 85, 86, 44, 44, + 86, 85, 86, 86, 87, 43, 44, 44, 92, 44, 2, 7, 7, 7, 7, 7, + 36, 36, 36, 36, 36, 36, 36, 44, 36, 36, 61, 44, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 44, 44, 36, 36, 36, 36, 36, 44, 44, 44, + 7, 7, 7, 7, 7,100, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67, + 36, 36, 36, 70, 85, 87, 44, 2, 36, 36, 94, 85, 43, 43, 43, 80, + 85, 85, 87, 43, 43, 43, 85, 86, 86, 87, 43, 43, 43, 43, 80, 57, + 2, 2, 2, 88, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,109, + 43, 43, 43, 43, 43, 43, 43, 80, 43, 43, 98, 36, 36, 36, 36, 36, + 36, 36, 85, 43, 43, 85, 85, 86, 86, 85, 98, 36, 36, 36, 61, 44, + 97, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 21, 64, + 43, 98, 36, 36, 36, 36, 36, 36, 94, 43, 43, 86, 43, 87, 43, 36, + 36, 36, 36, 85, 43, 86, 87, 87, 43, 86, 44, 44, 44, 44, 2, 2, + 36, 36, 86, 86, 86, 86, 43, 43, 43, 43, 86, 43, 44, 93, 2, 2, + 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 16, 16, 16, 16,110, 44, 44, 44, 11, 11, 11, 11, 11, 47, 48, 11, + 2, 2, 2, 2, 44, 44, 44, 44, 43, 60, 43, 43, 43, 43, 43, 43, + 85, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 94, 43, 61, 44, 44, + 16, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 45, 16, 16, + 16, 16, 16, 16, 45, 16, 16, 16, 16, 16, 16, 16, 16,111, 40, 40, + 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, + 16, 16, 16, 44, 11, 11, 11, 44, 16, 16, 16, 16, 48, 48, 48, 48, + 16, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, 16,112,112,112,112, + 16, 16,110, 16, 11, 11,113,114, 41, 16,110, 16, 11, 11,113, 41, + 16, 16, 44, 16, 11, 11,115, 41, 16, 16, 16, 16, 11, 11,116, 41, + 44, 16,110, 16, 11, 11,113,117,118,118,118,118,118,119, 65, 65, + 120,120,120, 2,121,122,121,122, 2, 2, 2, 2,123, 65, 65,124, + 2, 2, 2, 2,125,126, 2,127,128, 2,129,130, 2, 2, 2, 2, + 2, 9,128, 2, 2, 2, 2,131, 65, 65,132, 65, 65, 65, 65, 65, + 133, 44, 27, 27, 27, 8,129,134, 27, 27, 27, 27, 27, 8,129,104, + 40, 40, 40, 40, 40, 40, 81, 44, 20, 20, 20, 20, 20, 20, 20, 20, + 135, 44, 44, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43,136, 51, + 109, 51,109, 43, 43, 43, 43, 43, 80, 44, 44, 44, 44, 44, 44, 44, + 67,137, 67,138, 67, 34, 11, 16, 11, 32,138, 67, 49, 11, 11, 67, + 67, 67,137,137,137, 11, 11,139, 11, 11, 35, 36, 39, 67, 16, 11, + 8, 8, 49, 16, 16, 26, 67,140, 27, 27, 27, 27, 27, 27, 27, 27, + 105,105,105,105,105,105,105,105,105,141,142,105,143, 67, 44, 44, + 8, 8,144, 67, 67, 8, 67, 67,144, 26, 67,144, 67, 67, 67,144, + 67, 67, 67, 67, 67, 67, 67, 8, 67,144,144, 67, 67, 67, 67, 67, + 67, 67, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 67, 67, 67, 67, 4, 4, 67, 67, 8, 67, 67, 67,145,146, 67, 67, + 67, 67, 67, 67, 67, 67,144, 67, 67, 67, 67, 67, 67, 26, 8, 8, + 8, 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, + 8, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 92, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 92, 44, 44, 27, 27, 27, 27, 27, 27, 67, 67, + 67, 67, 67, 67, 67, 27, 27, 27, 67, 67, 67, 26, 67, 67, 67, 67, + 26, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67, 8, 8, 8, 8, + 67, 67, 67, 67, 67, 67, 67, 26, 67, 67, 67, 67, 4, 4, 4, 4, + 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, + 8, 8,129,147, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, + 8,129,148,148,148,148,148,148,148,148,148,148,147, 8, 8, 8, + 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, + 8, 8,144, 26, 8, 8,144, 67, 67, 67, 44, 67, 67, 67, 67, 67, + 67, 67, 67, 55, 67, 67, 67, 67, 32, 11, 32, 34, 34, 34, 34, 11, + 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,140, 67, 67,138, 34,149, + 43, 32, 44, 44, 93, 2, 99, 2, 16, 16, 16,150, 44, 44,150, 44, + 36, 36, 36, 36, 44, 44, 44, 52, 64, 44, 44, 44, 44, 44, 44, 57, + 36, 36, 36, 61, 44, 44, 44, 44, 36, 36, 36, 61, 36, 36, 36, 61, + 2,121,121, 2,125,126,121, 2, 2, 2, 2, 6, 2,108,121, 2, + 121, 4, 4, 4, 4, 2, 2, 88, 2, 2, 2, 2, 2,120, 2, 2, + 108,151, 2, 2, 2, 2, 2, 2, 67, 2,152,148,148,148,153, 44, + 67, 67, 67, 67, 67, 55, 67, 67, 67, 67, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 44, 44, 44, 44, 44, 1, 2,154,155, 4, 4, 4, 4, + 4, 67, 4, 4, 4, 4,156,157,158,105,105,105,105, 43, 43, 86, + 159, 40, 40, 67,105,160, 63, 67, 36, 36, 36, 61, 57,161,162, 69, + 36, 36, 36, 36, 36, 63, 40, 69, 44, 44, 62, 36, 36, 36, 36, 36, + 67, 27, 27, 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 44, 55, + 67, 67, 67, 67, 67, 67, 67, 92, 27, 27, 27, 27, 27, 67, 67, 67, + 67, 67, 67, 67, 27, 27, 27, 27,163, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 83, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,164, 2, + 7, 7, 7, 7, 7, 36, 44, 44, 32, 32, 32, 32, 32, 32, 32, 70, + 51,165, 43, 43, 43, 43, 43, 88, 32, 32, 32, 32, 32, 32, 40, 43, + 36, 36, 36,105,105,105,105,105, 43, 2, 2, 2, 44, 44, 44, 44, + 41, 41, 41,162, 40, 40, 40, 40, 41, 32, 32, 32, 32, 32, 32, 32, + 16, 32, 32, 32, 32, 32, 32, 32, 45, 16, 16, 16, 34, 34, 34, 32, + 32, 32, 32, 32, 42,166, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, + 32, 32, 11, 11, 34,110, 44, 44, 32,150,150, 32, 32, 44, 44, 44, + 44, 40,167, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36, + 36, 94, 87, 85, 67, 67, 80, 44, 27, 27, 27, 67,168, 44, 44, 44, + 36, 36, 2, 2, 44, 44, 44, 44, 86, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 86, 86, 86, 86, 86, 86, 86, 43, 44, 44, 44, 44, 2, + 43, 36, 36, 36, 2, 72, 72, 70, 36, 36, 36, 43, 43, 43, 43, 2, + 36, 36, 36, 70, 43, 43, 43, 43, 43, 86, 44, 44, 44, 44, 44, 93, + 36, 70, 86, 43, 43, 86, 43, 86,107, 2, 2, 2, 2, 2, 2, 52, + 7, 7, 7, 7, 7, 44, 44, 2, 36, 36, 70, 69, 36, 36, 36, 36, + 7, 7, 7, 7, 7, 36, 36, 61, 36, 36, 36, 36, 70, 43, 43, 85, + 87, 85, 87, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 85, 44, + 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 94, 85, 36, + 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 62, 83, 2, 36, 36, 36, 36, 36, 94, 43, 86, + 2, 83,169, 80, 44, 44, 44, 44, 62, 36, 36, 61, 62, 36, 36, 61, + 62, 36, 36, 61, 44, 44, 44, 44, 16, 16, 16, 16, 16,114, 40, 40, + 16, 16, 16, 16,111, 41, 44, 44, 36, 94, 87, 86, 85,107, 87, 44, + 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36, + 170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171, + 16, 16, 16,110, 44, 44, 44, 44, 44,150, 16, 16, 44, 44, 62, 71, + 36, 36, 36, 36,172, 36, 36, 36, 36, 36, 36, 61, 36, 36, 61, 61, + 36, 62, 61, 36, 36, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41,117, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36,148, 44, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 44, 55, 36, 36, 36, 36, 36, 36,168, 67, + 2, 2, 2,152,130, 44, 44, 44, 6,173,174,148,148,148,148,148, + 148,148,130,152,130, 2,127,175, 2, 64, 2, 2,156,148,148,130, + 2,176, 8,177, 66, 2, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 61, 79, 93, 2, 3, 2, 4, 5, 6, 2, + 16, 16, 16, 16, 16, 17, 18,129,130, 4, 2, 36, 36, 36, 36, 36, + 69, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40, + 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 36, 36, 44, 36, 61, 44, + 20,178, 56,135, 26, 8,144, 92, 44, 44, 44, 44, 79, 65, 67, 44, + 36, 36, 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, + 2, 64, 44,179, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67, + 105,105,143, 27, 91, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 92, + 67, 67, 67, 67, 67, 67, 92, 44, 92, 44, 44, 44, 44, 44, 44, 44, + 67, 67, 67, 67, 67, 67, 50, 44,180, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36, + 155, 36, 36, 36, 36,181, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 93, 36, 36, 44, 44, 36, 36, 36, 36, + 182,105,105, 44, 44, 44, 44, 44, 11, 11, 11, 11, 16, 16, 16, 16, + 11, 11, 44, 44, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 44, 44, + 36, 36, 36, 36, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, 44, 93, + 11, 11, 11, 11, 11, 47, 11, 11, 11, 47, 11,150, 16, 16, 16, 16, + 16,150, 16, 16, 16, 16, 16, 16, 16,150, 16, 16, 16,150,110, 44, + 40, 40, 40, 52, 40, 40, 40, 40, 81, 40, 40, 40, 40, 81, 44, 44, + 36, 36, 36, 44, 61, 36, 36, 36, 36, 36, 36, 62, 61, 44, 61, 62, + 36, 36, 36, 93, 27, 27, 27, 27, 36, 36, 36, 77,163, 27, 27, 27, + 44, 44, 44,179, 27, 27, 27, 27, 36, 61, 36, 44, 44,179, 27, 27, + 36, 36, 36, 27, 27, 27, 44, 93, 36, 36, 36, 36, 36, 44, 44, 93, + 36, 36, 36, 36, 44, 44, 27, 36, 44, 27, 27, 27, 27, 27, 27, 27, + 70, 43, 57, 80, 44, 44, 43, 43, 36, 36, 62, 36, 62, 36, 36, 36, + 36, 36, 36, 44, 43, 80, 44, 57, 27, 27, 27, 27,100, 44, 44, 44, + 2, 2, 2, 2, 64, 44, 44, 44, 36, 36, 36, 36, 36, 36,183, 30, + 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 78, 36, 36, 36, + 36, 36, 70, 80, 44,179, 27, 27, 2, 2, 2, 64, 44, 44, 44, 44, + 36, 36, 36, 44, 93, 2, 2, 2, 36, 36, 36, 44, 27, 27, 27, 27, + 36, 61, 44, 44, 27, 27, 27, 27, 36, 44, 44, 44, 93, 2, 64, 44, + 44, 44, 44, 44,179, 27, 27, 27, 11, 47, 44, 44, 44, 44, 44, 44, + 16,110, 44, 44, 44, 27, 27, 27, 36, 36, 43, 43, 44, 44, 44, 44, + 27, 27, 27, 27, 27, 27, 27,100, 36, 36, 36, 36, 36, 57,184, 44, + 36, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 43, + 27, 27, 27, 95, 44, 44, 44, 44,180, 27, 30, 2, 2, 44, 44, 44, + 36, 43, 43, 2, 2, 44, 44, 44, 36, 36,183, 27, 27, 27, 44, 44, + 87, 98, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, + 43, 43, 43, 60, 2, 2, 2, 44, 27, 27, 27, 7, 7, 7, 7, 7, + 71, 70, 71, 44, 44, 44, 44, 57, 86, 87, 43, 85, 87, 60,185, 2, + 2, 80, 44, 44, 44, 44, 79, 44, 43, 71, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 70, 43, 43, 87, 43, 43, 43, 80, 7, 7, 7, 7, 7, + 2, 2, 94, 98, 44, 44, 44, 44, 36, 70, 2, 61, 44, 44, 44, 44, + 36, 94, 86, 43, 43, 43, 43, 85, 98, 36, 63, 2, 59, 43, 60, 87, + 7, 7, 7, 7, 7, 63, 63, 2,179, 27, 27, 27, 27, 27, 27, 27, + 27, 27,100, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 86, 87, + 43, 86, 85, 43, 2, 2, 2, 71, 70, 44, 44, 44, 44, 44, 44, 44, + 36, 36, 36, 61, 61, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 62, + 36, 36, 36, 36, 63, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 70, + 86, 87, 43, 43, 43, 80, 44, 44, 43, 86, 62, 36, 36, 36, 61, 62, + 61, 36, 62, 36, 36, 57, 71, 86, 85, 86, 90, 89, 90, 89, 86, 44, + 61, 44, 44, 89, 44, 44, 62, 36, 36, 86, 44, 43, 43, 43, 80, 44, + 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 94, 86, 43, 43, 43, 43, + 86, 43, 85, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 93, 71, + 86, 87, 43, 43, 85, 85, 86, 87, 85, 43, 36, 72, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 36, 36, 94, 86, 43, 43, 44, 86, 86, 43, 87, + 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44, + 86, 87, 43, 43, 43, 85, 87, 87, 60, 2, 61, 44, 44, 44, 44, 44, + 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 87, 86, + 43, 43, 43, 87, 63, 44, 44, 44, 86, 43, 43, 87, 43, 43, 44, 44, + 7, 7, 7, 7, 7, 27, 2, 97, 43, 43, 43, 43, 87, 60, 44, 44, + 27,100, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36, + 36, 36, 62, 61, 36, 36, 36, 36, 86, 86, 86, 89, 90, 57, 85, 71, + 98, 87, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36, + 94, 86, 43, 43, 44, 43, 86, 86, 71, 72, 90, 44, 44, 44, 44, 44, + 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 85, 70, 43, 60, + 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 85, 87, 43, 36, 36, + 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 85, 43, 2, 72, 2, + 2, 64, 44, 44, 44, 44, 44, 44, 2, 2, 2, 2, 2, 44, 44, 44, + 43, 43, 43, 80, 43, 43, 43, 87, 63, 2, 2, 44, 44, 44, 44, 44, + 2, 36, 36, 36, 36, 36, 36, 36, 44, 43, 43, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 89, 43, 43, 43, 85, 43, 87, 80, 44, 44, 44, 44, + 36, 36, 36, 61, 36, 62, 36, 36, 70, 43, 43, 80, 44, 80, 43, 57, + 43, 43, 43, 70, 44, 44, 44, 44, 36, 36, 36, 62, 61, 36, 36, 36, + 36, 36, 36, 36, 36, 86, 86, 90, 43, 89, 87, 87, 61, 44, 44, 44, + 36, 70, 85,107, 64, 44, 44, 44, 43, 94, 36, 36, 36, 36, 36, 36, + 36, 36, 86, 43, 43, 80, 44, 86, 85, 60, 2, 2, 2, 2, 2, 2, + 27, 27, 91, 67, 67, 67, 56, 20,168, 67, 67, 67, 67, 67, 67, 67, + 67, 44, 44, 44, 44, 44, 44, 93,105,105,105,105,105,105,105,181, + 2, 2, 64, 44, 44, 44, 44, 44, 63, 64, 44, 44, 44, 44, 44, 44, + 65, 65, 65, 65, 65, 65, 65, 65, 71, 36, 36, 70, 43, 43, 43, 43, + 43, 43, 43, 44, 44, 44, 44, 44, 43, 43, 60, 44, 44, 44, 44, 44, + 43, 43, 43, 60, 2, 2, 67, 67, 40, 40, 97, 44, 44, 44, 44, 44, + 7, 7, 7, 7, 7,179, 27, 27, 27, 62, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 44, 44, 62, 36, 27, 27, 27, 30, 2, 64, 44, 44, + 36, 36, 36, 36, 36, 61, 44, 57, 94, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 44, 44, 44, 57, + 43, 74, 40, 40, 40, 40, 40, 40, 40, 88, 80, 44, 44, 44, 44, 44, + 86, 44, 44, 44, 44, 44, 44, 44, 40, 40, 52, 40, 40, 40, 52, 81, + 36, 61, 44, 44, 44, 44, 44, 44, 44, 61, 44, 44, 44, 44, 44, 44, + 36, 61, 62, 44, 44, 44, 44, 44, 44, 44, 36, 36, 44, 44, 44, 44, + 36, 36, 36, 36, 36, 44, 50, 60, 65, 65, 44, 44, 44, 44, 44, 44, + 43, 43, 43, 43, 43, 43, 43, 44, 43, 43, 43, 80, 44, 44, 44, 44, + 67, 67, 67, 92, 55, 67, 67, 67, 67, 67,186, 87, 43, 67,186, 86, + 86,187, 65, 65, 65, 84, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67, + 67, 67, 67, 67, 67, 43, 43, 67, 67, 43, 76, 44, 44, 44, 44, 44, + 27, 27, 44, 44, 44, 44, 44, 44, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16,110, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 47, 11, 44, 47, 48, 47, 48, 11, 47, 11, + 11, 11, 11, 16, 16,150,150, 16, 16, 16,150, 16, 16, 16, 16, 16, + 16, 16, 11, 48, 11, 47, 48, 11, 11, 11, 47, 11, 11, 11, 47, 16, + 16, 16, 16, 16, 11, 48, 11, 47, 11, 11, 47, 47, 44, 11, 11, 11, + 47, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 44, 11, 11, 11, 11, + 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, + 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, + 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, + 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 44, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 43, 43, 43, 76, 67, 50, 43, 43, + 43, 43, 43, 43, 43, 43, 76, 67, 67, 67, 50, 67, 67, 67, 67, 67, + 67, 67, 76, 21, 2, 2, 44, 44, 44, 44, 44, 44, 44, 57, 43, 43, + 16, 16, 16, 16, 16, 39, 16, 16, 16, 16, 16, 16, 16, 16, 16,110, + 44, 44,150, 16, 16,110, 44, 44, 43, 43, 43, 80, 43, 43, 43, 43, + 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44, + 40, 40, 40, 40, 40, 40, 40, 44, 44, 44, 44, 44, 44, 44, 44, 57, + 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77, + 36, 36, 36, 36, 36, 36, 36, 80, 36, 36, 36, 36, 36, 36, 43, 43, + 7, 7, 7, 7, 7, 44, 44, 96, 36, 36, 36, 36, 36, 83, 43, 43, + 36, 36, 36, 61, 36, 36, 62, 61, 36, 36, 61,179, 27, 27, 27, 27, + 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,163, 27, + 188, 27,100, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,163, + 27, 27, 27, 27, 27, 27, 27, 44, 36, 36, 62, 36, 36, 36, 36, 36, + 62, 61, 61, 62, 62, 36, 36, 36, 36, 61, 36, 36, 62, 62, 44, 44, + 44, 61, 44, 62, 62, 62, 62, 36, 62, 61, 61, 62, 62, 62, 62, 62, + 62, 61, 61, 62, 36, 61, 36, 36, 36, 61, 36, 36, 62, 36, 61, 61, + 36, 36, 36, 36, 36, 62, 36, 36, 62, 36, 62, 36, 36, 62, 36, 36, + 8, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 44, 44, + 55, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27, 27, 27, 91, 67, + 67, 67, 67, 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, + 67, 92, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 92, 44, 44, 44, + 67, 44, 44, 44, 44, 44, 44, 44, 67, 67, 67, 67, 67, 25, 41, 41, + 67, 67, 67, 67, 44, 44, 67, 67, 67, 67, 67, 92, 44, 55, 67, 67, + 67, 67, 67, 67, 44, 44, 44, 44, 67, 67, 67, 67, 67, 67, 67, 55, + 67, 67, 67, 44, 44, 44, 44, 67, 67, 92, 67, 67, 67, 67, 67, 67, + 79, 44, 44, 44, 44, 44, 44, 44,171,171,171,171,171,171,171, 44, + 171,171,171,171,171,171,171, 0, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, 25, 9, 26, 12, 11, 11, + 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, 2, 12, 17, 12, 21, 12, + 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, 1, 1, 21, 23, 26, 26, + 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, + 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, 6, 2, 24, 7, 7, 6, + 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 2, 10, 10, 2, 15, 26, + 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, 21, 26, 10, 7, 21, 15, + 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, 14, 14, 14, 7, 10, 21, + 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, 8, 24, 5, 24, 2, 24, + 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, 20, 19, 22, 20, 27, 28, + 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, + 1, 2, 15, 6, 18, 6, 23, 2, 12, 11, 9, 26, 26, 9, 26, 5, + 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, + 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, + 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, + 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, + 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 25, 2, 25, 24, 2, 15, + 12, 15, 14, 2, 21, 14, 7, 15, 12, 17, 21, 1, 26, 10, 10, 1, + 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, + 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 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, 17, 18, 19, 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, 20, + 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 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, 35, 0, 36, 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, + 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, + 0, 0, 40, 41, 42, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, + 6, 7, 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, + 16, 19, 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, + 0, 0, 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, + 0, 0, 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, + 0, 55, 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, + 0, 0, 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, + 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, + 0, 0, 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, + 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, 75, 76, 0, 77, 78, 0, + 0, 79, 80, 0, 81, 62, 0, 82, 83, 0, 0, 84, 85, 86, 0, 0, + 0, 87, 0, 88, 0, 0, 51, 89, 51, 0, 90, 0, 91, 0, 0, 0, + 80, 0, 0, 0, 92, 93, 0, 94, 95, 96, 97, 0, 0, 0, 0, 0, + 51, 0, 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, 0,100, 0, 0, + 0, 0, 0,101,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,103, + 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 0, + 0,107, 0, 0, 0, 0, 0, 0,108, 0,109, 0,102, 0, 0, 0, + 0, 0,110,111, 0, 0, 0, 0, 0, 0, 0,112, 0, 0, 0, 0, + 0, 0, 0,113, 0,114, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, + 0, 13, 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, + 21, 0, 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, + 0, 27, 0, 0, 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, + 33, 0, 0, 35, 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, + 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, + 42, 0, 0, 0, 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, + 47, 0, 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, + 0, 51, 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, + 0, 56, 0, 0, 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, + 0, 0, 0, 61, 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, + 0, 0, 0, 67, 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, + 0, 0, 77, 78, 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, + 0, 81, 0, 0, 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, + 84, 0, 85, 0, 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, + 0, 0, 0, 88, 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, + 0, 0, 33, 0, 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, + 0, 0, 93, 0, 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, + 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 0,100,101, 93, 0, 0, + 102, 0, 0, 0, 84, 0, 0,103, 0, 0, 0,104,105, 0, 0,106, + 107, 0, 0, 0, 0, 0, 0,108, 0, 0,109, 0, 0, 0, 0,110, + 33, 0,111,112,113, 35, 0, 0,114, 0, 0, 0,115, 0, 0, 0, + 0, 0, 0,116, 0, 0,117, 0, 0, 0, 0,118, 88, 0, 0, 0, + 0, 0, 57, 0, 0, 0, 0, 52,119, 0, 0, 0, 0,120, 0, 0, + 121, 0, 0, 0, 0,119, 0, 0,122, 0, 0, 0, 0, 0, 0,123, + 0, 0, 0,124, 0, 0, 0,125, 0,126, 0, 0, 0, 0,127,128, + 129, 0,130, 0,131, 0, 0, 0,132,133,134, 0, 77, 0, 0, 0, + 0, 0, 35, 0, 0, 0,135, 0, 0, 0,136, 0, 0,137, 0, 0, + 138, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, + 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, + 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, + 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, + 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, + 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, + 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, + 0, 0, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, + 54, 21, 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, + 0, 0, 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, + 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, + 0, 0, 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, + 0, 77, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, + 0, 80, 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, + 0, 0, 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, + 1, 52, 15, 86, 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, + 1, 0, 0, 0, 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, + 0, 78, 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, + 21, 1, 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, + 81, 99,100, 4, 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, + 0, 0, 0, 61, 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, + 0, 0, 0, 38, 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, + 61, 0, 0, 0, 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, + 0, 0, 0,107, 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, + 0, 0, 0,108, 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, + 0, 0, 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, + 62, 0, 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, + 62, 0, 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, + 0, 38, 1, 58, 1, 58, 0, 0, 63, 89, 0, 0,115, 0, 0, 0, + 55, 0, 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, + 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, + 8, 91, 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, 0,117, + 0,118,119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 38, 50, + 38, 58, 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, + 0, 0, 0, 1, 0, 0, 0,123, 4,122, 0, 0, 0, 1,124, 0, + 0, 0, 0, 0,230,230,230,230,230,232,220,220,220,220,232,216, + 220,220,220,220,220,202,202,220,220,220,220,202,202,220,220,220, + 1, 1, 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220, + 220,220,230,230,230,220,220, 0,230,230,230,220,220,220,220,230, + 232,220,220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, + 0,220,230,230,230,230,220,230,230,230,222,220,230,230,220,220, + 230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, + 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, + 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230, + 220,230,230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230, + 230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230, + 220,220,230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0, + 230,230, 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220, + 230,220,220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, + 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, + 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0, + 103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, + 220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0, + 132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, + 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, + 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220, + 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, + 0, 0, 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0, + 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, + 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, + 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, + 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, + 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, + 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, + 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, + 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, + 230,230, 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, + 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, + 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, + 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, + 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, + 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, + 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, + 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, + 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, + 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, + 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, + 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, + 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, + 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, + 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, + 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, + 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, + 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, + 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, + 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, + 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, + 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, + 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, + 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, + 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, + 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, + 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, + 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, + 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, + 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, + 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, + 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, + 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, + 9, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, + 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 13, 22, 13, 13, 13, + 13, 23, 24, 24, 25, 26, 13, 13, 13, 27, 28, 29, 13, 30, 31, 32, + 33, 34, 35, 36, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 37, 7, 38, 39, 7, 40, 7, 7, + 7, 41, 13, 42, 7, 7, 43, 7, 44, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 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, 32, 33, 34, 35, 36, 37, 37, + 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, + 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 59, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 79, 70, 70, 70, 70, 80, 80, 80, 80, 80, 80, 80, 80, 80, 81, + 82, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 95, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 70, 70, 97, 98, 99,100,101,101, + 102,103,104,105,106,107,108,109,110,111, 96,112,113,114,115,116, + 117,118,119,119,120,121,122,123,124,125,126,127,128,129,130,131, + 132, 96,133,134,135,136,137,138,139,140,141,142,143, 96,144,145, + 96,146,147,148,149, 96,150,151,152,153,154,155,156, 96,157,158, + 159,160, 96,161,162,163,164,164,164,164,164,164,164,165,166,164, + 167, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96,168,169,169,169,169,169,169,169,169,170, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,171,171, + 171,171,172, 96, 96, 96,173,173,173,173,174,175,176,177, 96, 96, + 96, 96,178,179,180,181,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,182,182,182,182,182,182,182,182,182,182,182, + 182,182,182,182,182,183,182,182,182,182,182,182,184,184,184,185, + 186, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96,187,188,189,190,191,191,192, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,193,194, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96,195,196, 59,197,198,199,200,201,202, 96,203,204, + 205, 59, 59,206, 59,207,208,208,208,208,208,209, 96, 96, 96, 96, + 96, 96, 96, 96,210, 96,211,212,213, 96, 96,214, 96, 96, 96,215, + 96, 96, 96, 96, 96,216,217,218,219, 96, 96, 96, 96, 96,220,221, + 222, 96,223,224, 96, 96,225,226, 59,227,228, 96, 59, 59, 59, 59, + 59, 59, 59,229,230,231,232,233, 59, 59,234,235, 59,236, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70,237, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70,238, 70,239, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70,240, 70, 70, 70, 70, 70, 70, 70, 70, 70,241, 70, 70, + 70, 70,242, 96, 96, 96, 70, 70, 70, 70,243, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 70, 70, 70, 70, 70, 70,244, 70, 70, 70, + 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,245, 96, 96, + 96, 96, 96, 96, 96, 96,246, 96,247,248, 0, 1, 2, 2, 0, 1, + 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, 0, + 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, + 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, + 9, 2, 9, 2, 9, 9, 9, 9, 2, 9, 9, 9, 55, 55, 55, 55, + 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 2, 2, 2, 2, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, + 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, + 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, + 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 2, 2, 64, 64, + 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, + 2, 2, 90, 90, 90, 2, 95, 95, 95, 95, 2, 2, 95, 2, 3, 3, + 3, 2, 3, 3, 2, 2, 3, 3, 0, 3, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, + 5, 5, 5, 2, 2, 5, 5, 2, 5, 5, 5, 2, 5, 2, 2, 2, + 5, 5, 5, 5, 2, 2, 5, 5, 5, 2, 2, 2, 2, 5, 5, 5, + 2, 5, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, + 2, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, + 2, 2, 2, 11, 2, 2, 11, 2, 11, 2, 2, 2, 11, 11, 2, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, + 2, 2, 10, 2, 2, 2, 2, 2, 10, 10, 2, 21, 21, 21, 21, 21, + 21, 21, 21, 2, 2, 21, 21, 2, 21, 21, 21, 21, 2, 2, 21, 21, + 2, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, + 22, 2, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, + 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 23, 23, 23, 23, 23, 2, + 23, 23, 23, 23, 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 23, + 2, 2, 2, 2, 23, 23, 2, 2, 2, 23, 16, 16, 16, 16, 16, 2, + 16, 16, 2, 16, 16, 16, 16, 16, 2, 2, 2, 16, 16, 2, 2, 2, + 16, 16, 20, 20, 20, 20, 20, 2, 20, 20, 2, 2, 20, 20, 2, 36, + 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, + 2, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 2, + 36, 2, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 2, 2, 2, 2, 0, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, + 18, 2, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, 2, 2, 18, 2, + 18, 2, 25, 25, 25, 25, 2, 25, 25, 25, 25, 2, 2, 2, 25, 2, + 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 33, 33, 33, 33, 8, 8, + 8, 8, 8, 8, 2, 8, 2, 8, 2, 2, 8, 8, 8, 0, 12, 12, + 12, 12, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, + 30, 2, 2, 30, 30, 30, 30, 2, 2, 2, 29, 29, 29, 29, 29, 29, + 2, 2, 28, 28, 28, 28, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, + 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 45, 45, + 45, 45, 45, 45, 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 0, + 0, 2, 43, 43, 43, 43, 46, 46, 46, 46, 46, 2, 46, 46, 31, 31, + 31, 31, 31, 31, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, + 32, 32, 32, 32, 2, 2, 32, 2, 2, 2, 32, 32, 32, 2, 28, 28, + 2, 2, 48, 48, 48, 48, 48, 48, 48, 2, 48, 2, 2, 2, 52, 52, + 52, 52, 52, 52, 2, 2, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, + 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 2, 2, + 54, 54, 91, 91, 91, 91, 91, 91, 91, 2, 91, 2, 2, 91, 91, 91, + 2, 2, 1, 1, 1, 2, 62, 62, 62, 62, 62, 2, 2, 2, 62, 62, + 62, 2, 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 2, 2, + 2, 70, 70, 70, 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 6, 2, + 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 0, + 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 19, 19, + 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, + 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, 9, 9, 2, 2, 2, 9, + 2, 9, 2, 9, 9, 9, 1, 1, 0, 0, 0, 2, 0, 0, 0, 19, + 2, 2, 0, 0, 0, 19, 0, 0, 0, 2, 19, 2, 2, 2, 0, 2, + 2, 2, 1, 2, 2, 2, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, + 27, 27, 2, 2, 0, 0, 0, 0, 2, 0, 56, 56, 56, 56, 2, 55, + 55, 55, 61, 61, 61, 61, 2, 2, 2, 61, 61, 2, 2, 2, 0, 0, + 2, 2, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, 2, 2, 0, 13, + 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 2, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, + 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 2, 26, + 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 2, 12, 12, + 12, 0, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, 39, 2, 86, 86, + 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 19, 19, 19, 2, 19, 19, + 2, 19, 2, 19, 19, 19, 19, 19, 2, 2, 2, 2, 19, 19, 60, 60, + 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 75, 75, 75, 75, 75, 75, + 2, 2, 2, 2, 75, 75, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, + 74, 74, 2, 2, 2, 74, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, + 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, 33, 2, 68, 68, + 68, 68, 68, 68, 68, 2, 68, 68, 2, 2, 92, 92, 92, 92, 92, 92, + 92, 2, 2, 2, 2, 92, 87, 87, 87, 87, 87, 87, 87, 2, 19, 9, + 19, 19, 19, 19, 0, 0, 87, 87, 2, 2, 2, 2, 2, 12, 2, 2, + 2, 4, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 2, 2, + 2, 3, 3, 3, 0, 0, 2, 2, 3, 3, 1, 1, 6, 6, 3, 2, + 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 17, 17, 17, 17, + 0, 0, 2, 2, 12, 12, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, + 49, 2, 49, 49, 2, 49, 49, 49, 2, 2, 9, 2, 2, 2, 0, 1, + 2, 2, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 2, + 2, 2, 42, 42, 42, 42, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, + 41, 2,118,118,118,118,118,118,118, 2, 53, 53, 53, 53, 53, 53, + 2, 53, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, 40, 40, 51, 51, + 51, 51, 50, 50, 50, 50, 50, 50, 2, 2,135,135,135,135,106,106, + 106,106,104,104,104,104, 2, 2, 2,104,161,161,161,161,161,161, + 161, 2,161,161, 2,161,161, 2, 2, 2,110,110,110,110,110,110, + 110, 2,110,110, 2, 2, 19, 2, 19, 19, 47, 47, 47, 47, 47, 47, + 2, 2, 47, 2, 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, + 2, 47, 81, 81, 81, 81, 81, 81, 2, 81,120,120,120,120,116,116, + 116,116,116,116,116, 2, 2, 2, 2,116,128,128,128,128,128,128, + 128, 2,128,128, 2, 2, 2, 2, 2,128, 66, 66, 66, 66, 2, 2, + 2, 66, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72, 98, 98, + 98, 98, 97, 97, 97, 97, 2, 2, 97, 97, 57, 57, 57, 57, 2, 57, + 57, 2, 2, 57, 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, + 2, 57, 57, 2, 2, 2, 88, 88, 88, 88,117,117,117,117,112,112, + 112,112,112,112,112, 2, 2, 2, 2,112, 78, 78, 78, 78, 78, 78, + 2, 2, 2, 78, 78, 78, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, + 82, 82, 82, 82, 82, 2,122,122,122,122,122,122, 2, 2, 2,122, + 122,122,122, 2, 2, 2, 89, 89, 89, 89, 89, 2, 2, 2,130,130, + 130,130,130,130,130, 2, 2, 2,130,130,144,144,144,144,144,144, + 2, 2,156,156,156,156,156,156, 2,156,156,156, 2, 2, 2, 3, + 3, 3,147,147,147,147,148,148,148,148,148,148, 2, 2,158,158, + 158,158,158,158, 2, 2,153,153,153,153,149,149,149,149,149,149, + 149, 2, 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 2, 2, + 2, 94, 85, 85, 85, 85, 85, 85, 85, 2, 2, 85, 2, 2,101,101, + 101,101,101, 2, 2, 2,101,101, 2, 2, 96, 96, 96, 96, 96, 2, + 96, 96,111,111,111,111,111,111,111, 2,100,100,100,100,108,108, + 108,108,108,108, 2,108,108,108, 2, 2,129,129,129,129,129,129, + 129, 2,129, 2,129,129,129,129, 2,129,129,129, 2, 2,109,109, + 109,109,109,109,109, 2,109,109, 2, 2,107,107,107,107, 2,107, + 107,107,107, 2, 2,107,107, 2,107,107,107,107, 2, 1,107,107, + 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, 2,107,107,137,137, + 137,137, 2,137,137,137,137,137, 2, 2,124,124,124,124,124,124, + 2, 2,123,123,123,123,123,123, 2, 2,114,114,114,114,114, 2, + 2, 2,114,114, 2, 2,102,102,102,102,102,102, 2, 2,126,126, + 126,126,126,126,126, 2, 2,126,126,126,142,142,142,142,125,125, + 125,125,125,125,125, 2, 2, 2, 2,125,154,154,154,154,154,154, + 154, 2, 2,154, 2, 2, 2,154,154, 2,154,154, 2,154,154, 2, + 2,154,154,154, 2, 2,150,150,150,150, 2, 2,150,150,150, 2, + 2, 2,141,141,141,141,140,140,140,140,140,140,140, 2,121,121, + 121,121,121, 2, 2, 2, 7, 7, 2, 2,133,133,133,133,133, 2, + 133,133,133,133,133, 2,133,133, 2, 2,133, 2, 2, 2,134,134, + 134,134, 2, 2,134,134, 2,134,134,134,134,134,134, 2,138,138, + 138,138,138,138,138, 2,138,138, 2,138, 2, 2,138, 2,138,138, + 2, 2,143,143,143,143,143,143, 2,143,143, 2,143,143,143,143, + 143, 2,143, 2, 2, 2,143,143, 2, 2,145,145,145,145,145, 2, + 2, 2,163,163,163,163,163, 2,163,163,163,163,163, 2, 2, 2, + 163,163,163,163, 2, 2, 86, 2, 2, 2, 63, 63, 63, 63, 63, 63, + 2, 2, 63, 63, 63, 2, 63, 2, 2, 2,157,157,157,157,157,157, + 157, 2, 80, 80, 80, 80, 80, 80, 2, 2,127,127,127,127,127,127, + 127, 2, 79, 2, 2, 2,115,115,115,115,115,115,115, 2,115,115, + 2, 2, 2, 2,115,115,159,159,159,159,159,159,159, 2,159,159, + 2, 2,103,103,103,103,103,103, 2, 2,119,119,119,119,119,119, + 2, 2,119,119, 2,119, 2,119,119,119,146,146,146,146,146,146, + 146, 2, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, 2, 99,136,139, + 13, 13,155, 2, 2, 2,136,136,136,136,155,155,155,155,155,155, + 2, 2,136, 2, 2, 2, 2, 17, 17, 17, 2, 17, 17, 2, 17, 15, + 15, 15, 17, 17, 17, 2, 2, 2, 15, 2, 2, 17, 2, 2,139,139, + 139,139,105,105,105,105,105,105,105, 2,105, 2, 2, 2,105,105, + 2, 2, 1, 1, 2, 2, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, + 1, 1, 2, 2, 0, 2, 2, 0, 0, 2, 0, 2, 0, 2,131,131, + 131,131, 2, 2, 2,131, 2,131,131,131, 56, 56, 56, 2, 56, 2, + 2, 56, 56, 56, 2, 56, 56, 2, 56, 56, 6, 6, 2, 2, 2, 2, + 2, 6,151,151,151,151,151, 2, 2, 2,151,151, 2, 2, 2, 2, + 151,151,160,160,160,160,160,160,160, 2,152,152,152,152,152,152, + 2, 2, 2, 2, 2,152,164,164,164,164,164,164, 2, 2, 2, 30, + 30, 2,113,113,113,113,113, 2, 2,113,113,113,113, 2,132,132, + 132,132,132,132, 2, 2, 2, 2,132,132, 2, 3, 3, 2, 3, 2, + 2, 3, 2, 3, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, + 2, 3, 15, 0, 0, 2, 13, 2, 2, 2, 13, 13, 13, 2, 2, 0, + 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, + 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, + 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 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, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, + 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, + 0, 0, 66, 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, 0, 0, 0, 0, 0, 0, 0, + 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0, + 108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, + 0, 0,116, 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,117, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0,127, + 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, + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, 0, + 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,162,163, 0, 0, 0, 0, 0, + 0, 0,164, 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,165, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,168, 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,169,170, 0, 0, 0, 0,171,172, 0, 0, 0, + 173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188, + 189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204, + 205,206, 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, +}; +static const uint16_t +_hb_ucd_u16[10060] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, + 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, + 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, + 48, 67, 68, 69, 48, 70, 71, 48, 72, 73, 48, 48, 74, 32, 75, 32, + 76, 48, 48, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 83, 84, 91, 92, 93, 94, 95, 96, 97, 84, 98, 99, 100, 88, 101, + 102, 83, 84, 103, 104, 105, 88, 106, 107, 108, 109, 110, 111, 112, 94, 113, + 114, 115, 84, 116, 117, 118, 88, 119, 120, 115, 84, 121, 122, 123, 88, 124, + 125, 115, 48, 126, 127, 128, 88, 129, 130, 131, 48, 132, 133, 134, 94, 135, + 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, + 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, + 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 168, 169, 48, 48, + 168, 48, 48, 170, 171, 172, 48, 48, 48, 171, 48, 48, 48, 173, 174, 175, + 48, 176, 9, 9, 9, 9, 9, 177, 178, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, + 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, + 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, + 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, + 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 216, 140, 140, 140, + 217, 48, 48, 218, 219, 160, 220, 221, 222, 48, 223, 64, 48, 48, 224, 225, + 48, 48, 226, 227, 228, 64, 48, 229, 230, 9, 9, 231, 232, 233, 234, 235, + 11, 11, 236, 27, 27, 27, 237, 238, 11, 239, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 240, 13, 13, 13, 13, 13, 13, + 241, 242, 241, 241, 242, 243, 241, 244, 245, 245, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 272, 273, 274, 275, 209, 276, 277, 209, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 280, 209, 281, 209, 209, 209, 209, 282, + 209, 283, 279, 284, 209, 285, 286, 209, 209, 209, 287, 140, 288, 140, 271, 271, + 271, 289, 209, 209, 209, 209, 290, 271, 209, 209, 209, 209, 209, 209, 209, 209, + 209, 209, 209, 291, 292, 209, 209, 293, 209, 209, 209, 209, 209, 209, 294, 209, + 209, 209, 209, 209, 209, 209, 295, 296, 271, 297, 209, 209, 298, 279, 299, 279, + 300, 301, 279, 279, 279, 302, 279, 303, 209, 209, 209, 279, 304, 209, 209, 305, + 209, 306, 209, 209, 209, 209, 209, 209, 9, 9, 9, 11, 11, 11, 307, 308, + 13, 13, 13, 13, 13, 13, 309, 310, 11, 11, 311, 48, 48, 48, 312, 313, + 48, 314, 315, 315, 315, 315, 32, 32, 316, 317, 318, 319, 320, 321, 140, 140, + 209, 322, 209, 209, 209, 209, 209, 323, 209, 209, 209, 209, 209, 324, 140, 209, + 325, 326, 327, 328, 136, 48, 48, 48, 48, 329, 178, 48, 48, 48, 48, 330, + 331, 48, 48, 136, 48, 48, 48, 48, 200, 332, 48, 48, 209, 209, 333, 48, + 209, 334, 335, 209, 336, 337, 209, 209, 335, 209, 209, 337, 209, 209, 209, 209, + 48, 48, 48, 48, 209, 209, 209, 209, 48, 338, 48, 48, 48, 48, 48, 48, + 151, 209, 209, 209, 287, 48, 48, 229, 339, 48, 340, 140, 13, 13, 341, 342, + 13, 343, 48, 48, 48, 48, 344, 345, 31, 346, 347, 348, 13, 13, 13, 349, + 350, 351, 352, 353, 354, 355, 140, 356, 357, 48, 358, 359, 48, 48, 48, 360, + 361, 48, 48, 362, 363, 192, 32, 364, 64, 48, 365, 48, 366, 367, 48, 151, + 76, 48, 48, 368, 369, 370, 371, 372, 48, 48, 373, 374, 375, 376, 48, 377, + 48, 48, 48, 378, 379, 380, 381, 382, 383, 384, 315, 11, 11, 385, 386, 11, + 11, 11, 11, 11, 48, 48, 387, 192, 48, 48, 388, 48, 389, 48, 48, 206, + 390, 390, 390, 390, 390, 390, 390, 390, 391, 391, 391, 391, 391, 391, 391, 391, + 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, + 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, + 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, + 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, + 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, + 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, + 422, 271, 271, 423, 272, 272, 272, 424, 425, 426, 427, 140, 140, 209, 209, 428, + 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, + 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, + 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, + 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 140, 140, 140, 140, + 48, 48, 48, 314, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, + 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, + 48, 454, 48, 455, 140, 140, 140, 140, 48, 48, 48, 456, 271, 457, 271, 271, + 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, + 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, + 48, 48, 475, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 271, 476, + 48, 48, 477, 478, 140, 140, 140, 479, 48, 464, 480, 48, 62, 481, 140, 48, + 482, 140, 140, 48, 483, 140, 48, 314, 484, 48, 48, 485, 486, 457, 487, 488, + 222, 48, 48, 489, 490, 48, 196, 192, 491, 48, 492, 493, 494, 48, 48, 495, + 222, 48, 48, 496, 497, 498, 499, 500, 48, 97, 501, 502, 503, 140, 140, 140, + 504, 505, 506, 48, 48, 507, 508, 192, 509, 83, 84, 510, 511, 512, 513, 514, + 48, 48, 48, 515, 516, 517, 478, 140, 48, 48, 48, 518, 519, 192, 140, 140, + 48, 48, 520, 521, 522, 523, 140, 140, 48, 48, 48, 524, 525, 192, 526, 140, + 48, 48, 527, 528, 192, 140, 140, 140, 48, 173, 529, 530, 314, 140, 140, 140, + 48, 48, 501, 531, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 532, + 533, 534, 48, 535, 536, 192, 140, 140, 140, 140, 537, 48, 48, 538, 539, 140, + 540, 48, 48, 541, 542, 543, 48, 48, 544, 545, 546, 48, 48, 48, 48, 196, + 547, 140, 140, 140, 140, 140, 140, 140, 84, 48, 520, 548, 549, 148, 175, 550, + 48, 551, 552, 553, 140, 140, 140, 140, 554, 48, 48, 555, 556, 192, 557, 48, + 558, 559, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 560, + 561, 115, 48, 562, 563, 192, 140, 140, 140, 140, 140, 100, 271, 564, 565, 566, + 48, 207, 140, 140, 140, 140, 140, 140, 272, 272, 272, 272, 272, 272, 567, 568, + 48, 48, 48, 48, 388, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 569, + 48, 48, 48, 570, 571, 572, 140, 140, 48, 48, 48, 48, 314, 140, 140, 140, + 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 573, + 48, 48, 48, 574, 575, 576, 577, 578, 48, 140, 140, 140, 140, 140, 140, 140, + 140, 140, 140, 140, 9, 9, 11, 11, 271, 579, 140, 140, 140, 140, 140, 140, + 48, 48, 48, 48, 580, 581, 582, 582, 583, 584, 140, 140, 140, 140, 585, 586, + 48, 48, 48, 48, 48, 48, 48, 440, 48, 48, 48, 48, 48, 199, 140, 140, + 196, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 587, + 48, 48, 588, 589, 140, 590, 591, 48, 48, 48, 48, 48, 48, 48, 48, 206, + 48, 48, 48, 48, 48, 48, 71, 151, 196, 592, 593, 140, 140, 140, 140, 140, + 32, 32, 594, 32, 595, 209, 209, 209, 209, 209, 209, 209, 323, 140, 140, 140, + 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 596, 209, 209, 209, 597, 598, + 599, 209, 600, 209, 209, 209, 288, 140, 209, 209, 209, 209, 601, 140, 140, 140, + 140, 140, 140, 140, 271, 602, 271, 602, 209, 209, 209, 209, 209, 287, 271, 461, + 9, 603, 11, 604, 605, 606, 241, 9, 607, 608, 609, 610, 611, 9, 603, 11, + 612, 613, 11, 614, 615, 616, 617, 9, 618, 11, 9, 603, 11, 604, 605, 11, + 241, 9, 607, 617, 9, 618, 11, 9, 603, 11, 619, 9, 620, 621, 622, 623, + 11, 624, 9, 625, 626, 627, 628, 11, 629, 9, 630, 11, 631, 632, 632, 632, + 32, 32, 32, 633, 32, 32, 634, 635, 636, 637, 45, 140, 140, 140, 140, 140, + 638, 639, 640, 140, 140, 140, 140, 140, 641, 642, 643, 27, 27, 27, 644, 140, + 645, 140, 140, 140, 140, 140, 140, 140, 48, 48, 151, 646, 647, 140, 140, 140, + 140, 48, 648, 140, 48, 48, 649, 650, 140, 140, 140, 140, 140, 48, 651, 192, + 140, 140, 140, 140, 140, 140, 652, 200, 48, 48, 48, 48, 653, 595, 140, 140, + 9, 9, 607, 11, 654, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 499, + 271, 271, 655, 656, 140, 140, 140, 140, 499, 271, 657, 658, 140, 140, 140, 140, + 659, 48, 660, 661, 662, 663, 664, 665, 666, 206, 667, 206, 140, 140, 140, 668, + 209, 209, 669, 209, 209, 209, 209, 209, 209, 323, 334, 670, 670, 670, 209, 324, + 671, 209, 209, 209, 209, 209, 209, 209, 209, 209, 672, 140, 140, 140, 673, 209, + 674, 209, 209, 669, 675, 676, 324, 140, 209, 209, 209, 209, 209, 209, 209, 677, + 209, 209, 209, 209, 209, 678, 426, 426, 209, 209, 209, 209, 209, 209, 209, 679, + 209, 209, 209, 209, 209, 176, 669, 427, 669, 209, 209, 209, 680, 176, 209, 209, + 680, 209, 672, 676, 140, 140, 140, 140, 209, 209, 209, 209, 209, 323, 672, 426, + 675, 209, 209, 681, 682, 669, 675, 675, 209, 683, 209, 209, 288, 140, 140, 192, + 48, 48, 48, 48, 48, 48, 140, 140, 48, 48, 48, 207, 48, 48, 48, 48, + 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 478, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 100, 48, 48, 48, 48, 48, 48, 204, 140, 140, + 48, 204, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 71, 48, 48, 48, + 48, 48, 48, 140, 140, 140, 140, 140, 684, 140, 570, 570, 570, 570, 570, 570, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, + 391, 391, 391, 391, 391, 391, 391, 685, 391, 391, 391, 391, 391, 391, 391, 686, + 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, + 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, + 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 16, 17, 18, 17, 17, 19, 20, 21, 21, 22, 21, 23, 24, + 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, + 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, + 39, 39, 40, 41, 42, 43, 44, 27, 45, 46, 27, 27, 27, 27, 47, 27, + 48, 48, 48, 48, 48, 49, 50, 48, 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, 109, 110, 111, 112, 109, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 122, 123, 122, 124, 125, 125, 126, 127, 128, 129, 130, 131, 125, 125, + 132, 132, 132, 132, 133, 132, 134, 135, 132, 133, 132, 136, 136, 137, 125, 125, + 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 140, 139, 139, 141, + 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 144, 145, 143, 143, + 144, 143, 143, 146, 147, 148, 143, 143, 143, 147, 143, 143, 143, 149, 143, 150, + 143, 151, 152, 152, 152, 152, 152, 153, 154, 154, 154, 154, 154, 154, 154, 154, + 155, 156, 157, 157, 157, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 168, 168, 168, 168, 169, 170, 170, 171, 172, 173, 173, 173, 173, 173, 174, + 173, 173, 175, 154, 154, 154, 154, 176, 177, 178, 179, 179, 180, 181, 182, 183, + 184, 184, 185, 184, 186, 187, 168, 168, 188, 189, 190, 190, 190, 191, 190, 192, + 193, 193, 194, 8, 195, 125, 125, 125, 196, 196, 196, 196, 197, 196, 196, 198, + 199, 199, 199, 199, 200, 200, 200, 201, 202, 202, 202, 203, 204, 205, 205, 205, + 206, 139, 139, 207, 208, 209, 210, 211, 4, 4, 212, 4, 4, 213, 214, 215, + 4, 4, 4, 216, 8, 8, 8, 8, 11, 217, 11, 11, 217, 218, 11, 219, + 11, 11, 11, 220, 220, 221, 11, 222, 223, 0, 0, 0, 0, 0, 224, 225, + 226, 227, 0, 0, 228, 8, 8, 229, 0, 0, 230, 231, 232, 0, 4, 4, + 233, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 234, 125, 235, 125, 0, 0, 236, 236, 236, 236, 236, 236, 236, 236, + 0, 0, 0, 0, 0, 0, 0, 237, 0, 238, 0, 0, 0, 0, 0, 0, + 239, 239, 239, 239, 239, 239, 4, 4, 240, 240, 240, 240, 240, 240, 240, 241, + 139, 139, 140, 242, 242, 242, 243, 244, 143, 245, 246, 246, 246, 246, 14, 14, + 0, 0, 0, 0, 0, 247, 125, 125, 248, 249, 248, 248, 248, 248, 248, 250, + 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 251, 125, 0, + 252, 0, 253, 254, 255, 256, 256, 256, 256, 257, 258, 259, 259, 259, 259, 260, + 261, 262, 262, 263, 142, 142, 142, 142, 264, 0, 262, 262, 0, 0, 265, 259, + 142, 264, 0, 0, 0, 0, 142, 266, 0, 0, 0, 0, 0, 259, 259, 267, + 259, 259, 259, 259, 259, 268, 0, 0, 248, 248, 248, 248, 0, 0, 0, 0, + 269, 269, 269, 269, 269, 269, 269, 269, 270, 269, 269, 269, 271, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 274, 125, 14, 14, 14, 14, + 14, 14, 275, 275, 275, 275, 275, 276, 0, 0, 277, 4, 4, 4, 4, 4, + 278, 4, 4, 4, 279, 280, 125, 281, 282, 282, 283, 284, 285, 285, 285, 286, + 287, 287, 287, 287, 288, 289, 48, 48, 290, 290, 291, 292, 292, 293, 142, 294, + 295, 295, 295, 295, 296, 297, 138, 298, 299, 299, 299, 300, 301, 302, 138, 138, + 303, 303, 303, 303, 304, 305, 306, 307, 308, 309, 246, 4, 4, 310, 311, 152, + 152, 152, 152, 152, 306, 306, 312, 313, 142, 142, 314, 142, 315, 142, 142, 316, + 125, 125, 125, 125, 125, 125, 125, 125, 248, 248, 248, 248, 248, 248, 317, 248, + 248, 248, 248, 248, 248, 318, 125, 125, 319, 320, 21, 321, 322, 27, 27, 27, + 27, 27, 27, 27, 323, 324, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 325, 27, 27, 27, 27, 27, 326, 27, 27, 327, 125, 125, 27, + 8, 284, 328, 0, 0, 329, 330, 331, 27, 27, 27, 27, 27, 27, 27, 332, + 333, 0, 1, 2, 1, 2, 334, 258, 259, 335, 142, 264, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 344, 125, 125, 341, 341, 341, 341, 341, 341, 341, 345, + 346, 0, 0, 347, 11, 11, 11, 11, 348, 349, 350, 125, 125, 0, 0, 351, + 352, 353, 354, 354, 354, 355, 356, 357, 358, 358, 359, 360, 361, 362, 362, 363, + 364, 365, 366, 366, 367, 368, 125, 125, 369, 369, 369, 369, 369, 370, 370, 370, + 371, 372, 373, 374, 374, 375, 374, 376, 377, 377, 378, 379, 379, 379, 380, 381, + 381, 382, 383, 384, 125, 125, 125, 125, 385, 385, 385, 385, 385, 385, 385, 385, + 385, 385, 385, 386, 385, 387, 388, 125, 389, 4, 4, 390, 125, 125, 125, 125, + 391, 392, 392, 393, 394, 395, 396, 396, 397, 398, 399, 125, 125, 125, 400, 401, + 402, 403, 404, 405, 125, 125, 125, 125, 406, 406, 407, 408, 407, 409, 407, 407, + 410, 411, 412, 413, 414, 414, 415, 415, 416, 416, 125, 125, 417, 417, 418, 419, + 420, 420, 420, 421, 422, 423, 424, 425, 426, 427, 428, 125, 125, 125, 125, 125, + 429, 429, 429, 429, 430, 125, 125, 125, 431, 431, 431, 432, 431, 431, 431, 433, + 434, 434, 435, 436, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 27, 45, + 437, 437, 438, 439, 125, 125, 125, 440, 441, 441, 442, 443, 443, 444, 125, 445, + 446, 125, 125, 447, 448, 125, 449, 450, 451, 451, 451, 451, 452, 453, 451, 454, + 455, 455, 455, 455, 456, 457, 458, 459, 460, 460, 460, 461, 462, 463, 463, 464, + 465, 465, 465, 465, 465, 465, 466, 467, 468, 469, 468, 468, 470, 125, 125, 125, + 471, 472, 473, 474, 474, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, + 485, 485, 485, 485, 485, 486, 487, 125, 488, 488, 488, 488, 489, 490, 125, 125, + 491, 491, 491, 492, 491, 493, 125, 125, 494, 494, 494, 494, 495, 496, 497, 125, + 498, 498, 498, 499, 499, 125, 125, 125, 500, 501, 502, 500, 503, 125, 125, 125, + 504, 504, 504, 505, 125, 125, 125, 125, 125, 125, 506, 506, 506, 506, 506, 507, + 508, 509, 510, 511, 512, 513, 125, 125, 125, 125, 514, 515, 515, 514, 516, 125, + 517, 517, 517, 517, 518, 519, 519, 519, 519, 519, 520, 154, 521, 521, 521, 522, + 523, 125, 125, 125, 125, 125, 125, 125, 524, 525, 525, 526, 527, 525, 528, 529, + 529, 530, 531, 532, 125, 125, 125, 125, 533, 534, 534, 535, 536, 537, 538, 539, + 540, 541, 542, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 543, 544, + 545, 546, 545, 547, 545, 548, 125, 125, 125, 125, 125, 549, 550, 550, 550, 551, + 552, 552, 552, 552, 552, 552, 552, 552, 552, 553, 125, 125, 125, 125, 125, 125, + 552, 552, 552, 552, 552, 552, 554, 555, 552, 552, 552, 552, 556, 125, 125, 125, + 125, 557, 557, 557, 557, 557, 557, 558, 559, 559, 559, 559, 559, 559, 559, 559, + 559, 559, 559, 559, 559, 560, 125, 125, 561, 561, 561, 561, 561, 561, 561, 561, + 561, 561, 561, 561, 562, 125, 125, 125, 275, 275, 275, 275, 275, 275, 275, 275, + 275, 275, 275, 563, 564, 565, 566, 567, 567, 567, 567, 568, 569, 570, 571, 572, + 573, 573, 573, 573, 574, 575, 576, 577, 573, 125, 125, 125, 125, 125, 125, 125, + 125, 125, 125, 125, 578, 578, 578, 578, 578, 579, 125, 125, 125, 125, 125, 125, + 580, 580, 580, 580, 581, 580, 580, 580, 582, 580, 125, 125, 125, 125, 583, 584, + 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 586, + 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 588, 125, 125, + 589, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 590, + 591, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 592, 593, 125, 594, 595, 596, 596, 596, 596, 596, 596, 596, 596, 596, + 596, 596, 596, 596, 596, 596, 596, 597, 598, 598, 598, 598, 598, 598, 599, 600, + 601, 602, 603, 125, 125, 125, 125, 125, 8, 8, 604, 8, 605, 0, 0, 0, + 0, 0, 0, 0, 603, 125, 125, 125, 0, 0, 0, 0, 0, 0, 0, 606, + 0, 0, 607, 0, 0, 0, 608, 609, 610, 0, 611, 0, 0, 0, 235, 125, + 11, 11, 11, 11, 612, 125, 125, 125, 125, 125, 125, 125, 0, 603, 0, 603, + 0, 0, 0, 0, 0, 234, 0, 613, 0, 0, 0, 0, 0, 224, 0, 0, + 0, 614, 615, 616, 617, 0, 0, 0, 618, 619, 0, 620, 621, 622, 0, 0, + 0, 0, 623, 0, 0, 0, 0, 0, 0, 0, 0, 0, 624, 0, 0, 0, + 625, 625, 625, 625, 625, 625, 625, 625, 626, 627, 628, 125, 125, 125, 125, 125, + 4, 629, 630, 125, 125, 125, 125, 125, 631, 632, 633, 14, 14, 14, 634, 125, + 635, 125, 125, 125, 125, 125, 125, 125, 636, 636, 637, 638, 639, 125, 125, 125, + 125, 640, 641, 125, 642, 642, 642, 643, 125, 125, 125, 125, 125, 644, 644, 645, + 125, 125, 125, 125, 125, 125, 646, 647, 648, 648, 648, 648, 648, 648, 648, 648, + 648, 648, 648, 648, 649, 650, 125, 125, 651, 651, 651, 651, 652, 653, 125, 125, + 125, 125, 125, 125, 125, 125, 125, 333, 0, 0, 0, 654, 125, 125, 125, 125, + 333, 0, 0, 247, 125, 125, 125, 125, 655, 27, 656, 657, 658, 659, 660, 661, + 662, 663, 664, 663, 125, 125, 125, 665, 0, 0, 357, 0, 0, 0, 0, 0, + 0, 603, 226, 333, 333, 333, 0, 606, 0, 0, 247, 125, 125, 125, 666, 0, + 667, 0, 0, 357, 613, 668, 606, 125, 0, 0, 0, 0, 0, 669, 349, 349, + 0, 0, 0, 0, 0, 0, 0, 670, 0, 0, 0, 0, 0, 284, 357, 228, + 357, 0, 0, 0, 671, 284, 0, 0, 671, 0, 247, 668, 125, 125, 125, 125, + 0, 0, 0, 0, 0, 603, 247, 349, 613, 0, 0, 672, 673, 357, 613, 613, + 0, 329, 0, 0, 235, 125, 125, 284, 248, 248, 248, 248, 248, 248, 125, 125, + 248, 248, 248, 318, 248, 248, 248, 248, 248, 317, 248, 248, 248, 248, 248, 248, + 248, 248, 584, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 674, 248, + 248, 248, 248, 248, 248, 317, 125, 125, 248, 317, 125, 125, 125, 125, 125, 125, + 248, 248, 248, 248, 675, 248, 248, 248, 248, 248, 248, 125, 125, 125, 125, 125, + 676, 125, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 1, 2, 2, 2, + 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 8, 8, 8, 8, 16, 8, 8, 8, 17, 18, 18, 18, + 19, 19, 19, 19, 19, 20, 19, 19, 21, 22, 22, 22, 22, 22, 22, 22, + 22, 23, 21, 22, 22, 22, 23, 21, 24, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 12, 12, 25, 25, 26, 27, 25, 28, 12, 12, 29, 30, 29, 31, + 29, 29, 32, 32, 29, 29, 29, 29, 31, 29, 33, 7, 7, 34, 29, 29, + 35, 29, 29, 29, 29, 29, 29, 30, 36, 36, 36, 37, 36, 36, 36, 36, + 36, 36, 38, 39, 40, 40, 40, 40, 41, 12, 12, 12, 42, 42, 42, 42, + 42, 42, 43, 44, 45, 45, 45, 45, 45, 45, 45, 46, 45, 45, 45, 47, + 48, 48, 48, 48, 48, 48, 48, 49, 36, 36, 38, 12, 29, 29, 29, 50, + 51, 12, 29, 29, 52, 29, 29, 29, 53, 53, 53, 53, 54, 55, 53, 53, + 53, 56, 53, 53, 57, 58, 57, 59, 59, 57, 57, 57, 57, 57, 60, 57, + 61, 62, 63, 57, 57, 59, 59, 64, 12, 65, 12, 66, 57, 62, 57, 57, + 57, 57, 57, 64, 67, 67, 68, 69, 70, 71, 71, 71, 71, 71, 72, 71, + 72, 73, 74, 72, 68, 69, 70, 74, 75, 12, 67, 76, 12, 77, 71, 71, + 71, 68, 12, 12, 78, 78, 79, 80, 80, 79, 79, 79, 79, 79, 81, 79, + 81, 78, 82, 79, 79, 80, 80, 82, 83, 12, 12, 12, 79, 84, 79, 79, + 82, 12, 78, 79, 85, 85, 86, 87, 87, 86, 86, 86, 86, 86, 88, 86, + 88, 85, 89, 86, 86, 87, 87, 89, 12, 85, 12, 90, 86, 91, 86, 86, + 86, 86, 12, 12, 92, 93, 94, 92, 95, 96, 97, 95, 98, 99, 94, 92, + 100, 100, 96, 92, 94, 92, 95, 96, 99, 98, 12, 12, 12, 92, 100, 100, + 100, 100, 94, 12, 101, 101, 101, 102, 102, 101, 101, 101, 101, 101, 102, 101, + 101, 101, 103, 101, 101, 102, 102, 103, 12, 104, 105, 106, 101, 107, 101, 101, + 12, 108, 101, 101, 109, 109, 109, 110, 110, 109, 109, 109, 109, 109, 110, 109, + 109, 111, 112, 109, 109, 110, 110, 112, 12, 113, 12, 113, 109, 114, 109, 109, + 111, 12, 12, 12, 115, 115, 115, 116, 116, 115, 115, 115, 115, 115, 115, 115, + 115, 116, 116, 115, 12, 115, 115, 115, 115, 117, 115, 115, 118, 118, 119, 119, + 119, 120, 121, 119, 119, 119, 119, 119, 122, 119, 119, 123, 119, 120, 124, 125, + 119, 126, 119, 119, 12, 121, 119, 119, 121, 127, 12, 12, 128, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 130, 131, 129, 129, 129, 12, 12, 12, 12, 12, + 132, 133, 134, 135, 135, 135, 135, 135, 135, 136, 135, 135, 135, 135, 135, 137, + 135, 138, 135, 134, 135, 135, 137, 135, 139, 139, 139, 139, 139, 139, 140, 139, + 139, 139, 139, 141, 140, 139, 139, 139, 139, 139, 139, 142, 139, 143, 144, 12, + 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 12, 148, 146, 146, 149, 146, + 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 152, 153, 151, 154, 152, 153, + 152, 153, 151, 154, 152, 153, 151, 151, 151, 154, 151, 151, 151, 151, 154, 155, + 151, 151, 151, 156, 151, 151, 153, 12, 157, 157, 157, 157, 157, 158, 157, 158, + 159, 159, 159, 159, 160, 160, 160, 160, 160, 160, 160, 161, 162, 162, 162, 162, + 162, 162, 163, 164, 162, 162, 165, 12, 166, 166, 166, 166, 166, 167, 12, 168, + 169, 169, 169, 169, 169, 170, 12, 12, 171, 171, 171, 171, 171, 12, 12, 12, + 172, 172, 172, 173, 173, 12, 12, 12, 174, 174, 174, 174, 174, 174, 174, 175, + 174, 174, 175, 12, 176, 177, 178, 178, 178, 178, 179, 12, 178, 178, 178, 178, + 178, 178, 180, 12, 178, 178, 181, 12, 159, 182, 12, 12, 183, 183, 183, 183, + 183, 183, 183, 184, 183, 183, 183, 12, 185, 183, 183, 183, 186, 186, 186, 186, + 186, 186, 186, 187, 186, 188, 12, 12, 189, 189, 189, 189, 189, 189, 189, 12, + 189, 189, 190, 12, 189, 189, 191, 192, 193, 193, 193, 193, 193, 193, 193, 194, + 195, 195, 195, 195, 195, 195, 195, 196, 195, 195, 195, 197, 195, 195, 198, 12, + 195, 195, 195, 198, 7, 7, 7, 199, 200, 200, 200, 200, 200, 200, 200, 201, + 200, 200, 200, 202, 203, 203, 203, 203, 204, 204, 204, 204, 204, 12, 12, 204, + 205, 205, 205, 205, 205, 205, 206, 205, 205, 205, 207, 208, 209, 209, 209, 209, + 19, 19, 210, 12, 146, 146, 211, 212, 203, 203, 12, 12, 213, 7, 7, 7, + 214, 7, 215, 216, 0, 215, 217, 12, 2, 218, 219, 2, 2, 2, 2, 220, + 221, 218, 222, 2, 2, 2, 223, 2, 2, 2, 2, 224, 8, 225, 8, 225, + 8, 8, 226, 226, 8, 8, 8, 225, 8, 15, 8, 8, 8, 10, 8, 227, + 10, 15, 8, 14, 0, 0, 0, 228, 0, 229, 0, 0, 230, 0, 0, 231, + 0, 0, 0, 232, 2, 2, 2, 233, 234, 12, 12, 12, 235, 12, 12, 12, + 0, 236, 237, 0, 4, 0, 0, 0, 0, 0, 0, 4, 2, 2, 5, 12, + 0, 232, 12, 12, 0, 0, 232, 12, 238, 238, 238, 238, 0, 239, 0, 0, + 0, 240, 0, 0, 241, 241, 241, 241, 18, 18, 18, 18, 18, 12, 242, 18, + 243, 243, 243, 243, 243, 243, 12, 244, 245, 12, 12, 244, 151, 154, 12, 12, + 151, 154, 151, 154, 0, 0, 0, 246, 247, 247, 247, 247, 247, 247, 248, 247, + 247, 12, 12, 12, 247, 249, 12, 12, 0, 250, 0, 0, 251, 247, 252, 253, + 0, 0, 247, 0, 254, 255, 255, 255, 255, 255, 255, 255, 255, 256, 257, 258, + 259, 260, 260, 260, 260, 260, 260, 260, 260, 260, 261, 259, 12, 262, 263, 263, + 263, 263, 263, 263, 264, 150, 150, 150, 150, 150, 150, 265, 0, 12, 12, 131, + 150, 150, 150, 266, 260, 260, 260, 261, 260, 260, 0, 0, 267, 267, 267, 267, + 267, 267, 267, 268, 267, 269, 12, 12, 270, 270, 270, 270, 271, 271, 271, 271, + 271, 271, 271, 12, 272, 272, 272, 272, 272, 272, 12, 12, 237, 2, 2, 2, + 2, 2, 231, 2, 2, 2, 273, 12, 274, 275, 276, 12, 277, 2, 2, 2, + 278, 278, 278, 278, 278, 278, 278, 279, 0, 0, 246, 12, 280, 280, 280, 280, + 280, 280, 12, 12, 281, 281, 281, 281, 281, 282, 12, 283, 281, 281, 282, 12, + 284, 284, 284, 284, 284, 284, 284, 285, 286, 286, 286, 286, 286, 12, 12, 287, + 150, 150, 150, 288, 289, 289, 289, 289, 289, 289, 289, 290, 289, 289, 291, 292, + 145, 145, 145, 293, 294, 294, 294, 294, 294, 295, 12, 12, 294, 294, 294, 296, + 294, 294, 296, 294, 297, 297, 297, 297, 298, 12, 12, 12, 12, 12, 299, 297, + 300, 300, 300, 300, 300, 301, 12, 12, 155, 154, 155, 154, 155, 154, 12, 12, + 2, 2, 3, 2, 2, 302, 303, 12, 300, 300, 300, 304, 300, 300, 304, 12, + 150, 12, 12, 12, 150, 265, 305, 150, 150, 150, 150, 12, 247, 247, 247, 249, + 247, 247, 249, 12, 2, 273, 12, 12, 306, 22, 12, 24, 25, 26, 25, 307, + 308, 309, 25, 25, 50, 12, 12, 12, 310, 29, 29, 29, 29, 29, 29, 311, + 312, 29, 29, 29, 29, 29, 12, 310, 7, 7, 7, 313, 232, 0, 0, 0, + 0, 232, 0, 12, 29, 314, 29, 29, 29, 29, 29, 315, 316, 0, 0, 0, + 0, 317, 260, 260, 260, 260, 260, 318, 319, 150, 319, 150, 319, 150, 319, 288, + 0, 232, 0, 232, 12, 12, 316, 246, 320, 320, 320, 321, 320, 320, 320, 320, + 320, 322, 320, 320, 320, 320, 322, 323, 320, 320, 320, 324, 320, 320, 322, 12, + 232, 131, 0, 0, 0, 131, 0, 0, 8, 8, 8, 14, 0, 0, 0, 234, + 325, 12, 12, 12, 0, 0, 0, 326, 327, 327, 327, 327, 327, 327, 327, 328, + 329, 329, 329, 329, 330, 12, 12, 12, 215, 0, 0, 0, 0, 0, 0, 12, + 331, 331, 331, 331, 331, 12, 12, 332, 333, 333, 333, 333, 333, 333, 334, 12, + 335, 335, 335, 335, 335, 335, 336, 12, 337, 337, 337, 337, 337, 337, 337, 338, + 339, 339, 339, 339, 339, 12, 339, 339, 339, 340, 12, 12, 341, 341, 341, 341, + 342, 342, 342, 342, 343, 343, 343, 343, 343, 343, 343, 344, 343, 343, 344, 12, + 345, 345, 345, 345, 345, 12, 345, 345, 345, 345, 345, 12, 346, 346, 346, 346, + 346, 346, 12, 12, 347, 347, 347, 347, 347, 12, 12, 348, 349, 349, 350, 349, + 350, 351, 349, 349, 351, 349, 349, 349, 351, 349, 351, 352, 353, 353, 353, 353, + 353, 354, 12, 12, 353, 355, 12, 12, 353, 353, 12, 12, 2, 274, 2, 2, + 356, 2, 273, 12, 357, 358, 359, 357, 357, 357, 357, 357, 357, 360, 361, 362, + 363, 363, 363, 363, 363, 364, 363, 363, 365, 365, 365, 365, 366, 366, 366, 366, + 366, 366, 366, 367, 12, 368, 366, 366, 369, 369, 369, 369, 370, 371, 372, 369, + 373, 373, 373, 373, 373, 373, 373, 374, 375, 375, 375, 375, 375, 375, 376, 377, + 378, 378, 378, 378, 379, 379, 379, 379, 379, 379, 12, 379, 380, 379, 379, 379, + 381, 382, 12, 381, 381, 383, 383, 381, 381, 381, 381, 381, 381, 384, 385, 386, + 381, 381, 387, 12, 388, 388, 388, 388, 389, 389, 389, 389, 390, 390, 390, 390, + 390, 391, 392, 390, 390, 391, 12, 12, 393, 393, 393, 393, 393, 394, 395, 393, + 396, 396, 396, 396, 396, 397, 396, 396, 398, 398, 398, 398, 399, 12, 398, 398, + 400, 400, 400, 400, 401, 12, 402, 403, 12, 12, 402, 400, 404, 404, 404, 404, + 404, 404, 405, 12, 406, 406, 406, 406, 407, 12, 12, 12, 407, 12, 408, 406, + 409, 409, 409, 409, 409, 409, 12, 12, 409, 409, 410, 12, 411, 411, 411, 411, + 411, 411, 412, 413, 413, 12, 12, 12, 12, 12, 12, 414, 415, 415, 415, 415, + 415, 415, 12, 12, 416, 416, 416, 416, 416, 416, 417, 12, 418, 418, 418, 418, + 418, 418, 419, 12, 420, 420, 420, 420, 420, 420, 420, 12, 421, 421, 421, 421, + 421, 422, 12, 12, 423, 423, 423, 423, 423, 423, 423, 424, 425, 423, 423, 423, + 423, 424, 12, 426, 427, 427, 427, 427, 428, 12, 12, 429, 430, 430, 430, 430, + 430, 430, 431, 12, 430, 430, 432, 12, 433, 433, 433, 433, 433, 434, 433, 433, + 433, 433, 12, 12, 435, 435, 435, 435, 435, 436, 12, 12, 437, 437, 437, 437, + 118, 119, 119, 119, 119, 127, 12, 12, 438, 438, 438, 438, 439, 438, 438, 438, + 440, 12, 12, 12, 441, 442, 443, 444, 441, 441, 441, 444, 441, 441, 445, 12, + 446, 446, 446, 446, 446, 446, 447, 12, 446, 446, 448, 12, 449, 450, 449, 451, + 451, 449, 449, 449, 449, 449, 452, 449, 452, 450, 453, 449, 449, 451, 451, 454, + 455, 456, 12, 450, 449, 457, 449, 455, 449, 455, 12, 12, 458, 458, 458, 458, + 458, 458, 458, 459, 460, 12, 12, 12, 461, 461, 461, 461, 461, 461, 12, 12, + 461, 461, 462, 12, 463, 463, 463, 463, 463, 464, 463, 463, 463, 463, 463, 464, + 465, 465, 465, 465, 465, 466, 12, 12, 465, 465, 467, 12, 178, 178, 178, 180, + 468, 468, 468, 468, 468, 468, 469, 12, 470, 470, 470, 470, 470, 470, 471, 472, + 470, 470, 470, 12, 470, 471, 12, 12, 473, 473, 473, 473, 473, 473, 473, 12, + 474, 474, 474, 474, 475, 12, 12, 476, 477, 478, 479, 477, 477, 480, 477, 477, + 477, 477, 477, 477, 477, 481, 482, 477, 477, 478, 12, 12, 477, 477, 483, 12, + 484, 484, 485, 484, 484, 484, 484, 484, 484, 486, 12, 12, 487, 487, 487, 487, + 487, 487, 12, 12, 488, 488, 488, 488, 489, 12, 12, 12, 490, 490, 490, 490, + 490, 490, 491, 12, 53, 53, 492, 12, 493, 493, 494, 493, 493, 493, 493, 493, + 493, 495, 493, 493, 493, 496, 12, 12, 493, 493, 493, 497, 498, 498, 498, 498, + 499, 498, 498, 498, 498, 498, 500, 498, 498, 501, 12, 12, 502, 503, 504, 502, + 502, 502, 502, 502, 502, 503, 505, 504, 502, 502, 12, 12, 502, 502, 506, 12, + 507, 508, 509, 507, 507, 507, 507, 507, 507, 507, 507, 510, 508, 507, 511, 12, + 507, 507, 512, 12, 513, 513, 513, 513, 513, 513, 514, 12, 515, 515, 515, 515, + 516, 515, 515, 515, 515, 515, 517, 518, 515, 515, 519, 12, 520, 12, 12, 12, + 100, 100, 100, 100, 96, 12, 12, 98, 521, 521, 521, 521, 521, 521, 522, 12, + 521, 521, 521, 523, 521, 524, 12, 12, 521, 12, 12, 12, 525, 525, 525, 525, + 526, 12, 12, 12, 527, 527, 527, 527, 527, 528, 12, 12, 529, 529, 529, 529, + 529, 530, 12, 12, 272, 272, 531, 12, 532, 532, 532, 532, 532, 532, 532, 533, + 532, 532, 534, 535, 536, 536, 536, 536, 536, 536, 536, 537, 536, 536, 538, 12, + 539, 539, 539, 539, 539, 539, 539, 540, 539, 540, 12, 12, 541, 541, 541, 541, + 541, 542, 12, 12, 541, 541, 543, 541, 543, 541, 541, 541, 541, 541, 12, 544, + 545, 545, 545, 545, 545, 545, 546, 12, 547, 547, 547, 547, 547, 547, 548, 549, + 547, 547, 12, 549, 550, 551, 12, 12, 249, 12, 12, 12, 552, 552, 552, 552, + 552, 552, 12, 12, 553, 553, 553, 553, 553, 554, 12, 12, 552, 552, 555, 12, + 260, 556, 260, 557, 558, 255, 255, 255, 559, 12, 12, 12, 560, 12, 12, 12, + 256, 561, 12, 12, 12, 260, 12, 12, 562, 562, 562, 562, 562, 562, 562, 12, + 563, 563, 563, 563, 563, 563, 564, 12, 563, 563, 563, 565, 563, 563, 565, 12, + 563, 563, 566, 563, 0, 12, 12, 12, 7, 7, 7, 567, 7, 199, 12, 12, + 0, 246, 12, 12, 0, 232, 316, 0, 0, 568, 228, 0, 0, 0, 568, 7, + 213, 569, 7, 0, 0, 0, 570, 228, 8, 225, 12, 12, 0, 0, 234, 12, + 0, 0, 0, 229, 571, 572, 316, 229, 0, 0, 240, 316, 0, 316, 0, 0, + 0, 240, 232, 316, 0, 229, 0, 229, 0, 0, 240, 232, 0, 573, 239, 0, + 229, 0, 0, 0, 0, 246, 0, 0, 0, 0, 0, 239, 574, 574, 574, 574, + 574, 574, 574, 12, 12, 12, 575, 574, 576, 574, 574, 574, 2, 2, 2, 273, + 12, 275, 273, 12, 241, 577, 241, 241, 241, 241, 578, 241, 579, 580, 577, 12, + 19, 19, 19, 581, 12, 12, 12, 582, 583, 583, 583, 583, 583, 583, 583, 584, + 583, 583, 583, 585, 583, 583, 585, 586, 587, 587, 587, 587, 587, 587, 587, 588, + 589, 589, 589, 589, 589, 589, 590, 591, 592, 592, 592, 592, 592, 592, 593, 12, + 151, 154, 151, 594, 151, 151, 151, 154, 595, 595, 595, 595, 595, 596, 595, 595, + 595, 597, 12, 12, 598, 598, 598, 598, 598, 598, 598, 12, 598, 598, 599, 600, + 0, 234, 12, 12, 29, 414, 29, 29, 601, 602, 414, 29, 50, 29, 603, 12, + 604, 310, 603, 414, 601, 602, 603, 603, 601, 602, 50, 29, 50, 29, 414, 605, + 29, 29, 606, 29, 29, 29, 29, 12, 414, 414, 606, 29, 51, 12, 12, 12, + 12, 239, 0, 0, 607, 12, 12, 12, 246, 12, 12, 12, 0, 0, 12, 0, + 0, 232, 131, 0, 0, 0, 12, 12, 0, 0, 0, 240, 0, 246, 12, 239, + 608, 12, 12, 12, 247, 247, 609, 12, 610, 12, 12, 12, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, + 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041, + 1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127, + 1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227, + 1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, + 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, + 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, + 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0, + 1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032, + 1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0, + 1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264, + 1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283, + 1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093,1280, 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, 949,1134,1010,1195,1050,1236,1090, + 1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, + 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, + 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, + 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248, + 1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, + 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10, + 1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461,1514, 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,1446,1458,1468,1476,1480,1486,1517, 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,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520, + 1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, + 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, + 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, + 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, + 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555, + 1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563, + 1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607, + 1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0, + 1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, + 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0, + 1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, + 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, + 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, + 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, + 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, + 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152, + 1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, + 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205, + 1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216, + 1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385, + 1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256, + 1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282, + 1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289, + 1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311, + 1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132, + 1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377, + 1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355, + 1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357, + 1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404, + 1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297, + 1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705, + 1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731, + 1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741, + 1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768, + 1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779, + 1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788, + 1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798, + 1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22, + 1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710, + 1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746, + 1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803, + 1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474, + 1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484, + 1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, + 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25, + 1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512, + 1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0, + 1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0, + 1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0, + 1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0, + 1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0, + 1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, + 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0, + 1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, + 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894, + 1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0, + 1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0, + 1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, + 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921, + 1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0, + 1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, + 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, + 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, + 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, + 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, + 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, + 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, + 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, + 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, + 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, + 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, + 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, + 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, + 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, + 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, + 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, + 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, + 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, + 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, + 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, + 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, + 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, + 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, + 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, + 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, + 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, + 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, + 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, + 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, + 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, + 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580, + 1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595, + 1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954, + 1955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, + 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, + 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, + 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, + 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, + 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, + 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, + 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, + 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, + 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, + 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, + 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, + 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, + 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, + 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, + 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, + 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, + 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, + 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, + 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, + 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, + 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, + 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, + 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, + 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, + 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, + 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, + 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, + 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, + 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, + 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, + 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, + 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, + 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, + 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[92] = +{ + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114110u?_hb_ucd_u8[6808+(((_hb_ucd_u8[1312+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[8800+(((_hb_ucd_u8[8244+(((_hb_ucd_u8[7784+(((_hb_ucd_u8[7432+(((_hb_ucd_u8[7186+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[9692+(((_hb_ucd_u8[9460+(((_hb_ucd_u8[9364+(((_hb_ucd_b4(9300+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918000u?_hb_ucd_u8[11126+(((_hb_ucd_u16[4040+(((_hb_ucd_u16[2048+(((_hb_ucd_u8[10390+(((_hb_ucd_u8[9940+(u>>2>>2>>3>>4)])<<4)+((u>>2>>2>>3)&15u))])<<3)+((u>>2>>2)&7u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[6748+(((_hb_ucd_u8[13952+(((_hb_ucd_u8[13570+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + + +#else + +static const uint8_t +_hb_ucd_u8[13386] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 21, 21, 21, 21, 23, 7, 7, + 7, 24, 21, 21, 21, 25, 26, 27, 21, 28, 29, 30, 31, 32, 33, 34, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 35, 21, 36, + 7, 7, 7, 7, 37, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 38, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 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, 34, 34, 34, 35, 36, 37, 34, 34, 34, 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, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, + 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, + 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, + 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, + 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, + 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, + 120,121,122,123,124,125,126,127, 34,128,129,111,130,131,132,133, + 134,135,136,137,138,139,140,111,141,142,111,143,144,145,146,111, + 147,148,149,150,151,152,153,111,154,155,156,157,111,158,159,160, + 34, 34, 34, 34, 34, 34, 34, 34,161, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,162, + 34, 34, 34, 34, 34, 34, 34, 34,163,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34, 34, 34, 34, 34,111,111,111, + 34, 34, 34, 34,164,165,166, 34,111,111,111,111,167,168,169,170, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, + 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111, 34,171,111,111,111,111,111,111, + 111,111,111,111,111,111,111,111,111,111,111,111,111,111,172, 67, + 67, 67,173,174,175,130, 65,111,176,177,178,179,180,181,182,183, + 67, 67, 67, 67,184,185,111,111,111,111,111,111,111,111,186,111, + 187,188,189,111,111,190,111,111,111,191,111,111,111,111,111, 34, + 34,192,193,111,111,111,111,111,130,194,195,111, 34,196,111,111, + 67, 67,197, 67, 67,111, 67,198, 67, 67, 67, 67, 67, 67, 67, 67, + 67, 67, 67, 67, 67, 67, 67,199,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, + 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, + 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111, + 200,111,188,188,111,111,111,111,111,111,111,111,111,111,111,111, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, + 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, + 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, + 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, + 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, + 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, + 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, + 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, + 16, 16, 39, 16, 16, 16, 16, 16, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 41, 41, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, + 40, 40, 42, 41, 41, 41, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, + 43, 43, 43, 43, 43, 43, 43, 43, 32, 32, 42, 32, 16, 44, 16, 10, + 41, 41, 41, 45, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, + 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 46, 34, 32, 34, 11, + 32, 47, 43, 43, 48, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, + 11, 11, 11, 11, 49, 2, 2, 2, 16, 16, 16, 16, 50, 51, 52, 53, + 54, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 55, + 56, 57, 43, 56, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 58, 2, 2, 2, 2, 2, 2, 59, 59, 59, 8, 9, 60, 2, 61, + 43, 43, 43, 43, 43, 57, 62, 2, 63, 36, 36, 36, 36, 64, 43, 43, + 7, 7, 7, 7, 7, 2, 2, 36, 65, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 66, 43, 43, 43, 67, 47, 43, 43, 68, 69, 70, 43, 43, 36, + 7, 7, 7, 7, 7, 36, 71, 72, 2, 2, 2, 2, 2, 2, 2, 73, + 64, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 65, 36, + 36, 36, 36, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7, 36, 36, 36, + 36, 36, 36, 36, 36, 64, 43, 43, 43, 43, 40, 21, 2, 40, 69, 20, + 36, 36, 36, 43, 43, 69, 43, 43, 43, 43, 69, 43, 69, 43, 43, 43, + 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 64, 43, 43, 2, + 36, 36, 36, 36, 74, 36, 36, 36, 59, 59, 59, 59, 43, 43, 43, 43, + 36, 36, 36, 36, 75, 43, 43, 43, 43, 76, 43, 43, 43, 43, 43, 43, + 43, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 65, 78, + 79, 43, 43, 43, 77, 78, 79, 78, 64, 43, 43, 43, 36, 36, 36, 36, + 36, 43, 2, 7, 7, 7, 7, 7, 80, 36, 36, 36, 36, 36, 36, 36, + 64, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 78, + 79, 43, 43, 77, 78, 78, 79, 36, 36, 36, 36, 82, 78, 78, 36, 36, + 36, 43, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 53, 58, 43, + 43, 77, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 78, + 79, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 65, 36, 36, 36, + 36, 36, 36, 7, 7, 7, 7, 7, 43, 36, 64, 2, 2, 2, 2, 2, + 79, 43, 43, 43, 77, 78, 79, 43, 60, 20, 20, 20, 83, 43, 43, 43, + 43, 78, 81, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 65, 79, + 79, 43, 43, 77, 78, 78, 79, 43, 43, 43, 43, 77, 78, 78, 36, 36, + 72, 27, 27, 27, 27, 27, 27, 27, 43, 65, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 78, 77, 78, 78, 78, 78, 78, 79, 43, + 36, 36, 36, 82, 78, 78, 78, 78, 78, 78, 78, 7, 7, 7, 7, 7, + 27, 84, 61, 61, 53, 61, 61, 61, 77, 78, 65, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 65, 43, 77, 78, 78, 43, 43, 43, 43, 43, + 43, 43, 43, 43, 36, 36, 36, 36, 7, 7, 7, 85, 27, 27, 27, 84, + 64, 78, 66, 36, 36, 36, 36, 36, 78, 78, 78, 77, 78, 78, 43, 43, + 43, 43, 77, 78, 78, 78, 81, 36, 86, 82, 78, 78, 78, 78, 78, 78, + 43, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 78, + 79, 43, 43, 78, 78, 78, 79, 71, 61, 61, 36, 82, 27, 27, 27, 87, + 27, 27, 27, 27, 84, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 77, + 78, 43, 43, 43, 78, 78, 78, 78, 7, 78, 2, 2, 2, 2, 2, 2, + 64, 36, 43, 43, 43, 43, 43, 88, 36, 36, 36, 69, 43, 43, 43, 57, + 7, 7, 7, 7, 7, 2, 2, 2, 64, 36, 43, 43, 43, 43, 65, 36, + 36, 36, 36, 40, 43, 43, 43, 43, 7, 7, 7, 7, 7, 7, 36, 36, + 71, 61, 2, 2, 2, 2, 2, 2, 2, 89, 89, 61, 43, 61, 61, 61, + 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 47, 47, 47, 4, 4, 78, + 64, 43, 43, 43, 43, 43, 43, 77, 43, 43, 57, 43, 36, 36, 64, 43, + 43, 43, 43, 43, 43, 43, 43, 61, 61, 61, 61, 70, 61, 61, 61, 61, + 2, 2, 89, 61, 21, 2, 2, 2, 36, 36, 36, 36, 36, 82, 79, 43, + 77, 43, 43, 43, 79, 77, 79, 65, 36, 36, 36, 78, 43, 36, 36, 43, + 65, 78, 81, 82, 78, 78, 78, 36, 64, 43, 65, 36, 36, 36, 36, 36, + 36, 77, 79, 77, 78, 78, 79, 82, 7, 7, 7, 7, 7, 78, 79, 61, + 16, 16, 16, 16, 16, 50, 44, 16, 36, 36, 36, 36, 36, 36, 64, 43, + 2, 2, 2, 2, 90, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 61, 61, 61, 61, 61, 61, 61, 61, 11, 11, 11, 11, 16, 16, 16, 16, + 91, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 71, 66, + 92, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 93, 94, 94, + 36, 36, 36, 36, 36, 58, 2, 95, 96, 36, 36, 36, 36, 36, 36, 36, + 36, 43, 77, 78, 78, 78, 78, 81, 36, 43, 97, 2, 2, 2, 2, 2, + 36, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 79, 43, 43, 43, 78, + 78, 78, 78, 77, 79, 43, 43, 43, 43, 43, 2, 80, 2, 60, 64, 43, + 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 98, 2, 56, 43, 76, + 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 64, 65, 36, 36, 36, 36, + 36, 36, 36, 36, 64, 36, 36, 36, 43, 77, 78, 79, 77, 78, 78, 78, + 78, 77, 78, 78, 79, 43, 43, 43, 61, 61, 2, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 27, 27, 61, 36, 36, 36, 64, 77, 79, 43, 2, + 36, 36, 82, 77, 43, 43, 43, 43, 77, 77, 79, 43, 43, 43, 77, 78, + 78, 79, 43, 43, 43, 43, 43, 43, 2, 2, 2, 80, 2, 2, 2, 2, + 43, 43, 43, 43, 43, 43, 43, 99, 43, 43, 81, 36, 36, 36, 36, 36, + 36, 36, 77, 43, 43, 77, 77, 78, 78, 77, 81, 36, 36, 36, 36, 36, + 89, 61, 61, 61, 61, 47, 43, 43, 43, 43, 61, 61, 61, 61, 21, 2, + 43, 81, 36, 36, 36, 36, 36, 36, 82, 43, 43, 78, 43, 79, 43, 36, + 36, 36, 36, 77, 43, 78, 79, 79, 43, 78, 78, 78, 78, 78, 2, 2, + 36, 36, 78, 78, 78, 78, 43, 43, 43, 43, 78, 43, 43, 57, 2, 2, + 7, 7, 7, 7, 7, 7, 86, 36, 36, 36, 36, 36, 40, 40, 40, 2, + 43, 57, 43, 43, 43, 43, 43, 43, 77, 43, 43, 43, 65, 36, 64, 36, + 36, 36, 65, 82, 43, 36, 36, 36, 16, 16, 16, 16, 16, 16, 40, 40, + 40, 40, 40, 40, 40, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, + 16, 16, 16, 16, 16,100, 40, 40, 32, 32, 32, 16, 16, 16, 16, 32, + 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 16, 34, 11, 11, 11, + 16, 16, 16, 16,101,101,101,101, 16, 16, 16, 16, 11, 11,102,103, + 41, 16, 16, 16, 11, 11,102, 41, 16, 16, 16, 16, 11, 11,104, 41, + 105,105,105,105,105,106, 59, 59, 51, 51, 51, 2,107,108,107,108, + 2, 2, 2, 2,109, 59, 59,110, 2, 2, 2, 2,111,112, 2,113, + 114, 2,115,116, 2, 2, 2, 2, 2, 9,114, 2, 2, 2, 2,117, + 59, 59, 59, 59, 59, 59, 59, 59,118, 40, 27, 27, 27, 8,115,119, + 27, 27, 27, 27, 27, 8,115, 94, 20, 20, 20, 20, 20, 20, 20, 20, + 43, 43, 43, 43, 43, 43,120, 48, 99, 48, 99, 43, 43, 43, 43, 43, + 61,121, 61,122, 61, 34, 11, 16, 11, 32,122, 61, 46, 11, 11, 61, + 61, 61,121,121,121, 11, 11,123, 11, 11, 35, 36, 39, 61, 16, 11, + 8, 8, 46, 16, 16, 26, 61,124, 95, 95, 95, 95, 95, 95, 95, 95, + 95,125,126, 95,127, 61, 61, 61, 8, 8,128, 61, 61, 8, 61, 61, + 128, 26, 61,128, 61, 61, 61,128, 61, 61, 61, 61, 61, 61, 61, 8, + 61,128,128, 61, 61, 61, 61, 61, 61, 61, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 61, 61, 61, 61, 4, 4, 61, 61, + 8, 61, 61, 61,129,130, 61, 61, 61, 61, 61, 61, 61, 61,128, 61, + 61, 61, 61, 61, 61, 26, 8, 8, 8, 8, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 61, 8, 8, 8, 61, 61, 61, 61, 61, 61, 61, + 27, 27, 27, 27, 27, 27, 61, 61, 61, 61, 61, 61, 61, 27, 27, 27, + 61, 61, 61, 26, 61, 61, 61, 61, 26, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 8, 8, 8, 8, 61, 61, 61, 61, 61, 61, 61, 26, + 61, 61, 61, 61, 4, 4, 4, 4, 4, 4, 4, 27, 27, 27, 27, 27, + 27, 27, 61, 61, 61, 61, 61, 61, 8, 8,115,131, 8, 8, 8, 8, + 8, 8, 8, 4, 4, 4, 4, 4, 8,115,132,132,132,132,132,132, + 132,132,132,132,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8, + 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,128, 26, 8, 8,128, 61, + 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11, + 32, 32,124, 61, 61,122, 34,133, 43, 32, 16, 16, 50, 2, 90, 2, + 36, 36, 36, 36, 36, 36, 36, 75, 2, 2, 2, 2, 2, 2, 2, 56, + 2,107,107, 2,111,112,107, 2, 2, 2, 2, 6, 2, 98,107, 2, + 107, 4, 4, 4, 4, 2, 2, 80, 2, 2, 2, 2, 2, 51, 2, 2, + 98,134, 2, 2, 2, 2, 2, 2, 61, 2,135,132,132,132,136, 51, + 51, 51, 51, 51, 51, 51, 51, 51, 1, 2,137,138, 4, 4, 4, 4, + 4, 61, 4, 4, 4, 4,139, 94,140, 95, 95, 95, 95, 43, 43, 78, + 141, 40, 40, 61, 95,142, 58, 61, 72, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 64,143,144, 63, 36, 36, 36, 36, 36, 58, 40, 63, + 61, 27, 27, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 61, 61, 61, + 61, 61, 61, 61, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 27, + 36, 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,146, 2, + 32, 32, 32, 32, 32, 32, 32, 64, 48,147, 43, 43, 43, 43, 43, 80, + 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36, 95, 95, 95, 95, 95, + 43, 2, 2, 2, 2, 2, 2, 2, 41, 41, 41,144, 40, 40, 40, 40, + 41, 32, 32, 32, 32, 32, 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, + 44, 16, 16, 16, 34, 34, 34, 32, 32, 32, 32, 32, 42,148, 34, 35, + 32, 32, 16, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 32, + 11, 11, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, 34, 16, 16, 16, + 32, 16, 16, 32, 32, 16, 16, 16, 16, 40,149, 35, 40, 35, 36, 36, + 36, 65, 36, 65, 36, 64, 36, 36, 36, 82, 79, 77, 61, 61, 43, 43, + 27, 27, 27, 61,150, 61, 61, 61, 36, 36, 2, 2, 2, 2, 2, 2, + 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 78, 78, 78, 78, 78, 78, + 78, 78, 43, 43, 43, 43, 43, 2, 43, 36, 36, 36, 2, 66, 66, 64, + 36, 36, 36, 43, 43, 43, 43, 2, 36, 36, 36, 64, 43, 43, 43, 43, + 43, 78, 78, 78, 78, 78, 78, 97, 36, 64, 78, 43, 43, 78, 43, 78, + 97, 2, 2, 2, 2, 2, 2, 80, 7, 7, 7, 7, 7, 7, 7, 2, + 36, 36, 64, 63, 36, 36, 36, 36, 36, 36, 36, 36, 64, 43, 43, 77, + 79, 77, 79, 43, 43, 43, 43, 43, 36, 64, 36, 36, 36, 36, 77, 78, + 7, 7, 7, 7, 7, 7, 2, 2, 63, 36, 36, 71, 61, 82, 77, 36, + 65, 43, 65, 64, 65, 36, 36, 43, 36, 36, 36, 36, 36, 36, 75, 2, + 36, 36, 36, 36, 36, 82, 43, 78, 2, 75,151, 43, 43, 43, 43, 43, + 16, 16, 16, 16, 16,103, 40, 40, 16, 16, 16, 16,100, 41, 41, 41, + 36, 82, 79, 78, 77, 97, 79, 43,152,152,152,152,152,152,152,152, + 153,153,153,153,153,153,153,153, 16, 16, 16, 16, 16, 16, 35, 65, + 36, 36, 36, 36,154, 36, 36, 36, 36, 41, 41, 41, 41, 41, 41, 41, + 41, 74, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,132, + 36, 36, 36, 36, 36, 36, 36, 71, 36, 36, 36, 36, 36, 36,150, 61, + 2, 2, 2,135,116, 2, 2, 2, 6,155,156,132,132,132,132,132, + 132,132,116,135,116, 2,113,157, 2, 2, 2, 2,139,132,132,116, + 2,158, 8, 8, 60, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36,159, + 2, 2, 3, 2, 4, 5, 6, 2, 16, 16, 16, 16, 16, 17, 18,115, + 116, 4, 2, 36, 36, 36, 36, 36, 63, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 40, 20,160, 53, 20, 26, 8,128, 61, + 61, 61, 61, 61,161, 59, 61, 61, 2, 2, 2, 90, 27, 27, 27, 27, + 27, 27, 27, 84, 61, 61, 61, 61, 95, 95,127, 27, 84, 61, 61, 61, + 61, 61, 61, 61, 61, 27, 61, 61, 61, 61, 61, 61, 61, 61, 47, 43, + 162,162,162,162,162,162,162,162,163, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 87, 36,138, 36, 36, 36, 36, 95, 95, 95, + 36, 36, 36, 36, 36, 36, 36, 58,164, 95, 95, 95, 95, 95, 95, 95, + 11, 11, 11, 32, 16, 16, 16, 16, 36, 36, 36, 58, 27, 27, 27, 27, + 36, 36, 36, 71,145, 27, 27, 27, 36, 36, 36,165, 27, 27, 27, 27, + 36, 36, 36, 36, 36,165, 27, 27, 36, 36, 36, 27, 27, 27, 27, 30, + 36, 36, 36, 36, 36, 36, 27, 36, 64, 43, 43, 43, 43, 43, 43, 43, + 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,165, 30, + 36, 36, 36, 36, 36, 36,165, 27, 36, 36, 36, 36, 72, 36, 36, 36, + 36, 36, 64, 43, 43,163, 27, 27, 36, 36, 36, 36, 58, 2, 2, 2, + 36, 36, 36, 36, 27, 27, 27, 27, 16, 16, 16, 16, 16, 27, 27, 27, + 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 64,166, 51, + 27, 27, 27, 87, 36, 36, 36, 36,163, 27, 30, 2, 2, 2, 2, 2, + 36, 43, 43, 2, 2, 2, 2, 2, 36, 36,165, 27, 27, 27, 27, 27, + 79, 81, 36, 36, 36, 36, 36, 36, 43, 43, 43, 57, 2, 2, 2, 2, + 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 7, 7, 7, + 65, 64, 65, 36, 36, 36, 36, 64, 78, 79, 43, 77, 79, 57, 73, 2, + 2, 43, 43, 43, 43, 43, 67, 59, 36, 36, 36, 64, 43, 43, 79, 43, + 43, 43, 43, 7, 7, 7, 7, 7, 2, 2, 82, 81, 36, 36, 36, 36, + 36, 64, 2, 36, 36, 36, 36, 36, 36, 82, 78, 43, 43, 43, 43, 77, + 81, 36, 58, 2, 56, 43, 57, 79, 7, 7, 7, 7, 7, 58, 58, 2, + 90, 27, 27, 27, 27, 27, 27, 27, 36, 36, 36, 36, 36, 36, 78, 79, + 43, 78, 77, 43, 2, 2, 2, 65, 36, 36, 36, 36, 36, 36, 36, 64, + 77, 78, 78, 78, 78, 78, 78, 78, 36, 36, 36, 82, 78, 78, 81, 36, + 36, 78, 78, 43, 43, 43, 43, 43, 36, 36, 82, 78, 43, 43, 43, 43, + 78, 43, 77, 65, 36, 58, 2, 2, 7, 7, 7, 7, 7, 2, 2, 65, + 78, 79, 43, 43, 77, 77, 78, 79, 77, 43, 36, 66, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 82, 78, 43, 43, 43, 78, 78, 43, 79, + 57, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 43, + 78, 79, 43, 43, 43, 77, 79, 79, 57, 2, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 64, 79, 78, 43, 43, 43, 79, 58, 2, 2, 2, + 78, 43, 43, 79, 43, 43, 43, 43, 7, 7, 7, 7, 7, 27, 2, 89, + 43, 43, 43, 43, 79, 57, 2, 2, 27, 27, 27, 27, 27, 27, 27, 87, + 78, 78, 78, 78, 78, 79, 77, 65, 81, 79, 2, 2, 2, 2, 2, 2, + 82, 78, 43, 43, 43, 43, 78, 78, 65, 66, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 64, 43, 43, 43, 43, 65, 36, 36, + 36, 64, 43, 43, 77, 64, 43, 57, 2, 2, 2, 56, 43, 43, 43, 43, + 64, 43, 43, 77, 79, 43, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, + 43, 43, 43, 77, 43, 2, 66, 2, 43, 43, 43, 43, 43, 43, 43, 79, + 58, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 36, + 43, 43, 43, 43, 77, 43, 43, 43, 77, 43, 79, 43, 43, 43, 43, 43, + 43, 43, 43, 64, 43, 43, 43, 43, 36, 36, 36, 36, 36, 78, 78, 78, + 43, 77, 79, 79, 36, 36, 36, 36, 36, 64, 77, 97, 2, 2, 2, 2, + 43, 82, 36, 36, 36, 36, 36, 36, 36, 36, 78, 43, 43, 43, 43, 78, + 77, 57, 2, 2, 2, 2, 2, 2, 27, 27, 84, 61, 61, 61, 53, 20, + 150, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 21, + 65, 36, 36, 64, 43, 43, 43, 43, 43, 43, 57, 2, 2, 2, 2, 2, + 43, 43, 43, 57, 2, 2, 61, 61, 40, 40, 89, 61, 61, 61, 61, 61, + 7, 7, 7, 7, 7,167, 27, 27, 27, 87, 36, 36, 36, 36, 36, 36, + 27, 27, 27, 30, 2, 2, 2, 2, 82, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 79, 43, 68, 40, 40, 40, 40, 40, 40, + 40, 80, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 47, 57, + 61, 61,168, 79, 43, 61,168, 78, 78,169, 59, 59, 59, 76, 43, 43, + 43, 70, 47, 43, 43, 43, 61, 61, 61, 61, 61, 61, 61, 43, 43, 61, + 61, 43, 70, 61, 61, 61, 61, 61, 11, 11, 11, 11, 11, 16, 16, 16, + 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, + 11, 11, 11, 16, 16, 16, 16, 16, 31, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, + 11, 11, 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, + 16, 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, + 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, + 16, 33, 16, 16, 16, 32, 16, 7, 43, 43, 43, 70, 61, 47, 43, 43, + 43, 43, 43, 43, 43, 43, 70, 61, 61, 61, 47, 61, 61, 61, 61, 61, + 61, 61, 70, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 56, 43, 43, + 16, 16, 16, 16, 16, 39, 16, 16, 43, 43, 43, 68, 40, 40, 40, 40, + 7, 7, 7, 7, 7, 7, 7, 71, 36, 36, 36, 36, 36, 36, 36, 43, + 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 7, 7,170, + 36, 36, 36, 36, 36, 75, 43, 43, 16, 16, 43, 43, 43, 68, 40, 40, + 27, 27, 27, 27, 27, 27,145, 27,171, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27,145, 27, 27, 27, 27, 27, 27, 84, 61, + 61, 61, 61, 61, 61, 25, 41, 41, 0, 0, 29, 21, 21, 21, 23, 21, + 22, 18, 21, 25, 21, 17, 13, 13, 25, 25, 25, 21, 21, 9, 9, 9, + 9, 22, 21, 18, 24, 16, 24, 5, 5, 5, 5, 22, 25, 18, 25, 0, + 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24, 26, 25, 15, 15, + 24, 15, 7, 19, 15, 21, 9, 25, 9, 5, 5, 25, 5, 9, 5, 7, + 7, 7, 9, 8, 8, 5, 7, 5, 6, 6, 24, 24, 6, 24, 12, 12, + 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, 9, 6, 5, 21, 17, 17, + 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, 12, 21, 7, 21, 1, 1, + 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, + 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 24, 7, 7, 6, + 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, 10, 7, 7, 10, 23, 7, + 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, 21, 26, 21, 15, 17, 7, + 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, + 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, 29, 29, 29, 1, 20, 19, + 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, 25, 22, + 18, 21, 21, 29, 15, 6, 18, 6, 12, 11, 9, 26, 26, 9, 26, 5, + 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, 26, 22, 18, 26, 18, 25, + 18, 22, 5, 12, 22, 21, 21, 22, 18, 17, 26, 6, 7, 14, 17, 22, + 26, 14, 17, 6, 14, 6, 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, + 24, 9, 6, 9, 23, 26, 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, + 16, 22, 16, 16, 25, 17, 7, 1, 25, 24, 26, 1, 2, 2, 12, 15, + 21, 14, 7, 15, 12, 17, 13, 15, 26, 10, 10, 1, 13, 23, 23, 15, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, + 14, 0, 0, 0, 0, 0, 15, 0, 16, 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, 17, 18, 19, 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, 20, 0, 21, + 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 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, 35, 0, 36, 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, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 0, 0, + 40, 41, 42, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, + 0, 8, 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, + 16, 19, 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, + 36, 0, 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, + 0, 47, 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, + 0, 50, 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, + 56, 57, 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, + 64, 65, 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, + 0, 70, 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 0, 0, 0, 0, 0, 75, 76, 0, 77, 78, 0, 0, 79, + 80, 0, 81, 62, 0, 82, 83, 0, 0, 84, 85, 86, 0, 0, 0, 87, + 0, 88, 0, 0, 51, 89, 51, 0, 90, 0, 91, 0, 0, 0, 80, 0, + 0, 0, 92, 93, 0, 94, 95, 96, 97, 0, 0, 0, 0, 0, 51, 0, + 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, 0,100, 0, 0, 0, 0, + 0,101,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,103, 0, 0, + 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105,106, 0, 0,107, + 0, 0, 0, 0, 0, 0,108, 0,109, 0,102, 0, 0, 0, 0, 0, + 110,111, 0, 0, 0, 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 0, + 0,113, 0,114, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, + 7, 0, 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, + 0, 0, 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, + 0, 0, 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, + 0, 0, 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, + 0, 35, 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, + 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, + 0, 0, 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, + 0, 0, 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, + 0, 52, 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, + 0, 0, 0, 0, 57, 58, 0, 0, 0, 59, 60, 0, 0, 0, 0, 0, + 0, 61, 52, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 66, 0, 0, + 0, 67, 0, 68, 69, 70, 71, 72, 1, 73, 0, 74, 75, 76, 0, 0, + 77, 78, 0, 0, 0, 79, 0, 0, 1, 1, 0, 0, 80, 0, 0, 81, + 0, 0, 0, 0, 77, 82, 0, 83, 0, 0, 0, 0, 0, 78, 84, 0, + 85, 0, 52, 0, 1, 78, 0, 0, 86, 0, 0, 87, 0, 0, 0, 0, + 0, 88, 57, 0, 0, 0, 0, 0, 0, 89, 90, 0, 0, 84, 0, 0, + 33, 0, 0, 91, 0, 0, 0, 0, 92, 0, 0, 0, 0, 49, 0, 0, + 93, 0, 0, 0, 0, 94, 95, 0, 0, 96, 0, 0, 97, 0, 0, 0, + 98, 0, 0, 0, 99, 0, 0, 0, 0,100,101, 93, 0, 0,102, 0, + 0, 0, 84, 0, 0,103, 0, 0, 0,104,105, 0, 0,106,107, 0, + 0, 0, 0, 0, 0,108, 0, 0,109, 0, 0, 0, 0,110, 33, 0, + 111,112,113, 35, 0, 0,114, 0, 0, 0,115, 0, 0, 0, 0, 0, + 0,116, 0, 0,117, 0, 0, 0, 0,118, 88, 0, 0, 0, 0, 0, + 57, 0, 0, 0, 0, 52,119, 0, 0, 0, 0,120, 0, 0,121, 0, + 0, 0, 0,119, 0, 0,122, 0, 0, 0, 0, 0, 0,123, 0, 0, + 0,124, 0, 0, 0,125, 0,126, 0, 0, 0, 0,127,128,129, 0, + 130, 0,131, 0, 0, 0,132,133,134, 0, 77, 0, 0, 0, 0, 0, + 35, 0, 0, 0,135, 0, 0, 0,136, 0, 0,137, 0, 0,138, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, + 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, + 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, + 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, + 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, + 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, + 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 0, + 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, + 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, + 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, + 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, + 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, + 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, + 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, + 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, + 15, 86, 36, 10, 21, 87, 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, + 0, 0, 0, 0, 88, 0, 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, + 0, 0, 87, 9, 12, 4, 90, 8, 91, 47, 0, 58, 50, 0, 21, 1, + 21, 92, 93, 1, 1, 1, 1, 94, 95, 96, 97, 1, 98, 58, 81, 99, + 100, 4, 58, 0, 0, 0, 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, + 0, 61, 0, 0,101,102, 0, 0,103, 0, 0, 1, 1, 50, 0, 0, + 0, 38, 0, 63, 0, 0, 0, 0, 0, 62, 0, 0,104, 68, 61, 0, + 0, 0, 78, 0, 0, 0,105,106, 58, 38, 81, 0, 0, 0, 0, 0, + 0,107, 1, 14, 4, 12, 84, 0, 0, 0, 0, 38, 87, 0, 0, 0, + 0,108, 0, 0,109, 61, 0,110, 0, 0, 0, 1, 0, 0, 0, 0, + 19, 58, 0, 0, 0, 51, 0,111, 14, 52,112, 41, 0, 0, 62, 0, + 0, 61, 0, 0,113, 0, 87, 0, 0, 0, 61, 62, 0, 0, 62, 0, + 89, 0, 0,113, 0, 0, 0, 0,114, 0, 0, 0, 78, 55, 0, 38, + 1, 58, 1, 58, 0, 0, 63, 89, 0, 0,115, 0, 0, 0, 55, 0, + 0, 0, 0,115, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, + 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, 8, 91, + 0, 0, 1, 87, 0, 0,116, 0, 0, 0, 0, 0, 0,117, 0,118, + 119,120,121, 0,104, 4,122, 49, 23, 0, 0, 0, 38, 50, 38, 58, + 0, 0, 1, 87, 1, 1, 1, 1, 39, 1, 48,105, 87, 0, 0, 0, + 0, 1, 0, 0, 0,123, 4,122, 0, 0, 0, 1,124, 0, 0, 0, + 0, 0,230,230,230,230,230,232,220,220,220,220,232,216,220,220, + 220,220,220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, + 1, 1, 1,220,220,220,220,230,230,230,230,240,230,220,220,220, + 230,230,230,220,220, 0,230,230,230,220,220,220,220,230,232,220, + 220,230,233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220, + 230,230,230,230,220,230,230,230,222,220,230,230,220,220,230,222, + 228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, + 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, + 0, 27, 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230, + 230,220, 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0, + 220,230,230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220, + 230,220,220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, + 0,230, 0,230,230,230,230,230, 0, 0, 0,220,220,220,230,220, + 220,220,230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, + 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, + 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, + 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122,220,220, + 0, 0, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, + 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0, + 230,230, 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, + 0, 0, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, + 0, 0,230, 0, 0,220,230,220, 0,220,230,230,230, 0, 0, 0, + 9, 9, 0, 0, 7, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234, + 214,220,202,230,230,230,230,230,232,228,228,220,218,230,233,220, + 230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, + 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, 0, 0, + 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, + 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 0, + 230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, + 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226, + 216,216,216,216,216, 0,220,220,220, 0,232,232,220,230,230,230, + 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, 26, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, + 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, + 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, + 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, + 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, + 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, + 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, + 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, + 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, + 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, + 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, + 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, + 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, + 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, + 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, + 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, + 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, + 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, + 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, + 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, + 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, + 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, + 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, + 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, + 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, + 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, + 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, + 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, + 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, + 0, 0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 3, 3, 3, 3, 3, 3, 3, 15, 3, 16, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 18, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 17, 17, 18, 17, 19, 20, 21, 22, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 25, 25, 26, 27, 28, 29, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 52, 53, 31, 31, 31, 31, 54, 55, 55, 56, 31, + 31, 31, 31, 31, 31, 31, 57, 58, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 59, 60, 31, 61, 62, 62, 62, 62, + 62, 62, 62, 62, 62, 62, 62, 62, 62, 63, 64, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 65, 66, 67, 31, 31, + 31, 31, 68, 31, 31, 31, 31, 31, 31, 31, 31, 69, 70, 71, 17, 17, + 72, 73, 31, 74, 75, 76, 77, 78, 79, 31, 80, 81, 17, 82, 17, 17, + 17, 17, 31, 31, 23, 23, 23, 23, 23, 23, 23, 83, 31, 31, 31, 31, + 23, 83, 31, 31, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 84, 0, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, + 7, 8, 9, 10, 11, 11, 12, 11, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 19, 27, 28, 29, 30, 30, 31, 31, 32, 32, + 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 40, 41, 41, + 42, 42, 42, 43, 44, 44, 45, 46, 47, 47, 47, 47, 48, 48, 48, 48, + 48, 48, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 53, + 54, 55, 56, 56, 57, 58, 59, 51, 60, 61, 62, 63, 64, 65, 66, 7, + 67, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 7, 4, 4, 4, 4, + 77, 77, 77, 77, 78, 79, 80, 81, 82, 83, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 85, 85, 85, 85, 0, 0, 0, 0, 86, 87, 88, 88, + 89, 90, 48, 91, 0, 0, 92, 92, 92, 92, 92, 93, 94, 95, 96, 97, + 98, 47, 99,100,101,102, 0,103,104,105, 0, 0, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 0,106,106,106,106, + 106,106,106,106,106,106,106,107,108,108,108,108,108, 11,109,110, + 111, 4,112, 4,113,114,115,116,117,118,119,120,121,122,123,124, + 125,126, 50,127, 47, 47, 47, 47, 47, 47, 47, 47,128,128,128,128, + 128,128,128,128,128,128,128,128, 92, 92, 92, 92, 92, 92, 92, 92, + 129,130, 19, 19, 19, 19, 19, 19,131, 19, 19, 19,132,133, 19,134, + 135,136,137,101,138,138,138,138, 0, 77,139,140,128,128,141,142, + 143,144,145,146,147,148,149,150,151,152,153,153,154,154,154,154, + 154,154, 4, 4,155,156,157,158,159,160,161,162,163,164,165,166, + 167,168,169,169,170,170,171,171,172,172,128,128, 19, 19,173,174, + 175,176,177,178,179,179,180,181,182,183,184,185,186,186,187,188, + 189,190,128,128,191,191,192,192,128,128,193,193,194,195,196,196, + 197,197,128,128,198,198,199,199,200,200,201,201,202,203,204,205, + 28, 28,128,128,206,207,208,208,209,210,211,211,128,128,212,212, + 213,213,214, 34,215,215,215,215,215,215,215,215,215,215,215,215, + 215,215,128,128,128,128,128,128,128,128,216,216,217,217,217,217, + 217,217,217,217,217,217,128,128,128,128,128,128,218,218,218,218, + 218,218,218,218,218,218,128,128,128,128,128,128,110,110,110,110, + 110,110,110,110,110,219,220,221,222,222,222,222,223,223,223,223, + 224,224,224,225,226,226,226,226,226,226,226,226,226,226,226,226, + 227,227,227,227,227,227,227,227,226,226,128,128,128,128,128,128, + 128,128,104,104,228,229,229,229,230,231,232,232,232,232,232,232, + 128,128,128,128,233,233,234, 0,128,128,128,128,128,128,128,128, + 7,235, 0, 0, 0, 0, 0, 0, 0,236,237, 0, 77, 77, 0, 0, + 0, 0,128,128,238,238,238,238,238,238,238,238,238,238,238,238, + 128,128,128,128,128,128,128,128, 4, 4,128,128,239, 11, 11, 11, + 240,240,128,128,128,128,241,242,128,128,128,128,128,128,243,243, + 128,128,128,128,128,128,128,128,128,128, 48, 48,244,244,244,244, + 245,245,128,128, 0, 0, 0, 0, 0, 0,128,128, 19, 19, 19, 19, + 128,128,128,128,246, 0,128,128, 0, 0, 0, 0, 92, 92,128,128, + 128,128,128,128, 0, 0,128,128, 7, 7, 7, 7, 0, 0, 0, 0, + 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, 4, 4, 4, + 4, 4, 4, 6, 0, 0, 7, 0, 8, 8, 8, 8, 8, 8, 8, 9, + 10, 11, 11, 11, 11, 11, 12, 11, 13, 13, 13, 13, 14, 13, 13, 13, + 13, 13, 13, 15, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, + 19, 20, 21, 21, 22, 23, 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, + 26, 26, 26, 26, 26, 21, 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, + 29, 29, 29, 29, 30, 30, 26, 21, 21, 21, 31, 21, 32, 32, 32, 32, + 32, 33, 34, 32, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, + 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, + 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 45, 44, 44, 44, 44, + 46, 46, 46, 46, 47, 47, 47, 47, 47, 48, 47, 47, 49, 49, 49, 49, + 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 52, 52, 53, 53, 53, 53, + 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, + 57, 57, 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 64, 64, + 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 55, 67, 67, 67, 67, + 67, 68, 68, 68, 69, 69, 69, 69, 69, 69, 64, 64, 70, 70, 71, 71, + 71, 71, 71, 71, 71, 71, 71, 8, 72, 72, 72, 72, 73, 73, 73, 73, + 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, 50, 50, 50, + 73, 77, 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, 4, 4, 84, + 11, 11, 11, 11, 85, 0, 0, 0, 0, 0, 0, 86, 0, 4, 0, 0, + 0, 8, 8, 8, 0, 0, 87, 88, 89, 0, 4, 4, 6, 0, 0, 0, + 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 4, 4, 92, 92, 92, 92, + 50, 50, 50, 93, 93, 93, 93, 93, 53, 53, 13, 13, 94, 94, 94, 94, + 94, 94, 94, 0, 95, 0, 96, 97, 98, 99, 99, 99, 99,100,101,102, + 102,102,102,103,104,104,104,105, 52, 0,104,104, 0, 0, 0,102, + 52, 52, 0, 0, 0, 0, 52,106, 0,102,102,107,102,102,102,102, + 102,108, 0, 0,109,109,109,109,109,110,110,110,111,111,111,111, + 13, 13,112,112,112,112,112,112, 0, 0,113, 4,114, 4, 4, 4, + 115,115,115, 0,116,116,116,116,117,117,117,117,117,117, 32, 32, + 118,118,119,120,120,120, 52, 52,121,121,121,121,122,121, 49, 49, + 123,123,123,123,123,123, 49, 49,124,124,124,124,124,124,125,125, + 53, 53, 53, 4, 4,126,127, 54,125,125,125,125,128,128,128,128, + 4,129, 18, 18, 18, 21, 21, 21, 21, 21, 21,130, 8, 0,131, 0, + 0, 0, 0, 21, 21, 21, 21,132, 0, 0, 1, 2, 1, 2,133,101, + 102,134, 52, 52,135,135,135,135, 11, 0, 11, 11, 11, 0, 0,136, + 137,137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142, + 143,143,144,144,144,144,144,144,145,145,145,145,145,146,146,146, + 147,147,147,148,148,148,148,148,149,149,149,150,150,150,150,151, + 151,151,151,151,152,152,152,152,153,153,153,153,154,154,155,155, + 156,156,156,156,156,156,157,157,158,158,159,159,159,159,159,159, + 160,160,161,161,161,161,161,161,162,162,162,162,162,162,163,163, + 164,164,164,164,165,165,165,165,166,166,166,166,167,167,168,168, + 169,169,169,169,170,170,170,170,171,171,171,171,172,172,172,172, + 173,173,173,173,173,173,173,174,175,175,175,176,176,176,176,177, + 177,177,177,178,178,178,179,179,180,180,180,180,181,181,181,181, + 181,182,182,182,183,183,183,183,183,184,184,184,185,185,185,185, + 185,185,186, 43,187,187,187,187,188,188,188,189,189,189,189,189, + 190,190,190,191,190,190,190,190,192,192,192,192,193,193,193,193, + 194,194,194,194,195,195,195,195,195,195, 66, 66,196,196,196,196, + 197,197,197,197,198,198,198,198,199,199,199,199,200,200,200,200, + 201,201,201,201,202,202,202,202,202,203,203,203,203,203,203, 55, + 204,204,204,204,205,205,205,205,205,205,205,206,206,206,206,206, + 207,207,207,207,207,207,208,208,208,208,208,208,209,209,209,209, + 210,210,210,210,110,110,110,110,211,211,211,211,212,212,212,212, + 213,213,213,213,214,214,214,214,215,215,215,216,216,216,216,216, + 216,217,217,217,218,218,218,218,219,219,219,219,220,220,220,220, + 220,220,221, 94,222,222,222,222,223,223,223,223,224, 99, 99, 99, + 99, 99, 99, 99, 99, 99,102,225, 99,226,102,227,227,227,227,227, + 228,228,228,228,228,228, 0, 0, 8, 0, 0, 0, 0, 0,229,230, + 231, 0,232, 0,233,233,233,233, 91, 91, 91, 13,234,234,234,234, + 235,235,235,235,236,236,236,236,237,237,237,237,238,238,238,238, + 239,239,239,239,240, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, + 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, + 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, + 8, 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, + 14, 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, 19, 19, + 19, 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, 20, 20, + 22, 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, 20, 21, + 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, + 31, 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, 33, 33, + 33, 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, + 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, + 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, + 48, 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, 52, 52, + 53, 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, 56, 56, + 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, + 60, 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 0, 0, + 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, 71, 71, + 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, + 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, + 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, 7, 7, + 83, 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, 2, 89, + 90, 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, 0, 86, + 1, 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, 0, 4, + 97, 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, 99, 99, + 100,100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0,100, 0, + 105,106,106,106,106,106,106,106,106,106,107,105,108,109,109,109, + 109,109,109,109,109,109,110,108,111,111,111,111,112, 55, 55, 55, + 55, 55, 55,113,109,109,109,110,109,109, 0, 0,114,114,114,114, + 115,115,115,115,116,116,116,116,117,117,117,117, 96, 2, 2, 2, + 2, 2, 94, 2,118,118,118,118,119,119,119,119,120,120,120,120, + 121,121,121,121,121,121,121,122,123,123,123,123,124,124,124,124, + 124,124,124,125,126,126,126,126,127,127,127,127,128,128,128,128, + 2, 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, 17, 18, + 20, 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135,109,109, + 109,109,109,136,137,137,137,137, 0, 0, 0,138,139,139,139,139, + 140,140,140,140, 84, 0, 0, 0,141,141,141,141,142,142,142,142, + 143,143,143,143,144,144,144,144,145,145,145,145,146,146,146,146, + 147,147,147,147,148,148,148,148,149,149,149,149,150,150,150,150, + 151,151,151,151,152,152,152,152,153,153,153,153,154,154,154,154, + 155,155,155,155,156,156,156,156,157,157,157,157,158,158,158,158, + 159,159,159,159,160,160,160,160,161,161,161,161,162,162,162,162, + 163,163,163,163,164,164,164,164,165,165,165,165,166,166,166,166, + 167,167,167,167,168,168,168,168,169,169,169,169,170,170,170,170, + 171,171,171,171,172,172,172,172,173,173,173,173,174,174,174,174, + 174,174,174,175,176,176,176,176,177,177,177,177,178,178,178,178, + 179,179,179,179,180,180,180,180,181,181,181,181,182,182,182,182, + 183,183,183,183,184,184,184,184,185,185,185,185,186,186,186,186, + 187, 45, 45, 45,188,188,188,188,189,189,189,189,190,190,190,190, + 191,191,191,191,191,191,192,191,193,193,193,193,194,194,194,194, + 195,195,195,195,196,196,196,196,197,197,197,197,198,198,198,198, + 199,199,199,199,200,200,200,200,201,201,201,201,202,202,202,202, + 203,203,203,203,204,204,204,204,205,205,205,205,206,206,206,206, + 207,207,207,207,208,208,208,208,209,209,209,209,210,210,210,210, + 211,211,211,211,212,212,212,212,213,213,213,213,214,214,214,214, + 215,215,215,215,216,216,216,216,217,217,217,217,218,218,218,218, + 219,219,219,219,220,220,220,220,221,221,221,221,222,223,223,223, + 224,224,224,224,223,223,223,223,225,106,106,106,226,106,106,106, + 106,227,109,109,228,228,228,228,229,229,229,229, 0,230, 86, 0, + 0, 0,230, 7, 82,138, 7, 0, 0, 0,231, 86,232,232,232,232, + 233,233,233,233,234,234,234,234,235,235,235,235,236,236,236,236, + 237,237,237,237,238,238,238,238,239, 0, 0, 0, 0, 0, 0, 0, + 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, + 19, 0, 0, 0, 0, 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, + 0, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, + 55, 55, 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, + 4, 4, 4, 4, 4, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, + 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, + 1, 1, 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, + 64, 64, 64, 64, 90, 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, + 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, + 5, 5, 5, 5, 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, + 22, 22, 22, 22, 23, 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, + 36, 36, 36, 36, 24, 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, + 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, + 8, 8, 8, 8, 8, 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, + 29, 29, 29, 29, 28, 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, + 35, 35, 35, 0, 0, 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, + 44, 0, 0, 0, 43, 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, + 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, + 52, 52, 52, 52, 58, 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, + 62, 62, 62, 62, 76, 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, + 73, 73, 73, 73, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, + 0, 1, 0, 0, 1, 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, + 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, + 19, 19, 19, 9, 0, 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, + 27, 27, 27, 27, 56, 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, + 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, + 0, 15, 15, 15, 15, 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, + 12, 12, 12, 0, 39, 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, + 79, 79, 79, 79, 60, 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, + 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, + 84, 84, 84, 0, 68, 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, + 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, + 3, 3, 0, 0, 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, + 49, 49, 49, 49, 0, 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, + 42, 42, 42, 42, 41, 41, 41, 41,118,118,118,118, 53, 53, 53, 53, + 59, 59, 59, 59, 40, 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50, + 135,135,135,135,106,106,106,106,104,104,104,104,161,161,161,161, + 110,110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120, + 116,116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72, + 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, 88, 88, 88, + 117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, 83, 83, 83, + 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130,130,130,130, + 144,144,144,144,156,156,156,156,156, 3, 3, 3,147,147,147,147, + 148,148,148,148,158,158,158,158,153,153,153,153,149,149,149,149, + 94, 94, 94, 94, 85, 85, 85, 85,101,101,101,101, 96, 96, 96, 96, + 111,111,111,111,100,100,100,100,100, 36, 36, 36,108,108,108,108, + 129,129,129,129,109,109,109,109,107,107,107,107,107,107,107, 1, + 137,137,137,137,124,124,124,124,123,123,123,123,114,114,114,114, + 102,102,102,102,126,126,126,126,142,142,142,142,125,125,125,125, + 154,154,154,154,150,150,150,150,141,141,141,141,140,140,140,140, + 121,121,121,121,133,133,133,133,134,134,134,134,138,138,138,138, + 143,143,143,143,145,145,145,145,163,163,163,163, 63, 63, 63, 63, + 157,157,157,157, 80, 80, 80, 80,127,127,127,127,115,115,115,115, + 159,159,159,159,103,103,103,103,119,119,119,119,146,146,146,146, + 99, 99, 99, 99,136,139, 13, 13,155,155,155,155,136,136,136,136, + 17, 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17,139,139,139,139, + 105,105,105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131, + 151,151,151,151,160,160,160,160,152,152,152,152,164,164,164,164, + 113,113,113,113,132,132,132,132, 15, 0, 0, 0, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, 9, 9, + 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 18, 19, 20, 9, 21, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 22, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 23, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, + 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, + 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 41, 42, 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, 43, 44, 0, 45, + 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, + 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, + 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, + 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, + 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 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, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, + 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0,110, 0, + 111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 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,117, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118,119, + 120,121, 0,122,123,124,125,126, 0,127, 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,128,129,130,131,132,133, + 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149, + 150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160,161, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,162,163, 0, 0, 0, 0, 0, 0, 0,164, 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,165, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,168, 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,169, + 170, 0, 0, 0, 0,171,172, 0, 0, 0,173,174,175,176,177,178, + 179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194, + 195,196,197,198,199,200,201,202,203,204,205,206, 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, +}; +static const uint16_t +_hb_ucd_u16[4920] = +{ + 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, + 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, + 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, + 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, + 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, + 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, + 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, + 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, + 47, 64, 65, 66, 47, 67, 47, 47, 68, 69, 47, 47, 70, 32, 71, 32, + 72, 47, 47, 73, 74, 75, 76, 77, 78, 47, 47, 79, 80, 81, 82, 83, + 84, 47, 47, 85, 86, 87, 88, 89, 84, 47, 47, 79, 90, 47, 82, 91, + 92, 47, 47, 93, 94, 95, 82, 96, 97, 47, 47, 98, 99, 100, 101, 102, + 103, 47, 47, 104, 105, 106, 82, 107, 108, 47, 47, 93, 109, 110, 82, 111, + 112, 47, 47, 113, 114, 115, 82, 116, 92, 47, 47, 47, 117, 118, 101, 119, + 47, 47, 47, 120, 121, 122, 66, 66, 47, 47, 47, 123, 124, 125, 47, 47, + 126, 127, 128, 129, 47, 47, 47, 130, 131, 32, 32, 132, 133, 134, 66, 66, + 47, 47, 135, 136, 122, 137, 138, 139, 140, 141, 9, 9, 9, 11, 11, 142, + 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 143, 144, 145, + 47, 146, 9, 9, 9, 9, 9, 147, 148, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 149, 47, 150, 151, 47, 47, 47, 47, 152, 153, + 47, 154, 47, 155, 47, 156, 47, 156, 47, 47, 47, 157, 158, 159, 160, 145, + 161, 160, 47, 47, 162, 47, 47, 47, 163, 47, 164, 47, 47, 47, 47, 47, + 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, + 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, + 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, + 47, 47, 184, 185, 186, 61, 47, 187, 11, 9, 9, 9, 66, 188, 189, 190, + 11, 11, 191, 27, 27, 27, 192, 193, 11, 194, 27, 27, 32, 32, 32, 32, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 195, 13, 13, 13, 13, 13, 13, + 196, 196, 196, 196, 196, 197, 196, 11, 198, 198, 198, 199, 200, 201, 201, 200, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 27, 211, 211, 211, 212, 213, 32, + 214, 215, 216, 217, 218, 145, 219, 219, 220, 221, 222, 146, 223, 224, 146, 225, + 226, 226, 226, 226, 226, 226, 226, 226, 227, 146, 228, 146, 146, 146, 146, 229, + 146, 230, 226, 231, 146, 232, 233, 146, 146, 146, 146, 146, 146, 146, 145, 145, + 145, 234, 146, 146, 146, 146, 235, 145, 146, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 236, 237, 146, 146, 238, 146, 146, 146, 146, 146, 146, 239, 146, + 146, 146, 146, 146, 146, 146, 240, 241, 145, 242, 146, 146, 243, 226, 244, 226, + 245, 246, 226, 226, 226, 247, 226, 248, 146, 146, 146, 226, 249, 146, 146, 146, + 9, 9, 9, 11, 11, 11, 250, 251, 13, 13, 13, 13, 13, 13, 252, 253, + 11, 11, 11, 47, 47, 47, 254, 255, 47, 47, 47, 47, 47, 47, 32, 32, + 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, 267, 47, 47, 47, + 47, 268, 148, 47, 47, 47, 47, 269, 47, 270, 47, 47, 146, 146, 146, 47, + 146, 146, 271, 146, 272, 273, 146, 146, 271, 146, 146, 273, 146, 146, 146, 146, + 47, 47, 47, 47, 146, 146, 146, 146, 47, 274, 47, 47, 47, 47, 47, 47, + 47, 146, 146, 146, 146, 47, 47, 187, 275, 47, 61, 47, 13, 13, 276, 277, + 13, 278, 47, 47, 47, 47, 279, 280, 31, 281, 282, 283, 13, 13, 13, 284, + 285, 286, 287, 288, 289, 290, 11, 291, 292, 47, 293, 294, 47, 47, 47, 295, + 296, 47, 47, 297, 298, 160, 32, 299, 61, 47, 300, 47, 301, 302, 47, 47, + 72, 47, 47, 303, 304, 305, 306, 61, 47, 47, 307, 308, 309, 310, 47, 311, + 47, 47, 47, 312, 58, 313, 314, 315, 47, 47, 47, 11, 11, 316, 317, 11, + 11, 11, 11, 11, 47, 47, 318, 160, 319, 319, 319, 319, 319, 319, 319, 319, + 320, 320, 320, 320, 320, 320, 320, 320, 11, 321, 322, 47, 47, 47, 47, 47, + 47, 47, 47, 323, 31, 324, 47, 47, 47, 47, 47, 325, 146, 47, 47, 47, + 47, 47, 47, 47, 326, 146, 146, 327, 32, 328, 32, 329, 330, 331, 332, 47, + 47, 47, 47, 47, 47, 47, 47, 333, 334, 2, 3, 4, 5, 335, 336, 337, + 47, 338, 47, 47, 47, 47, 339, 340, 341, 145, 145, 342, 219, 219, 219, 343, + 344, 146, 146, 146, 146, 146, 146, 345, 346, 346, 346, 346, 346, 346, 346, 346, + 47, 47, 47, 47, 47, 47, 347, 145, 47, 47, 348, 47, 349, 47, 47, 60, + 47, 350, 47, 47, 47, 351, 219, 219, 9, 9, 147, 11, 11, 47, 47, 47, + 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 350, 9, + 9, 352, 11, 11, 11, 11, 11, 11, 27, 27, 27, 27, 27, 27, 27, 27, + 47, 47, 47, 47, 47, 353, 47, 354, 47, 47, 355, 145, 145, 145, 47, 356, + 47, 357, 47, 350, 66, 66, 66, 66, 47, 47, 47, 358, 145, 145, 145, 145, + 359, 47, 47, 360, 145, 66, 47, 361, 47, 362, 145, 145, 363, 47, 364, 66, + 47, 47, 47, 365, 47, 366, 47, 366, 47, 365, 144, 145, 145, 145, 145, 145, + 9, 9, 9, 9, 11, 11, 11, 367, 47, 47, 368, 160, 160, 160, 160, 160, + 145, 145, 145, 145, 145, 145, 145, 145, 47, 47, 369, 47, 47, 47, 47, 143, + 47, 362, 370, 47, 60, 371, 66, 47, 372, 66, 66, 47, 373, 145, 47, 47, + 374, 47, 47, 360, 375, 376, 377, 378, 180, 47, 47, 379, 380, 47, 47, 160, + 97, 47, 381, 382, 383, 47, 47, 384, 180, 47, 47, 385, 386, 387, 388, 145, + 47, 47, 389, 390, 359, 32, 32, 32, 47, 47, 365, 47, 47, 391, 172, 160, + 92, 47, 47, 113, 392, 393, 394, 32, 47, 47, 47, 395, 396, 397, 47, 47, + 47, 47, 47, 398, 399, 160, 160, 160, 47, 47, 400, 401, 402, 403, 32, 32, + 47, 47, 47, 404, 405, 160, 66, 66, 47, 47, 406, 407, 160, 160, 160, 160, + 47, 143, 408, 409, 47, 47, 47, 47, 47, 47, 389, 410, 66, 66, 66, 66, + 9, 9, 9, 9, 11, 11, 128, 411, 47, 47, 47, 412, 413, 160, 160, 160, + 47, 47, 47, 47, 47, 414, 415, 416, 417, 47, 47, 418, 419, 420, 47, 47, + 421, 422, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 66, 66, + 47, 47, 400, 423, 424, 128, 145, 425, 47, 156, 426, 427, 32, 32, 32, 32, + 47, 47, 47, 359, 428, 160, 47, 47, 429, 430, 160, 160, 160, 160, 160, 160, + 47, 47, 47, 47, 47, 47, 47, 431, 432, 47, 47, 433, 434, 160, 160, 160, + 47, 47, 47, 47, 145, 435, 436, 437, 219, 219, 219, 219, 219, 219, 219, 66, + 47, 47, 47, 47, 47, 47, 47, 424, 47, 47, 47, 208, 438, 32, 32, 32, + 47, 47, 47, 47, 47, 47, 305, 47, 47, 47, 47, 47, 160, 47, 47, 439, + 47, 47, 47, 440, 441, 442, 443, 47, 9, 9, 9, 9, 9, 9, 11, 11, + 145, 444, 66, 66, 66, 66, 66, 66, 47, 47, 47, 47, 391, 445, 416, 416, + 446, 447, 27, 27, 27, 27, 448, 416, 47, 449, 208, 208, 208, 208, 208, 208, + 32, 32, 32, 32, 32, 146, 146, 146, 146, 146, 146, 146, 146, 146, 450, 451, + 452, 146, 453, 146, 146, 146, 146, 146, 146, 146, 146, 146, 454, 146, 146, 146, + 9, 455, 11, 456, 457, 11, 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, + 456, 457, 11, 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, 456, 457, 11, + 196, 9, 458, 459, 9, 460, 11, 9, 455, 11, 196, 9, 461, 462, 463, 464, + 11, 465, 9, 466, 467, 468, 469, 11, 470, 9, 471, 11, 472, 160, 160, 160, + 32, 32, 32, 473, 32, 32, 474, 475, 476, 477, 32, 32, 32, 32, 32, 32, + 478, 11, 11, 11, 11, 11, 11, 11, 32, 32, 32, 27, 27, 27, 27, 27, + 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 479, 480, 146, 146, 146, + 47, 47, 481, 32, 47, 47, 482, 483, 47, 47, 47, 47, 47, 47, 484, 160, + 47, 47, 47, 47, 355, 32, 32, 32, 9, 9, 458, 11, 485, 305, 66, 66, + 145, 145, 486, 487, 145, 145, 145, 145, 145, 145, 488, 145, 145, 145, 145, 145, + 47, 47, 47, 47, 47, 47, 47, 226, 489, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 490, 146, 146, 146, 146, 146, 146, 146, 160, + 208, 208, 208, 208, 208, 208, 208, 208, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, + 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, + 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, + 1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, + 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, + 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, + 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0, + 1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, + 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035, + 1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250, + 1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0, + 1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299, + 1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1093,1280, 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, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340, + 1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177, + 1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0, + 1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165, + 1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279, + 1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, + 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5, + 1434,1438,1443, 0,1450, 0,1455,1461,1514, 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,1446,1458,1468,1476,1480,1486,1517, 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,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, + 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, + 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, + 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, + 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0, + 1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648, + 1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, + 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0, + 1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, + 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0, + 1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142, + 1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, + 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, + 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210, + 1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222, + 1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243, + 1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389, + 1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284, + 1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291, + 1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260, + 1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343, + 1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, + 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698, + 1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359, + 1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274, + 1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304, + 1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707, + 1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0, + 1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743, + 1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770, + 1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0, + 1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, + 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800, + 1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24, + 1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714, + 1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750, + 1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807, + 1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825, + 1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829, + 1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515, + 1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518, + 1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830, + 1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, + 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, + 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, + 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0, + 1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0, + 1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, + 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0, + 1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, + 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, + 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0, + 1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, + 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, + 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929, + 1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, + 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, + 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, + 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, + 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, + 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, + 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, + 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, + 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, + 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, + 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, + 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, + 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, + 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, + 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, + 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, + 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, + 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, + 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, + 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, + 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, + 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, + 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, + 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, + 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, + 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, + 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, + 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, + 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, + 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, + 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, + 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, + 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1936, 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1938, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1939,1940, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1944,1943, 0,1945, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,1946,1947, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1949,1950,1951,1952,1953,1954,1955, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1956,1957,1958,1960,1959,1961, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, + 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, + 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, + 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, + 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, + 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, + 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, + 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, + 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, + 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, + 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, + 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, + 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, + 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, + 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, + 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, + 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, + 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, + 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, + 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, + 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, + 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, + 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, + 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, + 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, + 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, + 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, + 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, + 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, + 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, + 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, + 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, + 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, + 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, + 817, 818, 819, 820, 821, 935, 0, 0, +}; +static const int16_t +_hb_ucd_i16[92] = +{ + 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, + 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, + 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, + -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, + 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, +}; + +static inline uint_fast8_t +_hb_ucd_gc (unsigned u) +{ + return u<1114112u?_hb_ucd_u8[5096+(((_hb_ucd_u8[1168+(((_hb_ucd_u16[((_hb_ucd_u8[544+(((_hb_ucd_u8[u>>1>>3>>3>>4])<<4)+((u>>1>>3>>3)&15u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:2; +} +static inline uint_fast8_t +_hb_ucd_ccc (unsigned u) +{ + return u<125259u?_hb_ucd_u8[7054+(((_hb_ucd_u8[6498+(((_hb_ucd_u8[6038+(((_hb_ucd_u8[5686+(((_hb_ucd_u8[5440+(u>>2>>2>>2>>3)])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:0; +} +static inline unsigned +_hb_ucd_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline int_fast16_t +_hb_ucd_bmg (unsigned u) +{ + return u<65380u?_hb_ucd_i16[((_hb_ucd_u8[7946+(((_hb_ucd_u8[7714+(((_hb_ucd_u8[7618+(((_hb_ucd_b4(7554+_hb_ucd_u8,u>>1>>2>>3>>3))<<3)+((u>>1>>2>>3)&7u))])<<3)+((u>>1>>2)&7u))])<<2)+((u>>1)&3u))])<<1)+((u)&1u)]:0; +} +static inline uint_fast8_t +_hb_ucd_sc (unsigned u) +{ + return u<918016u?_hb_ucd_u8[11244+(((_hb_ucd_u8[10280+(((_hb_ucd_u8[9292+(((_hb_ucd_u8[8612+(((_hb_ucd_u8[8308+(((_hb_ucd_u8[8194+(u>>2>>2>>2>>3>>4)])<<4)+((u>>2>>2>>2>>3)&15u))])<<3)+((u>>2>>2>>2)&7u))])<<2)+((u>>2>>2)&3u))])<<2)+((u>>2)&3u))])<<2)+((u)&3u))]:2; +} +static inline uint_fast16_t +_hb_ucd_dm (unsigned u) +{ + return u<195102u?_hb_ucd_u16[1608+(((_hb_ucd_u8[12586+(((_hb_ucd_u8[12204+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0; +} + +#endif + + +#endif /* HB_UCD_TABLE_HH */ + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-ucd.cc b/gfx/harfbuzz/src/hb-ucd.cc new file mode 100644 index 0000000000..4c8b1ee5e6 --- /dev/null +++ b/gfx/harfbuzz/src/hb-ucd.cc @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hb.hh" +#include "hb-unicode.hh" +#include "hb-machinery.hh" + +#include "hb-ucd-table.hh" + +static hb_unicode_combining_class_t +hb_ucd_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_combining_class_t) _hb_ucd_ccc (unicode); +} + +static hb_unicode_general_category_t +hb_ucd_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return (hb_unicode_general_category_t) _hb_ucd_gc (unicode); +} + +static hb_codepoint_t +hb_ucd_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return unicode + _hb_ucd_bmg (unicode); +} + +static hb_script_t +hb_ucd_script (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return _hb_ucd_sc_map[_hb_ucd_sc (unicode)]; +} + + +#define SBASE 0xAC00u +#define LBASE 0x1100u +#define VBASE 0x1161u +#define TBASE 0x11A7u +#define SCOUNT 11172u +#define LCOUNT 19u +#define VCOUNT 21u +#define TCOUNT 28u +#define NCOUNT (VCOUNT * TCOUNT) + +static inline bool +_hb_ucd_decompose_hangul (hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b) +{ + unsigned si = ab - SBASE; + + if (si >= SCOUNT) + return false; + + if (si % TCOUNT) + { + /* LV,T */ + *a = SBASE + (si / TCOUNT) * TCOUNT; + *b = TBASE + (si % TCOUNT); + return true; + } else { + /* L,V */ + *a = LBASE + (si / NCOUNT); + *b = VBASE + (si % NCOUNT) / TCOUNT; + return true; + } +} + +static inline bool +_hb_ucd_compose_hangul (hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab) +{ + if (a >= SBASE && a < (SBASE + SCOUNT) && b > TBASE && b < (TBASE + TCOUNT) && + !((a - SBASE) % TCOUNT)) + { + /* LV,T */ + *ab = a + (b - TBASE); + return true; + } + else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT)) + { + /* L,V */ + int li = a - LBASE; + int vi = b - VBASE; + *ab = SBASE + li * NCOUNT + vi * TCOUNT; + return true; + } + else + return false; +} + +static int +_cmp_pair (const void *_key, const void *_item) +{ + uint64_t& a = * (uint64_t*) _key; + uint64_t b = (* (uint64_t*) _item) & HB_CODEPOINT_ENCODE3(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} +static int +_cmp_pair_11_7_14 (const void *_key, const void *_item) +{ + uint32_t& a = * (uint32_t*) _key; + uint32_t b = (* (uint32_t*) _item) & HB_CODEPOINT_ENCODE3_11_7_14(0x1FFFFFu, 0x1FFFFFu, 0); + + return a < b ? -1 : a > b ? +1 : 0; +} + +static hb_bool_t +hb_ucd_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) +{ + // Hangul is handled algorithmically. + if (_hb_ucd_compose_hangul (a, b, ab)) return true; + + hb_codepoint_t u = 0; + + if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u) + { + /* If "a" is small enough and "b" is in the U+0300 range, + * the composition data is encoded in a 32bit array sorted + * by "a,b" pair. */ + uint32_t k = HB_CODEPOINT_ENCODE3_11_7_14 (a, b, 0); + const uint32_t *v = hb_bsearch (k, + _hb_ucd_dm2_u32_map, + ARRAY_LENGTH (_hb_ucd_dm2_u32_map), + sizeof (*_hb_ucd_dm2_u32_map), + _cmp_pair_11_7_14); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_11_7_14_3 (*v); + } + else + { + /* Otherwise it is stored in a 64bit array sorted by + * "a,b" pair. */ + uint64_t k = HB_CODEPOINT_ENCODE3 (a, b, 0); + const uint64_t *v = hb_bsearch (k, + _hb_ucd_dm2_u64_map, + ARRAY_LENGTH (_hb_ucd_dm2_u64_map), + sizeof (*_hb_ucd_dm2_u64_map), + _cmp_pair); + if (likely (!v)) return false; + u = HB_CODEPOINT_DECODE3_3 (*v); + } + + if (unlikely (!u)) return false; + *ab = u; + return true; +} + +static hb_bool_t +hb_ucd_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 (_hb_ucd_decompose_hangul (ab, a, b)) return true; + + unsigned i = _hb_ucd_dm (ab); + + /* If no data, there's no decomposition. */ + if (likely (!i)) return false; + i--; + + /* Check if it's a single-character decomposition. */ + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map)) + { + /* Single-character decompositions currently are only in plane 0 or plane 2. */ + if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map)) + { + /* Plane 0. */ + *a = _hb_ucd_dm1_p0_map[i]; + } + else + { + /* Plane 2. */ + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map); + *a = 0x20000 | _hb_ucd_dm1_p2_map[i]; + } + *b = 0; + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map); + + /* Otherwise they are encoded either in a 32bit array or a 64bit array. */ + if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map)) + { + /* 32bit array. */ + uint32_t v = _hb_ucd_dm2_u32_map[i]; + *a = HB_CODEPOINT_DECODE3_11_7_14_1 (v); + *b = HB_CODEPOINT_DECODE3_11_7_14_2 (v); + return true; + } + i -= ARRAY_LENGTH (_hb_ucd_dm2_u32_map); + + /* 64bit array. */ + uint64_t v = _hb_ucd_dm2_u64_map[i]; + *a = HB_CODEPOINT_DECODE3_1 (v); + *b = HB_CODEPOINT_DECODE3_2 (v); + return true; +} + + +static void free_static_ucd_funcs (); + +static struct hb_ucd_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_ucd_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_ucd_combining_class, nullptr, nullptr); + hb_unicode_funcs_set_general_category_func (funcs, hb_ucd_general_category, nullptr, nullptr); + hb_unicode_funcs_set_mirroring_func (funcs, hb_ucd_mirroring, nullptr, nullptr); + hb_unicode_funcs_set_script_func (funcs, hb_ucd_script, nullptr, nullptr); + hb_unicode_funcs_set_compose_func (funcs, hb_ucd_compose, nullptr, nullptr); + hb_unicode_funcs_set_decompose_func (funcs, hb_ucd_decompose, nullptr, nullptr); + + hb_unicode_funcs_make_immutable (funcs); + + hb_atexit (free_static_ucd_funcs); + + return funcs; + } +} static_ucd_funcs; + +static inline +void free_static_ucd_funcs () +{ + static_ucd_funcs.free_instance (); +} + +hb_unicode_funcs_t * +hb_ucd_get_unicode_funcs () +{ +#ifdef HB_NO_UCD + return hb_unicode_funcs_get_empty (); +#endif + return static_ucd_funcs.get_unconst (); +} diff --git a/gfx/harfbuzz/src/hb-unicode-emoji-table.hh b/gfx/harfbuzz/src/hb-unicode-emoji-table.hh new file mode 100644 index 0000000000..e607e8ca82 --- /dev/null +++ b/gfx/harfbuzz/src/hb-unicode-emoji-table.hh @@ -0,0 +1,79 @@ +/* == Start of generated table == */ +/* + * The following tables are generated by running: + * + * ./gen-emoji-table.py emoji-data.txt + * + * on file with this header: + * + * # emoji-data.txt + * # Date: 2023-02-01, 02:22:54 GMT + * # © 2023 Unicode®, Inc. + * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. + * # For terms of use, see https://www.unicode.org/terms_of_use.html + * # + * # Emoji Data for UTS #51 + * # Used with Emoji Version 15.1 and subsequent minor revisions (if any) + * # + * # For documentation and usage, see https://www.unicode.org/reports/tr51 + */ + +#ifndef HB_UNICODE_EMOJI_TABLE_HH +#define HB_UNICODE_EMOJI_TABLE_HH + +#include "hb-unicode.hh" + +static const uint8_t +_hb_emoji_u8[464] = +{ + 16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,118,152, + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 3, 4, 0, 0, 5, 6, 0, 7, 0, 8, 9, 10, 11, 12, + 0, 0, 13, 0, 0, 0, 14, 0, 15, 0, 0, 0, 0, 16, 0, 0, + 17, 17, 18, 19, 20, 17, 17, 21, 17, 17, 22, 17, 23, 17, 24, 25, + 26, 27, 28, 17, 17, 17, 0, 0, 17, 17, 17, 17, 17, 17, 17, 29, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 4, 0, 0, + 5, 6, 0, 0, 7, 8, 0, 0, 8, 0, 9, 10, 0, 0, 11, 0, + 0, 12, 13, 14, 15, 16, 16, 16, 17, 16, 16, 16, 18, 19, 20, 21, + 22, 23, 0, 0, 0, 24, 0, 0, 25, 0, 26, 0, 0, 27, 0, 0, + 28, 0, 0, 0, 16, 16, 16, 16, 29, 9, 0, 30, 31, 32, 16, 33, + 34, 35, 36, 16, 16, 16, 16, 37, 16, 38, 39, 16, 16, 16, 40, 0, + 0, 0, 0, 41, 0, 0, 42, 16, 43, 0, 44, 0, 45, 46, 16, 16, + 47, 48, 49, 16, 16, 16, 16, 38, 0, 0, 0, 0, 0, 66, 0, 0, + 0, 0, 0, 16, 0, 2, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, + 0, 6, 0, 0, 0, 0, 0, 12, 0, 1, 0, 0, 0,128, 0, 0, + 0,254, 15, 7, 4, 0, 0, 0, 0, 12, 64, 0, 1, 0, 0, 0, + 0, 0, 0,120,191,255,247,255,255,255,255,255, 63, 0,255,255, + 63,255, 87, 32, 2, 1, 24, 0,144, 80,184, 0,248, 0, 0, 0, + 0, 0,224, 0, 2, 0, 1,128, 0, 0, 48, 0,224, 0, 0, 24, + 0, 0, 33, 0, 0, 0, 1, 32, 0, 0,128, 2, 0,224, 0, 0, + 0,240, 3,192, 0, 64,254, 7, 0,224,255,255, 63, 0, 0, 0, + 254,255, 0, 4, 0,128,252,247, 0,254,255,255,255,255,255, 7, + 255,255,255, 63,192,255,255,255,255,255, 0, 0, 0, 0,240,255, + 0, 0,224,255, 0,240, 0, 0, 0,255, 0,252, 0,255, 0, 0, + 0,192,255,255, 0,240,255,255,255,255,255,247,191,255,255,255, +}; + +static inline unsigned +_hb_emoji_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1u)<<2))&15u; +} +static inline unsigned +_hb_emoji_b1 (const uint8_t* a, unsigned i) +{ + return (a[i>>3]>>((i&7u)<<0))&1u; +} +static inline uint_fast8_t +_hb_emoji_is_Extended_Pictographic (unsigned u) +{ + return u<131070u?_hb_emoji_b1(264+_hb_emoji_u8,((_hb_emoji_u8[144+(((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7u))])<<2)+((u>>5)&3u))])<<5)+((u)&31u)):0; +} + + +#endif /* HB_UNICODE_EMOJI_TABLE_HH */ + +/* == End of generated table == */ diff --git a/gfx/harfbuzz/src/hb-unicode.cc b/gfx/harfbuzz/src/hb-unicode.cc new file mode 100644 index 0000000000..aa2735bedb --- /dev/null +++ b/gfx/harfbuzz/src/hb-unicode.cc @@ -0,0 +1,625 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * 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 + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-unicode.hh" + + +/** + * SECTION: hb-unicode + * @title: hb-unicode + * @short_description: Unicode character property access + * @include: hb.h + * + * Unicode functions are used to access Unicode character properties. + * With these functions, client programs can query various properties from + * the Unicode Character Database for any code point, such as General + * Category (gc), Script (sc), Canonical Combining Class (ccc), etc. + * + * Client programs can optionally pass in their own Unicode functions + * that implement the same queries. The set of functions available is + * defined by the virtual methods in #hb_unicode_funcs_t. + * + * HarfBuzz provides built-in default functions for each method in + * #hb_unicode_funcs_t. + **/ + + +/* + * hb_unicode_funcs_t + */ + +static hb_unicode_combining_class_t +hb_unicode_combining_class_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_UNICODE_COMBINING_CLASS_NOT_REORDERED; +} + +#ifndef HB_DISABLE_DEPRECATED +static unsigned int +hb_unicode_eastasian_width_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 1; +} +#endif + +static hb_unicode_general_category_t +hb_unicode_general_category_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER; +} + +static hb_codepoint_t +hb_unicode_mirroring_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode, + void *user_data HB_UNUSED) +{ + return unicode; +} + +static hb_script_t +hb_unicode_script_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t unicode HB_UNUSED, + void *user_data HB_UNUSED) +{ + return HB_SCRIPT_UNKNOWN; +} + +static hb_bool_t +hb_unicode_compose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t a HB_UNUSED, + hb_codepoint_t b HB_UNUSED, + hb_codepoint_t *ab HB_UNUSED, + void *user_data HB_UNUSED) +{ + return false; +} + +static hb_bool_t +hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t ab HB_UNUSED, + hb_codepoint_t *a HB_UNUSED, + hb_codepoint_t *b HB_UNUSED, + void *user_data HB_UNUSED) +{ + return false; +} + + +#ifndef HB_DISABLE_DEPRECATED +static unsigned int +hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs HB_UNUSED, + hb_codepoint_t u HB_UNUSED, + hb_codepoint_t *decomposed HB_UNUSED, + void *user_data HB_UNUSED) +{ + return 0; +} +#endif + +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) +#include "hb-glib.h" +#endif +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) +#include "hb-icu.h" +#endif + +/** + * hb_unicode_funcs_get_default: + * + * Fetches a pointer to the default Unicode-functions structure that is used + * when no functions are explicitly set on #hb_buffer_t. + * + * Return value: (transfer none): a pointer to the #hb_unicode_funcs_t Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_default () +{ +#if !defined(HB_NO_UNICODE_FUNCS) && !defined(HB_NO_UCD) + return hb_ucd_get_unicode_funcs (); +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_GLIB) + return hb_glib_get_unicode_funcs (); +#elif !defined(HB_NO_UNICODE_FUNCS) && defined(HAVE_ICU) && defined(HAVE_ICU_BUILTIN) + return hb_icu_get_unicode_funcs (); +#else +#define HB_UNICODE_FUNCS_NIL 1 + return hb_unicode_funcs_get_empty (); +#endif +} + +#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL) +#error "Could not find any Unicode functions implementation, you have to provide your own" +#error "Consider building hb-ucd.cc. If you absolutely want to build without any, define HB_NO_UNICODE_FUNCS." +#endif + +/** + * hb_unicode_funcs_create: + * @parent: (nullable): Parent Unicode-functions structure + * + * Creates a new #hb_unicode_funcs_t structure of Unicode functions. + * + * Return value: (transfer full): The Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_create (hb_unicode_funcs_t *parent) +{ + hb_unicode_funcs_t *ufuncs; + + if (!(ufuncs = hb_object_create<hb_unicode_funcs_t> ())) + return hb_unicode_funcs_get_empty (); + + if (!parent) + parent = hb_unicode_funcs_get_empty (); + + hb_unicode_funcs_make_immutable (parent); + ufuncs->parent = hb_unicode_funcs_reference (parent); + + ufuncs->func = parent->func; + + /* We can safely copy user_data from parent since we hold a reference + * onto it and it's immutable. We should not copy the destroy notifiers + * though. */ + ufuncs->user_data = parent->user_data; + + return ufuncs; +} + + +DEFINE_NULL_INSTANCE (hb_unicode_funcs_t) = +{ + HB_OBJECT_HEADER_STATIC, + + nullptr, /* parent */ + { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_nil, + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } +}; + +/** + * hb_unicode_funcs_get_empty: + * + * Fetches the singleton empty Unicode-functions structure. + * + * Return value: (transfer full): The empty Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_empty () +{ + return const_cast<hb_unicode_funcs_t *> (&Null (hb_unicode_funcs_t)); +} + +/** + * hb_unicode_funcs_reference: (skip) + * @ufuncs: The Unicode-functions structure + * + * Increases the reference count on a Unicode-functions structure. + * + * Return value: (transfer full): The Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs) +{ + return hb_object_reference (ufuncs); +} + +/** + * hb_unicode_funcs_destroy: (skip) + * @ufuncs: The Unicode-functions structure + * + * Decreases the reference count on a Unicode-functions structure. When + * the reference count reaches zero, the Unicode-functions structure is + * destroyed, freeing all memory. + * + * Since: 0.9.2 + **/ +void +hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs) +{ + if (!hb_object_destroy (ufuncs)) return; + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + if (ufuncs->destroy.name) ufuncs->destroy.name (ufuncs->user_data.name); + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + + hb_unicode_funcs_destroy (ufuncs->parent); + + hb_free (ufuncs); +} + +/** + * hb_unicode_funcs_set_user_data: (skip) + * @ufuncs: The Unicode-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 Unicode-functions structure. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (ufuncs, key, data, destroy, replace); +} + +/** + * hb_unicode_funcs_get_user_data: (skip) + * @ufuncs: The Unicode-functions structure + * @key: The user-data key to query + * + * Fetches the user-data associated with the specified key, + * attached to the specified Unicode-functions structure. + * + * Return value: (transfer none): A pointer to the user data + * + * Since: 0.9.2 + **/ +void * +hb_unicode_funcs_get_user_data (const hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (ufuncs, key); +} + + +/** + * hb_unicode_funcs_make_immutable: + * @ufuncs: The Unicode-functions structure + * + * Makes the specified Unicode-functions structure + * immutable. + * + * Since: 0.9.2 + **/ +void +hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs) +{ + if (hb_object_is_immutable (ufuncs)) + return; + + hb_object_make_immutable (ufuncs); +} + +/** + * hb_unicode_funcs_is_immutable: + * @ufuncs: The Unicode-functions structure + * + * Tests whether the specified Unicode-functions structure + * is immutable. + * + * Return value: `true` if @ufuncs is immutable, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs) +{ + return hb_object_is_immutable (ufuncs); +} + +/** + * hb_unicode_funcs_get_parent: + * @ufuncs: The Unicode-functions structure + * + * Fetches the parent of the Unicode-functions structure + * @ufuncs. + * + * Return value: The parent Unicode-functions structure + * + * Since: 0.9.2 + **/ +hb_unicode_funcs_t * +hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs) +{ + return ufuncs->parent ? ufuncs->parent : hb_unicode_funcs_get_empty (); +} + + +#define HB_UNICODE_FUNC_IMPLEMENT(name) \ + \ +void \ +hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t *ufuncs, \ + hb_unicode_##name##_func_t func, \ + void *user_data, \ + hb_destroy_func_t destroy) \ +{ \ + if (hb_object_is_immutable (ufuncs)) \ + goto fail; \ + \ + if (!func) \ + { \ + if (destroy) \ + destroy (user_data); \ + destroy = nullptr; \ + user_data = ufuncs->parent->user_data.name; \ + } \ + \ + if (ufuncs->destroy.name) \ + ufuncs->destroy.name (ufuncs->user_data.name); \ + \ + if (func) \ + ufuncs->func.name = func; \ + else \ + ufuncs->func.name = ufuncs->parent->func.name; \ + ufuncs->user_data.name = user_data; \ + ufuncs->destroy.name = destroy; \ + return; \ + \ +fail: \ + if (destroy) \ + destroy (user_data); \ +} + +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + + +#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \ + \ +return_type \ +hb_unicode_##name (hb_unicode_funcs_t *ufuncs, \ + hb_codepoint_t unicode) \ +{ \ + return ufuncs->name (unicode); \ +} +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE +#undef HB_UNICODE_FUNC_IMPLEMENT + +/** + * hb_unicode_compose: + * @ufuncs: The Unicode-functions structure + * @a: The first Unicode code point to compose + * @b: The second Unicode code point to compose + * @ab: (out): The composition of @a, @b + * + * Fetches the composition of a sequence of two Unicode + * code points. + * + * Calls the composition function of the specified + * Unicode-functions structure @ufuncs. + * + * Return value: `true` if @a and @b composed, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_compose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab) +{ + return ufuncs->compose (a, b, ab); +} + +/** + * hb_unicode_decompose: + * @ufuncs: The Unicode-functions structure + * @ab: Unicode code point to decompose + * @a: (out): The first code point of the decomposition of @ab + * @b: (out): The second code point of the decomposition of @ab + * + * Fetches the decomposition of a Unicode code point. + * + * Calls the decomposition function of the specified + * Unicode-functions structure @ufuncs. + * + * Return value: `true` if @ab was decomposed, `false` otherwise + * + * Since: 0.9.2 + **/ +hb_bool_t +hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b) +{ + return ufuncs->decompose (ab, a, b); +} + +#ifndef HB_DISABLE_DEPRECATED +/** + * hb_unicode_decompose_compatibility: + * @ufuncs: The Unicode-functions structure + * @u: Code point to decompose + * @decomposed: (out): Compatibility decomposition of @u + * + * Fetches the compatibility decomposition of a Unicode + * code point. Deprecated. + * + * Return value: length of @decomposed. + * + * Since: 0.9.2 + * Deprecated: 2.0.0 + **/ +unsigned int +hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t u, + hb_codepoint_t *decomposed) +{ + return ufuncs->decompose_compatibility (u, decomposed); +} +#endif + + +#ifndef HB_NO_OT_SHAPE +/* See hb-unicode.hh for details. */ +const uint8_t +_hb_modified_combining_class[256] = +{ + 0, /* HB_UNICODE_COMBINING_CLASS_NOT_REORDERED */ + 1, /* HB_UNICODE_COMBINING_CLASS_OVERLAY */ + 2, 3, 4, 5, 6, + 7, /* HB_UNICODE_COMBINING_CLASS_NUKTA */ + 8, /* HB_UNICODE_COMBINING_CLASS_KANA_VOICING */ + 9, /* HB_UNICODE_COMBINING_CLASS_VIRAMA */ + + /* Hebrew */ + HB_MODIFIED_COMBINING_CLASS_CCC10, + HB_MODIFIED_COMBINING_CLASS_CCC11, + HB_MODIFIED_COMBINING_CLASS_CCC12, + HB_MODIFIED_COMBINING_CLASS_CCC13, + HB_MODIFIED_COMBINING_CLASS_CCC14, + HB_MODIFIED_COMBINING_CLASS_CCC15, + HB_MODIFIED_COMBINING_CLASS_CCC16, + HB_MODIFIED_COMBINING_CLASS_CCC17, + HB_MODIFIED_COMBINING_CLASS_CCC18, + HB_MODIFIED_COMBINING_CLASS_CCC19, + HB_MODIFIED_COMBINING_CLASS_CCC20, + HB_MODIFIED_COMBINING_CLASS_CCC21, + HB_MODIFIED_COMBINING_CLASS_CCC22, + HB_MODIFIED_COMBINING_CLASS_CCC23, + HB_MODIFIED_COMBINING_CLASS_CCC24, + HB_MODIFIED_COMBINING_CLASS_CCC25, + HB_MODIFIED_COMBINING_CLASS_CCC26, + + /* Arabic */ + HB_MODIFIED_COMBINING_CLASS_CCC27, + HB_MODIFIED_COMBINING_CLASS_CCC28, + HB_MODIFIED_COMBINING_CLASS_CCC29, + HB_MODIFIED_COMBINING_CLASS_CCC30, + HB_MODIFIED_COMBINING_CLASS_CCC31, + HB_MODIFIED_COMBINING_CLASS_CCC32, + HB_MODIFIED_COMBINING_CLASS_CCC33, + HB_MODIFIED_COMBINING_CLASS_CCC34, + HB_MODIFIED_COMBINING_CLASS_CCC35, + + /* Syriac */ + HB_MODIFIED_COMBINING_CLASS_CCC36, + + 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, + + /* Telugu */ + HB_MODIFIED_COMBINING_CLASS_CCC84, + 85, 86, 87, 88, 89, 90, + HB_MODIFIED_COMBINING_CLASS_CCC91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + + /* Thai */ + HB_MODIFIED_COMBINING_CLASS_CCC103, + 104, 105, 106, + HB_MODIFIED_COMBINING_CLASS_CCC107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + + /* Lao */ + HB_MODIFIED_COMBINING_CLASS_CCC118, + 119, 120, 121, + HB_MODIFIED_COMBINING_CLASS_CCC122, + 123, 124, 125, 126, 127, 128, + + /* Tibetan */ + HB_MODIFIED_COMBINING_CLASS_CCC129, + HB_MODIFIED_COMBINING_CLASS_CCC130, + 131, + HB_MODIFIED_COMBINING_CLASS_CCC132, + 133, 134, 135, 136, 137, 138, 139, + + + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + + 200, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT */ + 201, + 202, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW */ + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE */ + 215, + 216, /* HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT */ + 217, + 218, /* HB_UNICODE_COMBINING_CLASS_BELOW_LEFT */ + 219, + 220, /* HB_UNICODE_COMBINING_CLASS_BELOW */ + 221, + 222, /* HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT */ + 223, + 224, /* HB_UNICODE_COMBINING_CLASS_LEFT */ + 225, + 226, /* HB_UNICODE_COMBINING_CLASS_RIGHT */ + 227, + 228, /* HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT */ + 229, + 230, /* HB_UNICODE_COMBINING_CLASS_ABOVE */ + 231, + 232, /* HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT */ + 233, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW */ + 234, /* HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE */ + 235, 236, 237, 238, 239, + 240, /* HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT */ + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, /* HB_UNICODE_COMBINING_CLASS_INVALID */ +}; +#endif + + +/* + * Emoji + */ +#ifndef HB_NO_EMOJI_SEQUENCES + +#include "hb-unicode-emoji-table.hh" + +bool +_hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp) +{ + return _hb_emoji_is_Extended_Pictographic (cp); +} +#endif diff --git a/gfx/harfbuzz/src/hb-unicode.h b/gfx/harfbuzz/src/hb-unicode.h new file mode 100644 index 0000000000..5b5c45cae3 --- /dev/null +++ b/gfx/harfbuzz/src/hb-unicode.h @@ -0,0 +1,643 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * 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): Behdad Esfahbod + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_UNICODE_H +#define HB_UNICODE_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_UNICODE_MAX: + * + * Maximum valid Unicode code point. + * + * Since: 1.9.0 + **/ +#define HB_UNICODE_MAX 0x10FFFFu + + +/** + * hb_unicode_general_category_t: + * @HB_UNICODE_GENERAL_CATEGORY_CONTROL: [Cc] + * @HB_UNICODE_GENERAL_CATEGORY_FORMAT: [Cf] + * @HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED: [Cn] + * @HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE: [Co] + * @HB_UNICODE_GENERAL_CATEGORY_SURROGATE: [Cs] + * @HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER: [Ll] + * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER: [Lm] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER: [Lo] + * @HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER: [Lt] + * @HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER: [Lu] + * @HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK: [Mc] + * @HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK: [Me] + * @HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK: [Mn] + * @HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER: [Nd] + * @HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER: [Nl] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER: [No] + * @HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION: [Pc] + * @HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION: [Pd] + * @HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION: [Pe] + * @HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION: [Pf] + * @HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION: [Pi] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION: [Po] + * @HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION: [Ps] + * @HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL: [Sc] + * @HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL: [Sk] + * @HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL: [Sm] + * @HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL: [So] + * @HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR: [Zl] + * @HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR: [Zp] + * @HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR: [Zs] + * + * Data type for the "General_Category" (gc) property from + * the Unicode Character Database. + **/ + +/* Unicode Character Database property: General_Category (gc) */ +typedef enum +{ + HB_UNICODE_GENERAL_CATEGORY_CONTROL, /* Cc */ + HB_UNICODE_GENERAL_CATEGORY_FORMAT, /* Cf */ + HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED, /* Cn */ + HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE, /* Co */ + HB_UNICODE_GENERAL_CATEGORY_SURROGATE, /* Cs */ + HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER, /* Ll */ + HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER, /* Lm */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER, /* Lo */ + HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER, /* Lt */ + HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER, /* Lu */ + HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK, /* Mc */ + HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK, /* Me */ + HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK, /* Mn */ + HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER, /* Nd */ + HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER, /* Nl */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER, /* No */ + HB_UNICODE_GENERAL_CATEGORY_CONNECT_PUNCTUATION, /* Pc */ + HB_UNICODE_GENERAL_CATEGORY_DASH_PUNCTUATION, /* Pd */ + HB_UNICODE_GENERAL_CATEGORY_CLOSE_PUNCTUATION, /* Pe */ + HB_UNICODE_GENERAL_CATEGORY_FINAL_PUNCTUATION, /* Pf */ + HB_UNICODE_GENERAL_CATEGORY_INITIAL_PUNCTUATION, /* Pi */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_PUNCTUATION, /* Po */ + HB_UNICODE_GENERAL_CATEGORY_OPEN_PUNCTUATION, /* Ps */ + HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL, /* Sc */ + HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL, /* Sk */ + HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL, /* Sm */ + HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL, /* So */ + HB_UNICODE_GENERAL_CATEGORY_LINE_SEPARATOR, /* Zl */ + HB_UNICODE_GENERAL_CATEGORY_PARAGRAPH_SEPARATOR, /* Zp */ + HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR /* Zs */ +} hb_unicode_general_category_t; + +/** + * hb_unicode_combining_class_t: + * @HB_UNICODE_COMBINING_CLASS_NOT_REORDERED: Spacing and enclosing marks; also many vowel and consonant signs, even if nonspacing + * @HB_UNICODE_COMBINING_CLASS_OVERLAY: Marks which overlay a base letter or symbol + * @HB_UNICODE_COMBINING_CLASS_NUKTA: Diacritic nukta marks in Brahmi-derived scripts + * @HB_UNICODE_COMBINING_CLASS_KANA_VOICING: Hiragana/Katakana voicing marks + * @HB_UNICODE_COMBINING_CLASS_VIRAMA: Viramas + * @HB_UNICODE_COMBINING_CLASS_CCC10: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC11: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC12: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC13: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC14: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC15: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC16: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC17: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC18: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC19: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC20: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC21: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC22: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC23: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC24: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC25: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC26: [Hebrew] + * @HB_UNICODE_COMBINING_CLASS_CCC27: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC28: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC29: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC30: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC31: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC32: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC33: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC34: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC35: [Arabic] + * @HB_UNICODE_COMBINING_CLASS_CCC36: [Syriac] + * @HB_UNICODE_COMBINING_CLASS_CCC84: [Telugu] + * @HB_UNICODE_COMBINING_CLASS_CCC91: [Telugu] + * @HB_UNICODE_COMBINING_CLASS_CCC103: [Thai] + * @HB_UNICODE_COMBINING_CLASS_CCC107: [Thai] + * @HB_UNICODE_COMBINING_CLASS_CCC118: [Lao] + * @HB_UNICODE_COMBINING_CLASS_CCC122: [Lao] + * @HB_UNICODE_COMBINING_CLASS_CCC129: [Tibetan] + * @HB_UNICODE_COMBINING_CLASS_CCC130: [Tibetan] + * @HB_UNICODE_COMBINING_CLASS_CCC132: [Tibetan] Since: 7.2.0 + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT: Marks attached at the bottom left + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW: Marks attached directly below + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE: Marks attached directly above + * @HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT: Marks attached at the top right + * @HB_UNICODE_COMBINING_CLASS_BELOW_LEFT: Distinct marks at the bottom left + * @HB_UNICODE_COMBINING_CLASS_BELOW: Distinct marks directly below + * @HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT: Distinct marks at the bottom right + * @HB_UNICODE_COMBINING_CLASS_LEFT: Distinct marks to the left + * @HB_UNICODE_COMBINING_CLASS_RIGHT: Distinct marks to the right + * @HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT: Distinct marks at the top left + * @HB_UNICODE_COMBINING_CLASS_ABOVE: Distinct marks directly above + * @HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT: Distinct marks at the top right + * @HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW: Distinct marks subtending two bases + * @HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE: Distinct marks extending above two bases + * @HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT: Greek iota subscript only + * @HB_UNICODE_COMBINING_CLASS_INVALID: Invalid combining class + * + * Data type for the Canonical_Combining_Class (ccc) property + * from the Unicode Character Database. + * + * <note>Note: newer versions of Unicode may add new values. + * Client programs should be ready to handle any value in the 0..254 range + * being returned from hb_unicode_combining_class().</note> + * + **/ +typedef enum +{ + HB_UNICODE_COMBINING_CLASS_NOT_REORDERED = 0, + HB_UNICODE_COMBINING_CLASS_OVERLAY = 1, + HB_UNICODE_COMBINING_CLASS_NUKTA = 7, + HB_UNICODE_COMBINING_CLASS_KANA_VOICING = 8, + HB_UNICODE_COMBINING_CLASS_VIRAMA = 9, + + /* Hebrew */ + HB_UNICODE_COMBINING_CLASS_CCC10 = 10, + HB_UNICODE_COMBINING_CLASS_CCC11 = 11, + HB_UNICODE_COMBINING_CLASS_CCC12 = 12, + HB_UNICODE_COMBINING_CLASS_CCC13 = 13, + HB_UNICODE_COMBINING_CLASS_CCC14 = 14, + HB_UNICODE_COMBINING_CLASS_CCC15 = 15, + HB_UNICODE_COMBINING_CLASS_CCC16 = 16, + HB_UNICODE_COMBINING_CLASS_CCC17 = 17, + HB_UNICODE_COMBINING_CLASS_CCC18 = 18, + HB_UNICODE_COMBINING_CLASS_CCC19 = 19, + HB_UNICODE_COMBINING_CLASS_CCC20 = 20, + HB_UNICODE_COMBINING_CLASS_CCC21 = 21, + HB_UNICODE_COMBINING_CLASS_CCC22 = 22, + HB_UNICODE_COMBINING_CLASS_CCC23 = 23, + HB_UNICODE_COMBINING_CLASS_CCC24 = 24, + HB_UNICODE_COMBINING_CLASS_CCC25 = 25, + HB_UNICODE_COMBINING_CLASS_CCC26 = 26, + + /* Arabic */ + HB_UNICODE_COMBINING_CLASS_CCC27 = 27, + HB_UNICODE_COMBINING_CLASS_CCC28 = 28, + HB_UNICODE_COMBINING_CLASS_CCC29 = 29, + HB_UNICODE_COMBINING_CLASS_CCC30 = 30, + HB_UNICODE_COMBINING_CLASS_CCC31 = 31, + HB_UNICODE_COMBINING_CLASS_CCC32 = 32, + HB_UNICODE_COMBINING_CLASS_CCC33 = 33, + HB_UNICODE_COMBINING_CLASS_CCC34 = 34, + HB_UNICODE_COMBINING_CLASS_CCC35 = 35, + + /* Syriac */ + HB_UNICODE_COMBINING_CLASS_CCC36 = 36, + + /* Telugu */ + HB_UNICODE_COMBINING_CLASS_CCC84 = 84, + HB_UNICODE_COMBINING_CLASS_CCC91 = 91, + + /* Thai */ + HB_UNICODE_COMBINING_CLASS_CCC103 = 103, + HB_UNICODE_COMBINING_CLASS_CCC107 = 107, + + /* Lao */ + HB_UNICODE_COMBINING_CLASS_CCC118 = 118, + HB_UNICODE_COMBINING_CLASS_CCC122 = 122, + + /* Tibetan */ + HB_UNICODE_COMBINING_CLASS_CCC129 = 129, + HB_UNICODE_COMBINING_CLASS_CCC130 = 130, + HB_UNICODE_COMBINING_CLASS_CCC132 = 132, + + + HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT = 200, + HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW = 202, + HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE = 214, + HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT = 216, + HB_UNICODE_COMBINING_CLASS_BELOW_LEFT = 218, + HB_UNICODE_COMBINING_CLASS_BELOW = 220, + HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT = 222, + HB_UNICODE_COMBINING_CLASS_LEFT = 224, + HB_UNICODE_COMBINING_CLASS_RIGHT = 226, + HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT = 228, + HB_UNICODE_COMBINING_CLASS_ABOVE = 230, + HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT = 232, + HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW = 233, + HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE = 234, + + HB_UNICODE_COMBINING_CLASS_IOTA_SUBSCRIPT = 240, + + HB_UNICODE_COMBINING_CLASS_INVALID = 255 +} hb_unicode_combining_class_t; + + +/* + * hb_unicode_funcs_t + */ + +/** + * hb_unicode_funcs_t: + * + * Data type containing a set of virtual methods used for + * accessing various Unicode character properties. + * + * HarfBuzz provides a default function for each of the + * methods in #hb_unicode_funcs_t. Client programs can implement + * their own replacements for the individual Unicode functions, as + * needed, and replace the default by calling the setter for a + * method. + **/ +typedef struct hb_unicode_funcs_t hb_unicode_funcs_t; + + +/* + * just give me the best implementation you've got there. + */ +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_default (void); + + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_create (hb_unicode_funcs_t *parent); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_empty (void); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN void +hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_bool_t +hb_unicode_funcs_set_user_data (hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key, + void * data, + hb_destroy_func_t destroy, + hb_bool_t replace); + + +HB_EXTERN void * +hb_unicode_funcs_get_user_data (const hb_unicode_funcs_t *ufuncs, + hb_user_data_key_t *key); + + +HB_EXTERN void +hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_bool_t +hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs); + +HB_EXTERN hb_unicode_funcs_t * +hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs); + + +/* + * funcs + */ + +/* typedefs */ + +/** + * hb_unicode_combining_class_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. + * + * This method should retrieve the Canonical Combining Class (ccc) + * property for a specified Unicode code point. + * + * Return value: The #hb_unicode_combining_class_t of @unicode + * + **/ +typedef hb_unicode_combining_class_t (*hb_unicode_combining_class_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_general_category_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. + * + * This method should retrieve the General Category property for + * a specified Unicode code point. + * + * Return value: The #hb_unicode_general_category_t of @unicode + * + **/ +typedef hb_unicode_general_category_t (*hb_unicode_general_category_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_mirroring_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. + * + * This method should retrieve the Bi-Directional Mirroring Glyph + * code point for a specified Unicode code point. + * + * <note>Note: If a code point does not have a specified + * Bi-Directional Mirroring Glyph defined, the method should + * return the original code point.</note> + * + * Return value: The #hb_codepoint_t of the Mirroring Glyph for @unicode + * + **/ +typedef hb_codepoint_t (*hb_unicode_mirroring_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_script_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. + * + * This method should retrieve the Script property for a + * specified Unicode code point. + * + * Return value: The #hb_script_t of @unicode + * + **/ +typedef hb_script_t (*hb_unicode_script_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode, + void *user_data); + +/** + * hb_unicode_compose_func_t: + * @ufuncs: A Unicode-functions structure + * @a: The first code point to compose + * @b: The second code point to compose + * @ab: (out): The composed code point + * @user_data: user data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should compose a sequence of two input Unicode code + * points by canonical equivalence, returning the composed code + * point in a #hb_codepoint_t output parameter (if successful). + * The method must return an #hb_bool_t indicating the success + * of the composition. + * + * Return value: `true` is @a,@b composed, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_unicode_compose_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab, + void *user_data); + +/** + * hb_unicode_decompose_func_t: + * @ufuncs: A Unicode-functions structure + * @ab: The code point to decompose + * @a: (out): The first decomposed code point + * @b: (out): The second decomposed code point + * @user_data: user data pointer passed by the caller + * + * A virtual method for the #hb_unicode_funcs_t structure. + * + * This method should decompose an input Unicode code point, + * returning the two decomposed code points in #hb_codepoint_t + * output parameters (if successful). The method must return an + * #hb_bool_t indicating the success of the composition. + * + * Return value: `true` if @ab decomposed, `false` otherwise + * + **/ +typedef hb_bool_t (*hb_unicode_decompose_func_t) (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b, + void *user_data); + +/* func setters */ + +/** + * hb_unicode_funcs_set_combining_class_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_combining_class_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_combining_class_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_general_category_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_general_category_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_general_category_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_mirroring_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_mirroring_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_mirroring_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_script_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_script_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_script_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_compose_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_compose_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_compose_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_compose_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/** + * hb_unicode_funcs_set_decompose_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_func_t. + * + * Since: 0.9.2 + **/ +HB_EXTERN void +hb_unicode_funcs_set_decompose_func (hb_unicode_funcs_t *ufuncs, + hb_unicode_decompose_func_t func, + void *user_data, hb_destroy_func_t destroy); + +/* accessors */ + +/** + * hb_unicode_combining_class: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the Canonical Combining Class (ccc) property + * of code point @unicode. + * + * Return value: The #hb_unicode_combining_class_t of @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_unicode_combining_class_t +hb_unicode_combining_class (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_general_category: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the General Category (gc) property + * of code point @unicode. + * + * Return value: The #hb_unicode_general_category_t of @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_unicode_general_category_t +hb_unicode_general_category (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_mirroring: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the Bi-directional Mirroring Glyph code + * point defined for code point @unicode. + * + * Return value: The #hb_codepoint_t of the Mirroring Glyph for @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_codepoint_t +hb_unicode_mirroring (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +/** + * hb_unicode_script: + * @ufuncs: The Unicode-functions structure + * @unicode: The code point to query + * + * Retrieves the #hb_script_t script to which code + * point @unicode belongs. + * + * Return value: The #hb_script_t of @unicode + * + * Since: 0.9.2 + **/ +HB_EXTERN hb_script_t +hb_unicode_script (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t unicode); + +HB_EXTERN hb_bool_t +hb_unicode_compose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t a, + hb_codepoint_t b, + hb_codepoint_t *ab); + +HB_EXTERN hb_bool_t +hb_unicode_decompose (hb_unicode_funcs_t *ufuncs, + hb_codepoint_t ab, + hb_codepoint_t *a, + hb_codepoint_t *b); + +HB_END_DECLS + +#endif /* HB_UNICODE_H */ diff --git a/gfx/harfbuzz/src/hb-unicode.hh b/gfx/harfbuzz/src/hb-unicode.hh new file mode 100644 index 0000000000..39aaee5baa --- /dev/null +++ b/gfx/harfbuzz/src/hb-unicode.hh @@ -0,0 +1,403 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2011 Codethink Limited + * 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 + * Codethink Author(s): Ryan Lortie + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_UNICODE_HH +#define HB_UNICODE_HH + +#include "hb.hh" + + +extern HB_INTERNAL const uint8_t _hb_modified_combining_class[256]; + +/* + * hb_unicode_funcs_t + */ + +#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS \ + HB_UNICODE_FUNC_IMPLEMENT (combining_class) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (eastasian_width)) \ + HB_UNICODE_FUNC_IMPLEMENT (general_category) \ + HB_UNICODE_FUNC_IMPLEMENT (mirroring) \ + HB_UNICODE_FUNC_IMPLEMENT (script) \ + HB_UNICODE_FUNC_IMPLEMENT (compose) \ + HB_UNICODE_FUNC_IMPLEMENT (decompose) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility)) \ + /* ^--- Add new callbacks here */ + +/* Simple callbacks are those taking a hb_codepoint_t and returning a hb_codepoint_t */ +#define HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE \ + HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_combining_class_t, combining_class) \ + HB_IF_NOT_DEPRECATED (HB_UNICODE_FUNC_IMPLEMENT (unsigned int, eastasian_width)) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_unicode_general_category_t, general_category) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_codepoint_t, mirroring) \ + HB_UNICODE_FUNC_IMPLEMENT (hb_script_t, script) \ + /* ^--- Add new simple callbacks here */ + +struct hb_unicode_funcs_t +{ + hb_object_header_t header; + + hb_unicode_funcs_t *parent; + +#define HB_UNICODE_FUNC_IMPLEMENT(return_type, name) \ + return_type name (hb_codepoint_t unicode) { return func.name (this, unicode, user_data.name); } +HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE +#undef HB_UNICODE_FUNC_IMPLEMENT + + hb_bool_t compose (hb_codepoint_t a, hb_codepoint_t b, + hb_codepoint_t *ab) + { + *ab = 0; + if (unlikely (!a || !b)) return false; + return func.compose (this, a, b, ab, user_data.compose); + } + + hb_bool_t decompose (hb_codepoint_t ab, + hb_codepoint_t *a, hb_codepoint_t *b) + { + *a = ab; *b = 0; + return func.decompose (this, ab, a, b, user_data.decompose); + } + + unsigned int decompose_compatibility (hb_codepoint_t u, + hb_codepoint_t *decomposed) + { +#ifdef HB_DISABLE_DEPRECATED + unsigned int ret = 0; +#else + unsigned int ret = func.decompose_compatibility (this, u, decomposed, user_data.decompose_compatibility); +#endif + if (ret == 1 && u == decomposed[0]) { + decomposed[0] = 0; + return 0; + } + decomposed[ret] = 0; + return ret; + } + + unsigned int + modified_combining_class (hb_codepoint_t u) + { + /* Reorder SAKOT to ensure it comes after any tone marks. */ + if (unlikely (u == 0x1A60u)) return 254; + /* Reorder PADMA to ensure it comes after any vowel marks. */ + if (unlikely (u == 0x0FC6u)) return 254; + /* Reorder TSA -PHRU to reorder before U+0F74 */ + if (unlikely (u == 0x0F39u)) return 127; + + return _hb_modified_combining_class[combining_class (u)]; + } + + static hb_bool_t + is_variation_selector (hb_codepoint_t unicode) + { + /* U+180B..180D, U+180F MONGOLIAN FREE VARIATION SELECTORs are handled in the + * Arabic shaper. No need to match them here. */ + return unlikely (hb_in_ranges<hb_codepoint_t> (unicode, + 0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */ + 0xE0100u, 0xE01EFu)); /* VARIATION SELECTOR-17..256 */ + } + + /* Default_Ignorable codepoints: + * + * Note: While U+115F, U+1160, U+3164 and U+FFA0 are Default_Ignorable, + * we do NOT want to hide them, as the way Uniscribe has implemented them + * is with regular spacing glyphs, and that's the way fonts are made to work. + * As such, we make exceptions for those four. + * Also ignoring U+1BCA0..1BCA3. https://github.com/harfbuzz/harfbuzz/issues/503 + * + * Unicode 14.0: + * $ grep '; Default_Ignorable_Code_Point ' DerivedCoreProperties.txt | sed 's/;.*#/#/' + * 00AD # Cf SOFT HYPHEN + * 034F # Mn COMBINING GRAPHEME JOINER + * 061C # Cf ARABIC LETTER MARK + * 115F..1160 # Lo [2] HANGUL CHOSEONG FILLER..HANGUL JUNGSEONG FILLER + * 17B4..17B5 # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA + * 180B..180D # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE + * 180E # Cf MONGOLIAN VOWEL SEPARATOR + * 180F # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR + * 200B..200F # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK + * 202A..202E # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE + * 2060..2064 # Cf [5] WORD JOINER..INVISIBLE PLUS + * 2065 # Cn <reserved-2065> + * 2066..206F # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES + * 3164 # Lo HANGUL FILLER + * FE00..FE0F # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 + * FEFF # Cf ZERO WIDTH NO-BREAK SPACE + * FFA0 # Lo HALFWIDTH HANGUL FILLER + * FFF0..FFF8 # Cn [9] <reserved-FFF0>..<reserved-FFF8> + * 1BCA0..1BCA3 # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP + * 1D173..1D17A # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE + * E0000 # Cn <reserved-E0000> + * E0001 # Cf LANGUAGE TAG + * E0002..E001F # Cn [30] <reserved-E0002>..<reserved-E001F> + * E0020..E007F # Cf [96] TAG SPACE..CANCEL TAG + * E0080..E00FF # Cn [128] <reserved-E0080>..<reserved-E00FF> + * E0100..E01EF # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 + * E01F0..E0FFF # Cn [3600] <reserved-E01F0>..<reserved-E0FFF> + */ + static hb_bool_t + is_default_ignorable (hb_codepoint_t ch) + { + hb_codepoint_t plane = ch >> 16; + if (likely (plane == 0)) + { + /* BMP */ + hb_codepoint_t page = ch >> 8; + switch (page) { + case 0x00: return unlikely (ch == 0x00ADu); + case 0x03: return unlikely (ch == 0x034Fu); + case 0x06: return unlikely (ch == 0x061Cu); + case 0x17: return hb_in_range<hb_codepoint_t> (ch, 0x17B4u, 0x17B5u); + case 0x18: return hb_in_range<hb_codepoint_t> (ch, 0x180Bu, 0x180Eu); + case 0x20: return hb_in_ranges<hb_codepoint_t> (ch, 0x200Bu, 0x200Fu, + 0x202Au, 0x202Eu, + 0x2060u, 0x206Fu); + case 0xFE: return hb_in_range<hb_codepoint_t> (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu; + case 0xFF: return hb_in_range<hb_codepoint_t> (ch, 0xFFF0u, 0xFFF8u); + default: return false; + } + } + else + { + /* Other planes */ + switch (plane) { + case 0x01: return hb_in_range<hb_codepoint_t> (ch, 0x1D173u, 0x1D17Au); + case 0x0E: return hb_in_range<hb_codepoint_t> (ch, 0xE0000u, 0xE0FFFu); + default: return false; + } + } + } + + /* Space estimates based on: + * https://unicode.org/charts/PDF/U2000.pdf + * https://docs.microsoft.com/en-us/typography/develop/character-design-standards/whitespace + */ + enum space_t { + NOT_SPACE = 0, + SPACE_EM = 1, + SPACE_EM_2 = 2, + SPACE_EM_3 = 3, + SPACE_EM_4 = 4, + SPACE_EM_5 = 5, + SPACE_EM_6 = 6, + SPACE_EM_16 = 16, + SPACE_4_EM_18, /* 4/18th of an EM! */ + SPACE, + SPACE_FIGURE, + SPACE_PUNCTUATION, + SPACE_NARROW, + }; + static space_t + space_fallback_type (hb_codepoint_t u) + { + switch (u) + { + /* All GC=Zs chars that can use a fallback. */ + default: return NOT_SPACE; /* U+1680 OGHAM SPACE MARK */ + case 0x0020u: return SPACE; /* U+0020 SPACE */ + case 0x00A0u: return SPACE; /* U+00A0 NO-BREAK SPACE */ + case 0x2000u: return SPACE_EM_2; /* U+2000 EN QUAD */ + case 0x2001u: return SPACE_EM; /* U+2001 EM QUAD */ + case 0x2002u: return SPACE_EM_2; /* U+2002 EN SPACE */ + case 0x2003u: return SPACE_EM; /* U+2003 EM SPACE */ + case 0x2004u: return SPACE_EM_3; /* U+2004 THREE-PER-EM SPACE */ + case 0x2005u: return SPACE_EM_4; /* U+2005 FOUR-PER-EM SPACE */ + case 0x2006u: return SPACE_EM_6; /* U+2006 SIX-PER-EM SPACE */ + case 0x2007u: return SPACE_FIGURE; /* U+2007 FIGURE SPACE */ + case 0x2008u: return SPACE_PUNCTUATION; /* U+2008 PUNCTUATION SPACE */ + case 0x2009u: return SPACE_EM_5; /* U+2009 THIN SPACE */ + case 0x200Au: return SPACE_EM_16; /* U+200A HAIR SPACE */ + case 0x202Fu: return SPACE_NARROW; /* U+202F NARROW NO-BREAK SPACE */ + case 0x205Fu: return SPACE_4_EM_18; /* U+205F MEDIUM MATHEMATICAL SPACE */ + case 0x3000u: return SPACE_EM; /* U+3000 IDEOGRAPHIC SPACE */ + } + } + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } func; + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) void *name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } user_data; + + struct { +#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_destroy_func_t name; + HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS +#undef HB_UNICODE_FUNC_IMPLEMENT + } destroy; +}; +DECLARE_NULL_INSTANCE (hb_unicode_funcs_t); + + +/* + * Modified combining marks + */ + +/* Hebrew + * + * We permute the "fixed-position" classes 10-26 into the order + * described in the SBL Hebrew manual: + * + * https://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf + * + * (as recommended by: + * https://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering/msg22823/) + * + * More details here: + * https://bugzilla.mozilla.org/show_bug.cgi?id=662055 + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC10 22 /* sheva */ +#define HB_MODIFIED_COMBINING_CLASS_CCC11 15 /* hataf segol */ +#define HB_MODIFIED_COMBINING_CLASS_CCC12 16 /* hataf patah */ +#define HB_MODIFIED_COMBINING_CLASS_CCC13 17 /* hataf qamats */ +#define HB_MODIFIED_COMBINING_CLASS_CCC14 23 /* hiriq */ +#define HB_MODIFIED_COMBINING_CLASS_CCC15 18 /* tsere */ +#define HB_MODIFIED_COMBINING_CLASS_CCC16 19 /* segol */ +#define HB_MODIFIED_COMBINING_CLASS_CCC17 20 /* patah */ +#define HB_MODIFIED_COMBINING_CLASS_CCC18 21 /* qamats & qamats qatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC19 14 /* holam & holam haser for vav*/ +#define HB_MODIFIED_COMBINING_CLASS_CCC20 24 /* qubuts */ +#define HB_MODIFIED_COMBINING_CLASS_CCC21 12 /* dagesh */ +#define HB_MODIFIED_COMBINING_CLASS_CCC22 25 /* meteg */ +#define HB_MODIFIED_COMBINING_CLASS_CCC23 13 /* rafe */ +#define HB_MODIFIED_COMBINING_CLASS_CCC24 10 /* shin dot */ +#define HB_MODIFIED_COMBINING_CLASS_CCC25 11 /* sin dot */ +#define HB_MODIFIED_COMBINING_CLASS_CCC26 26 /* point varika */ + +/* + * Arabic + * + * Modify to move Shadda (ccc=33) before other marks. See: + * https://unicode.org/faq/normalization.html#8 + * https://unicode.org/faq/normalization.html#9 + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC27 28 /* fathatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC28 29 /* dammatan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC29 30 /* kasratan */ +#define HB_MODIFIED_COMBINING_CLASS_CCC30 31 /* fatha */ +#define HB_MODIFIED_COMBINING_CLASS_CCC31 32 /* damma */ +#define HB_MODIFIED_COMBINING_CLASS_CCC32 33 /* kasra */ +#define HB_MODIFIED_COMBINING_CLASS_CCC33 27 /* shadda */ +#define HB_MODIFIED_COMBINING_CLASS_CCC34 34 /* sukun */ +#define HB_MODIFIED_COMBINING_CLASS_CCC35 35 /* superscript alef */ + +/* Syriac */ +#define HB_MODIFIED_COMBINING_CLASS_CCC36 36 /* superscript alaph */ + +/* Telugu + * + * Modify Telugu length marks (ccc=84, ccc=91). + * These are the only matras in the main Indic scripts range that have + * a non-zero ccc. That makes them reorder with the Halant (ccc=9). + * Assign 4 and 5, which are otherwise unassigned. + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC84 4 /* length mark */ +#define HB_MODIFIED_COMBINING_CLASS_CCC91 5 /* ai length mark */ + +/* Thai + * + * Modify U+0E38 and U+0E39 (ccc=103) to be reordered before U+0E3A (ccc=9). + * Assign 3, which is unassigned otherwise. + * Uniscribe does this reordering too. + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC103 3 /* sara u / sara uu */ +#define HB_MODIFIED_COMBINING_CLASS_CCC107 107 /* mai * */ + +/* Lao */ +#define HB_MODIFIED_COMBINING_CLASS_CCC118 118 /* sign u / sign uu */ +#define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */ + +/* Tibetan + * + * In case of multiple vowel-signs, use u first (but after achung) + * this allows Dzongkha multi-vowel shortcuts to render correctly + */ +#define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */ +#define HB_MODIFIED_COMBINING_CLASS_CCC130 132 /* sign i */ +#define HB_MODIFIED_COMBINING_CLASS_CCC132 131 /* sign u */ + +/* Misc */ + +#define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \ + (FLAG_UNSAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))) + +#define HB_UNICODE_GENERAL_CATEGORY_IS_LETTER(gen_cat) \ + (FLAG_UNSAFE (gen_cat) & \ + (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) | \ + FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER))) + +/* + * Ranges, used for bsearch tables. + */ + +struct hb_unicode_range_t +{ + static int + cmp (const void *_key, const void *_item) + { + hb_codepoint_t cp = *((hb_codepoint_t *) _key); + const hb_unicode_range_t *range = (hb_unicode_range_t *) _item; + + if (cp < range->start) + return -1; + else if (cp <= range->end) + return 0; + else + return +1; + } + + hb_codepoint_t start; + hb_codepoint_t end; +}; + +/* + * Emoji. + */ + +HB_INTERNAL bool +_hb_unicode_is_emoji_Extended_Pictographic (hb_codepoint_t cp); + + +extern "C" HB_INTERNAL hb_unicode_funcs_t *hb_ucd_get_unicode_funcs (); + + +#endif /* HB_UNICODE_HH */ diff --git a/gfx/harfbuzz/src/hb-uniscribe.cc b/gfx/harfbuzz/src/hb-uniscribe.cc new file mode 100644 index 0000000000..1b8ac367e1 --- /dev/null +++ b/gfx/harfbuzz/src/hb-uniscribe.cc @@ -0,0 +1,889 @@ +/* + * Copyright © 2011,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. + * + * Google Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#ifdef HAVE_UNISCRIBE + +#ifdef HB_NO_OT_TAG +#error "Cannot compile 'uniscribe' shaper with HB_NO_OT_TAG." +#endif + +#include "hb-shaper-impl.hh" + +#include <windows.h> +#include <usp10.h> +#include <rpc.h> + +#ifndef E_NOT_SUFFICIENT_BUFFER +#define E_NOT_SUFFICIENT_BUFFER HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER) +#endif + +#include "hb-uniscribe.h" + +#include "hb-ms-feature-ranges.hh" +#include "hb-open-file.hh" +#include "hb-ot-name-table.hh" +#include "hb-ot-layout.h" + + +/** + * SECTION:hb-uniscribe + * @title: hb-uniscribe + * @short_description: Windows integration + * @include: hb-uniscribe.h + * + * Functions for using HarfBuzz with Windows fonts. + **/ + +typedef HRESULT (WINAPI *SIOT) /*ScriptItemizeOpenType*/( + const WCHAR *pwcInChars, + int cInChars, + int cMaxItems, + const SCRIPT_CONTROL *psControl, + const SCRIPT_STATE *psState, + SCRIPT_ITEM *pItems, + OPENTYPE_TAG *pScriptTags, + int *pcItems +); + +typedef HRESULT (WINAPI *SSOT) /*ScriptShapeOpenType*/( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + int cChars, + int cMaxGlyphs, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + WORD *pwOutGlyphs, + SCRIPT_GLYPHPROP *pOutGlyphProps, + int *pcGlyphs +); + +typedef HRESULT (WINAPI *SPOT) /*ScriptPlaceOpenType*/( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + int cChars, + const WORD *pwGlyphs, + const SCRIPT_GLYPHPROP *pGlyphProps, + int cGlyphs, + int *piAdvance, + GOFFSET *pGoffset, + ABC *pABC +); + + +/* Fallback implementations. */ + +static HRESULT WINAPI +hb_ScriptItemizeOpenType( + const WCHAR *pwcInChars, + int cInChars, + int cMaxItems, + const SCRIPT_CONTROL *psControl, + const SCRIPT_STATE *psState, + SCRIPT_ITEM *pItems, + OPENTYPE_TAG *pScriptTags, + int *pcItems +) +{ +{ + return ScriptItemize (pwcInChars, + cInChars, + cMaxItems, + psControl, + psState, + pItems, + pcItems); +} +} + +static HRESULT WINAPI +hb_ScriptShapeOpenType( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + int cChars, + int cMaxGlyphs, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + WORD *pwOutGlyphs, + SCRIPT_GLYPHPROP *pOutGlyphProps, + int *pcGlyphs +) +{ + SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pOutGlyphProps; + return ScriptShape (hdc, + psc, + pwcChars, + cChars, + cMaxGlyphs, + psa, + pwOutGlyphs, + pwLogClust, + psva, + pcGlyphs); +} + +static HRESULT WINAPI +hb_ScriptPlaceOpenType( + HDC hdc, + SCRIPT_CACHE *psc, + SCRIPT_ANALYSIS *psa, + OPENTYPE_TAG tagScript, + OPENTYPE_TAG tagLangSys, + int *rcRangeChars, + TEXTRANGE_PROPERTIES **rpRangeProperties, + int cRanges, + const WCHAR *pwcChars, + WORD *pwLogClust, + SCRIPT_CHARPROP *pCharProps, + int cChars, + const WORD *pwGlyphs, + const SCRIPT_GLYPHPROP *pGlyphProps, + int cGlyphs, + int *piAdvance, + GOFFSET *pGoffset, + ABC *pABC +) +{ + SCRIPT_VISATTR *psva = (SCRIPT_VISATTR *) pGlyphProps; + return ScriptPlace (hdc, + psc, + pwGlyphs, + cGlyphs, + psva, + psa, + piAdvance, + pGoffset, + pABC); +} + + +struct hb_uniscribe_shaper_funcs_t +{ + SIOT ScriptItemizeOpenType; + SSOT ScriptShapeOpenType; + SPOT ScriptPlaceOpenType; + + void init () + { + HMODULE hinstLib; + this->ScriptItemizeOpenType = nullptr; + this->ScriptShapeOpenType = nullptr; + this->ScriptPlaceOpenType = nullptr; + + hinstLib = GetModuleHandle (TEXT ("usp10.dll")); + if (hinstLib) + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" + this->ScriptItemizeOpenType = (SIOT) GetProcAddress (hinstLib, "ScriptItemizeOpenType"); + this->ScriptShapeOpenType = (SSOT) GetProcAddress (hinstLib, "ScriptShapeOpenType"); + this->ScriptPlaceOpenType = (SPOT) GetProcAddress (hinstLib, "ScriptPlaceOpenType"); +#pragma GCC diagnostic pop + } + if (!this->ScriptItemizeOpenType || + !this->ScriptShapeOpenType || + !this->ScriptPlaceOpenType) + { + DEBUG_MSG (UNISCRIBE, nullptr, "OpenType versions of functions not found; falling back."); + this->ScriptItemizeOpenType = hb_ScriptItemizeOpenType; + this->ScriptShapeOpenType = hb_ScriptShapeOpenType; + this->ScriptPlaceOpenType = hb_ScriptPlaceOpenType; + } + } +}; + +static inline void free_static_uniscribe_shaper_funcs (); + +static struct hb_uniscribe_shaper_funcs_lazy_loader_t : hb_lazy_loader_t<hb_uniscribe_shaper_funcs_t, + hb_uniscribe_shaper_funcs_lazy_loader_t> +{ + static hb_uniscribe_shaper_funcs_t *create () + { + hb_uniscribe_shaper_funcs_t *funcs = (hb_uniscribe_shaper_funcs_t *) hb_calloc (1, sizeof (hb_uniscribe_shaper_funcs_t)); + if (unlikely (!funcs)) + return nullptr; + + funcs->init (); + + hb_atexit (free_static_uniscribe_shaper_funcs); + + return funcs; + } + static void destroy (hb_uniscribe_shaper_funcs_t *p) + { + hb_free ((void *) p); + } + static hb_uniscribe_shaper_funcs_t *get_null () + { + return nullptr; + } +} static_uniscribe_shaper_funcs; + +static inline +void free_static_uniscribe_shaper_funcs () +{ + static_uniscribe_shaper_funcs.free_instance (); +} + +static hb_uniscribe_shaper_funcs_t * +hb_uniscribe_shaper_get_funcs () +{ + return static_uniscribe_shaper_funcs.get_unconst (); +} + + +/* + * shaper face data + */ + +struct hb_uniscribe_face_data_t { + HANDLE fh; + hb_uniscribe_shaper_funcs_t *funcs; + wchar_t face_name[LF_FACESIZE]; +}; + +/* face_name should point to a wchar_t[LF_FACESIZE] object. */ +static void +_hb_generate_unique_face_name (wchar_t *face_name, unsigned int *plen) +{ + /* We'll create a private name for the font from a UUID using a simple, + * somewhat base64-like encoding scheme */ + const char *enc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + UUID id; + UuidCreate ((UUID*) &id); + static_assert ((2 + 3 * (16/2) < LF_FACESIZE), ""); + unsigned int name_str_len = 0; + face_name[name_str_len++] = 'F'; + face_name[name_str_len++] = '_'; + unsigned char *p = (unsigned char *) &id; + for (unsigned int i = 0; i < 16; i += 2) + { + /* Spread the 16 bits from two bytes of the UUID across three chars of face_name, + * using the bits in groups of 5,5,6 to select chars from enc. + * This will generate 24 characters; with the 'F_' prefix we already provided, + * the name will be 26 chars (plus the NUL terminator), so will always fit within + * face_name (LF_FACESIZE = 32). */ + face_name[name_str_len++] = enc[p[i] >> 3]; + face_name[name_str_len++] = enc[((p[i] << 2) | (p[i + 1] >> 6)) & 0x1f]; + face_name[name_str_len++] = enc[p[i + 1] & 0x3f]; + } + face_name[name_str_len] = 0; + if (plen) + *plen = name_str_len; +} + +/* Destroys blob. */ +static hb_blob_t * +_hb_rename_font (hb_blob_t *blob, wchar_t *new_name) +{ + /* Create a copy of the font data, with the 'name' table replaced by a + * table that names the font with our private F_* name created above. + * For simplicity, we just append a new 'name' table and update the + * sfnt directory; the original table is left in place, but unused. + * + * The new table will contain just 5 name IDs: family, style, unique, + * full, PS. All of them point to the same name data with our unique name. + */ + + blob = hb_sanitize_context_t ().sanitize_blob<OT::OpenTypeFontFile> (blob); + + unsigned int length, new_length, name_str_len; + const char *orig_sfnt_data = hb_blob_get_data (blob, &length); + + _hb_generate_unique_face_name (new_name, &name_str_len); + + static const uint16_t name_IDs[] = { 1, 2, 3, 4, 6 }; + + unsigned int name_table_length = OT::name::min_size + + ARRAY_LENGTH (name_IDs) * OT::NameRecord::static_size + + name_str_len * 2; /* for name data in UTF16BE form */ + unsigned int padded_name_table_length = ((name_table_length + 3) & ~3); + unsigned int name_table_offset = (length + 3) & ~3; + + new_length = name_table_offset + padded_name_table_length; + void *new_sfnt_data = hb_calloc (1, new_length); + if (!new_sfnt_data) + { + hb_blob_destroy (blob); + return nullptr; + } + + hb_memcpy(new_sfnt_data, orig_sfnt_data, length); + + OT::name &name = StructAtOffset<OT::name> (new_sfnt_data, name_table_offset); + name.format = 0; + name.count = ARRAY_LENGTH (name_IDs); + name.stringOffset = name.get_size (); + for (unsigned int i = 0; i < ARRAY_LENGTH (name_IDs); i++) + { + OT::NameRecord &record = name.nameRecordZ[i]; + record.platformID = 3; + record.encodingID = 1; + record.languageID = 0x0409u; /* English */ + record.nameID = name_IDs[i]; + record.length = name_str_len * 2; + record.offset = 0; + } + + /* Copy string data from new_name, converting wchar_t to UTF16BE. */ + unsigned char *p = &StructAfter<unsigned char> (name); + for (unsigned int i = 0; i < name_str_len; i++) + { + *p++ = new_name[i] >> 8; + *p++ = new_name[i] & 0xff; + } + + /* Adjust name table entry to point to new name table */ + const OT::OpenTypeFontFile &file = * (OT::OpenTypeFontFile *) (new_sfnt_data); + unsigned int face_count = file.get_face_count (); + for (unsigned int face_index = 0; face_index < face_count; face_index++) + { + /* Note: doing multiple edits (ie. TTC) can be unsafe. There may be + * toe-stepping. But we don't really care. */ + const OT::OpenTypeFontFace &face = file.get_face (face_index); + unsigned int index; + if (face.find_table_index (HB_OT_TAG_name, &index)) + { + OT::TableRecord &record = const_cast<OT::TableRecord &> (face.get_table (index)); + record.checkSum.set_for_data (&name, padded_name_table_length); + record.offset = name_table_offset; + record.length = name_table_length; + } + else if (face_index == 0) /* Fail if first face doesn't have 'name' table. */ + { + hb_free (new_sfnt_data); + hb_blob_destroy (blob); + return nullptr; + } + } + + /* The checkSumAdjustment field in the 'head' table is now wrong, + * but that doesn't actually seem to cause any problems so we don't + * bother. */ + + hb_blob_destroy (blob); + return hb_blob_create ((const char *) new_sfnt_data, new_length, + HB_MEMORY_MODE_WRITABLE, new_sfnt_data, hb_free); +} + +hb_uniscribe_face_data_t * +_hb_uniscribe_shaper_face_data_create (hb_face_t *face) +{ + hb_uniscribe_face_data_t *data = (hb_uniscribe_face_data_t *) hb_calloc (1, sizeof (hb_uniscribe_face_data_t)); + if (unlikely (!data)) + return nullptr; + + data->funcs = hb_uniscribe_shaper_get_funcs (); + if (unlikely (!data->funcs)) + { + hb_free (data); + return nullptr; + } + + hb_blob_t *blob = hb_face_reference_blob (face); + if (unlikely (!hb_blob_get_length (blob))) + DEBUG_MSG (UNISCRIBE, face, "Face has empty blob"); + + blob = _hb_rename_font (blob, data->face_name); + if (unlikely (!blob)) + { + hb_free (data); + return nullptr; + } + + DWORD num_fonts_installed; + data->fh = AddFontMemResourceEx ((void *) hb_blob_get_data (blob, nullptr), + hb_blob_get_length (blob), + 0, &num_fonts_installed); + if (unlikely (!data->fh)) + { + DEBUG_MSG (UNISCRIBE, face, "Face AddFontMemResourceEx() failed"); + hb_free (data); + return nullptr; + } + + return data; +} + +void +_hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_face_data_t *data) +{ + RemoveFontMemResourceEx (data->fh); + hb_free (data); +} + + +/* + * shaper font data + */ + +struct hb_uniscribe_font_data_t +{ + HDC hdc; + mutable LOGFONTW log_font; + HFONT hfont; + mutable SCRIPT_CACHE script_cache; + double x_mult, y_mult; /* From LOGFONT space to HB space. */ +}; + +static bool +populate_log_font (LOGFONTW *lf, + hb_font_t *font, + unsigned int font_size) +{ + hb_memset (lf, 0, sizeof (*lf)); + lf->lfHeight = - (int) font_size; + lf->lfCharSet = DEFAULT_CHARSET; + + hb_memcpy (lf->lfFaceName, font->face->data.uniscribe->face_name, sizeof (lf->lfFaceName)); + + return true; +} + +hb_uniscribe_font_data_t * +_hb_uniscribe_shaper_font_data_create (hb_font_t *font) +{ + hb_uniscribe_font_data_t *data = (hb_uniscribe_font_data_t *) hb_calloc (1, sizeof (hb_uniscribe_font_data_t)); + if (unlikely (!data)) + return nullptr; + + int font_size = font->face->get_upem (); /* Default... */ + /* No idea if the following is even a good idea. */ + if (font->y_ppem) + font_size = font->y_ppem; + + if (font_size < 0) + font_size = -font_size; + data->x_mult = (double) font->x_scale / font_size; + data->y_mult = (double) font->y_scale / font_size; + + data->hdc = GetDC (nullptr); + + if (unlikely (!populate_log_font (&data->log_font, font, font_size))) { + DEBUG_MSG (UNISCRIBE, font, "Font populate_log_font() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return nullptr; + } + + data->hfont = CreateFontIndirectW (&data->log_font); + if (unlikely (!data->hfont)) { + DEBUG_MSG (UNISCRIBE, font, "Font CreateFontIndirectW() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return nullptr; + } + + if (!SelectObject (data->hdc, data->hfont)) { + DEBUG_MSG (UNISCRIBE, font, "Font SelectObject() failed"); + _hb_uniscribe_shaper_font_data_destroy (data); + return nullptr; + } + + return data; +} + +void +_hb_uniscribe_shaper_font_data_destroy (hb_uniscribe_font_data_t *data) +{ + if (data->hdc) + ReleaseDC (nullptr, data->hdc); + if (data->hfont) + DeleteObject (data->hfont); + if (data->script_cache) + ScriptFreeCache (&data->script_cache); + hb_free (data); +} + +/** + * hb_uniscribe_font_get_logfontw: + * @font: The #hb_font_t to work upon + * + * Fetches the LOGFONTW structure that corresponds to the + * specified #hb_font_t font. + * + * Return value: a pointer to the LOGFONTW retrieved + * + **/ +LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font) +{ + const hb_uniscribe_font_data_t *data = font->data.uniscribe; + return data ? &data->log_font : nullptr; +} + +/** + * hb_uniscribe_font_get_hfont: + * @font: The #hb_font_t to work upon + * + * Fetches the HFONT handle that corresponds to the + * specified #hb_font_t font. + * + * Return value: the HFONT retreieved + * + **/ +HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font) +{ + const hb_uniscribe_font_data_t *data = font->data.uniscribe; + return data ? data->hfont : nullptr; +} + + +/* + * shaper + */ + + +hb_bool_t +_hb_uniscribe_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_uniscribe_face_data_t *face_data = face->data.uniscribe; + const hb_uniscribe_font_data_t *font_data = font->data.uniscribe; + hb_uniscribe_shaper_funcs_t *funcs = face_data->funcs; + +#define FAIL(...) \ + HB_STMT_START { \ + DEBUG_MSG (UNISCRIBE, nullptr, __VA_ARGS__); \ + return false; \ + } HB_STMT_END + + HRESULT hr; + +retry: + + 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, pchars, 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)) + pchars[chars_len++] = c; + else if (unlikely (c > 0x10FFFFu)) + pchars[chars_len++] = 0xFFFDu; + else { + pchars[chars_len++] = 0xD800u + ((c - 0x10000u) >> 10); + pchars[chars_len++] = 0xDC00u + ((c - 0x10000u) & ((1u << 10) - 1)); + } + } + + ALLOCATE_ARRAY (WORD, log_clusters, chars_len); + ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len); + + if (num_features) + { + /* 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. */ + } + } + + /* 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 (SCRIPT_GLYPHPROP) + + sizeof (int) + + sizeof (GOFFSET) + + sizeof (uint32_t)); + + ALLOCATE_ARRAY (WORD, glyphs, glyphs_size); + ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size); + ALLOCATE_ARRAY (int, advances, glyphs_size); + ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size); + ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); + + /* Note: + * We can't touch the contents of glyph_props. Our fallback + * implementations of Shape and Place functions use that buffer + * by casting it to a different type. It works because they + * both agree about it, but if we want to access it here we + * need address that issue first. + */ + +#undef ALLOCATE_ARRAY + +#define MAX_ITEMS 256 + + SCRIPT_ITEM items[MAX_ITEMS + 1]; + SCRIPT_CONTROL bidi_control = {0}; + SCRIPT_STATE bidi_state = {0}; + ULONG script_tags[MAX_ITEMS]; + int item_count; + + /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */ + //bidi_control.fMergeNeutralItems = true; + *(uint32_t*)&bidi_control |= 1u<<24; + + bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1; + bidi_state.fOverrideDirection = 1; + + hr = funcs->ScriptItemizeOpenType (pchars, + chars_len, + MAX_ITEMS, + &bidi_control, + &bidi_state, + items, + script_tags, + &item_count); + if (unlikely (FAILED (hr))) + FAIL ("ScriptItemizeOpenType() failed: 0x%08lx", (unsigned long) hr); + +#undef MAX_ITEMS + + hb_tag_t lang_tag; + unsigned int lang_count = 1; + hb_ot_tags_from_script_and_language (buffer->props.script, + buffer->props.language, + nullptr, nullptr, + &lang_count, &lang_tag); + OPENTYPE_TAG language_tag = hb_uint32_swap (lang_count ? lang_tag : HB_TAG_NONE); + + /* + * Set up features. + */ + static_assert ((sizeof (TEXTRANGE_PROPERTIES) == sizeof (hb_ms_features_t)), ""); + static_assert ((sizeof (OPENTYPE_FEATURE_RECORD) == sizeof (hb_ms_feature_t)), ""); + hb_vector_t<hb_ms_feature_t> feature_records; + hb_vector_t<hb_ms_range_record_t> range_records; + bool has_features = false; + if (num_features) + has_features = hb_ms_setup_features (features, + num_features, + feature_records, + range_records); + + hb_vector_t<hb_ms_features_t*> range_properties; + hb_vector_t<uint32_t> range_char_counts; + + unsigned int glyphs_offset = 0; + unsigned int glyphs_len; + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + for (int i = 0; i < item_count; i++) + { + unsigned int chars_offset = items[i].iCharPos; + unsigned int item_chars_len = items[i + 1].iCharPos - chars_offset; + + if (has_features) + hb_ms_make_feature_ranges (feature_records, + range_records, + item_chars_len, + chars_offset, + log_clusters, + range_properties, + range_char_counts); + + /* Asking for glyphs in logical order circumvents at least + * one bug in Uniscribe. */ + items[i].a.fLogicalOrder = true; + + retry_shape: + hr = funcs->ScriptShapeOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tags[i], + language_tag, + (int *) range_char_counts.arrayZ, + (TEXTRANGE_PROPERTIES**) range_properties.arrayZ, + range_properties.length, + pchars + chars_offset, + item_chars_len, + glyphs_size - glyphs_offset, + /* out */ + log_clusters + chars_offset, + char_props + chars_offset, + glyphs + glyphs_offset, + glyph_props + glyphs_offset, + (int *) &glyphs_len); + + if (unlikely (items[i].a.fNoGlyphIndex)) + FAIL ("ScriptShapeOpenType() set fNoGlyphIndex"); + if (unlikely (hr == E_OUTOFMEMORY || hr == E_NOT_SUFFICIENT_BUFFER)) + { + if (unlikely (!buffer->ensure (buffer->allocated * 2))) + FAIL ("Buffer resize failed"); + goto retry; + } + if (unlikely (hr == USP_E_SCRIPT_NOT_IN_FONT)) + { + if (items[i].a.eScript == SCRIPT_UNDEFINED) + FAIL ("ScriptShapeOpenType() failed: Font doesn't support script"); + items[i].a.eScript = SCRIPT_UNDEFINED; + goto retry_shape; + } + if (unlikely (FAILED (hr))) + { + FAIL ("ScriptShapeOpenType() failed: 0x%08lx", (unsigned long) hr); + } + + for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++) + log_clusters[j] += glyphs_offset; + + hr = funcs->ScriptPlaceOpenType (font_data->hdc, + &font_data->script_cache, + &items[i].a, + script_tags[i], + language_tag, + (int *) range_char_counts.arrayZ, + (TEXTRANGE_PROPERTIES**) range_properties.arrayZ, + range_properties.length, + pchars + chars_offset, + log_clusters + chars_offset, + char_props + chars_offset, + item_chars_len, + glyphs + glyphs_offset, + glyph_props + glyphs_offset, + glyphs_len, + /* out */ + advances + glyphs_offset, + offsets + glyphs_offset, + nullptr); + if (unlikely (FAILED (hr))) + FAIL ("ScriptPlaceOpenType() failed: 0x%08lx", (unsigned long) hr); + + if (DEBUG_ENABLED (UNISCRIBE)) + fprintf (stderr, "Item %d RTL %d LayoutRTL %d LogicalOrder %d ScriptTag %c%c%c%c\n", + i, + items[i].a.fRTL, + items[i].a.fLayoutRTL, + items[i].a.fLogicalOrder, + HB_UNTAG (hb_uint32_swap (script_tags[i]))); + + glyphs_offset += glyphs_len; + } + glyphs_len = glyphs_offset; + + /* 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 < glyphs_len; 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 < glyphs_len; i++) + if (vis_clusters[i] == (uint32_t) -1) + vis_clusters[i] = vis_clusters[i - 1]; + +#undef utf16_index + + if (unlikely (!buffer->ensure (glyphs_len))) + FAIL ("Buffer in error"); + +#undef FAIL + + /* Set glyph infos */ + buffer->len = 0; + for (unsigned int i = 0; i < glyphs_len; i++) + { + hb_glyph_info_t *info = &buffer->info[buffer->len++]; + + info->codepoint = glyphs[i]; + info->cluster = vis_clusters[i]; + + /* The rest is crap. Let's store position info there for now. */ + info->mask = advances[i]; + info->var1.i32 = offsets[i].du; + info->var2.i32 = offsets[i].dv; + } + + /* Set glyph positions */ + buffer->clear_positions (); + double x_mult = font_data->x_mult, y_mult = font_data->y_mult; + for (unsigned int i = 0; i < glyphs_len; 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 * (backward ? -info->var1.i32 : info->var1.i32); + pos->y_offset = y_mult * info->var2.i32; + } + + if (backward) + hb_buffer_reverse (buffer); + + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + + /* Wow, done! */ + return true; +} + + +#endif diff --git a/gfx/harfbuzz/src/hb-uniscribe.h b/gfx/harfbuzz/src/hb-uniscribe.h new file mode 100644 index 0000000000..4e4ef9986a --- /dev/null +++ b/gfx/harfbuzz/src/hb-uniscribe.h @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#ifndef HB_UNISCRIBE_H +#define HB_UNISCRIBE_H + +#include "hb.h" + +#include <windows.h> + +HB_BEGIN_DECLS + + +HB_EXTERN LOGFONTW * +hb_uniscribe_font_get_logfontw (hb_font_t *font); + +HB_EXTERN HFONT +hb_uniscribe_font_get_hfont (hb_font_t *font); + + +HB_END_DECLS + +#endif /* HB_UNISCRIBE_H */ diff --git a/gfx/harfbuzz/src/hb-utf.hh b/gfx/harfbuzz/src/hb-utf.hh new file mode 100644 index 0000000000..1120bd1ccc --- /dev/null +++ b/gfx/harfbuzz/src/hb-utf.hh @@ -0,0 +1,481 @@ +/* + * Copyright © 2011,2012,2014 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_UTF_HH +#define HB_UTF_HH + +#include "hb.hh" + +#include "hb-open-type.hh" + + +struct hb_utf8_t +{ + typedef uint8_t codepoint_t; + static constexpr unsigned max_len = 4; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + /* Written to only accept well-formed sequences. + * Based on ideas from ICU's U8_NEXT. + * Generates one "replacement" for each ill-formed byte. */ + + hb_codepoint_t c = *text++; + + if (c > 0x7Fu) + { + if (hb_in_range<hb_codepoint_t> (c, 0xC2u, 0xDFu)) /* Two-byte */ + { + unsigned int t1; + if (likely (text < end && + (t1 = text[0] - 0x80u) <= 0x3Fu)) + { + c = ((c&0x1Fu)<<6) | t1; + text++; + } + else + goto error; + } + else if (hb_in_range<hb_codepoint_t> (c, 0xE0u, 0xEFu)) /* Three-byte */ + { + unsigned int t1, t2; + if (likely (1 < end - text && + (t1 = text[0] - 0x80u) <= 0x3Fu && + (t2 = text[1] - 0x80u) <= 0x3Fu)) + { + c = ((c&0xFu)<<12) | (t1<<6) | t2; + if (unlikely (c < 0x0800u || hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu))) + goto error; + text += 2; + } + else + goto error; + } + else if (hb_in_range<hb_codepoint_t> (c, 0xF0u, 0xF4u)) /* Four-byte */ + { + unsigned int t1, t2, t3; + if (likely (2 < end - text && + (t1 = text[0] - 0x80u) <= 0x3Fu && + (t2 = text[1] - 0x80u) <= 0x3Fu && + (t3 = text[2] - 0x80u) <= 0x3Fu)) + { + c = ((c&0x7u)<<18) | (t1<<12) | (t2<<6) | t3; + if (unlikely (!hb_in_range<hb_codepoint_t> (c, 0x10000u, 0x10FFFFu))) + goto error; + text += 3; + } + else + goto error; + } + else + goto error; + } + + *unicode = c; + return text; + + error: + *unicode = replacement; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + const codepoint_t *end = text--; + while (start < text && (*text & 0xc0) == 0x80 && end - text < 4) + text--; + + if (likely (next (text, end, unicode, replacement) == end)) + return text; + + *unicode = replacement; + return end - 1; + } + + static unsigned int + strlen (const codepoint_t *text) + { return ::strlen ((const char *) text); } + + static unsigned int + encode_len (hb_codepoint_t unicode) + { + if (unicode < 0x0080u) return 1; + if (unicode < 0x0800u) return 2; + if (unicode < 0x10000u) return 3; + if (unicode < 0x110000u) return 4; + return 3; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu))) + unicode = 0xFFFDu; + if (unicode < 0x0080u) + *text++ = unicode; + else if (unicode < 0x0800u) + { + if (end - text >= 2) + { + *text++ = 0xC0u + (0x1Fu & (unicode >> 6)); + *text++ = 0x80u + (0x3Fu & (unicode )); + } + } + else if (unicode < 0x10000u) + { + if (end - text >= 3) + { + *text++ = 0xE0u + (0x0Fu & (unicode >> 12)); + *text++ = 0x80u + (0x3Fu & (unicode >> 6)); + *text++ = 0x80u + (0x3Fu & (unicode )); + } + } + else + { + if (end - text >= 4) + { + *text++ = 0xF0u + (0x07u & (unicode >> 18)); + *text++ = 0x80u + (0x3Fu & (unicode >> 12)); + *text++ = 0x80u + (0x3Fu & (unicode >> 6)); + *text++ = 0x80u + (0x3Fu & (unicode )); + } + } + return text; + } +}; + + +template <typename TCodepoint> +struct hb_utf16_xe_t +{ + static_assert (sizeof (TCodepoint) == 2, ""); + typedef TCodepoint codepoint_t; + static constexpr unsigned max_len = 2; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *text++; + + if (likely (!hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu))) + { + *unicode = c; + return text; + } + + if (likely (c <= 0xDBFFu && text < end)) + { + /* High-surrogate in c */ + hb_codepoint_t l = *text; + if (likely (hb_in_range<hb_codepoint_t> (l, 0xDC00u, 0xDFFFu))) + { + /* Low-surrogate in l */ + *unicode = (c << 10) + l - ((0xD800u << 10) - 0x10000u + 0xDC00u); + text++; + return text; + } + } + + /* Lonely / out-of-order surrogate. */ + *unicode = replacement; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *--text; + + if (likely (!hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu))) + { + *unicode = c; + return text; + } + + if (likely (c >= 0xDC00u && start < text)) + { + /* Low-surrogate in c */ + hb_codepoint_t h = text[-1]; + if (likely (hb_in_range<hb_codepoint_t> (h, 0xD800u, 0xDBFFu))) + { + /* High-surrogate in h */ + *unicode = (h << 10) + c - ((0xD800u << 10) - 0x10000u + 0xDC00u); + text--; + return text; + } + } + + /* Lonely / out-of-order surrogate. */ + *unicode = replacement; + return text; + } + + + static unsigned int + strlen (const codepoint_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode) + { + return unicode < 0x10000 ? 1 : 2; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu))) + unicode = 0xFFFDu; + if (unicode < 0x10000u) + *text++ = unicode; + else if (end - text >= 2) + { + unicode -= 0x10000u; + *text++ = 0xD800u + (unicode >> 10); + *text++ = 0xDC00u + (unicode & 0x03FFu); + } + return text; + } +}; + +typedef hb_utf16_xe_t<uint16_t> hb_utf16_t; +typedef hb_utf16_xe_t<OT::HBUINT16> hb_utf16_be_t; + + +template <typename TCodepoint, bool validate=true> +struct hb_utf32_xe_t +{ + static_assert (sizeof (TCodepoint) == 4, ""); + typedef TCodepoint codepoint_t; + static constexpr unsigned max_len = 1; + + static const TCodepoint * + next (const TCodepoint *text, + const TCodepoint *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *unicode = *text++; + if (validate && unlikely (c >= 0xD800u && (c <= 0xDFFFu || c > 0x10FFFFu))) + *unicode = replacement; + return text; + } + + static const TCodepoint * + prev (const TCodepoint *text, + const TCodepoint *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + hb_codepoint_t c = *unicode = *--text; + if (validate && unlikely (c >= 0xD800u && (c <= 0xDFFFu || c > 0x10FFFFu))) + *unicode = replacement; + return text; + } + + static unsigned int + strlen (const TCodepoint *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode HB_UNUSED) + { + return 1; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t unicode) + { + if (validate && unlikely (unicode >= 0xD800u && (unicode <= 0xDFFFu || unicode > 0x10FFFFu))) + unicode = 0xFFFDu; + *text++ = unicode; + return text; + } +}; + +typedef hb_utf32_xe_t<uint32_t> hb_utf32_t; +typedef hb_utf32_xe_t<uint32_t, false> hb_utf32_novalidate_t; + + +struct hb_latin1_t +{ + typedef uint8_t codepoint_t; + static constexpr unsigned max_len = 1; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement HB_UNUSED) + { + *unicode = *text++; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement HB_UNUSED) + { + *unicode = *--text; + return text; + } + + static unsigned int + strlen (const codepoint_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode HB_UNUSED) + { + return 1; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0x0100u)) + unicode = '?'; + *text++ = unicode; + return text; + } +}; + + +struct hb_ascii_t +{ + typedef uint8_t codepoint_t; + static constexpr unsigned max_len = 1; + + static const codepoint_t * + next (const codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + *unicode = *text++; + if (*unicode >= 0x0080u) + *unicode = replacement; + return text; + } + + static const codepoint_t * + prev (const codepoint_t *text, + const codepoint_t *start HB_UNUSED, + hb_codepoint_t *unicode, + hb_codepoint_t replacement) + { + *unicode = *--text; + if (*unicode >= 0x0080u) + *unicode = replacement; + return text; + } + + static unsigned int + strlen (const codepoint_t *text) + { + unsigned int l = 0; + while (*text++) l++; + return l; + } + + static unsigned int + encode_len (hb_codepoint_t unicode HB_UNUSED) + { + return 1; + } + + static codepoint_t * + encode (codepoint_t *text, + const codepoint_t *end HB_UNUSED, + hb_codepoint_t unicode) + { + if (unlikely (unicode >= 0x0080u)) + unicode = '?'; + *text++ = unicode; + return text; + } +}; + +template <typename utf_t> +static inline const typename utf_t::codepoint_t * +hb_utf_offset_to_pointer (const typename utf_t::codepoint_t *start, + signed offset) +{ + hb_codepoint_t unicode; + + while (offset-- > 0) + start = utf_t::next (start, + start + utf_t::max_len, + &unicode, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); + + while (offset++ < 0) + start = utf_t::prev (start, + start - utf_t::max_len, + &unicode, + HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT); + + return start; +} + + +#endif /* HB_UTF_HH */ diff --git a/gfx/harfbuzz/src/hb-vector.hh b/gfx/harfbuzz/src/hb-vector.hh new file mode 100644 index 0000000000..dfe1b7d1c7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-vector.hh @@ -0,0 +1,539 @@ +/* + * Copyright © 2017,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_VECTOR_HH +#define HB_VECTOR_HH + +#include "hb.hh" +#include "hb-array.hh" +#include "hb-meta.hh" +#include "hb-null.hh" + + +template <typename Type, + bool sorted=false> +struct hb_vector_t +{ + static constexpr bool realloc_move = true; + + typedef Type item_t; + static constexpr unsigned item_size = hb_static_size (Type); + using array_t = typename std::conditional<sorted, hb_sorted_array_t<Type>, hb_array_t<Type>>::type; + using c_array_t = typename std::conditional<sorted, hb_sorted_array_t<const Type>, hb_array_t<const Type>>::type; + + hb_vector_t () = default; + hb_vector_t (std::initializer_list<Type> lst) : hb_vector_t () + { + alloc (lst.size (), true); + for (auto&& item : lst) + push (item); + } + template <typename Iterable, + hb_requires (hb_is_iterable (Iterable))> + hb_vector_t (const Iterable &o) : hb_vector_t () + { + auto iter = hb_iter (o); + if (iter.is_random_access_iterator || iter.has_fast_len) + alloc (hb_len (iter), true); + hb_copy (iter, *this); + } + hb_vector_t (const hb_vector_t &o) : hb_vector_t () + { + alloc (o.length, true); + if (unlikely (in_error ())) return; + copy_array (o.as_array ()); + } + hb_vector_t (array_t o) : hb_vector_t () + { + alloc (o.length, true); + if (unlikely (in_error ())) return; + copy_array (o); + } + hb_vector_t (c_array_t o) : hb_vector_t () + { + alloc (o.length, true); + if (unlikely (in_error ())) return; + copy_array (o); + } + hb_vector_t (hb_vector_t &&o) + { + allocated = o.allocated; + length = o.length; + arrayZ = o.arrayZ; + o.init (); + } + ~hb_vector_t () { fini (); } + + public: + int allocated = 0; /* < 0 means allocation failed. */ + unsigned int length = 0; + public: + Type *arrayZ = nullptr; + + void init () + { + allocated = length = 0; + arrayZ = nullptr; + } + void init0 () + { + } + + void fini () + { + /* We allow a hack to make the vector point to a foreign array + * by the user. In that case length/arrayZ are non-zero but + * allocated is zero. Don't free anything. */ + if (allocated) + { + shrink_vector (0); + hb_free (arrayZ); + } + init (); + } + + void reset () + { + if (unlikely (in_error ())) + reset_error (); + resize (0); + } + + friend void swap (hb_vector_t& a, hb_vector_t& b) + { + hb_swap (a.allocated, b.allocated); + hb_swap (a.length, b.length); + hb_swap (a.arrayZ, b.arrayZ); + } + + hb_vector_t& operator = (const hb_vector_t &o) + { + reset (); + alloc (o.length, true); + if (unlikely (in_error ())) return *this; + + copy_array (o.as_array ()); + + return *this; + } + hb_vector_t& operator = (hb_vector_t &&o) + { + hb_swap (*this, o); + return *this; + } + + hb_bytes_t as_bytes () const + { return hb_bytes_t ((const char *) arrayZ, get_size ()); } + + bool operator == (const hb_vector_t &o) const { return as_array () == o.as_array (); } + bool operator != (const hb_vector_t &o) const { return !(*this == o); } + uint32_t hash () const { return as_array ().hash (); } + + Type& operator [] (int i_) + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= length)) + return Crap (Type); + return arrayZ[i]; + } + const Type& operator [] (int i_) const + { + unsigned int i = (unsigned int) i_; + if (unlikely (i >= length)) + return Null (Type); + return arrayZ[i]; + } + + Type& tail () { return (*this)[length - 1]; } + const Type& tail () const { return (*this)[length - 1]; } + + explicit operator bool () const { return length; } + unsigned get_size () const { return length * item_size; } + + /* Sink interface. */ + template <typename T> + hb_vector_t& operator << (T&& v) { push (std::forward<T> (v)); return *this; } + + array_t as_array () { return hb_array (arrayZ, length); } + c_array_t as_array () const { return hb_array (arrayZ, length); } + + /* Iterator. */ + typedef c_array_t iter_t; + typedef array_t 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. */ + Type *begin () const { return arrayZ; } + Type *end () const { return arrayZ + length; } + + + hb_sorted_array_t<Type> as_sorted_array () + { return hb_sorted_array (arrayZ, length); } + hb_sorted_array_t<const Type> as_sorted_array () const + { return hb_sorted_array (arrayZ, length); } + + template <typename T> explicit operator T * () { return arrayZ; } + template <typename T> explicit operator const T * () const { return arrayZ; } + + Type * operator + (unsigned int i) { return arrayZ + i; } + const Type * operator + (unsigned int i) const { return arrayZ + i; } + + Type *push () + { + if (unlikely (!resize (length + 1))) + return std::addressof (Crap (Type)); + return std::addressof (arrayZ[length - 1]); + } + template <typename... Args> Type *push (Args&&... args) + { + if (unlikely ((int) length >= allocated && !alloc (length + 1))) + // If push failed to allocate then don't copy v, since this may cause + // the created copy to leak memory since we won't have stored a + // reference to it. + return std::addressof (Crap (Type)); + + /* Emplace. */ + Type *p = std::addressof (arrayZ[length++]); + return new (p) Type (std::forward<Args> (args)...); + } + + bool in_error () const { return allocated < 0; } + void set_error () + { + assert (allocated >= 0); + allocated = -allocated - 1; + } + void reset_error () + { + assert (allocated < 0); + allocated = -(allocated + 1); + } + + template <typename T = Type, + hb_enable_if (hb_is_trivially_copy_assignable(T))> + Type * + realloc_vector (unsigned new_allocated, hb_priority<0>) + { + if (!new_allocated) + { + hb_free (arrayZ); + return nullptr; + } + return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); + } + template <typename T = Type, + hb_enable_if (!hb_is_trivially_copy_assignable(T))> + Type * + realloc_vector (unsigned new_allocated, hb_priority<0>) + { + if (!new_allocated) + { + hb_free (arrayZ); + return nullptr; + } + Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type)); + if (likely (new_array)) + { + for (unsigned i = 0; i < length; i++) + { + new (std::addressof (new_array[i])) Type (); + new_array[i] = std::move (arrayZ[i]); + arrayZ[i].~Type (); + } + hb_free (arrayZ); + } + return new_array; + } + /* Specialization for types that can be moved using realloc(). */ + template <typename T = Type, + hb_enable_if (T::realloc_move)> + Type * + realloc_vector (unsigned new_allocated, hb_priority<1>) + { + if (!new_allocated) + { + hb_free (arrayZ); + return nullptr; + } + return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type)); + } + + template <typename T = Type, + hb_enable_if (hb_is_trivially_constructible(T))> + void + grow_vector (unsigned size, hb_priority<0>) + { + hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ)); + length = size; + } + template <typename T = Type, + hb_enable_if (!hb_is_trivially_constructible(T))> + void + grow_vector (unsigned size, hb_priority<0>) + { + for (; length < size; length++) + new (std::addressof (arrayZ[length])) Type (); + } + /* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */ + template <typename T = Type, + hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) || + hb_is_same (T, hb_array_t <typename T::item_t>))> + void + grow_vector (unsigned size, hb_priority<1>) + { + hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ)); + length = size; + } + + template <typename T = Type, + hb_enable_if (hb_is_trivially_copyable (T))> + void + copy_array (hb_array_t<const Type> other) + { + length = other.length; + if (!HB_OPTIMIZE_SIZE_VAL && sizeof (T) >= sizeof (long long)) + /* This runs faster because of alignment. */ + for (unsigned i = 0; i < length; i++) + arrayZ[i] = other.arrayZ[i]; + else + hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size); + } + template <typename T = Type, + hb_enable_if (!hb_is_trivially_copyable (T) && + std::is_copy_constructible<T>::value)> + void + copy_array (hb_array_t<const Type> other) + { + length = 0; + while (length < other.length) + { + length++; + new (std::addressof (arrayZ[length - 1])) Type (other.arrayZ[length - 1]); + } + } + template <typename T = Type, + hb_enable_if (!hb_is_trivially_copyable (T) && + !std::is_copy_constructible<T>::value && + std::is_default_constructible<T>::value && + std::is_copy_assignable<T>::value)> + void + copy_array (hb_array_t<const Type> other) + { + length = 0; + while (length < other.length) + { + length++; + new (std::addressof (arrayZ[length - 1])) Type (); + arrayZ[length - 1] = other.arrayZ[length - 1]; + } + } + + void + shrink_vector (unsigned size) + { + assert (size <= length); + if (!std::is_trivially_destructible<Type>::value) + { + unsigned count = length - size; + Type *p = arrayZ + length - 1; + while (count--) + p--->~Type (); + } + length = size; + } + + void + shift_down_vector (unsigned i) + { + for (; i < length; i++) + arrayZ[i - 1] = std::move (arrayZ[i]); + } + + /* Allocate for size but don't adjust length. */ + bool alloc (unsigned int size, bool exact=false) + { + if (unlikely (in_error ())) + return false; + + unsigned int new_allocated; + if (exact) + { + /* If exact was specified, we allow shrinking the storage. */ + size = hb_max (size, length); + if (size <= (unsigned) allocated && + size >= (unsigned) allocated >> 2) + return true; + + new_allocated = size; + } + else + { + if (likely (size <= (unsigned) allocated)) + return true; + + new_allocated = allocated; + while (size > new_allocated) + new_allocated += (new_allocated >> 1) + 8; + } + + + /* Reallocate */ + + bool overflows = + (int) in_error () || + (new_allocated < size) || + hb_unsigned_mul_overflows (new_allocated, sizeof (Type)); + + if (unlikely (overflows)) + { + set_error (); + return false; + } + + Type *new_array = realloc_vector (new_allocated, hb_prioritize); + + if (unlikely (new_allocated && !new_array)) + { + if (new_allocated <= (unsigned) allocated) + return true; // shrinking failed; it's okay; happens in our fuzzer + + set_error (); + return false; + } + + arrayZ = new_array; + allocated = new_allocated; + + return true; + } + + bool resize (int size_, bool initialize = true, bool exact = false) + { + unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; + if (!alloc (size, exact)) + return false; + + if (size > length) + { + if (initialize) + grow_vector (size, hb_prioritize); + } + else if (size < length) + { + if (initialize) + shrink_vector (size); + } + + length = size; + return true; + } + bool resize_exact (int size_, bool initialize = true) + { + return resize (size_, initialize, true); + } + + Type pop () + { + if (!length) return Null (Type); + Type v (std::move (arrayZ[length - 1])); + arrayZ[length - 1].~Type (); + length--; + return v; + } + + void remove_ordered (unsigned int i) + { + if (unlikely (i >= length)) + return; + shift_down_vector (i + 1); + arrayZ[length - 1].~Type (); + length--; + } + + template <bool Sorted = sorted, + hb_enable_if (!Sorted)> + void remove_unordered (unsigned int i) + { + if (unlikely (i >= length)) + return; + if (i != length - 1) + arrayZ[i] = std::move (arrayZ[length - 1]); + arrayZ[length - 1].~Type (); + length--; + } + + void shrink (int size_, bool shrink_memory = true) + { + unsigned int size = size_ < 0 ? 0u : (unsigned int) size_; + if (size >= length) + return; + + shrink_vector (size); + + if (shrink_memory) + alloc (size, true); /* To force shrinking memory if needed. */ + } + + + /* Sorting API. */ + void qsort (int (*cmp)(const void*, const void*) = Type::cmp) + { as_array ().qsort (cmp); } + + /* Unsorted search API. */ + template <typename T> + Type *lsearch (const T &x, Type *not_found = nullptr) + { return as_array ().lsearch (x, not_found); } + template <typename T> + const Type *lsearch (const T &x, const Type *not_found = nullptr) const + { return as_array ().lsearch (x, not_found); } + template <typename T> + bool lfind (const T &x, unsigned *pos = nullptr) const + { return as_array ().lfind (x, pos); } + + /* Sorted search API. */ + template <typename T, + bool Sorted=sorted, hb_enable_if (Sorted)> + Type *bsearch (const T &x, Type *not_found = nullptr) + { return as_array ().bsearch (x, not_found); } + template <typename T, + bool Sorted=sorted, hb_enable_if (Sorted)> + const Type *bsearch (const T &x, const Type *not_found = nullptr) const + { return as_array ().bsearch (x, not_found); } + template <typename T, + bool Sorted=sorted, hb_enable_if (Sorted)> + 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 <typename Type> +using hb_sorted_vector_t = hb_vector_t<Type, true>; + +#endif /* HB_VECTOR_HH */ diff --git a/gfx/harfbuzz/src/hb-version.h b/gfx/harfbuzz/src/hb-version.h new file mode 100644 index 0000000000..b08dd1f09f --- /dev/null +++ b/gfx/harfbuzz/src/hb-version.h @@ -0,0 +1,95 @@ +/* + * 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_VERSION_H +#define HB_VERSION_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_VERSION_MAJOR: + * + * The major component of the library version available at compile-time. + */ +#define HB_VERSION_MAJOR 8 +/** + * HB_VERSION_MINOR: + * + * The minor component of the library version available at compile-time. + */ +#define HB_VERSION_MINOR 3 +/** + * HB_VERSION_MICRO: + * + * The micro component of the library version available at compile-time. + */ +#define HB_VERSION_MICRO 0 + +/** + * HB_VERSION_STRING: + * + * A string literal containing the library version available at compile-time. + */ +#define HB_VERSION_STRING "8.3.0" + +/** + * HB_VERSION_ATLEAST: + * @major: the major component of the version number + * @minor: the minor component of the version number + * @micro: the micro component of the version number + * + * Tests the library version at compile-time against a minimum value, + * as three integer components. + */ +#define HB_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ + HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) + + +HB_EXTERN void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro); + +HB_EXTERN const char * +hb_version_string (void); + +HB_EXTERN hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro); + + +HB_END_DECLS + +#endif /* HB_VERSION_H */ diff --git a/gfx/harfbuzz/src/hb-version.h.in b/gfx/harfbuzz/src/hb-version.h.in new file mode 100644 index 0000000000..abcb73f417 --- /dev/null +++ b/gfx/harfbuzz/src/hb-version.h.in @@ -0,0 +1,95 @@ +/* + * 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 + */ + +#if !defined(HB_H_IN) && !defined(HB_NO_SINGLE_HEADER_ERROR) +#error "Include <hb.h> instead." +#endif + +#ifndef HB_VERSION_H +#define HB_VERSION_H + +#include "hb-common.h" + +HB_BEGIN_DECLS + + +/** + * HB_VERSION_MAJOR: + * + * The major component of the library version available at compile-time. + */ +#define HB_VERSION_MAJOR @HB_VERSION_MAJOR@ +/** + * HB_VERSION_MINOR: + * + * The minor component of the library version available at compile-time. + */ +#define HB_VERSION_MINOR @HB_VERSION_MINOR@ +/** + * HB_VERSION_MICRO: + * + * The micro component of the library version available at compile-time. + */ +#define HB_VERSION_MICRO @HB_VERSION_MICRO@ + +/** + * HB_VERSION_STRING: + * + * A string literal containing the library version available at compile-time. + */ +#define HB_VERSION_STRING "@HB_VERSION@" + +/** + * HB_VERSION_ATLEAST: + * @major: the major component of the version number + * @minor: the minor component of the version number + * @micro: the micro component of the version number + * + * Tests the library version at compile-time against a minimum value, + * as three integer components. + */ +#define HB_VERSION_ATLEAST(major,minor,micro) \ + ((major)*10000+(minor)*100+(micro) <= \ + HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO) + + +HB_EXTERN void +hb_version (unsigned int *major, + unsigned int *minor, + unsigned int *micro); + +HB_EXTERN const char * +hb_version_string (void); + +HB_EXTERN hb_bool_t +hb_version_atleast (unsigned int major, + unsigned int minor, + unsigned int micro); + + +HB_END_DECLS + +#endif /* HB_VERSION_H */ diff --git a/gfx/harfbuzz/src/hb-wasm-api-blob.hh b/gfx/harfbuzz/src/hb-wasm-api-blob.hh new file mode 100644 index 0000000000..310f4023fc --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api-blob.hh @@ -0,0 +1,50 @@ +/* + * Copyright © 2023 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_WASM_API_BLOB_HH +#define HB_WASM_API_BLOB_HH + +#include "hb-wasm-api.hh" + +namespace hb { +namespace wasm { + + +HB_WASM_API (void, blob_free) (HB_WASM_EXEC_ENV + ptr_d(blob_t, blob)) +{ + HB_PTR_PARAM (blob_t, blob); + if (unlikely (!blob)) + return; + + module_free (blob->data); + + blob->data = nullref; + blob->length = 0; +} + + +}} + +#endif /* HB_WASM_API_BLOB_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-api-buffer.hh b/gfx/harfbuzz/src/hb-wasm-api-buffer.hh new file mode 100644 index 0000000000..64217a041f --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api-buffer.hh @@ -0,0 +1,217 @@ +/* + * Copyright © 2023 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_WASM_API_BUFFER_HH +#define HB_WASM_API_BUFFER_HH + +#include "hb-wasm-api.hh" + +#include "hb-buffer.hh" + +namespace hb { +namespace wasm { + +static_assert (sizeof (glyph_info_t) == sizeof (hb_glyph_info_t), ""); +static_assert (sizeof (glyph_position_t) == sizeof (hb_glyph_position_t), ""); + +HB_WASM_API (bool_t, buffer_contents_realloc) (HB_WASM_EXEC_ENV + ptr_d(buffer_contents_t, contents), + uint32_t size) +{ + HB_PTR_PARAM (buffer_contents_t, contents); + if (unlikely (!contents)) + return false; + + if (size <= contents->length) + return true; + + unsigned bytes; + if (hb_unsigned_mul_overflows (size, sizeof (glyph_info_t), &bytes)) + return false; + + glyph_info_t *info = HB_ARRAY_APP2NATIVE (glyph_info_t, contents->info, contents->length); + glyph_position_t *pos = HB_ARRAY_APP2NATIVE (glyph_position_t, contents->pos, contents->length); + + if (unlikely (!info || !pos)) + return false; + + glyph_info_t *new_info = nullptr; + uint32_t new_inforef = module_malloc (bytes, (void **) &new_info); + glyph_position_t *new_pos = nullptr; + uint32_t new_posref = module_malloc (bytes, (void **) &new_pos); + + unsigned old_bytes = contents->length * sizeof (glyph_info_t); + if (likely (new_inforef)) + { + hb_memcpy (new_info, info, old_bytes); + module_free (contents->info); + contents->info = new_inforef; + } + if (likely (new_posref)) + { + hb_memcpy (new_pos, pos, old_bytes); + module_free (contents->pos); + contents->pos = new_posref; + } + + if (likely (new_info && new_pos)) + { + contents->length = size; + return true; + } + + return false; +} + +HB_WASM_API (void, buffer_contents_free) (HB_WASM_EXEC_ENV + ptr_d(buffer_contents_t, contents)) +{ + HB_PTR_PARAM (buffer_contents_t, contents); + if (unlikely (!contents)) + return; + + module_free (contents->info); + module_free (contents->pos); + + contents->info = nullref; + contents->pos = nullref; + contents->length = 0; +} + +HB_WASM_API (bool_t, buffer_copy_contents) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer), + ptr_d(buffer_contents_t, contents)) +{ + HB_REF2OBJ (buffer); + HB_PTR_PARAM (buffer_contents_t, contents); + if (unlikely (!contents)) + return false; + + if (buffer->have_output) + buffer->sync (); + if (!buffer->have_positions) + buffer->clear_positions (); + + unsigned length = buffer->len; + + if (length <= contents->length) + { + glyph_info_t *info = HB_ARRAY_APP2NATIVE (glyph_info_t, contents->info, length); + glyph_position_t *pos = HB_ARRAY_APP2NATIVE (glyph_position_t, contents->pos, length); + + if (unlikely (!info || !pos)) + { + contents->length = 0; + return false; + } + + unsigned bytes = length * sizeof (hb_glyph_info_t); + hb_memcpy (info, buffer->info, bytes); + hb_memcpy (pos, buffer->pos, bytes); + + return true; + } + + module_free (contents->info); + module_free (contents->pos); + + contents->length = length; + unsigned bytes = length * sizeof (hb_glyph_info_t); + contents->info = wasm_runtime_module_dup_data (module_inst, (const char *) buffer->info, bytes); + contents->pos = wasm_runtime_module_dup_data (module_inst, (const char *) buffer->pos, bytes); + + if (length && (!contents->info || !contents->pos)) + { + contents->length = 0; + return false; + } + + return true; +} + +HB_WASM_API (bool_t, buffer_set_contents) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer), + ptr_d(const buffer_contents_t, contents)) +{ + HB_REF2OBJ (buffer); + HB_PTR_PARAM (buffer_contents_t, contents); + if (unlikely (!contents)) + return false; + + unsigned length = contents->length; + unsigned bytes; + if (unlikely (hb_unsigned_mul_overflows (length, sizeof (buffer->info[0]), &bytes))) + return false; + + if (unlikely (!buffer->resize (length))) + return false; + + glyph_info_t *info = (glyph_info_t *) (validate_app_addr (contents->info, bytes) ? addr_app_to_native (contents->info) : nullptr); + glyph_position_t *pos = (glyph_position_t *) (validate_app_addr (contents->pos, bytes) ? addr_app_to_native (contents->pos) : nullptr); + + if (!buffer->have_positions) + buffer->clear_positions (); /* This is wasteful. */ + + hb_memcpy (buffer->info, info, bytes); + hb_memcpy (buffer->pos, pos, bytes); + buffer->len = length; + + return true; +} + +HB_WASM_API (direction_t, buffer_get_direction) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)) +{ + HB_REF2OBJ (buffer); + + return (direction_t) hb_buffer_get_direction (buffer); +} + +HB_WASM_API (script_t, buffer_get_script) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)) +{ + HB_REF2OBJ (buffer); + + return hb_script_to_iso15924_tag (hb_buffer_get_script (buffer)); +} + +HB_WASM_API (void, buffer_reverse) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)) +{ + HB_REF2OBJ (buffer); + + hb_buffer_reverse (buffer); +} + +HB_WASM_API (void, buffer_reverse_clusters) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)) +{ + HB_REF2OBJ (buffer); + + hb_buffer_reverse_clusters (buffer); +} + +}} + +#endif /* HB_WASM_API_BUFFER_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-api-common.hh b/gfx/harfbuzz/src/hb-wasm-api-common.hh new file mode 100644 index 0000000000..c38ca9cedd --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api-common.hh @@ -0,0 +1,44 @@ +/* + * Copyright © 2023 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_WASM_API_COMMON_HH +#define HB_WASM_API_COMMON_HH + +#include "hb-wasm-api.hh" + +namespace hb { +namespace wasm { + + +HB_WASM_API (direction_t, script_get_horizontal_direction) (HB_WASM_EXEC_ENV + script_t script) +{ + return (direction_t) + hb_script_get_horizontal_direction (hb_script_from_iso15924_tag (script)); +} + + +}} + +#endif /* HB_WASM_API_COMMON_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-api-face.hh b/gfx/harfbuzz/src/hb-wasm-api-face.hh new file mode 100644 index 0000000000..22e50e9446 --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api-face.hh @@ -0,0 +1,109 @@ +/* + * Copyright © 2023 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_WASM_API_FACE_HH +#define HB_WASM_API_FACE_HH + +#include "hb-wasm-api.hh" + +namespace hb { +namespace wasm { + + +HB_WASM_API (ptr_t(face_t), face_create) (HB_WASM_EXEC_ENV + ptr_d(blob_t, blob), + unsigned int index) +{ + HB_PTR_PARAM (blob_t, blob); + hb_blob_t *hb_blob = hb_blob_create( + HB_ARRAY_APP2NATIVE (char, blob->data, blob->length), + blob->length, + HB_MEMORY_MODE_DUPLICATE, + NULL, + NULL); + + hb_face_t *face = hb_face_create(hb_blob, index); + + HB_OBJ2REF (face); + return faceref; +} + +HB_WASM_API (bool_t, face_copy_table) (HB_WASM_EXEC_ENV + ptr_d(face_t, face), + tag_t table_tag, + ptr_d(blob_t, blob)) +{ + HB_REF2OBJ (face); + HB_PTR_PARAM (blob_t, blob); + if (unlikely (!blob)) + return false; + + hb_blob_t *hb_blob = hb_face_reference_table (face, table_tag); + + unsigned length; + const char *hb_data = hb_blob_get_data (hb_blob, &length); + + if (length <= blob->length) + { + char *data = HB_ARRAY_APP2NATIVE (char, blob->data, length); + + if (unlikely (!data)) + { + blob->length = 0; + return false; + } + + hb_memcpy (data, hb_data, length); + + return true; + } + + module_free (blob->data); + + blob->length = length; + blob->data = wasm_runtime_module_dup_data (module_inst, hb_data, length); + + hb_blob_destroy (hb_blob); + + if (blob->length && !blob->data) + { + blob->length = 0; + return false; + } + + return true; +} + +HB_WASM_API (unsigned, face_get_upem) (HB_WASM_EXEC_ENV + ptr_d(face_t, face)) +{ + HB_REF2OBJ (face); + + return hb_face_get_upem (face); +} + + +}} + +#endif /* HB_WASM_API_FACE_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-api-font.hh b/gfx/harfbuzz/src/hb-wasm-api-font.hh new file mode 100644 index 0000000000..2d85db4aac --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api-font.hh @@ -0,0 +1,263 @@ +/* + * Copyright © 2023 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_WASM_API_FONT_HH +#define HB_WASM_API_FONT_HH + +#include "hb-wasm-api.hh" + +#include "hb-outline.hh" + +namespace hb { +namespace wasm { + + +HB_WASM_API (ptr_t(font_t), font_create) (HB_WASM_EXEC_ENV + ptr_d(face_t, face)) +{ + HB_REF2OBJ (face); + + hb_font_t *font = hb_font_create (face); + + HB_OBJ2REF (font); + return fontref; +} + +HB_WASM_API (ptr_t(face_t), font_get_face) (HB_WASM_EXEC_ENV + ptr_d(font_t, font)) +{ + HB_REF2OBJ (font); + + hb_face_t *face = hb_font_get_face (font); + + HB_OBJ2REF (face); + return faceref; +} + +HB_WASM_API (void, font_get_scale) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(int32_t, x_scale), + ptr_d(int32_t, y_scale)) +{ + HB_REF2OBJ (font); + + HB_PTR_PARAM(int32_t, x_scale); + HB_PTR_PARAM(int32_t, y_scale); + + hb_font_get_scale (font, x_scale, y_scale); +} + +HB_WASM_API (codepoint_t, font_get_glyph) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t unicode, + codepoint_t variation_selector) +{ + HB_REF2OBJ (font); + codepoint_t glyph; + + hb_font_get_glyph (font, unicode, variation_selector, &glyph); + return glyph; +} + +HB_WASM_API (position_t, font_get_glyph_h_advance) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph) +{ + HB_REF2OBJ (font); + return hb_font_get_glyph_h_advance (font, glyph); +} + +HB_WASM_API (position_t, font_get_glyph_v_advance) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph) +{ + HB_REF2OBJ (font); + return hb_font_get_glyph_v_advance (font, glyph); +} + +static_assert (sizeof (glyph_extents_t) == sizeof (hb_glyph_extents_t), ""); + +HB_WASM_API (bool_t, font_get_glyph_extents) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph, + ptr_d(glyph_extents_t, extents)) +{ + HB_REF2OBJ (font); + HB_PTR_PARAM (glyph_extents_t, extents); + if (unlikely (!extents)) + return false; + + return hb_font_get_glyph_extents (font, glyph, + (hb_glyph_extents_t *) extents); +} + +HB_WASM_API (void, font_glyph_to_string) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph, + char *s, uint32_t size) +{ + HB_REF2OBJ (font); + + hb_font_glyph_to_string (font, glyph, s, size); +} + +static_assert (sizeof (glyph_outline_point_t) == sizeof (hb_outline_point_t), ""); +static_assert (sizeof (uint32_t) == sizeof (hb_outline_t::contours[0]), ""); + +HB_WASM_API (bool_t, font_copy_glyph_outline) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph, + ptr_d(glyph_outline_t, outline)) +{ + HB_REF2OBJ (font); + HB_PTR_PARAM (glyph_outline_t, outline); + if (unlikely (!outline)) + return false; + + hb_outline_t hb_outline; + auto *funcs = hb_outline_recording_pen_get_funcs (); + + hb_font_draw_glyph (font, glyph, funcs, &hb_outline); + + if (unlikely (hb_outline.points.in_error () || + hb_outline.contours.in_error ())) + { + outline->n_points = outline->n_contours = 0; + return false; + } + + // TODO Check two buffers separately + if (hb_outline.points.length <= outline->n_points && + hb_outline.contours.length <= outline->n_contours) + { + glyph_outline_point_t *points = HB_ARRAY_APP2NATIVE (glyph_outline_point_t, outline->points, hb_outline.points.length); + uint32_t *contours = HB_ARRAY_APP2NATIVE (uint32_t, outline->contours, hb_outline.contours.length); + + if (unlikely (!points || !contours)) + { + outline->n_points = outline->n_contours = 0; + return false; + } + + hb_memcpy (points, hb_outline.points.arrayZ, hb_outline.points.get_size ()); + hb_memcpy (contours, hb_outline.contours.arrayZ, hb_outline.contours.get_size ()); + + return true; + } + + outline->n_points = hb_outline.points.length; + outline->points = wasm_runtime_module_dup_data (module_inst, + (const char *) hb_outline.points.arrayZ, + hb_outline.points.get_size ()); + outline->n_contours = hb_outline.contours.length; + outline->contours = wasm_runtime_module_dup_data (module_inst, + (const char *) hb_outline.contours.arrayZ, + hb_outline.contours.get_size ()); + + if ((outline->n_points && !outline->points) || + (!outline->n_contours && !outline->contours)) + { + outline->n_points = outline->n_contours = 0; + return false; + } + + return true; +} + +HB_WASM_API (void, glyph_outline_free) (HB_WASM_EXEC_ENV + ptr_d(glyph_outline_t, outline)) +{ + HB_PTR_PARAM (glyph_outline_t, outline); + if (unlikely (!outline)) + return; + + module_free (outline->points); + module_free (outline->contours); + + outline->n_points = 0; + outline->points = nullref; + outline->n_contours = 0; + outline->contours = nullref; +} + +HB_WASM_API (bool_t, font_copy_coords) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(coords_t, coords)) +{ + HB_REF2OBJ (font); + HB_PTR_PARAM (coords_t, coords); + if (unlikely (!coords)) + return false; + + unsigned our_length; + const int* our_coords = hb_font_get_var_coords_normalized(font, &our_length); + + if (our_length <= coords->length) { + int *their_coords = HB_ARRAY_APP2NATIVE (int, coords->coords, our_length); + if (unlikely(!their_coords)) { + coords->length = 0; + return false; + } + unsigned bytes = our_length * sizeof (int); + hb_memcpy (their_coords, our_coords, bytes); + + return true; + } + + module_free (coords->coords); + coords->length = our_length; + unsigned bytes = our_length * sizeof (int); + coords->coords = wasm_runtime_module_dup_data (module_inst, (const char *) our_coords, bytes); + if (our_length && !coords->coords) + { + coords->length = 0; + return false; + } + + return true; +} + +HB_WASM_API (bool_t, font_set_coords) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(coords_t, coords)) +{ + HB_REF2OBJ (font); + HB_PTR_PARAM (coords_t, coords); + if (unlikely (!coords)) + return false; + + unsigned length = coords->length; + unsigned bytes; + if (unlikely (hb_unsigned_mul_overflows (length, sizeof (int), &bytes))) + return false; + + const int *our_coords = (const int *) (validate_app_addr (coords->coords, bytes) ? addr_app_to_native (coords->coords) : nullptr); + hb_font_set_var_coords_normalized(font, our_coords, length); + return true; +} + + +}} + +#endif /* HB_WASM_API_FONT_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-api-list.hh b/gfx/harfbuzz/src/hb-wasm-api-list.hh new file mode 100644 index 0000000000..5db45c74cb --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api-list.hh @@ -0,0 +1,109 @@ +/* + * Copyright © 2023 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_WASM_API_LIST_HH +#define HB_WASM_API_LIST_HH + +#include "hb-wasm-api.hh" + + +#ifdef HB_DEBUG_WASM +namespace hb { namespace wasm { + +static void debugprint (HB_WASM_EXEC_ENV char *str) +{ DEBUG_MSG (WASM, exec_env, "%s", str); } +static void debugprint1 (HB_WASM_EXEC_ENV char *str, int32_t i1) +{ DEBUG_MSG (WASM, exec_env, "%s: %d", str, i1); } +static void debugprint2 (HB_WASM_EXEC_ENV char *str, int32_t i1, int32_t i2) +{ DEBUG_MSG (WASM, exec_env, "%s: %d, %d", str, i1, i2); } +static void debugprint3 (HB_WASM_EXEC_ENV char *str, int32_t i1, int32_t i2, int32_t i3) +{ DEBUG_MSG (WASM, exec_env, "%s: %d, %d, %d", str, i1, i2, i3); } +static void debugprint4 (HB_WASM_EXEC_ENV char *str, int32_t i1, int32_t i2, int32_t i3, int32_t i4) +{ DEBUG_MSG (WASM, exec_env, "%s: %d, %d, %d, %d", str, i1, i2, i3, i4); } + +}} +#endif + +#define NATIVE_SYMBOL(signature, name) {#name, (void *) hb::wasm::name, signature, NULL} +/* Note: the array must be static defined since runtime will keep it after registration. + * Also not const, because it modifies it (sorts it). + * https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/doc/export_native_api.md + * + * TODO Allocate this lazily in _hb_wasm_init(). */ +static NativeSymbol _hb_wasm_native_symbols[] = +{ + /* common */ + NATIVE_SYMBOL ("(i)i", script_get_horizontal_direction), + + /* blob */ + NATIVE_SYMBOL ("(i)", blob_free), + + /* buffer */ + NATIVE_SYMBOL ("(i)", buffer_contents_free), + NATIVE_SYMBOL ("(ii)i", buffer_contents_realloc), + NATIVE_SYMBOL ("(ii)i", buffer_copy_contents), + NATIVE_SYMBOL ("(ii)i", buffer_set_contents), + NATIVE_SYMBOL ("(i)i", buffer_get_direction), + NATIVE_SYMBOL ("(i)i", buffer_get_script), + NATIVE_SYMBOL ("(i)", buffer_reverse), + NATIVE_SYMBOL ("(i)", buffer_reverse_clusters), + + /* face */ + NATIVE_SYMBOL ("(ii)i", face_create), + NATIVE_SYMBOL ("(iii)i", face_copy_table), + NATIVE_SYMBOL ("(i)i", face_get_upem), + + /* font */ + NATIVE_SYMBOL ("(i)i", font_create), + NATIVE_SYMBOL ("(i)i", font_get_face), + NATIVE_SYMBOL ("(iii)", font_get_scale), + NATIVE_SYMBOL ("(iii)i", font_get_glyph), + NATIVE_SYMBOL ("(ii)i", font_get_glyph_h_advance), + NATIVE_SYMBOL ("(ii)i", font_get_glyph_v_advance), + NATIVE_SYMBOL ("(iii)i", font_get_glyph_extents), + NATIVE_SYMBOL ("(ii$*)", font_glyph_to_string), + NATIVE_SYMBOL ("(iii)i", font_copy_glyph_outline), + NATIVE_SYMBOL ("(ii)i", font_copy_coords), + NATIVE_SYMBOL ("(ii)i", font_set_coords), + + /* outline */ + NATIVE_SYMBOL ("(i)", glyph_outline_free), + + /* shape */ + NATIVE_SYMBOL ("(iiii$)i", shape_with), + + /* debug */ +#ifdef HB_DEBUG_WASM + NATIVE_SYMBOL ("($)", debugprint), + NATIVE_SYMBOL ("($i)", debugprint1), + NATIVE_SYMBOL ("($ii)", debugprint2), + NATIVE_SYMBOL ("($iii)", debugprint3), + NATIVE_SYMBOL ("($iiii)", debugprint4), +#endif + +}; +#undef NATIVE_SYMBOL + + +#endif /* HB_WASM_API_LIST_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-api-shape.hh b/gfx/harfbuzz/src/hb-wasm-api-shape.hh new file mode 100644 index 0000000000..622ff052b2 --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api-shape.hh @@ -0,0 +1,70 @@ +/* + * Copyright © 2023 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_WASM_API_SHAPE_HH +#define HB_WASM_API_SHAPE_HH + +#include "hb-wasm-api.hh" + +namespace hb { +namespace wasm { + + +static_assert (sizeof (feature_t) == sizeof (hb_feature_t), ""); + +HB_WASM_INTERFACE (bool_t, shape_with) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(buffer_t, buffer), + ptr_d(const feature_t, features), + uint32_t num_features, + const char *shaper) +{ + if (unlikely (0 == strcmp (shaper, "wasm"))) + return false; + + HB_REF2OBJ (font); + HB_REF2OBJ (buffer); + + /* Pre-conditions that make hb_shape_full() crash should be checked here. */ + + if (unlikely (!buffer->ensure_unicode ())) + return false; + + if (unlikely (!HB_DIRECTION_IS_VALID (buffer->props.direction))) + return false; + + HB_ARRAY_PARAM (const feature_t, features, num_features); + if (unlikely (!features && num_features)) + return false; + + const char * shaper_list[] = {shaper, nullptr}; + return hb_shape_full (font, buffer, + (hb_feature_t *) features, num_features, + shaper_list); +} + + +}} + +#endif /* HB_WASM_API_SHAPE_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-api.cc b/gfx/harfbuzz/src/hb-wasm-api.cc new file mode 100644 index 0000000000..1da8347a08 --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api.cc @@ -0,0 +1,46 @@ +/* + * Copyright © 2023 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. + */ + +#include "hb.hh" + +#ifdef HAVE_WASM + +#include "hb-wasm-api.hh" + +#define module_inst wasm_runtime_get_module_inst (exec_env) + + +#include "hb-wasm-api-blob.hh" +#include "hb-wasm-api-buffer.hh" +#include "hb-wasm-api-common.hh" +#include "hb-wasm-api-face.hh" +#include "hb-wasm-api-font.hh" +#include "hb-wasm-api-shape.hh" + + +#undef module_inst + +hb_user_data_key_t _hb_wasm_ref_type_key = {}; + +#endif diff --git a/gfx/harfbuzz/src/hb-wasm-api.h b/gfx/harfbuzz/src/hb-wasm-api.h new file mode 100644 index 0000000000..f9bd98e5ea --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api.h @@ -0,0 +1,319 @@ +/* + * Copyright © 2023 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_WASM_API_H +#define HB_WASM_API_H + +/* +#include "hb.h" +*/ + +#include <stdint.h> + + +#ifndef HB_WASM_BEGIN_DECLS +# ifdef __cplusplus +# define HB_WASM_BEGIN_DECLS extern "C" { +# define HB_WASM_END_DECLS } +# else /* !__cplusplus */ +# define HB_WASM_BEGIN_DECLS +# define HB_WASM_END_DECLS +# endif /* !__cplusplus */ +#endif + + +HB_WASM_BEGIN_DECLS + +#ifndef HB_WASM_API +#define HB_WASM_API(ret_t, name) ret_t name +#endif +#ifndef HB_WASM_API_COMPOUND /* compound return type */ +#define HB_WASM_API_COMPOUND(ret_t, name) HB_WASM_API(ret_t, name) +#endif +#ifndef HB_WASM_INTERFACE +#define HB_WASM_INTERFACE(ret_t, name) ret_t name +#endif +#ifndef HB_WASM_EXEC_ENV +#define HB_WASM_EXEC_ENV +#endif +#ifndef HB_WASM_EXEC_ENV_COMPOUND +#define HB_WASM_EXEC_ENV_COMPOUND HB_WASM_EXEC_ENV +#endif + + +#ifndef bool_t +#define bool_t uint32_t +#endif +#ifndef ptr_t +#define ptr_t(type_t) type_t * +#endif +#ifndef ptr_d +#define ptr_d(type_t, name) type_t *name +#endif + +typedef uint32_t codepoint_t; +typedef int32_t position_t; +typedef uint32_t mask_t; +typedef uint32_t tag_t; +#define TAG(c1,c2,c3,c4) ((tag_t)((((uint32_t)(c1)&0xFF)<<24)|(((uint32_t)(c2)&0xFF)<<16)|(((uint32_t)(c3)&0xFF)<<8)|((uint32_t)(c4)&0xFF))) + +typedef enum { + DIRECTION_INVALID = 0, + DIRECTION_LTR = 4, + DIRECTION_RTL, + DIRECTION_TTB, + DIRECTION_BTT +} direction_t; +#define DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4) +#define DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4) +#define DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6) +#define DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4) +#define DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5) +#define DIRECTION_REVERSE(dir) ((direction_t) (((unsigned int) (dir)) ^ 1)) + +typedef tag_t script_t; /* ISO 15924 representation of Unicode scripts. */ + + +/* common */ + +HB_WASM_API (direction_t, script_get_horizontal_direction) (HB_WASM_EXEC_ENV + script_t script); + + +/* blob */ + +typedef struct +{ + uint32_t length; + ptr_t(char) data; +} blob_t; +#define BLOB_INIT {0, 0} + +HB_WASM_API (void, blob_free) (HB_WASM_EXEC_ENV + ptr_d(blob_t, blob)); + +/* buffer */ + +typedef struct +{ + uint32_t codepoint; + uint32_t mask; + uint32_t cluster; + uint32_t var1; + uint32_t var2; +} glyph_info_t; + +typedef struct +{ + position_t x_advance; + position_t y_advance; + position_t x_offset; + position_t y_offset; + uint32_t var; +} glyph_position_t; + +typedef struct +{ + uint32_t length; + ptr_t(glyph_info_t) info; + ptr_t(glyph_position_t) pos; +} buffer_contents_t; +#define BUFFER_CONTENTS_INIT {0, 0, 0} + +HB_WASM_API (bool_t, buffer_contents_realloc) (HB_WASM_EXEC_ENV + ptr_d(buffer_contents_t, contents), + uint32_t size); + +HB_WASM_API (void, buffer_contents_free) (HB_WASM_EXEC_ENV + ptr_d(buffer_contents_t, contents)); + +typedef struct buffer_t buffer_t; + +HB_WASM_API (bool_t, buffer_copy_contents) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer), + ptr_d(buffer_contents_t, contents)); + +HB_WASM_API (bool_t, buffer_set_contents) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer), + ptr_d(const buffer_contents_t, contents)); + +HB_WASM_API (direction_t, buffer_get_direction) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)); + +HB_WASM_API (script_t, buffer_get_script) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)); + +HB_WASM_API (void, buffer_reverse) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)); + +HB_WASM_API (void, buffer_reverse_clusters) (HB_WASM_EXEC_ENV + ptr_d(buffer_t, buffer)); + +/* face */ + +typedef struct face_t face_t; + +HB_WASM_API (ptr_t(face_t), face_create) (HB_WASM_EXEC_ENV + ptr_d(blob_t, blob), + unsigned int); + +HB_WASM_API (bool_t, face_copy_table) (HB_WASM_EXEC_ENV + ptr_d(face_t, face), + tag_t table_tag, + ptr_d(blob_t, blob)); + +HB_WASM_API (unsigned, face_get_upem) (HB_WASM_EXEC_ENV + ptr_d(face_t, face)); + +/* font */ + +typedef struct font_t font_t; + +HB_WASM_API (ptr_t(font_t), font_create) (HB_WASM_EXEC_ENV + ptr_d(face_t, face)); + +HB_WASM_API (ptr_t(face_t), font_get_face) (HB_WASM_EXEC_ENV + ptr_d(font_t, font)); + +HB_WASM_API (void, font_get_scale) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(int32_t, x_scale), + ptr_d(int32_t, y_scale)); + +HB_WASM_API (codepoint_t, font_get_glyph) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t unicode, + codepoint_t variation_selector); + +HB_WASM_API (position_t, font_get_glyph_h_advance) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph); + +HB_WASM_API (position_t, font_get_glyph_v_advance) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph); + +typedef struct +{ + position_t x_bearing; + position_t y_bearing; + position_t width; + position_t height; +} glyph_extents_t; + +HB_WASM_API (bool_t, font_get_glyph_extents) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph, + ptr_d(glyph_extents_t, extents)); + +HB_WASM_API (void, font_glyph_to_string) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph, + char *s, uint32_t size); + + +typedef struct +{ + unsigned int length; + ptr_t(int) coords; +} coords_t; + +HB_WASM_API (bool_t, font_copy_coords) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(coords_t, coords)); + +HB_WASM_API (bool_t, font_set_coords) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(coords_t, coords)); + +/* outline */ + +enum glyph_outline_point_type_t +{ + MOVE_TO, + LINE_TO, + QUADRATIC_TO, + CUBIC_TO, +}; + +typedef struct +{ + float x; + float y; + uint32_t type; +} glyph_outline_point_t; + +typedef struct +{ + uint32_t n_points; + ptr_t(glyph_outline_point_t) points; + uint32_t n_contours; + ptr_t(uint32_t) contours; +} glyph_outline_t; +#define GLYPH_OUTLINE_INIT {0, 0, 0, 0} + +HB_WASM_API (void, glyph_outline_free) (HB_WASM_EXEC_ENV + ptr_d(glyph_outline_t, outline)); + +HB_WASM_API (bool_t, font_copy_glyph_outline) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + codepoint_t glyph, + ptr_d(glyph_outline_t, outline)); + + +/* shape */ + +typedef struct +{ + tag_t tag; + uint32_t value; + uint32_t start; + uint32_t end; +} feature_t; +#define FEATURE_GLOBAL_START 0 +#define FEATURE_GLOBAL_END ((uint32_t) -1) + +HB_WASM_API (bool_t, shape_with) (HB_WASM_EXEC_ENV + ptr_d(font_t, font), + ptr_d(buffer_t, buffer), + ptr_d(const feature_t, features), + uint32_t num_features, + const char *shaper); + +/* Implement these in your shaper. */ + +HB_WASM_INTERFACE (ptr_t(void), shape_plan_create) (ptr_d(face_t, face)); + +HB_WASM_INTERFACE (bool_t, shape) (ptr_d(void, shape_plan), + ptr_d(font_t, font), + ptr_d(buffer_t, buffer), + ptr_d(const feature_t, features), + uint32_t num_features); + +HB_WASM_INTERFACE (void, shape_plan_destroy) (ptr_d(void, shape_plan)); + + +HB_WASM_END_DECLS + +#endif /* HB_WASM_API_H */ diff --git a/gfx/harfbuzz/src/hb-wasm-api.hh b/gfx/harfbuzz/src/hb-wasm-api.hh new file mode 100644 index 0000000000..4ead01c1f7 --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-api.hh @@ -0,0 +1,117 @@ +/* + * Copyright © 2023 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_WASM_API_HH +#define HB_WASM_API_HH + +#include "hb.hh" + +#include <wasm_export.h> + +#define HB_WASM_BEGIN_DECLS namespace hb { namespace wasm { +#define HB_WASM_END_DECLS }} + +#define HB_WASM_API(ret_t, name) HB_INTERNAL ret_t name +#define HB_WASM_API_COMPOUND(ret_t, name) HB_INTERNAL void name + +#define HB_WASM_EXEC_ENV wasm_exec_env_t exec_env, +#define HB_WASM_EXEC_ENV_COMPOUND wasm_exec_env_t exec_env, ptr_t() retptr, + +#define ptr_t(type_t) uint32_t +#define ptr_d(type_t, name) uint32_t name##ptr + +#include "hb-wasm-api.h" + +#undef HB_WASM_BEGIN_DECLS +#undef HB_WASM_END_DECLS + + +enum { + hb_wasm_ref_type_none, + hb_wasm_ref_type_face, + hb_wasm_ref_type_font, + hb_wasm_ref_type_buffer, +}; + +HB_INTERNAL extern hb_user_data_key_t _hb_wasm_ref_type_key; + +#define nullref 0 + +#define HB_REF2OBJ(obj) \ + hb_##obj##_t *obj = nullptr; \ + HB_STMT_START { \ + (void) wasm_externref_ref2obj (obj##ptr, (void **) &obj); \ + /* Check object type. */ \ + /* This works because all our objects have the same hb_object_t layout. */ \ + if (unlikely (hb_##obj##_get_user_data (obj, &_hb_wasm_ref_type_key) != \ + (void *) hb_wasm_ref_type_##obj)) \ + obj = hb_##obj##_get_empty (); \ + } HB_STMT_END + +#define HB_OBJ2REF(obj) \ + uint32_t obj##ref = nullref; \ + HB_STMT_START { \ + hb_##obj##_set_user_data (obj, &_hb_wasm_ref_type_key, \ + (void *) hb_wasm_ref_type_##obj, \ + nullptr, false); \ + (void) wasm_externref_obj2ref (module_inst, obj, &obj##ref); \ + } HB_STMT_END + +#define HB_RETURN_STRUCT(type, name) \ + type *_name_ptr = nullptr; \ + { \ + if (likely (wasm_runtime_validate_app_addr (module_inst, \ + retptr, sizeof (type)))) \ + { \ + _name_ptr = (type *) wasm_runtime_addr_app_to_native (module_inst, retptr); \ + if (unlikely (!_name_ptr)) \ + return; \ + } \ + } \ + type &name = *_name_ptr + +#define HB_PTR_PARAM(type, name) \ + type *name = nullptr; \ + HB_STMT_START { \ + if (likely (wasm_runtime_validate_app_addr (module_inst, \ + name##ptr, sizeof (type)))) \ + name = (type *) wasm_runtime_addr_app_to_native (module_inst, name##ptr); \ + } HB_STMT_END + +#define HB_ARRAY_PARAM(type, name, length) \ + type *name = nullptr; \ + HB_STMT_START { \ + if (likely (!hb_unsigned_mul_overflows (length, sizeof (type)) && \ + wasm_runtime_validate_app_addr (module_inst, \ + name##ptr, length * sizeof (type)))) \ + name = (type *) wasm_runtime_addr_app_to_native (module_inst, name##ptr); \ + } HB_STMT_END + +#define HB_ARRAY_APP2NATIVE(type, name, length) \ + ((type *) (!hb_unsigned_mul_overflows (length, sizeof (type)) && \ + validate_app_addr (name, (length) * sizeof (type)) ? \ + addr_app_to_native (name) : nullptr)) + + +#endif /* HB_WASM_API_HH */ diff --git a/gfx/harfbuzz/src/hb-wasm-shape.cc b/gfx/harfbuzz/src/hb-wasm-shape.cc new file mode 100644 index 0000000000..a70b766646 --- /dev/null +++ b/gfx/harfbuzz/src/hb-wasm-shape.cc @@ -0,0 +1,470 @@ +/* + * 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 + */ + +#undef HB_DEBUG_WASM +#define HB_DEBUG_WASM 1 + +#include "hb-shaper-impl.hh" + +#ifdef HAVE_WASM + +/* Compile wasm-micro-runtime with: + * + * $ cmake -DWAMR_BUILD_MULTI_MODULE=1 -DWAMR_BUILD_REF_TYPES=1 -DWAMR_BUILD_FAST_JIT=1 + * $ make + * + * If you manage to build a wasm shared module successfully and want to use it, + * do the following: + * + * - Add -DWAMR_BUILD_MULTI_MODULE=1 to your cmake build for wasm-micro-runtime, + * + * - Remove the #define HB_WASM_NO_MODULES line below, + * + * - Install your shared module with name ending in .wasm in + * $(prefix)/$(libdir)/harfbuzz/wasm/ + * + * - Build your font's wasm code importing the shared modules with the desired + * name. This can be done eg.: __attribute__((import_module("graphite2"))) + * before each symbol in the shared-module's headers. + * + * - Try shaping your font and hope for the best... + * + * I haven't been able to get this to work since emcc's support for shared libraries + * requires support from the host that seems to be missing from wasm-micro-runtime? + */ + +#include "hb-wasm-api.hh" +#include "hb-wasm-api-list.hh" + +#ifndef HB_WASM_NO_MODULES +#define HB_WASM_NO_MODULES +#endif + + +#ifndef HB_WASM_NO_MODULES +static bool HB_UNUSED +_hb_wasm_module_reader (const char *module_name, + uint8_t **p_buffer, uint32_t *p_size) +{ + char path[sizeof (HB_WASM_MODULE_DIR) + 64] = HB_WASM_MODULE_DIR "/"; + strncat (path, module_name, sizeof (path) - sizeof (HB_WASM_MODULE_DIR) - 16); + strncat (path, ".wasm", 6); + + auto *blob = hb_blob_create_from_file (path); + + unsigned length; + auto *data = hb_blob_get_data (blob, &length); + + *p_buffer = (uint8_t *) hb_malloc (length); + + if (length && !p_buffer) + return false; + + memcpy (*p_buffer, data, length); + *p_size = length; + + hb_blob_destroy (blob); + + return true; +} + +static void HB_UNUSED +_hb_wasm_module_destroyer (uint8_t *buffer, uint32_t size) +{ + hb_free (buffer); +} +#endif + +/* + * shaper face data + */ + +#define HB_WASM_TAG_WASM HB_TAG('W','a','s','m') + +struct hb_wasm_shape_plan_t { + wasm_module_inst_t module_inst; + wasm_exec_env_t exec_env; + ptr_d(void, wasm_shape_plan); +}; + +struct hb_wasm_face_data_t { + hb_blob_t *wasm_blob; + wasm_module_t wasm_module; + mutable hb_atomic_ptr_t<hb_wasm_shape_plan_t> plan; +}; + +static bool +_hb_wasm_init () +{ + /* XXX + * + * Umm. Make this threadsafe. How?! + * It's clunky that we can't allocate a static mutex. + * So we have to first allocate one on the heap atomically... + * + * Do we also need to lock around module creation? + * + * Also, wasm-micro-runtime uses a singleton instance. So if + * another library or client uses it, all bets are off. :-( + * If nothing else, around HB_REF2OBJ(). + */ + + static bool initialized; + if (initialized) + return true; + + RuntimeInitArgs init_args; + hb_memset (&init_args, 0, sizeof (RuntimeInitArgs)); + + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = (void *) hb_malloc; + init_args.mem_alloc_option.allocator.realloc_func = (void *) hb_realloc; + init_args.mem_alloc_option.allocator.free_func = (void *) hb_free; + + // Native symbols need below registration phase + init_args.n_native_symbols = ARRAY_LENGTH (_hb_wasm_native_symbols); + init_args.native_module_name = "env"; + init_args.native_symbols = _hb_wasm_native_symbols; + + if (unlikely (!wasm_runtime_full_init (&init_args))) + { + DEBUG_MSG (WASM, nullptr, "Init runtime environment failed."); + return false; + } + +#ifndef HB_WASM_NO_MODULES + wasm_runtime_set_module_reader (_hb_wasm_module_reader, + _hb_wasm_module_destroyer); +#endif + + initialized = true; + return true; +} + +hb_wasm_face_data_t * +_hb_wasm_shaper_face_data_create (hb_face_t *face) +{ + char error[128]; + hb_wasm_face_data_t *data = nullptr; + hb_blob_t *wasm_blob = nullptr; + wasm_module_t wasm_module = nullptr; + + wasm_blob = hb_face_reference_table (face, HB_WASM_TAG_WASM); + unsigned length = hb_blob_get_length (wasm_blob); + if (!length) + goto fail; + + if (!_hb_wasm_init ()) + goto fail; + + wasm_module = wasm_runtime_load ((uint8_t *) hb_blob_get_data_writable (wasm_blob, nullptr), + length, error, sizeof (error)); + if (unlikely (!wasm_module)) + { + DEBUG_MSG (WASM, nullptr, "Load wasm module failed: %s", error); + goto fail; + } + + data = (hb_wasm_face_data_t *) hb_calloc (1, sizeof (hb_wasm_face_data_t)); + if (unlikely (!data)) + goto fail; + + data->wasm_blob = wasm_blob; + data->wasm_module = wasm_module; + + return data; + +fail: + if (wasm_module) + wasm_runtime_unload (wasm_module); + hb_blob_destroy (wasm_blob); + hb_free (data); + return nullptr; +} + +static hb_wasm_shape_plan_t * +acquire_shape_plan (hb_face_t *face, + const hb_wasm_face_data_t *face_data) +{ + char error[128]; + + /* Fetch cached one if available. */ + hb_wasm_shape_plan_t *plan = face_data->plan.get_acquire (); + if (likely (plan && face_data->plan.cmpexch (plan, nullptr))) + return plan; + + plan = (hb_wasm_shape_plan_t *) hb_calloc (1, sizeof (hb_wasm_shape_plan_t)); + + wasm_module_inst_t module_inst = nullptr; + wasm_exec_env_t exec_env = nullptr; + wasm_function_inst_t func = nullptr; + + constexpr uint32_t stack_size = 32 * 1024, heap_size = 2 * 1024 * 1024; + + module_inst = plan->module_inst = wasm_runtime_instantiate (face_data->wasm_module, + stack_size, heap_size, + error, sizeof (error)); + if (unlikely (!module_inst)) + { + DEBUG_MSG (WASM, face_data, "Create wasm module instance failed: %s", error); + goto fail; + } + + exec_env = plan->exec_env = wasm_runtime_create_exec_env (module_inst, + stack_size); + if (unlikely (!exec_env)) { + DEBUG_MSG (WASM, face_data, "Create wasm execution environment failed."); + goto fail; + } + + func = wasm_runtime_lookup_function (module_inst, "shape_plan_create", nullptr); + if (func) + { + wasm_val_t results[1]; + wasm_val_t arguments[1]; + + HB_OBJ2REF (face); + if (unlikely (!faceref)) + { + DEBUG_MSG (WASM, face_data, "Failed to register face object."); + goto fail; + } + + results[0].kind = WASM_I32; + arguments[0].kind = WASM_I32; + arguments[0].of.i32 = faceref; + bool ret = wasm_runtime_call_wasm_a (exec_env, func, + ARRAY_LENGTH (results), results, + ARRAY_LENGTH (arguments), arguments); + + if (unlikely (!ret)) + { + DEBUG_MSG (WASM, module_inst, "Calling shape_plan_create() failed: %s", + wasm_runtime_get_exception (module_inst)); + goto fail; + } + plan->wasm_shape_planptr = results[0].of.i32; + } + + return plan; + +fail: + + if (exec_env) + wasm_runtime_destroy_exec_env (exec_env); + if (module_inst) + wasm_runtime_deinstantiate (module_inst); + hb_free (plan); + return nullptr; +} + +static void +release_shape_plan (const hb_wasm_face_data_t *face_data, + hb_wasm_shape_plan_t *plan, + bool cache = false) +{ + if (cache && face_data->plan.cmpexch (nullptr, plan)) + return; + + auto *module_inst = plan->module_inst; + auto *exec_env = plan->exec_env; + + /* Is there even any point to having a shape_plan_destroy function + * and calling it? */ + if (plan->wasm_shape_planptr) + { + + auto *func = wasm_runtime_lookup_function (module_inst, "shape_plan_destroy", nullptr); + if (func) + { + wasm_val_t arguments[1]; + + arguments[0].kind = WASM_I32; + arguments[0].of.i32 = plan->wasm_shape_planptr; + bool ret = wasm_runtime_call_wasm_a (exec_env, func, + 0, nullptr, + ARRAY_LENGTH (arguments), arguments); + + if (unlikely (!ret)) + { + DEBUG_MSG (WASM, module_inst, "Calling shape_plan_destroy() failed: %s", + wasm_runtime_get_exception (module_inst)); + } + } + } + + wasm_runtime_destroy_exec_env (exec_env); + wasm_runtime_deinstantiate (module_inst); + hb_free (plan); +} + +void +_hb_wasm_shaper_face_data_destroy (hb_wasm_face_data_t *data) +{ + if (data->plan.get_relaxed ()) + release_shape_plan (data, data->plan); + wasm_runtime_unload (data->wasm_module); + hb_blob_destroy (data->wasm_blob); + hb_free (data); +} + + +/* + * shaper font data + */ + +struct hb_wasm_font_data_t {}; + +hb_wasm_font_data_t * +_hb_wasm_shaper_font_data_create (hb_font_t *font HB_UNUSED) +{ + return (hb_wasm_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; +} + +void +_hb_wasm_shaper_font_data_destroy (hb_wasm_font_data_t *data HB_UNUSED) +{ +} + + +/* + * shaper + */ + +hb_bool_t +_hb_wasm_shape (hb_shape_plan_t *shape_plan, + hb_font_t *font, + hb_buffer_t *buffer, + const hb_feature_t *features, + unsigned int num_features) +{ + if (unlikely (buffer->in_error ())) + return false; + + bool ret = true; + hb_face_t *face = font->face; + const hb_wasm_face_data_t *face_data = face->data.wasm; + + bool retried = false; + if (0) + { +retry: + DEBUG_MSG (WASM, font, "Retrying..."); + } + + wasm_function_inst_t func = nullptr; + + hb_wasm_shape_plan_t *plan = acquire_shape_plan (face, face_data); + if (unlikely (!plan)) + { + DEBUG_MSG (WASM, face_data, "Acquiring shape-plan failed."); + return false; + } + + auto *module_inst = plan->module_inst; + auto *exec_env = plan->exec_env; + + HB_OBJ2REF (font); + HB_OBJ2REF (buffer); + if (unlikely (!fontref || !bufferref)) + { + DEBUG_MSG (WASM, module_inst, "Failed to register objects."); + goto fail; + } + + func = wasm_runtime_lookup_function (module_inst, "shape", nullptr); + if (unlikely (!func)) + { + DEBUG_MSG (WASM, module_inst, "Shape function not found."); + goto fail; + } + + wasm_val_t results[1]; + wasm_val_t arguments[5]; + + results[0].kind = WASM_I32; + arguments[0].kind = WASM_I32; + arguments[0].of.i32 = plan->wasm_shape_planptr; + arguments[1].kind = WASM_I32; + arguments[1].of.i32 = fontref; + arguments[2].kind = WASM_I32; + arguments[2].of.i32 = bufferref; + arguments[3].kind = WASM_I32; + arguments[3].of.i32 = num_features ? wasm_runtime_module_dup_data (module_inst, + (const char *) features, + num_features * sizeof (features[0])) : 0; + arguments[4].kind = WASM_I32; + arguments[4].of.i32 = num_features; + + ret = wasm_runtime_call_wasm_a (exec_env, func, + ARRAY_LENGTH (results), results, + ARRAY_LENGTH (arguments), arguments); + + if (num_features) + wasm_runtime_module_free (module_inst, arguments[2].of.i32); + + if (unlikely (!ret || !results[0].of.i32)) + { + DEBUG_MSG (WASM, module_inst, "Calling shape() failed: %s", + wasm_runtime_get_exception (module_inst)); + if (!buffer->ensure_unicode ()) + { + DEBUG_MSG (WASM, font, "Shape failed but buffer is not in Unicode; failing..."); + goto fail; + } + if (retried) + { + DEBUG_MSG (WASM, font, "Giving up..."); + goto fail; + } + buffer->successful = true; + retried = true; + release_shape_plan (face_data, plan); + plan = nullptr; + goto retry; + } + + /* TODO Regularize clusters according to direction & cluster level, + * such that client doesn't crash with unmet expectations. */ + + if (!results[0].of.i32) + { +fail: + ret = false; + } + + release_shape_plan (face_data, plan, ret); + + if (ret) + { + buffer->clear_glyph_flags (); + buffer->unsafe_to_break (); + } + + return ret; +} + +#endif diff --git a/gfx/harfbuzz/src/hb.h b/gfx/harfbuzz/src/hb.h new file mode 100644 index 0000000000..5a6ae6607c --- /dev/null +++ b/gfx/harfbuzz/src/hb.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2009 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): Behdad Esfahbod + */ + +#ifndef HB_H +#define HB_H +#define HB_H_IN + +#include "hb-blob.h" +#include "hb-buffer.h" +#include "hb-common.h" +#include "hb-deprecated.h" +#include "hb-draw.h" +#include "hb-face.h" +#include "hb-font.h" +#include "hb-map.h" +#include "hb-paint.h" +#include "hb-set.h" +#include "hb-shape.h" +#include "hb-shape-plan.h" +#include "hb-style.h" +#include "hb-unicode.h" +#include "hb-version.h" + +HB_BEGIN_DECLS +HB_END_DECLS + +#undef HB_H_IN +#endif /* HB_H */ diff --git a/gfx/harfbuzz/src/hb.hh b/gfx/harfbuzz/src/hb.hh new file mode 100644 index 0000000000..972608d6a3 --- /dev/null +++ b/gfx/harfbuzz/src/hb.hh @@ -0,0 +1,543 @@ +/* + * Copyright © 2007,2008,2009 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): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_HH +#define HB_HH + +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC +#ifdef _MSC_VER +#pragma warning( disable: 4068 ) /* Unknown pragma */ +#endif +#if defined(__GNUC__) || defined(__clang__) +/* Rules: + * + * - All pragmas are declared GCC even if they are clang ones. Otherwise GCC + * nags, even though we instruct it to ignore -Wunknown-pragmas. ¯\_(ツ)_/¯ + * + * - Within each category, keep sorted. + * + * - Warnings whose scope can be expanded in future compiler versions shall + * be declared as "warning". Otherwise, either ignored or error. + */ + +/* Setup. Don't sort order within this category. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING +#pragma GCC diagnostic warning "-Wall" +#pragma GCC diagnostic warning "-Wextra" +#endif +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#endif +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING +//#pragma GCC diagnostic warning "-Weverything" +#endif + +/* Error. Should never happen. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_ERROR +#pragma GCC diagnostic error "-Wbitwise-instead-of-logical" +#pragma GCC diagnostic error "-Wcast-align" +#pragma GCC diagnostic error "-Wcast-function-type" +#pragma GCC diagnostic error "-Wconstant-conversion" +#pragma GCC diagnostic error "-Wcomma" +#pragma GCC diagnostic error "-Wdelete-non-virtual-dtor" +#pragma GCC diagnostic error "-Wembedded-directive" +#pragma GCC diagnostic error "-Wextra-semi-stmt" +#pragma GCC diagnostic error "-Wformat-security" +#pragma GCC diagnostic error "-Wimplicit-function-declaration" +#pragma GCC diagnostic error "-Winit-self" +#pragma GCC diagnostic error "-Winjected-class-name" +#pragma GCC diagnostic error "-Wmissing-braces" +#pragma GCC diagnostic error "-Wmissing-declarations" +#pragma GCC diagnostic error "-Wmissing-prototypes" +#pragma GCC diagnostic error "-Wnarrowing" +#pragma GCC diagnostic error "-Wnested-externs" +#pragma GCC diagnostic error "-Wold-style-definition" +#pragma GCC diagnostic error "-Wpointer-arith" +#pragma GCC diagnostic error "-Wredundant-decls" +#pragma GCC diagnostic error "-Wreorder" +#pragma GCC diagnostic error "-Wsign-compare" +#pragma GCC diagnostic error "-Wstrict-prototypes" +#pragma GCC diagnostic error "-Wstring-conversion" +#pragma GCC diagnostic error "-Wswitch-enum" +#pragma GCC diagnostic error "-Wtautological-overlap-compare" +#pragma GCC diagnostic error "-Wunneeded-internal-declaration" +#pragma GCC diagnostic error "-Wunused" +#pragma GCC diagnostic error "-Wunused-local-typedefs" +#pragma GCC diagnostic error "-Wunused-value" +#pragma GCC diagnostic error "-Wunused-variable" +#pragma GCC diagnostic error "-Wvla" +#pragma GCC diagnostic error "-Wwrite-strings" +#endif + +/* Warning. To be investigated if happens. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_WARNING +#pragma GCC diagnostic warning "-Wbuiltin-macro-redefined" +#pragma GCC diagnostic warning "-Wdeprecated" +#pragma GCC diagnostic warning "-Wdeprecated-declarations" +#pragma GCC diagnostic warning "-Wdisabled-optimization" +#pragma GCC diagnostic warning "-Wdouble-promotion" +#pragma GCC diagnostic warning "-Wformat=2" +#pragma GCC diagnostic warning "-Wformat-signedness" +#pragma GCC diagnostic warning "-Wignored-pragma-optimize" +#pragma GCC diagnostic warning "-Wlogical-op" +#pragma GCC diagnostic warning "-Wmaybe-uninitialized" +#pragma GCC diagnostic warning "-Wmissing-format-attribute" +#pragma GCC diagnostic warning "-Wundef" +#pragma GCC diagnostic warning "-Wunsafe-loop-optimizations" +#pragma GCC diagnostic warning "-Wunused-but-set-variable" +#endif + +/* Ignored currently, but should be fixed at some point. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED +#pragma GCC diagnostic ignored "-Wconversion" // TODO fix +#pragma GCC diagnostic ignored "-Wshadow" // TODO fix +#pragma GCC diagnostic ignored "-Wunused-parameter" // TODO fix +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic ignored "-Wunused-result" // TODO fix +#endif +#endif + +/* Ignored intentionally. */ +#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#pragma GCC diagnostic ignored "-Wcast-function-type-strict" // https://github.com/harfbuzz/harfbuzz/pull/3859#issuecomment-1295409126 +#pragma GCC diagnostic ignored "-Wdangling-reference" // https://github.com/harfbuzz/harfbuzz/issues/4043 +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#pragma GCC diagnostic ignored "-Wformat-zero-length" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic ignored "-Wpacked" // Erratic impl in clang +#pragma GCC diagnostic ignored "-Wrange-loop-analysis" // https://github.com/harfbuzz/harfbuzz/issues/2834 +#pragma GCC diagnostic ignored "-Wstrict-aliasing" +#pragma GCC diagnostic ignored "-Wtype-limits" +#pragma GCC diagnostic ignored "-Wc++11-compat" // only gcc raises it +#endif + +#endif +#endif + + +#include "hb-config.hh" +#include "hb-limits.hh" + + +/* + * Following added based on what AC_USE_SYSTEM_EXTENSIONS adds to + * config.h.in. Copied here for the convenience of those embedding + * HarfBuzz and not using our build system. + */ +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + +#if defined (_MSC_VER) && defined (HB_DLL_EXPORT) +#define HB_EXTERN __declspec (dllexport) extern +#endif + +#include "hb.h" +#define HB_H_IN +#include "hb-ot.h" +#define HB_OT_H_IN +#include "hb-aat.h" +#define HB_AAT_H_IN + +#include <cassert> +#include <cfloat> +#include <climits> +#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) +# define _USE_MATH_DEFINES +#endif +#include <cmath> +#include <cstdarg> +#include <cstddef> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#if (defined(_MSC_VER) && _MSC_VER >= 1500) || defined(__MINGW32__) +#ifdef __MINGW32_VERSION +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#else +#include <intrin.h> +#endif +#endif + +#ifdef _WIN32 +#include <windows.h> +#include <winapifamily.h> +#endif + +#define HB_PASTE1(a,b) a##b +#define HB_PASTE(a,b) HB_PASTE1(a,b) + + +/* Compile-time custom allocator support. */ + +#if !defined(HB_CUSTOM_MALLOC) \ + && defined(hb_malloc_impl) \ + && defined(hb_calloc_impl) \ + && defined(hb_realloc_impl) \ + && defined(hb_free_impl) +#define HB_CUSTOM_MALLOC +#endif + +#ifdef HB_CUSTOM_MALLOC +extern "C" void* hb_malloc_impl(size_t size); +extern "C" void* hb_calloc_impl(size_t nmemb, size_t size); +extern "C" void* hb_realloc_impl(void *ptr, size_t size); +extern "C" void hb_free_impl(void *ptr); +#define hb_malloc hb_malloc_impl +#define hb_calloc hb_calloc_impl +#define hb_realloc hb_realloc_impl +#define hb_free hb_free_impl +#else +#define hb_malloc malloc +#define hb_calloc calloc +#define hb_realloc realloc +#define hb_free free +#endif + + +/* + * Compiler attributes + */ + +// gcc 10 has __has_builtin but not earlier versions. Sanction any gcc >= 5 +// clang defines it so no need. +#ifdef __has_builtin +#define hb_has_builtin __has_builtin +#else +#define hb_has_builtin(x) ((defined(__GNUC__) && __GNUC__ >= 5)) +#endif + +#if defined(__OPTIMIZE__) && hb_has_builtin(__builtin_expect) +#define likely(expr) __builtin_expect (bool(expr), 1) +#define unlikely(expr) __builtin_expect (bool(expr), 0) +#else +#define likely(expr) (expr) +#define unlikely(expr) (expr) +#endif + +#if !defined(__GNUC__) && !defined(__clang__) +#undef __attribute__ +#define __attribute__(x) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx))) +#else +#define HB_PRINTF_FUNC(format_idx, arg_idx) +#endif +#if defined(__GNUC__) && (__GNUC__ >= 4) || (__clang__) +#define HB_UNUSED __attribute__((unused)) +#elif defined(_MSC_VER) /* https://github.com/harfbuzz/harfbuzz/issues/635 */ +#define HB_UNUSED __pragma(warning(suppress: 4100 4101)) +#else +#define HB_UNUSED +#endif + +#ifndef HB_INTERNAL +# if !defined(HB_NO_VISIBILITY) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(_MSC_VER) && !defined(__SUNPRO_CC) +# define HB_INTERNAL __attribute__((__visibility__("hidden"))) +# elif defined(__MINGW32__) + /* We use -export-symbols on mingw32, since it does not support visibility attributes. */ +# define HB_INTERNAL +# elif defined (_MSC_VER) && defined (HB_DLL_EXPORT) + /* We do not try to export internal symbols on Visual Studio */ +# define HB_INTERNAL +#else +# define HB_INTERNAL +# define HB_NO_VISIBILITY 1 +# endif +#endif + +/* https://github.com/harfbuzz/harfbuzz/issues/1651 */ +#if defined(__clang__) && __clang_major__ < 10 +#define static_const static +#else +#define static_const static const +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define HB_FUNC __PRETTY_FUNCTION__ +#elif defined(_MSC_VER) +#define HB_FUNC __FUNCSIG__ +#else +#define HB_FUNC __func__ +#endif + +#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5140) +/* https://github.com/harfbuzz/harfbuzz/issues/630 */ +#define __restrict +#endif + +#ifndef HB_ALWAYS_INLINE +#if defined(_MSC_VER) +#define HB_ALWAYS_INLINE __forceinline +#else +#define HB_ALWAYS_INLINE __attribute__((always_inline)) inline +#endif +#endif + +/* + * Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411 + * HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch + * cases that fall through without a break or return statement. HB_FALLTHROUGH + * is only needed on cases that have code: + * + * switch (foo) { + * case 1: // These cases have no code. No fallthrough annotations are needed. + * case 2: + * case 3: + * foo = 4; // This case has code, so a fallthrough annotation is needed: + * HB_FALLTHROUGH; + * default: + * return foo; + * } + */ +#if defined(__clang__) && __cplusplus >= 201103L + /* clang's fallthrough annotations are only available starting in C++11. */ +# define HB_FALLTHROUGH [[clang::fallthrough]] +#elif defined(__GNUC__) && (__GNUC__ >= 7) + /* GNU fallthrough attribute is available from GCC7 */ +# define HB_FALLTHROUGH __attribute__((fallthrough)) +#elif defined(_MSC_VER) + /* + * MSVC's __fallthrough annotations are checked by /analyze (Code Analysis): + * https://msdn.microsoft.com/en-us/library/ms235402%28VS.80%29.aspx + */ +# include <sal.h> +# define HB_FALLTHROUGH __fallthrough +#else +# define HB_FALLTHROUGH /* FALLTHROUGH */ +#endif + +/* A tag to enforce use of return value for a function */ +#if __cplusplus >= 201703L +# define HB_NODISCARD [[nodiscard]] +#elif defined(__GNUC__) || defined(__clang__) +# define HB_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +# define HB_NODISCARD _Check_return_ +#else +# define HB_NODISCARD +#endif + +/* https://github.com/harfbuzz/harfbuzz/issues/1852 */ +#if defined(__clang__) && !(defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__))) +/* Disable certain sanitizer errors. */ +/* https://github.com/harfbuzz/harfbuzz/issues/1247 */ +#define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW __attribute__((no_sanitize("signed-integer-overflow"))) +#else +#define HB_NO_SANITIZE_SIGNED_INTEGER_OVERFLOW +#endif + + +#ifdef _WIN32 + /* We need Windows Vista for both Uniscribe backend and for + * MemoryBarrier. We don't support compiling on Windows XP, + * though we run on it fine. */ +# if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600 +# undef _WIN32_WINNT +# endif +# ifndef _WIN32_WINNT +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define _WIN32_WINNT 0x0600 +# endif +# endif +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# ifndef STRICT +# define STRICT 1 +# endif + +# if defined(_WIN32_WCE) + /* Some things not defined on Windows CE. */ +# define vsnprintf _vsnprintf +# ifndef HB_NO_GETENV +# define HB_NO_GETENV +# endif +# if _WIN32_WCE < 0x800 +# define HB_NO_SETLOCALE +# define HB_NO_ERRNO +# endif +# elif !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# ifndef HB_NO_GETENV +# define HB_NO_GETENV +# endif +# endif +# if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +# endif +#endif + +#ifdef HB_NO_GETENV +#define getenv(Name) nullptr +#endif + +#ifndef HB_NO_ERRNO +# include <cerrno> +#else +static int HB_UNUSED _hb_errno = 0; +# undef errno +# define errno _hb_errno +#endif + +#define HB_STMT_START do +#define HB_STMT_END while (0) + +#if defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT) +/* atexit() is only safe to be called from shared libraries on certain + * platforms. Whitelist. + * https://bugs.freedesktop.org/show_bug.cgi?id=82246 */ +# if defined(__linux) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2,3) +/* From atexit() manpage, it's safe with glibc 2.2.3 on Linux. */ +# define HB_USE_ATEXIT 1 +# endif +# elif defined(_MSC_VER) || defined(__MINGW32__) +/* For MSVC: + * https://msdn.microsoft.com/en-us/library/tze57ck3.aspx + * https://msdn.microsoft.com/en-us/library/zk17ww08.aspx + * mingw32 headers say atexit is safe to use in shared libraries. + */ +# define HB_USE_ATEXIT 1 +# elif defined(__ANDROID__) +/* This is available since Android NKD r8 or r8b: + * https://issuetracker.google.com/code/p/android/issues/detail?id=6455 + */ +# define HB_USE_ATEXIT 1 +# elif defined(__APPLE__) +/* For macOS and related platforms, the atexit man page indicates + * that it will be invoked when the library is unloaded, not only + * at application exit. + */ +# define HB_USE_ATEXIT 1 +# endif +#endif /* defined(HAVE_ATEXIT) && !defined(HB_USE_ATEXIT) */ +#ifdef HB_NO_ATEXIT +# undef HB_USE_ATEXIT +#endif +#ifndef HB_USE_ATEXIT +# define HB_USE_ATEXIT 0 +#endif +#ifndef hb_atexit +#if !HB_USE_ATEXIT +# define hb_atexit(_) HB_STMT_START { if (0) (_) (); } HB_STMT_END +#else /* HB_USE_ATEXIT */ +# ifdef HAVE_ATEXIT +# define hb_atexit atexit +# else + template <void (*function) (void)> struct hb_atexit_t { ~hb_atexit_t () { function (); } }; +# define hb_atexit(f) static hb_atexit_t<f> _hb_atexit_##__LINE__; +# endif +#endif +#endif + + +// Locale business + +#if !defined(HB_NO_SETLOCALE) && (!defined(HAVE_NEWLOCALE) || !defined(HAVE_USELOCALE)) +#define HB_NO_SETLOCALE 1 +#endif + +#ifndef HB_NO_SETLOCALE + +#include <locale.h> +#ifdef HAVE_XLOCALE_H +#include <xlocale.h> // Needed on BSD/OS X for uselocale +#endif + +#ifdef WIN32 +#define hb_locale_t _locale_t +#else +#define hb_locale_t locale_t +#endif +#define hb_setlocale setlocale +#define hb_uselocale uselocale + +#else + +#define hb_locale_t void * +#define hb_setlocale(Category, Locale) "C" +#define hb_uselocale(Locale) ((hb_locale_t) 0) + +#endif + + +/* Lets assert int types. Saves trouble down the road. */ +static_assert ((sizeof (hb_codepoint_t) == 4), ""); +static_assert ((sizeof (hb_position_t) == 4), ""); +static_assert ((sizeof (hb_mask_t) == 4), ""); +static_assert ((sizeof (hb_var_int_t) == 4), ""); + + +/* Pie time. */ +// https://github.com/harfbuzz/harfbuzz/issues/4166 +#define HB_PI 3.14159265358979f +#define HB_2_PI (2.f * HB_PI) + + +/* Headers we include for everyone. Keep topologically sorted by dependency. + * They express dependency amongst themselves, but no other file should include + * them directly.*/ +#include "hb-cplusplus.hh" +#include "hb-meta.hh" +#include "hb-mutex.hh" +#include "hb-number.hh" +#include "hb-atomic.hh" // Requires: hb-meta +#include "hb-null.hh" // Requires: hb-meta +#include "hb-algs.hh" // Requires: hb-meta hb-null hb-number +#include "hb-iter.hh" // Requires: hb-algs hb-meta +#include "hb-debug.hh" // Requires: hb-algs hb-atomic +#include "hb-array.hh" // Requires: hb-algs hb-iter hb-null +#include "hb-vector.hh" // Requires: hb-array hb-null +#include "hb-object.hh" // Requires: hb-atomic hb-mutex hb-vector + +#endif /* HB_HH */ diff --git a/gfx/harfbuzz/src/justify.py b/gfx/harfbuzz/src/justify.py new file mode 100644 index 0000000000..db2fb70104 --- /dev/null +++ b/gfx/harfbuzz/src/justify.py @@ -0,0 +1,288 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, HarfBuzz as hb + + +POOL = {} + + +def move_to_f(funcs, draw_data, st, to_x, to_y, user_data): + context = POOL[draw_data] + context.move_to(to_x, to_y) + + +def line_to_f(funcs, draw_data, st, to_x, to_y, user_data): + context = POOL[draw_data] + context.line_to(to_x, to_y) + + +def cubic_to_f( + funcs, + draw_data, + st, + control1_x, + control1_y, + control2_x, + control2_y, + to_x, + to_y, + user_data, +): + context = POOL[draw_data] + context.curve_to(control1_x, control1_y, control2_x, control2_y, to_x, to_y) + + +def close_path_f(funcs, draw_data, st, user_data): + context = POOL[draw_data] + context.close_path() + + +DFUNCS = hb.draw_funcs_create() +hb.draw_funcs_set_move_to_func(DFUNCS, move_to_f, None) +hb.draw_funcs_set_line_to_func(DFUNCS, line_to_f, None) +hb.draw_funcs_set_cubic_to_func(DFUNCS, cubic_to_f, None) +hb.draw_funcs_set_close_path_func(DFUNCS, close_path_f, None) + + +def push_transform_f(funcs, paint_data, xx, yx, xy, yy, dx, dy, user_data): + raise NotImplementedError + + +def pop_transform_f(funcs, paint_data, user_data): + raise NotImplementedError + + +def color_f(funcs, paint_data, is_foreground, color, user_data): + context = POOL[paint_data] + r = hb.color_get_red(color) / 255 + g = hb.color_get_green(color) / 255 + b = hb.color_get_blue(color) / 255 + a = hb.color_get_alpha(color) / 255 + context.set_source_rgba(r, g, b, a) + context.paint() + + +def push_clip_rectangle_f(funcs, paint_data, xmin, ymin, xmax, ymax, user_data): + context = POOL[paint_data] + context.save() + context.rectangle(xmin, ymin, xmax, ymax) + context.clip() + + +def push_clip_glyph_f(funcs, paint_data, glyph, font, user_data): + context = POOL[paint_data] + context.save() + context.new_path() + hb.font_draw_glyph(font, glyph, DFUNCS, paint_data) + context.close_path() + context.clip() + + +def pop_clip_f(funcs, paint_data, user_data): + context = POOL[paint_data] + context.restore() + + +def push_group_f(funcs, paint_data, user_data): + raise NotImplementedError + + +def pop_group_f(funcs, paint_data, mode, user_data): + raise NotImplementedError + + +PFUNCS = hb.paint_funcs_create() +hb.paint_funcs_set_push_transform_func(PFUNCS, push_transform_f, None) +hb.paint_funcs_set_pop_transform_func(PFUNCS, pop_transform_f, None) +hb.paint_funcs_set_color_func(PFUNCS, color_f, None) +hb.paint_funcs_set_push_clip_glyph_func(PFUNCS, push_clip_glyph_f, None) +hb.paint_funcs_set_push_clip_rectangle_func(PFUNCS, push_clip_rectangle_f, None) +hb.paint_funcs_set_pop_clip_func(PFUNCS, pop_clip_f, None) +hb.paint_funcs_set_push_group_func(PFUNCS, push_group_f, None) +hb.paint_funcs_set_pop_group_func(PFUNCS, pop_group_f, None) + + +def makebuffer(words): + buf = hb.buffer_create() + + text = " ".join(words) + hb.buffer_add_codepoints(buf, [ord(c) for c in text], 0, len(text)) + + hb.buffer_guess_segment_properties(buf) + + return buf + + +def justify(face, words, advance, target_advance): + font = hb.font_create(face) + buf = makebuffer(words) + + wiggle = 5 + shrink = target_advance - wiggle < advance + expand = target_advance + wiggle > advance + + ret, advance, tag, value = hb.shape_justify( + font, + buf, + None, + None, + target_advance, + target_advance, + advance, + ) + + if not ret: + return False, buf, None + + if tag: + variation = hb.variation_t() + variation.tag = tag + variation.value = value + else: + variation = None + + if shrink and advance > target_advance + wiggle: + return False, buf, variation + if expand and advance < target_advance - wiggle: + return False, buf, variation + + return True, buf, variation + + +def shape(face, words): + font = hb.font_create(face) + buf = makebuffer(words) + hb.shape(font, buf) + positions = hb.buffer_get_glyph_positions(buf) + advance = sum(p.x_advance for p in positions) + return buf, advance + + +def typeset(face, text, target_advance): + lines = [] + words = [] + for word in text.split(): + words.append(word) + buf, advance = shape(face, words) + if advance > target_advance: + # Shrink + ret, buf, variation = justify(face, words, advance, target_advance) + if ret: + lines.append((buf, variation)) + words = [] + # If if fails, pop the last word and shrink, and hope for the best. + # A too short line is better than too long. + elif len(words) > 1: + words.pop() + _, buf, variation = justify(face, words, advance, target_advance) + lines.append((buf, variation)) + words = [word] + # But if it is one word, meh. + else: + lines.append((buf, variation)) + words = [] + + # Justify last line + if words: + _, buf, variation = justify(face, words, advance, target_advance) + lines.append((buf, variation)) + + return lines + + +def render(face, text, context, width, height, fontsize): + font = hb.font_create(face) + + margin = fontsize * 2 + scale = fontsize / hb.face_get_upem(face) + target_advance = (width - (margin * 2)) / scale + + lines = typeset(face, text, target_advance) + + _, extents = hb.font_get_h_extents(font) + lineheight = extents.ascender - extents.descender + extents.line_gap + lineheight *= scale + + context.save() + context.translate(0, margin) + context.set_font_size(12) + context.set_source_rgb(1, 0, 0) + for buf, variation in lines: + rtl = hb.buffer_get_direction(buf) == hb.direction_t.RTL + if rtl: + hb.buffer_reverse(buf) + infos = hb.buffer_get_glyph_infos(buf) + positions = hb.buffer_get_glyph_positions(buf) + advance = sum(p.x_advance for p in positions) + + context.translate(0, lineheight) + context.save() + + context.save() + context.move_to(0, -20) + if variation: + tag = hb.tag_to_string(variation.tag).decode("ascii") + context.show_text(f" {tag}={variation.value:g}") + context.move_to(0, 0) + context.show_text(f" {advance:g}/{target_advance:g}") + context.restore() + + if variation: + hb.font_set_variations(font, [variation]) + + context.translate(margin, 0) + context.scale(scale, -scale) + + if rtl: + context.translate(target_advance, 0) + + for info, pos in zip(infos, positions): + if rtl: + context.translate(-pos.x_advance, pos.y_advance) + context.save() + context.translate(pos.x_offset, pos.y_offset) + hb.font_paint_glyph(font, info.codepoint, PFUNCS, id(context), 0, 0x0000FF) + context.restore() + if not rtl: + context.translate(+pos.x_advance, pos.y_advance) + + context.restore() + context.restore() + + +def main(fontpath, textpath): + fontsize = 70 + + blob = hb.blob_create_from_file(fontpath) + face = hb.face_create(blob, 0) + + with open(textpath) as f: + text = f.read() + + def on_draw(da, context): + alloc = da.get_allocation() + POOL[id(context)] = context + render(face, text, context, alloc.width, alloc.height, fontsize) + del POOL[id(context)] + + drawingarea = Gtk.DrawingArea() + drawingarea.connect("draw", on_draw) + + win = Gtk.Window() + win.connect("destroy", Gtk.main_quit) + win.set_default_size(1000, 700) + win.add(drawingarea) + + win.show_all() + Gtk.main() + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="HarfBuzz justification demo.") + parser.add_argument("fontfile", help="font file") + parser.add_argument("textfile", help="text") + args = parser.parse_args() + main(args.fontfile, args.textfile) diff --git a/gfx/harfbuzz/src/main.cc b/gfx/harfbuzz/src/main.cc new file mode 100644 index 0000000000..4af9569d11 --- /dev/null +++ b/gfx/harfbuzz/src/main.cc @@ -0,0 +1,532 @@ +/* + * Copyright © 2007,2008,2009 Red Hat, Inc. + * Copyright © 2018,2019,2020 Ebrahim Byagowi + * Copyright © 2018 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. + * + * Red Hat Author(s): Behdad Esfahbod + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "hb.h" +#include "hb-ot.h" + +#include <cassert> +#include <cstdlib> +#include <cstdio> +#include <cstring> + +#ifdef HB_NO_OPEN +#define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty () +#endif + +#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) +static void +svg_dump (hb_face_t *face, unsigned face_index) +{ + unsigned glyph_count = hb_face_get_glyph_count (face); + + for (unsigned glyph_id = 0; glyph_id < glyph_count; ++glyph_id) + { + hb_blob_t *blob = hb_ot_color_glyph_reference_svg (face, glyph_id); + + if (hb_blob_get_length (blob) == 0) continue; + + unsigned length; + const char *data = hb_blob_get_data (blob, &length); + + char output_path[255]; + snprintf (output_path, sizeof output_path, + "out/svg-%u-%u.svg%s", + glyph_id, + face_index, + // append "z" if the content is gzipped, https://stackoverflow.com/a/6059405 + (length > 2 && (data[0] == '\x1F') && (data[1] == '\x8B')) ? "z" : ""); + + FILE *f = fopen (output_path, "wb"); + fwrite (data, 1, length, f); + fclose (f); + + hb_blob_destroy (blob); + } +} + +/* _png API is so easy to use unlike the below code, don't get confused */ +static void +png_dump (hb_face_t *face, unsigned face_index) +{ + unsigned glyph_count = hb_face_get_glyph_count (face); + hb_font_t *font = hb_font_create (face); + + /* scans the font for strikes */ + unsigned sample_glyph_id; + /* we don't care about different strikes for different glyphs at this point */ + for (sample_glyph_id = 0; sample_glyph_id < glyph_count; ++sample_glyph_id) + { + hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id); + unsigned blob_length = hb_blob_get_length (blob); + hb_blob_destroy (blob); + if (blob_length != 0) + break; + } + + unsigned upem = hb_face_get_upem (face); + unsigned blob_length = 0; + unsigned strike = 0; + for (unsigned ppem = 1; ppem < upem; ++ppem) + { + hb_font_set_ppem (font, ppem, ppem); + hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, sample_glyph_id); + unsigned new_blob_length = hb_blob_get_length (blob); + hb_blob_destroy (blob); + if (new_blob_length != blob_length) + { + for (unsigned glyph_id = 0; glyph_id < glyph_count; ++glyph_id) + { + hb_blob_t *blob = hb_ot_color_glyph_reference_png (font, glyph_id); + + if (hb_blob_get_length (blob) == 0) continue; + + unsigned length; + const char *data = hb_blob_get_data (blob, &length); + + char output_path[255]; + snprintf (output_path, sizeof output_path, "out/png-%u-%u-%u.png", glyph_id, strike, face_index); + + FILE *f = fopen (output_path, "wb"); + fwrite (data, 1, length, f); + fclose (f); + + hb_blob_destroy (blob); + } + + strike++; + blob_length = new_blob_length; + } + } + + hb_font_destroy (font); +} + +struct draw_data_t +{ + FILE *f; + hb_position_t ascender; +}; + +static void +move_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "M%g,%g", to_x, draw_data->ascender - to_y); +} + +static void +line_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "L%g,%g", to_x, draw_data->ascender - to_y); +} + +static void +quadratic_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float control_x, float control_y, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "Q%g,%g %g,%g", control_x, draw_data->ascender - control_y, + to_x, draw_data->ascender - to_y); +} + +static void +cubic_to (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *) +{ + fprintf (draw_data->f, "C%g,%g %g,%g %g,%g", control1_x, draw_data->ascender - control1_y, + control2_x, draw_data->ascender - control2_y, + to_x, draw_data->ascender - to_y); +} + +static void +close_path (hb_draw_funcs_t *, draw_data_t *draw_data, + hb_draw_state_t *, + void *) +{ + fprintf (draw_data->f, "Z"); +} + +static void +layered_glyph_dump (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index) +{ + hb_face_t *face = hb_font_get_face (font); + unsigned palette_count = hb_ot_color_palette_get_count (face); + for (unsigned palette = 0; palette < palette_count; ++palette) + { + unsigned num_colors = hb_ot_color_palette_get_colors (face, palette, 0, nullptr, nullptr); + if (!num_colors) continue; + + hb_color_t *colors = (hb_color_t*) calloc (num_colors, sizeof (hb_color_t)); + hb_ot_color_palette_get_colors (face, palette, 0, &num_colors, colors); + if (!num_colors) + { + free (colors); + continue; + } + + unsigned num_glyphs = hb_face_get_glyph_count (face); + for (hb_codepoint_t gid = 0; gid < num_glyphs; ++gid) + { + unsigned num_layers = hb_ot_color_glyph_get_layers (face, gid, 0, nullptr, nullptr); + if (!num_layers) continue; + + hb_ot_color_layer_t *layers = (hb_ot_color_layer_t*) malloc (num_layers * sizeof (hb_ot_color_layer_t)); + + hb_ot_color_glyph_get_layers (face, gid, 0, &num_layers, layers); + if (num_layers) + { + hb_font_extents_t font_extents; + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + hb_glyph_extents_t extents = {0}; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + { + printf ("Skip gid: %u\n", gid); + continue; + } + + char output_path[255]; + snprintf (output_path, sizeof output_path, "out/colr-%u-%u-%u.svg", gid, palette, face_index); + FILE *f = fopen (output_path, "wb"); + fprintf (f, "<svg xmlns=\"http://www.w3.org/2000/svg\"" + " viewBox=\"%d %d %d %d\">\n", + extents.x_bearing, 0, + extents.x_bearing + extents.width, -extents.height); + draw_data_t draw_data; + draw_data.ascender = extents.y_bearing; + draw_data.f = f; + + for (unsigned layer = 0; layer < num_layers; ++layer) + { + hb_color_t color = 0x000000FF; + if (layers[layer].color_index != 0xFFFF) + color = colors[layers[layer].color_index]; + fprintf (f, "<path fill=\"#%02X%02X%02X\" ", + hb_color_get_red (color), hb_color_get_green (color), hb_color_get_green (color)); + if (hb_color_get_alpha (color) != 255) + fprintf (f, "fill-opacity=\"%.3f\"", (double) hb_color_get_alpha (color) / 255.); + fprintf (f, "d=\""); + hb_font_draw_glyph (font, layers[layer].glyph, funcs, &draw_data); + fprintf (f, "\"/>\n"); + } + + fprintf (f, "</svg>"); + fclose (f); + } + free (layers); + } + + free (colors); + } +} + +static void +dump_glyphs (hb_font_t *font, hb_draw_funcs_t *funcs, unsigned face_index) +{ + unsigned num_glyphs = hb_face_get_glyph_count (hb_font_get_face (font)); + for (unsigned gid = 0; gid < num_glyphs; ++gid) + { + hb_font_extents_t font_extents; + hb_font_get_extents_for_direction (font, HB_DIRECTION_LTR, &font_extents); + hb_glyph_extents_t extents = {0}; + if (!hb_font_get_glyph_extents (font, gid, &extents)) + { + printf ("Skip gid: %u\n", gid); + continue; + } + + char output_path[255]; + snprintf (output_path, sizeof output_path, "out/%u-%u.svg", face_index, gid); + FILE *f = fopen (output_path, "wb"); + fprintf (f, "<svg xmlns=\"http://www.w3.org/2000/svg\"" + " viewBox=\"%d %d %d %d\"><path d=\"", + extents.x_bearing, 0, + extents.x_bearing + extents.width, font_extents.ascender - font_extents.descender); + draw_data_t draw_data; + draw_data.ascender = font_extents.ascender; + draw_data.f = f; + hb_font_draw_glyph (font, gid, funcs, &draw_data); + fprintf (f, "\"/></svg>"); + fclose (f); + } +} + +static void +dump_glyphs (hb_blob_t *blob, const char *font_name) +{ + FILE *font_name_file = fopen ("out/.dumped_font_name", "r"); + if (font_name_file) + { + fprintf (stderr, "Purge or rename ./out folder if you like to run a glyph dump,\n" + "run it like `rm -rf out && mkdir out && src/main font-file.ttf`\n"); + return; + } + + font_name_file = fopen ("out/.dumped_font_name", "w"); + if (!font_name_file) + { + fprintf (stderr, "./out is not accessible as a folder, create it please\n"); + return; + } + fwrite (font_name, 1, strlen (font_name), font_name_file); + fclose (font_name_file); + + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) close_path, nullptr, nullptr); + + unsigned num_faces = hb_face_count (blob); + for (unsigned face_index = 0; face_index < num_faces; ++face_index) + { + hb_face_t *face = hb_face_create (blob, face_index); + hb_font_t *font = hb_font_create (face); + + if (hb_ot_color_has_png (face)) + printf ("Dumping png (CBDT/sbix)...\n"); + png_dump (face, face_index); + + if (hb_ot_color_has_svg (face)) + printf ("Dumping svg (SVG )...\n"); + svg_dump (face, face_index); + + if (hb_ot_color_has_layers (face) && hb_ot_color_has_palettes (face)) + printf ("Dumping layered color glyphs (COLR/CPAL)...\n"); + layered_glyph_dump (font, funcs, face_index); + + dump_glyphs (font, funcs, face_index); + + hb_font_destroy (font); + hb_face_destroy (face); + } + + hb_draw_funcs_destroy (funcs); +} +#endif + +#ifndef MAIN_CC_NO_PRIVATE_API +/* Only this part of this mini app uses private API */ +#include "hb-static.cc" +#include "hb-open-file.hh" +#include "hb-ot-layout-gdef-table.hh" +#include "hb-ot-layout-gsubgpos.hh" + +using namespace OT; + +static void +print_layout_info_using_private_api (hb_blob_t *blob) +{ + const char *font_data = hb_blob_get_data (blob, nullptr); + hb_blob_t *font_blob = hb_sanitize_context_t ().sanitize_blob<OpenTypeFontFile> (blob); + const OpenTypeFontFile* sanitized = font_blob->as<OpenTypeFontFile> (); + if (!font_blob->data) + { + printf ("Sanitization of the file wasn't successful. Exit"); + exit (1); + } + const OpenTypeFontFile& ot = *sanitized; + + switch (ot.get_tag ()) + { + case OpenTypeFontFile::TrueTypeTag: + printf ("OpenType font with TrueType outlines\n"); + break; + case OpenTypeFontFile::CFFTag: + printf ("OpenType font with CFF (Type1) outlines\n"); + break; + case OpenTypeFontFile::TTCTag: + printf ("TrueType Collection of OpenType fonts\n"); + break; + case OpenTypeFontFile::TrueTag: + printf ("Obsolete Apple TrueType font\n"); + break; + case OpenTypeFontFile::Typ1Tag: + printf ("Obsolete Apple Type1 font in SFNT container\n"); + break; + case OpenTypeFontFile::DFontTag: + printf ("DFont Mac Resource Fork\n"); + break; + default: + printf ("Unknown font format\n"); + break; + } + + unsigned num_faces = hb_face_count (blob); + printf ("%u font(s) found in file\n", num_faces); + for (unsigned n_font = 0; n_font < num_faces; ++n_font) + { + const OpenTypeFontFace &font = ot.get_face (n_font); + printf ("Font %u of %u:\n", n_font, num_faces); + + unsigned num_tables = font.get_table_count (); + printf (" %u table(s) found in font\n", num_tables); + for (unsigned n_table = 0; n_table < num_tables; ++n_table) + { + const OpenTypeTable &table = font.get_table (n_table); + printf (" Table %2u of %2u: %.4s (0x%08x+0x%08x)\n", n_table, num_tables, + (const char *) table.tag, + (unsigned) table.offset, + (unsigned) table.length); + + switch (table.tag) + { + + case HB_OT_TAG_GSUB: + case HB_OT_TAG_GPOS: + { + + const GSUBGPOS &g = *reinterpret_cast<const GSUBGPOS *> (font_data + table.offset); + + unsigned num_scripts = g.get_script_count (); + printf (" %u script(s) found in table\n", num_scripts); + for (unsigned n_script = 0; n_script < num_scripts; ++n_script) + { + const Script &script = g.get_script (n_script); + printf (" Script %2u of %2u: %.4s\n", n_script, num_scripts, + (const char *) g.get_script_tag (n_script)); + + if (!script.has_default_lang_sys ()) + printf (" No default language system\n"); + int num_langsys = script.get_lang_sys_count (); + printf (" %d language system(s) found in script\n", num_langsys); + for (int n_langsys = script.has_default_lang_sys () ? -1 : 0; n_langsys < num_langsys; ++n_langsys) + { + const LangSys &langsys = n_langsys == -1 + ? script.get_default_lang_sys () + : script.get_lang_sys (n_langsys); + if (n_langsys == -1) + printf (" Default Language System\n"); + else + printf (" Language System %2d of %2d: %.4s\n", n_langsys, num_langsys, + (const char *) script.get_lang_sys_tag (n_langsys)); + if (!langsys.has_required_feature ()) + printf (" No required feature\n"); + else + printf (" Required feature index: %u\n", + langsys.get_required_feature_index ()); + + unsigned num_features = langsys.get_feature_count (); + printf (" %u feature(s) found in language system\n", num_features); + for (unsigned n_feature = 0; n_feature < num_features; ++n_feature) + { + printf (" Feature index %2u of %2u: %u\n", n_feature, num_features, + langsys.get_feature_index (n_feature)); + } + } + } + + unsigned num_features = g.get_feature_count (); + printf (" %u feature(s) found in table\n", num_features); + for (unsigned n_feature = 0; n_feature < num_features; ++n_feature) + { + const Feature &feature = g.get_feature (n_feature); + unsigned num_lookups = feature.get_lookup_count (); + printf (" Feature %2u of %2u: %c%c%c%c\n", n_feature, num_features, + HB_UNTAG (g.get_feature_tag (n_feature))); + + printf (" %u lookup(s) found in feature\n", num_lookups); + for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup) { + printf (" Lookup index %2u of %2u: %u\n", n_lookup, num_lookups, + feature.get_lookup_index (n_lookup)); + } + } + + unsigned num_lookups = g.get_lookup_count (); + printf (" %u lookup(s) found in table\n", num_lookups); + for (unsigned n_lookup = 0; n_lookup < num_lookups; ++n_lookup) + { + const Lookup &lookup = g.get_lookup (n_lookup); + printf (" Lookup %2u of %2u: type %u, props 0x%04X\n", n_lookup, num_lookups, + lookup.get_type (), lookup.get_props ()); + } + + } + break; + + case GDEF::tableTag: + { + + const GDEF &gdef = *reinterpret_cast<const GDEF *> (font_data + table.offset); + + printf (" Has %sglyph classes\n", + gdef.has_glyph_classes () ? "" : "no "); + printf (" Has %smark attachment types\n", + gdef.has_mark_attachment_types () ? "" : "no "); + printf (" Has %sattach list\n", + gdef.has_attach_list () ? "" : "no "); + printf (" Has %slig carets\n", + gdef.has_lig_carets () ? "" : "no "); + printf (" Has %smark glyph sets\n", + gdef.has_mark_glyph_sets () ? "" : "no "); + break; + } + } + } + } +} +/* end of private API use */ +#endif + +int +main (int argc, char **argv) +{ + if (argc != 2) + { + fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (argv[1]); + assert (blob); + printf ("Opened font file %s: %u bytes long\n", argv[1], hb_blob_get_length (blob)); +#ifndef MAIN_CC_NO_PRIVATE_API + print_layout_info_using_private_api (blob); +#endif +#if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW) + dump_glyphs (blob, argv[1]); +#endif + hb_blob_destroy (blob); + + return 0; +} diff --git a/gfx/harfbuzz/src/meson.build b/gfx/harfbuzz/src/meson.build new file mode 100644 index 0000000000..77c7e75017 --- /dev/null +++ b/gfx/harfbuzz/src/meson.build @@ -0,0 +1,1033 @@ +fs = import('fs') + +hb_version_h = configure_file( + command: [find_program('gen-hb-version.py'), meson.project_version(), '@OUTPUT@', '@INPUT@'], + input: 'hb-version.h.in', + output: 'hb-version.h', + install: true, + install_dir: get_option('includedir') / meson.project_name()) + +# Base and default-included sources and headers +hb_base_sources = files( + 'hb-aat-layout-ankr-table.hh', + 'hb-aat-layout-bsln-table.hh', + 'hb-aat-layout-common.hh', + 'hb-aat-layout-feat-table.hh', + 'hb-aat-layout-just-table.hh', + 'hb-aat-layout-kerx-table.hh', + 'hb-aat-layout-morx-table.hh', + 'hb-aat-layout-opbd-table.hh', + 'hb-aat-layout-trak-table.hh', + 'hb-aat-layout.cc', + 'hb-aat-layout.hh', + 'hb-aat-ltag-table.hh', + 'hb-aat-map.cc', + 'hb-aat-map.hh', + 'hb-algs.hh', + 'hb-array.hh', + 'hb-atomic.hh', + 'hb-bimap.hh', + 'hb-bit-page.hh', + 'hb-blob.cc', + 'hb-blob.hh', + 'hb-buffer-serialize.cc', + 'hb-buffer-verify.cc', + 'hb-buffer.cc', + 'hb-buffer.hh', + 'hb-cache.hh', + 'hb-cff-interp-common.hh', + 'hb-cff-interp-cs-common.hh', + 'hb-cff-interp-dict-common.hh', + 'hb-cff1-interp-cs.hh', + 'hb-cff2-interp-cs.hh', + 'hb-common.cc', + 'hb-config.hh', + 'hb-debug.hh', + 'hb-dispatch.hh', + 'hb-draw.cc', + 'hb-draw.hh', + 'hb-paint.cc', + 'hb-paint.hh', + 'hb-paint-extents.cc', + 'hb-paint-extents.hh', + 'hb-face.cc', + 'hb-face.hh', + 'hb-face-builder.cc', + 'hb-fallback-shape.cc', + 'hb-font.cc', + 'hb-font.hh', + 'hb-iter.hh', + 'hb-kern.hh', + 'hb-limits.hh', + 'hb-machinery.hh', + 'hb-map.cc', + 'hb-map.hh', + 'hb-meta.hh', + 'hb-ms-feature-ranges.hh', + 'hb-multimap.hh', + 'hb-mutex.hh', + 'hb-null.hh', + 'hb-number.cc', + 'hb-number.hh', + 'hb-object.hh', + 'hb-open-file.hh', + 'hb-open-type.hh', + 'hb-ot-cff-common.hh', + 'hb-ot-cff1-std-str.hh', + 'hb-ot-cff1-table.cc', + 'hb-ot-cff1-table.hh', + 'hb-ot-cff2-table.cc', + 'hb-ot-cff2-table.hh', + 'hb-ot-cmap-table.hh', + 'hb-ot-color.cc', + 'hb-ot-face-table-list.hh', + 'hb-ot-face.cc', + 'hb-ot-face.hh', + 'hb-ot-font.cc', + 'hb-ot-gasp-table.hh', + 'hb-ot-glyf-table.hh', + 'hb-ot-hdmx-table.hh', + 'hb-ot-head-table.hh', + 'hb-ot-hhea-table.hh', + 'hb-ot-hmtx-table.hh', + 'hb-ot-kern-table.hh', + 'hb-ot-layout-base-table.hh', + 'hb-ot-layout-common.hh', + 'hb-ot-layout-gdef-table.hh', + 'hb-ot-layout-gpos-table.hh', + 'hb-ot-layout-gsub-table.hh', + 'hb-outline.hh', + 'hb-outline.cc', + 'OT/Color/CBDT/CBDT.hh', + 'OT/Color/COLR/COLR.hh', + 'OT/Color/CPAL/CPAL.hh', + 'OT/Color/sbix/sbix.hh', + 'OT/Color/svg/svg.hh', + 'OT/glyf/glyf.hh', + 'OT/glyf/glyf-helpers.hh', + 'OT/glyf/loca.hh', + 'OT/glyf/path-builder.hh', + 'OT/glyf/Glyph.hh', + 'OT/glyf/GlyphHeader.hh', + 'OT/glyf/SimpleGlyph.hh', + 'OT/glyf/CompositeGlyph.hh', + 'OT/glyf/SubsetGlyph.hh', + 'OT/Layout/types.hh', + 'OT/Layout/Common/Coverage.hh', + 'OT/Layout/Common/CoverageFormat1.hh', + 'OT/Layout/Common/CoverageFormat2.hh', + 'OT/Layout/Common/RangeRecord.hh', + 'OT/Layout/GDEF/GDEF.hh', + 'OT/Layout/GPOS/AnchorFormat1.hh', + 'OT/Layout/GPOS/AnchorFormat2.hh', + 'OT/Layout/GPOS/AnchorFormat3.hh', + 'OT/Layout/GPOS/Anchor.hh', + 'OT/Layout/GPOS/AnchorMatrix.hh', + 'OT/Layout/GPOS/ChainContextPos.hh', + 'OT/Layout/GPOS/Common.hh', + 'OT/Layout/GPOS/ContextPos.hh', + 'OT/Layout/GPOS/CursivePosFormat1.hh', + 'OT/Layout/GPOS/CursivePos.hh', + 'OT/Layout/GPOS/ExtensionPos.hh', + 'OT/Layout/GPOS/GPOS.hh', + 'OT/Layout/GPOS/LigatureArray.hh', + 'OT/Layout/GPOS/MarkArray.hh', + 'OT/Layout/GPOS/MarkBasePosFormat1.hh', + 'OT/Layout/GPOS/MarkBasePos.hh', + 'OT/Layout/GPOS/MarkLigPosFormat1.hh', + 'OT/Layout/GPOS/MarkLigPos.hh', + 'OT/Layout/GPOS/MarkMarkPosFormat1.hh', + 'OT/Layout/GPOS/MarkMarkPos.hh', + 'OT/Layout/GPOS/MarkRecord.hh', + 'OT/Layout/GPOS/PairPosFormat1.hh', + 'OT/Layout/GPOS/PairPosFormat2.hh', + 'OT/Layout/GPOS/PairPos.hh', + 'OT/Layout/GPOS/PairSet.hh', + 'OT/Layout/GPOS/PairValueRecord.hh', + 'OT/Layout/GPOS/PosLookup.hh', + 'OT/Layout/GPOS/PosLookupSubTable.hh', + 'OT/Layout/GPOS/SinglePosFormat1.hh', + 'OT/Layout/GPOS/SinglePosFormat2.hh', + 'OT/Layout/GPOS/SinglePos.hh', + 'OT/Layout/GPOS/ValueFormat.hh', + 'OT/Layout/GSUB/AlternateSet.hh', + 'OT/Layout/GSUB/AlternateSubstFormat1.hh', + 'OT/Layout/GSUB/AlternateSubst.hh', + 'OT/Layout/GSUB/ChainContextSubst.hh', + 'OT/Layout/GSUB/Common.hh', + 'OT/Layout/GSUB/ContextSubst.hh', + 'OT/Layout/GSUB/ExtensionSubst.hh', + 'OT/Layout/GSUB/GSUB.hh', + 'OT/Layout/GSUB/Ligature.hh', + 'OT/Layout/GSUB/LigatureSet.hh', + 'OT/Layout/GSUB/LigatureSubstFormat1.hh', + 'OT/Layout/GSUB/LigatureSubst.hh', + 'OT/Layout/GSUB/MultipleSubstFormat1.hh', + 'OT/Layout/GSUB/MultipleSubst.hh', + 'OT/Layout/GSUB/ReverseChainSingleSubstFormat1.hh', + 'OT/Layout/GSUB/ReverseChainSingleSubst.hh', + 'OT/Layout/GSUB/Sequence.hh', + 'OT/Layout/GSUB/SingleSubstFormat1.hh', + 'OT/Layout/GSUB/SingleSubstFormat2.hh', + 'OT/Layout/GSUB/SingleSubst.hh', + 'OT/Layout/GSUB/SubstLookup.hh', + 'OT/Layout/GSUB/SubstLookupSubTable.hh', + 'OT/name/name.hh', + 'hb-ot-layout-gsubgpos.hh', + 'hb-ot-layout-jstf-table.hh', + 'hb-ot-layout.cc', + 'hb-ot-layout.hh', + 'hb-ot-map.cc', + 'hb-ot-map.hh', + 'hb-ot-math-table.hh', + 'hb-ot-math.cc', + 'hb-ot-maxp-table.hh', + 'hb-ot-meta-table.hh', + 'hb-ot-meta.cc', + 'hb-ot-metrics.cc', + 'hb-ot-metrics.hh', + 'hb-ot-name-language-static.hh', + 'hb-ot-name-language.hh', + 'hb-ot-name-table.hh', + 'hb-ot-name.cc', + 'hb-ot-os2-table.hh', + 'hb-ot-os2-unicode-ranges.hh', + 'hb-ot-post-macroman.hh', + 'hb-ot-post-table.hh', + 'hb-ot-shaper-arabic-fallback.hh', + 'hb-ot-shaper-arabic-joining-list.hh', + 'hb-ot-shaper-arabic-pua.hh', + 'hb-ot-shaper-arabic-table.hh', + 'hb-ot-shaper-arabic-win1256.hh', + 'hb-ot-shaper-arabic.cc', + 'hb-ot-shaper-arabic.hh', + 'hb-ot-shaper-default.cc', + 'hb-ot-shaper-hangul.cc', + 'hb-ot-shaper-hebrew.cc', + 'hb-ot-shaper-indic-table.cc', + 'hb-ot-shaper-indic.cc', + 'hb-ot-shaper-indic.hh', + 'hb-ot-shaper-khmer.cc', + 'hb-ot-shaper-myanmar.cc', + 'hb-ot-shaper-syllabic.cc', + 'hb-ot-shaper-syllabic.hh', + 'hb-ot-shaper-thai.cc', + 'hb-ot-shaper-use-table.hh', + 'hb-ot-shaper-use.cc', + 'hb-ot-shaper-vowel-constraints.cc', + 'hb-ot-shaper-vowel-constraints.hh', + 'hb-ot-shaper.hh', + 'hb-ot-shape-fallback.cc', + 'hb-ot-shape-fallback.hh', + 'hb-ot-shape-normalize.cc', + 'hb-ot-shape-normalize.hh', + 'hb-ot-shape.cc', + 'hb-ot-shape.hh', + 'hb-ot-stat-table.hh', + 'hb-ot-tag-table.hh', + 'hb-ot-tag.cc', + 'hb-ot-var-avar-table.hh', + 'hb-ot-var-common.hh', + 'hb-ot-var-cvar-table.hh', + 'hb-ot-var-fvar-table.hh', + 'hb-ot-var-gvar-table.hh', + 'hb-ot-var-hvar-table.hh', + 'hb-ot-var-mvar-table.hh', + 'hb-ot-var.cc', + 'hb-ot-vorg-table.hh', + 'hb-pool.hh', + 'hb-sanitize.hh', + 'hb-serialize.hh', + 'hb-set-digest.hh', + 'hb-set.cc', + 'hb-set.hh', + 'hb-shape-plan.cc', + 'hb-shape-plan.hh', + 'hb-shape.cc', + 'hb-shaper-impl.hh', + 'hb-shaper-list.hh', + 'hb-shaper.cc', + 'hb-shaper.hh', + 'hb-static.cc', + 'hb-string-array.hh', + 'hb-style.cc', + 'hb-ucd-table.hh', + 'hb-ucd.cc', + 'hb-unicode-emoji-table.hh', + 'hb-unicode.cc', + 'hb-unicode.hh', + 'hb-utf.hh', + 'hb-vector.hh', + 'hb.hh', +) + +hb_base_ragel_generated_sources = files( + 'hb-buffer-deserialize-json.hh', + 'hb-buffer-deserialize-text-glyphs.hh', + 'hb-buffer-deserialize-text-unicode.hh', + 'hb-number-parser.hh', + 'hb-ot-shaper-indic-machine.hh', + 'hb-ot-shaper-khmer-machine.hh', + 'hb-ot-shaper-myanmar-machine.hh', + 'hb-ot-shaper-use-machine.hh', +) +hb_base_ragel_sources = [ + 'hb-buffer-deserialize-json.rl', + 'hb-buffer-deserialize-text-glyphs.rl', + 'hb-buffer-deserialize-text-unicode.rl', + 'hb-number-parser.rl', + 'hb-ot-shaper-indic-machine.rl', + 'hb-ot-shaper-khmer-machine.rl', + 'hb-ot-shaper-myanmar-machine.rl', + 'hb-ot-shaper-use-machine.rl', +] + +hb_base_headers = files( + 'hb-aat-layout.h', + 'hb-aat.h', + 'hb-blob.h', + 'hb-buffer.h', + 'hb-common.h', + 'hb-cplusplus.hh', + 'hb-deprecated.h', + 'hb-draw.h', + 'hb-paint.h', + 'hb-face.h', + 'hb-font.h', + 'hb-map.h', + 'hb-ot-color.h', + 'hb-ot-deprecated.h', + 'hb-ot-font.h', + 'hb-ot-layout.h', + 'hb-ot-math.h', + 'hb-ot-meta.h', + 'hb-ot-metrics.h', + 'hb-ot-name.h', + 'hb-ot-shape.h', + 'hb-ot-var.h', + 'hb-ot.h', + 'hb-set.h', + 'hb-shape-plan.h', + 'hb-shape.h', + 'hb-style.h', + 'hb-unicode.h', + 'hb.h', +) +hb_base_headers += hb_version_h + +# Optional Sources and Headers with external deps + +hb_ft_sources = files('hb-ft.cc', 'hb-ft-colr.hh') +hb_ft_headers = files('hb-ft.h') + +hb_glib_sources = files('hb-glib.cc') +hb_glib_headers = files('hb-glib.h') + +hb_graphite2_sources = files('hb-graphite2.cc') +hb_graphite2_headers = files('hb-graphite2.h') + +hb_wasm_sources = files( + 'hb-wasm-api.cc', + 'hb-wasm-api.hh', + 'hb-wasm-api-blob.hh', + 'hb-wasm-api-buffer.hh', + 'hb-wasm-api-common.hh', + 'hb-wasm-api-face.hh', + 'hb-wasm-api-font.hh', + 'hb-wasm-api-shape.hh', + 'hb-wasm-shape.cc', +) +hb_wasm_headers = files() + +# System-dependent sources and headers + +hb_coretext_sources = files('hb-coretext.cc') +hb_coretext_headers = files('hb-coretext.h') + +hb_directwrite_sources = files('hb-directwrite.cc') +hb_directwrite_headers = files('hb-directwrite.h') + +hb_gdi_sources = files('hb-gdi.cc') +hb_gdi_headers = files('hb-gdi.h') + +hb_uniscribe_sources = files('hb-uniscribe.cc') +hb_uniscribe_headers = files('hb-uniscribe.h') + +# Sources for libharfbuzz-gobject and libharfbuzz-icu +hb_icu_sources = files('hb-icu.cc') +hb_icu_headers = files('hb-icu.h') + +# Sources for libharfbuzz-subset +hb_subset_sources = files( + 'hb-number.cc', + 'hb-number.hh', + 'hb-ot-cff1-table.cc', + 'hb-ot-cff2-table.cc', + 'hb-static.cc', + 'hb-subset-accelerator.hh', + 'hb-subset-cff-common.cc', + 'hb-subset-cff-common.hh', + 'hb-subset-cff1.cc', + 'hb-subset-cff2.cc', + 'hb-subset-input.cc', + 'hb-subset-input.hh', + 'hb-subset-instancer-solver.hh', + 'hb-subset-instancer-solver.cc', + 'hb-subset-plan.cc', + 'hb-subset-plan.hh', + 'hb-subset-plan-member-list.hh', + 'hb-subset-repacker.cc', + 'graph/gsubgpos-context.cc', + 'graph/gsubgpos-context.hh', + 'graph/gsubgpos-graph.hh', + 'graph/pairpos-graph.hh', + 'graph/markbasepos-graph.hh', + 'graph/coverage-graph.hh', + 'graph/classdef-graph.hh', + 'graph/split-helpers.hh', + 'hb-subset.cc', + 'hb-subset.hh', +) + +hb_subset_headers = files( + 'hb-subset.h', + 'hb-subset-repacker.h' +) + +hb_gobject_sources = files( + 'hb-gobject-structs.cc' +) + +hb_gobject_headers = files( + 'hb-gobject.h', + 'hb-gobject-structs.h', +) + +ragel = find_program('ragel', version: '6.10', required: false) +has_ragel = ragel.found() +if not has_ragel and get_option('ragel_subproject') + ragel = subproject('ragel').get_variable('ragel') + has_ragel = true +endif +if not has_ragel + if not meson.is_subproject() + warning('You have to install ragel if you are going to develop HarfBuzz itself') + endif +else + ragel_helper = find_program('gen-ragel-artifacts.py') + foreach rl : hb_base_ragel_sources + hh = rl.split('.')[0] + '.hh' + custom_target('@0@'.format(hh), + build_by_default: true, + input: rl, + output: hh, + command: [ragel_helper, ragel, '@OUTPUT@', meson.current_source_dir(), '@INPUT@'], + ) + endforeach +endif + +custom_target('harfbuzz.cc', + build_by_default: true, + output: 'harfbuzz.cc', + input: hb_base_sources + hb_glib_sources + hb_ft_sources + + hb_graphite2_sources + hb_uniscribe_sources + hb_gdi_sources + + hb_directwrite_sources + hb_coretext_sources + hb_wasm_sources, + command: [find_program('gen-harfbuzzcc.py'), + '@OUTPUT@', meson.current_source_dir(), '@INPUT@'], +) + +incsrc = include_directories('.') + +hb_sources = hb_base_sources + hb_base_ragel_generated_sources +hb_headers = hb_base_headers + +harfbuzz_deps = [thread_dep, m_dep] + harfbuzz_extra_deps + +libharfbuzz_link_language = 'c' + +if conf.get('HAVE_FREETYPE', 0) == 1 + hb_sources += hb_ft_sources + hb_headers += hb_ft_headers + harfbuzz_deps += [freetype_dep] +endif + +if conf.get('HAVE_GLIB', 0) == 1 + hb_sources += hb_glib_sources + hb_headers += hb_glib_headers + harfbuzz_deps += [glib_dep] +endif + +# We set those here to not include the sources below that are of no use to +# GObject Introspection +gir_sources = hb_sources + hb_gobject_sources +gir_headers = hb_headers + hb_gobject_headers + +if conf.get('HAVE_GDI', 0) == 1 + hb_sources += hb_gdi_sources + hb_headers += hb_gdi_headers + harfbuzz_deps += gdi_uniscribe_deps +endif + +if conf.get('HAVE_GRAPHITE2', 0) == 1 + hb_sources += hb_graphite2_sources + hb_headers += hb_graphite2_headers + harfbuzz_deps += [graphite2_dep, graphite_dep] +endif + +if conf.get('HAVE_WASM', 0) == 1 + hb_sources += hb_wasm_sources + hb_headers += hb_wasm_headers + harfbuzz_deps += wasm_dep + #harfbuzz_deps += llvm_dep +endif + +if conf.get('HAVE_UNISCRIBE', 0) == 1 + hb_sources += hb_uniscribe_sources + hb_headers += hb_uniscribe_headers +endif + +if conf.get('HAVE_DIRECTWRITE', 0) == 1 + hb_sources += hb_directwrite_sources + hb_headers += hb_directwrite_headers + # hb-directwrite needs a C++ linker + libharfbuzz_link_language = 'cpp' +endif + +if conf.get('HAVE_CORETEXT', 0) == 1 + hb_sources += hb_coretext_sources + hb_headers += hb_coretext_headers + harfbuzz_deps += coretext_deps +endif + +have_icu = conf.get('HAVE_ICU', 0) == 1 +have_icu_builtin = conf.get('HAVE_ICU_BUILTIN', 0) == 1 +if have_icu and have_icu_builtin + hb_sources += hb_icu_sources + hb_headers += hb_icu_headers + harfbuzz_deps += [icu_dep] +endif + +features = [ + 'CAIRO', + 'CORETEXT', + 'DIRECTWRITE', + 'FREETYPE', + 'GDI', + 'GLIB', + 'GOBJECT', + 'GRAPHITE', + 'ICU', + 'UNISCRIBE', + 'WASM', +] + +hb_enabled_features = configuration_data() +hb_supported_features = configuration_data() +foreach feature : features + key = 'HB_HAS_@0@'.format(feature) + hb_enabled_features.set(key, conf.get('HAVE_@0@'.format(feature), false)) + hb_supported_features.set(key, 1) +endforeach + +# The enabled features. This file is installed. +hb_features_h = configure_file(input: 'hb-features.h.in', + output: 'hb-features.h', + configuration: hb_enabled_features, + install: true, + install_dir: get_option('includedir') / meson.project_name()) + +# This file is generated to convince gtk-doc to generate documentation for all +# HB_HAS_* macros, whether they are enabled for the current build or not. +# This file should not be installed. +hb_supported_features_h = configure_file(input: 'hb-features.h.in', + output: 'hb-supported-features.h', + configuration: hb_supported_features, + install: false) + +# Base and default-included sources and headers + +gen_def = find_program('gen-def.py') +gen_def_cmd = [gen_def, '@OUTPUT@', '@INPUT@'] +if get_option('experimental_api') + gen_def_cmd += '--experimental-api' +endif + +# harfbuzz +harfbuzz_def = custom_target('harfbuzz.def', + command: gen_def_cmd, + input: hb_headers, + output: 'harfbuzz.def') +defs_list = [harfbuzz_def] + +version = '0.@0@.0'.format(hb_version_int) + +extra_hb_cpp_args = [] +if cpp.get_define('_MSC_FULL_VER') != '' + if get_option('default_library') != 'static' + extra_hb_cpp_args += '-DHB_DLL_EXPORT' + endif + hb_so_version = '' +else + hb_so_version = '0' +endif + +if get_option('fuzzer_ldflags') != '' + extra_hb_cpp_args += ['-DHB_CUSTOM_MALLOC'] + hb_sources += 'failing-alloc.c' + hb_subset_sources += 'failing-alloc.c' + hb_icu_sources += 'failing-alloc.c' + hb_gobject_sources += 'failing-alloc.c' +endif + +darwin_versions = [hb_version_int, '@0@.0.0'.format(hb_version_int)] + +libharfbuzz = library('harfbuzz', hb_sources, + include_directories: incconfig, + dependencies: harfbuzz_deps, + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + link_language: libharfbuzz_link_language, +) + +libharfbuzz_dep = declare_dependency( + link_with: libharfbuzz, + include_directories: incsrc, + dependencies: harfbuzz_deps) +meson.override_dependency('harfbuzz', libharfbuzz_dep) + +# harfbuzz-subset +harfbuzz_subset_def = custom_target('harfbuzz-subset.def', + command: gen_def_cmd, + input: hb_subset_headers, + output: 'harfbuzz-subset.def') +defs_list += [harfbuzz_subset_def] + +libharfbuzz_subset = library('harfbuzz-subset', hb_subset_sources, + include_directories: incconfig, + dependencies: [m_dep], + link_with: [libharfbuzz], + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + link_language: 'c', +) + +custom_target('harfbuzz-subset.cc', + build_by_default: true, + output: 'harfbuzz-subset.cc', + input: hb_base_sources + hb_subset_sources, + command: [find_program('gen-harfbuzzcc.py'), + '@OUTPUT@', meson.current_source_dir(), '@INPUT@'], +) + +libharfbuzz_subset_dep = declare_dependency( + link_with: libharfbuzz_subset, + include_directories: incsrc, + dependencies: [m_dep]) +meson.override_dependency('harfbuzz-subset', libharfbuzz_subset_dep) + +libharfbuzz_cairo_dep = null_dep +if conf.get('HAVE_CAIRO', 0) == 1 + hb_cairo_sources = [ + 'hb-cairo.cc', + 'hb-cairo-utils.cc', + 'hb-static.cc' + ] + + hb_cairo_headers = [ + 'hb-cairo.h', + ] + + cairo_dep = dependency('cairo') + + libharfbuzz_cairo = library('harfbuzz-cairo', hb_cairo_sources, + include_directories: incconfig, + dependencies: [m_dep, cairo_dep], + link_with: [libharfbuzz], + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + link_language: 'c', + ) + + install_headers(hb_cairo_headers, subdir: meson.project_name()) + + libharfbuzz_cairo_dep = declare_dependency( + link_with: libharfbuzz_cairo, + include_directories: incsrc, + dependencies: [m_dep, cairo_dep]) + meson.override_dependency('harfbuzz-cairo', libharfbuzz_cairo_dep) + + harfbuzz_cairo_def = custom_target('harfbuzz-cairo.def', + command: gen_def_cmd, + input: hb_cairo_headers, + output: 'harfbuzz-cairo.def') + defs_list += [harfbuzz_cairo_def] + + pkgmod.generate(libharfbuzz_cairo, + description: 'HarfBuzz cairo support', + requires: ['harfbuzz = @0@'.format(meson.project_version())], + subdirs: [meson.project_name()], + version: meson.project_version(), + ) +endif + +if get_option('tests').enabled() + # TODO: MSVC gives the following, + # error LNK2019: unresolved external symbol "unsigned __int64 const * const _hb_NullPool" + if cpp.get_define('_MSC_FULL_VER') == '' + noinst_programs = { + 'main': 'main.cc', + 'test-basics': 'test.cc', + 'test-buffer-serialize': 'test-buffer-serialize.cc', + 'test-ot-meta': 'test-ot-meta.cc', + 'test-ot-name': 'test-ot-name.cc', + 'test-ot-glyphname': 'test-ot-glyphname.cc', + 'test-ot-gpos-size-params': 'test-gpos-size-params.cc', + 'test-ot-gsub-get-alternates': 'test-gsub-get-alternates.cc', + 'test-ot-gsub-would-substitute': 'test-gsub-would-substitute.cc', + 'test-use-table': 'test-use-table.cc', + } + foreach name, source : noinst_programs + executable(name, source, + include_directories: incconfig, + cpp_args: cpp_args + ['-UNDEBUG'], + dependencies: libharfbuzz_dep, + install: false, + ) + endforeach + endif + + compiled_tests = { + 'test-algs': ['test-algs.cc', 'hb-static.cc'], + 'test-array': ['test-array.cc'], + 'test-bimap': ['test-bimap.cc', 'hb-static.cc'], + 'test-classdef-graph': ['graph/test-classdef-graph.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], + 'test-iter': ['test-iter.cc', 'hb-static.cc'], + 'test-machinery': ['test-machinery.cc', 'hb-static.cc'], + 'test-map': ['test-map.cc', 'hb-static.cc'], + 'test-multimap': ['test-multimap.cc', 'hb-static.cc'], + 'test-number': ['test-number.cc', 'hb-number.cc'], + 'test-ot-tag': ['hb-ot-tag.cc'], + 'test-set': ['test-set.cc', 'hb-static.cc'], + 'test-serialize': ['test-serialize.cc', 'hb-static.cc'], + 'test-vector': ['test-vector.cc', 'hb-static.cc'], + 'test-repacker': ['test-repacker.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'], + 'test-instancer-solver': ['test-subset-instancer-solver.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], + 'test-priority-queue': ['test-priority-queue.cc', 'hb-static.cc'], + 'test-tuple-varstore': ['test-tuple-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], + 'test-item-varstore': ['test-item-varstore.cc', 'hb-subset-instancer-solver.cc', 'hb-static.cc'], + 'test-unicode-ranges': ['test-unicode-ranges.cc'], + } + foreach name, source : compiled_tests + if cpp.get_argument_syntax() == 'msvc' and source.contains('hb-static.cc') + # TODO: MSVC doesn't like tests having hb-static.cc, fix them + continue + endif + test(name, executable(name, source, + include_directories: incconfig, + cpp_args: cpp_args + ['-DMAIN', '-UNDEBUG'], + dependencies: libharfbuzz_dep, + install: false, + ), suite: ['src']) + endforeach +endif + +pkgmod.generate(libharfbuzz, + description: 'HarfBuzz text shaping library', + subdirs: [meson.project_name()], + version: meson.project_version(), +) + +pkgmod.generate(libharfbuzz_subset, + description: 'HarfBuzz font subsetter', + requires: ['harfbuzz = @0@'.format(meson.project_version())], + subdirs: [meson.project_name()], + version: meson.project_version(), +) + +libharfbuzz_icu_dep = null_dep +if have_icu and not have_icu_builtin + harfbuzz_icu_def = custom_target('harfbuzz-icu.def', + command: gen_def_cmd, + input: [hb_icu_headers], + output: 'harfbuzz-icu.def') + defs_list += [harfbuzz_icu_def] + + libharfbuzz_icu = library('harfbuzz-icu', [hb_icu_sources, hb_icu_headers], + include_directories: incconfig, + dependencies: icu_dep, + link_with: [libharfbuzz], + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + # ICU links to stdc++ anyway so the default linker is good + # link_language: 'c', + ) + + libharfbuzz_icu_dep = declare_dependency( + link_with: libharfbuzz_icu, + include_directories: incsrc, + dependencies: icu_dep) + meson.override_dependency('harfbuzz-icu', libharfbuzz_icu_dep) + + pkgmod.generate(libharfbuzz_icu, + description: 'HarfBuzz text shaping library ICU integration', + requires: ['harfbuzz = @0@'.format(meson.project_version())], + subdirs: [meson.project_name()], + version: meson.project_version(), + ) + + install_headers(hb_icu_headers, subdir: meson.project_name()) +endif + +have_gobject = conf.get('HAVE_GOBJECT', 0) == 1 + +# This code (especially PACKAGE_INIT) kept similar to what CMake's own +# configure_package_config_file() generates, see +# https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html#command:configure_package_config_file + +cmake_config = configuration_data() +cmake_config_dir = cmake_package_install_dir / 'harfbuzz' + +have_fs_relative_to = meson.version().version_compare('>=1.3.0') + +if not have_fs_relative_to + relative_to = find_program('relative_to.py') +endif + +if have_fs_relative_to + cmake_package_prefix_dir = fs.relative_to(get_option('prefix'), get_option('prefix') / cmake_config_dir) +else + cmake_package_prefix_dir = run_command(relative_to, get_option('prefix'), get_option('prefix') / cmake_config_dir, check: true).stdout().strip() +endif + +cmake_package_prefix_dir = '${CMAKE_CURRENT_LIST_DIR}/@0@'.format(cmake_package_prefix_dir) + +# Make all the relevant paths relative to our prefix, so we can later append +# them onto ${PACKAGE_PREFIX_DIR} to get the correct paths. + +cmake_install_includedir = get_option('includedir') + +if fs.is_absolute(cmake_install_includedir) + if have_fs_relative_to + cmake_install_includedir = fs.relative_to(cmake_install_includedir, get_option('prefix')) + else + cmake_install_includedir = run_command(relative_to, cmake_install_includedir, get_option('prefix'), check: true).stdout().strip() + endif +endif + +cmake_install_libdir = get_option('libdir') + +if fs.is_absolute(cmake_install_libdir) + if have_fs_relative_to + cmake_install_libdir = fs.relative_to(cmake_install_libdir, get_option('prefix')) + else + cmake_install_libdir = run_command(relative_to, cmake_install_libdir, get_option('prefix'), check: true).stdout().strip() + endif +endif + +cmake_config.set('PACKAGE_INIT', ''' +get_filename_component(PACKAGE_PREFIX_DIR "@0@" ABSOLUTE) + +macro(set_and_check _var _file) + set(${_var} "${_file}") + if(NOT EXISTS "${_file}") + message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !") + endif() +endmacro() + +macro(check_required_components _NAME) + foreach(comp ${${_NAME}_FIND_COMPONENTS}) + if(NOT ${_NAME}_${comp}_FOUND) + if(${_NAME}_FIND_REQUIRED_${comp}) + set(${_NAME}_FOUND FALSE) + endif() + endif() + endforeach() +endmacro() +'''.format(cmake_package_prefix_dir)) + +cmake_config.set('PACKAGE_CMAKE_INSTALL_INCLUDEDIR', '${PACKAGE_PREFIX_DIR}/@0@'.format(cmake_install_includedir)) +cmake_config.set('PACKAGE_CMAKE_INSTALL_LIBDIR', '${PACKAGE_PREFIX_DIR}/@0@'.format(cmake_install_libdir)) +cmake_config.set('PACKAGE_INCLUDE_INSTALL_DIR', '${PACKAGE_PREFIX_DIR}/@0@/@1@'.format(cmake_install_includedir, meson.project_name())) +cmake_config.set('HB_HAVE_GOBJECT', have_gobject ? 'YES' : 'NO') +cmake_config.set('HB_LIBRARY_TYPE', get_option('default_library') == 'static' ? 'STATIC' : 'SHARED') + +if get_option('default_library') == 'static' + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_STATIC_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_STATIC_LIBRARY_SUFFIX}') +elif host_machine.system() == 'darwin' + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '.@0@${CMAKE_SHARED_LIBRARY_SUFFIX}'.format(hb_so_version)) +elif host_machine.system() == 'windows' + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_IMPORT_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_IMPORT_LIBRARY_SUFFIX}') +else + cmake_config.set('HB_LIB_PREFIX', '${CMAKE_SHARED_LIBRARY_PREFIX}') + cmake_config.set('HB_LIB_SUFFIX', '${CMAKE_SHARED_LIBRARY_SUFFIX}.@0@'.format(version)) +endif + +configure_file(input: 'harfbuzz-config.cmake.in', + output: 'harfbuzz-config.cmake', + configuration: cmake_config, + install_dir: cmake_config_dir, +) + +gobject_enums_c = [] +gobject_enums_h = [] +libharfbuzz_gobject_dep = null_dep +if have_gobject + gnome = import('gnome') + + h_templ = configure_file( + input: 'hb-gobject-enums.h.tmpl', + output: 'hb-gobject-enums-tmp.h.tmpl', + copy: true) + + cc_templ = configure_file( + input: 'hb-gobject-enums.cc.tmpl', + output: 'hb-gobject-enums-tmp.cc.tmpl', + copy: true) + + enums = gnome.mkenums('hb-gobject', + sources: hb_headers, + h_template: h_templ, + c_template: cc_templ, + identifier_prefix: 'hb_', + symbol_prefix: 'hb_gobject', + ) + + gobject_enums_c = custom_target('hb-gobject-enums.cc', + input: enums[0], + output: 'hb-gobject-enums.cc', + command: [find_program('fix_get_types.py'), '@INPUT@', '@OUTPUT@'] + ) + + gobject_enums_h = custom_target('hb-gobject-enums.h', + input: enums[1], + output: 'hb-gobject-enums.h', + command: [find_program('fix_get_types.py'), '@INPUT@', '@OUTPUT@'], + install: true, + install_dir: get_option('prefix') / get_option('includedir') / meson.project_name(), + ) + + hb_gobject_sources += [gobject_enums_c] + + harfbuzz_gobject_def = custom_target('harfbuzz-gobject.def', + command: gen_def_cmd, + input: [hb_gobject_headers, gobject_enums_h], + output: 'harfbuzz-gobject.def') + defs_list += [harfbuzz_gobject_def] + + libharfbuzz_gobject = library('harfbuzz-gobject', [hb_gobject_sources, gobject_enums_c, gobject_enums_h], + include_directories: incconfig, + dependencies: [glib_dep, gobject_dep], + link_with: [libharfbuzz], + cpp_args: cpp_args + extra_hb_cpp_args, + soversion: hb_so_version, + version: version, + install: true, + darwin_versions: darwin_versions, + link_language: 'c', + ) + + gir = find_program('g-ir-scanner', required: get_option('introspection')) + build_gir = gir.found() and (not meson.is_cross_build() or get_option('introspection').enabled()) + + build_gir = build_gir and get_option('default_library') != 'static' + if not build_gir and get_option('introspection').enabled() + error('Introspection support is requested but the default library option should be shared or both') + endif + + if build_gir + conf.set('HAVE_INTROSPECTION', 1) + hb_gen_files_gir = gnome.generate_gir([libharfbuzz_gobject, libharfbuzz], + sources: [gir_headers, gir_sources, gobject_enums_h], + dependencies: libharfbuzz_dep, + namespace: 'HarfBuzz', + nsversion: '0.0', + identifier_prefix: 'hb_', + symbol_prefix: ['hb', 'hb_gobject'], + includes: ['GObject-2.0', 'freetype2-2.0'], + export_packages: ['harfbuzz-gobject', 'harfbuzz'], + header: 'hb-gobject.h', + install: true, + extra_args: ['--cflags-begin', + '-DHB_NO_SINGLE_HEADER_ERROR', + '-DHAVE_GOBJECT', + '-DHB_EXTERN=', + '--cflags-end']) + endif + + libharfbuzz_gobject_dep = declare_dependency( + link_with: libharfbuzz_gobject, + include_directories: incsrc, + sources: build_gir ? hb_gen_files_gir : hb_gobject_sources, + dependencies: [glib_dep, gobject_dep]) + meson.override_dependency('harfbuzz-gobject', libharfbuzz_gobject_dep) + + pkgmod.generate(libharfbuzz_gobject, + description: 'HarfBuzz text shaping library GObject integration', + requires: ['harfbuzz = @0@'.format(meson.project_version()), 'glib-2.0', 'gobject-2.0'], + subdirs: [meson.project_name()], + version: meson.project_version(), + ) + + install_headers(hb_gobject_headers, subdir: meson.project_name()) +else + if get_option('introspection').enabled() + error('introspection requires gobject to be enabled') + endif +endif + +if get_option('tests').enabled() + dist_check_script = [ + 'check-c-linkage-decls', + 'check-externs', + 'check-header-guards', + 'check-includes', + ] + + env = environment() + env.set('srcdir', meson.current_source_dir()) + env.set('base_srcdir', meson.source_root()) + env.set('builddir', meson.current_build_dir()) + env.set('libs', meson.current_build_dir()) # TODO: Merge this with builddir after autotools removal + HBSOURCES = [] + foreach f : hb_sources + HBSOURCES += '@0@'.format(f) + endforeach + env.set('HBSOURCES', ' '.join(HBSOURCES)) + HBHEADERS = [] + foreach f : hb_headers + HBHEADERS += '@0@'.format(f) + endforeach + env.set('HBHEADERS', ' '.join(HBHEADERS)) + + if cpp.get_argument_syntax() != 'msvc' and not meson.is_cross_build() # ensure the local tools are usable + dist_check_script += ['check-static-inits', 'check-symbols'] + if get_option('wasm').disabled() + dist_check_script += ['check-libstdc++'] + endif + endif + + foreach name : dist_check_script + test(name, find_program(name + '.py'), + env: env, + depends: name == 'check-symbols' ? defs_list : [], + suite: ['src'], + ) + endforeach +endif + +install_headers(hb_headers + hb_subset_headers, subdir: meson.project_name()) diff --git a/gfx/harfbuzz/src/moz.build b/gfx/harfbuzz/src/moz.build new file mode 100644 index 0000000000..7944026c27 --- /dev/null +++ b/gfx/harfbuzz/src/moz.build @@ -0,0 +1,125 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +FILES_PER_UNIFIED_FILE = 100 + +EXPORTS.harfbuzz += [ + 'hb-aat-layout.h', + 'hb-aat.h', + 'hb-blob.h', + 'hb-buffer.h', + 'hb-common.h', + 'hb-deprecated.h', + 'hb-draw.h', + 'hb-face.h', + 'hb-font.h', + 'hb-map.h', + 'hb-ot-color.h', + 'hb-ot-deprecated.h', + 'hb-ot-font.h', + 'hb-ot-layout.h', + 'hb-ot-math.h', + 'hb-ot-meta.h', + 'hb-ot-metrics.h', + 'hb-ot-name.h', + 'hb-ot-shape.h', + 'hb-ot-var.h', + 'hb-ot.h', + 'hb-paint.h', + 'hb-set.h', + 'hb-shape-plan.h', + 'hb-shape.h', + 'hb-style.h', + 'hb-unicode.h', + 'hb-version.h', + 'hb.h', +] + +UNIFIED_SOURCES += [ + 'hb-aat-layout.cc', + 'hb-aat-map.cc', + 'hb-blob.cc', + 'hb-buffer.cc', + 'hb-common.cc', + 'hb-draw.cc', + 'hb-face-builder.cc', + 'hb-face.cc', + 'hb-font.cc', + 'hb-map.cc', + 'hb-number.cc', + 'hb-ot-cff1-table.cc', + 'hb-ot-cff2-table.cc', + 'hb-ot-color.cc', + 'hb-ot-face.cc', + 'hb-ot-font.cc', + 'hb-ot-layout.cc', + 'hb-ot-map.cc', + 'hb-ot-math.cc', + 'hb-ot-metrics.cc', + 'hb-ot-name.cc', + 'hb-ot-shape-fallback.cc', + 'hb-ot-shape-normalize.cc', + 'hb-ot-shape.cc', + 'hb-ot-shaper-arabic.cc', + 'hb-ot-shaper-default.cc', + 'hb-ot-shaper-hangul.cc', + 'hb-ot-shaper-hebrew.cc', + 'hb-ot-shaper-indic-table.cc', + 'hb-ot-shaper-indic.cc', + 'hb-ot-shaper-khmer.cc', + 'hb-ot-shaper-myanmar.cc', + 'hb-ot-shaper-syllabic.cc', + 'hb-ot-shaper-thai.cc', + 'hb-ot-shaper-use.cc', + 'hb-ot-shaper-vowel-constraints.cc', + 'hb-ot-tag.cc', + 'hb-ot-var.cc', + 'hb-outline.cc', + 'hb-paint-extents.cc', + 'hb-paint.cc', + 'hb-set.cc', + 'hb-shape-plan.cc', + 'hb-shape.cc', + 'hb-shaper.cc', + 'hb-static.cc', + 'hb-style.cc', + 'hb-unicode.cc', + 'hb-wasm-api.cc', + 'hb-wasm-shape.cc', +] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk': + EXPORTS.harfbuzz += [ + 'hb-glib.h', + ] + UNIFIED_SOURCES += [ + 'hb-glib.cc', + ] + CXXFLAGS += CONFIG['GLIB_CFLAGS'] + +# We allow warnings for third-party code that can be updated from upstream. +AllowCompilerWarnings() + +FINAL_LIBRARY = 'gkmedias' + +DEFINES['PACKAGE_VERSION'] = '"moz"' +DEFINES['PACKAGE_BUGREPORT'] = '"http://bugzilla.mozilla.org/"' +DEFINES['HAVE_OT'] = 1 +DEFINES['HAVE_ROUND'] = 1 +DEFINES['HB_NO_BUFFER_VERIFY'] = True +DEFINES['HB_NO_FALLBACK_SHAPE'] = True +DEFINES['HB_NO_UCD'] = True +DEFINES['HB_NO_UNICODE_FUNCS'] = True + +# On Android, we want to use harfbuzz's mmap support for file access, +# so we need to set the appropriate configuration flags as we're not +# running harfbuzz's own configure script. +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': + DEFINES['HAVE_MMAP'] = 1 + DEFINES['HAVE_SYS_MMAN_H'] = 1 + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': + DEFINES['UNICODE'] = True diff --git a/gfx/harfbuzz/src/ms-use/COPYING b/gfx/harfbuzz/src/ms-use/COPYING new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/gfx/harfbuzz/src/ms-use/COPYING @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + 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 diff --git a/gfx/harfbuzz/src/ms-use/IndicPositionalCategory-Additional.txt b/gfx/harfbuzz/src/ms-use/IndicPositionalCategory-Additional.txt new file mode 100644 index 0000000000..cb07643bbb --- /dev/null +++ b/gfx/harfbuzz/src/ms-use/IndicPositionalCategory-Additional.txt @@ -0,0 +1,121 @@ +# Override values For Indic_Positional_Category +# Not derivable +# Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 +# Updated for Unicode 10.0 by Andrew Glass 2017-07-25 +# Ammended for Unicode 10.0 by Andrew Glass 2018-09-21 +# Updated for L2/19-083 by Andrew Glass 2019-05-06 +# Updated for Unicode 12.1 by Andrew Glass 2019-05-30 +# Updated for Unicode 13.0 by Andrew Glass 2020-07-28 +# Updated for Unicode 14.0 by Andrew Glass 2021-09-28 +# Updated for Unicode 15.0 by Andrew Glass 2022-09-16 +# Updated for Unicode 15.1 by Andrew Glass 2023-09-14 + +# ================================================ +# ================================================ +# OVERRIDES TO ASSIGNED VALUES +# ================================================ +# ================================================ + +# Indic_Positional_Category=Bottom +0F72 ; Bottom # Mn TIBETAN VOWEL SIGN I # Not really below, but need to override to fit into Universal model +0F7A..0F7D ; Bottom # Mn [4] TIBETAN VOWEL SIGN E..TIBETAN VOWEL SIGN OO # Not really below, but need to override to fit into Universal model +0F80 ; Bottom # Mn TIBETAN VOWEL SIGN REVERSED I # Not really below, but need to override to fit into Universal model +A9BF ; Bottom # Mc JAVANESE CONSONANT SIGN CAKRA +10A38 ; Bottom # Mn KHAROSHTHI SIGN BAR ABOVE # Overriden, ccc controls order USE issue #26 +11127..11129 ; Bottom # Mn [3] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN II +1112D ; Bottom # Mn CHAKMA VOWEL SIGN AI +11130 ; Bottom # Mn CHAKMA VOWEL SIGN OI +1BF2..1BF3 ; Bottom # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN # see USE issue #20 + + +# ================================================ + +# Indic_Positional_Category=Left +1C29 ; Left # Mc LEPCHA VOWEL SIGN OO # Reduced from Top_And_Left + +# ================================================ + + +# Indic_Positional_Category=Right +A9BE ; Right # Mc JAVANESE CONSONANT SIGN PENGKAL # Reduced from Bottom_And_Right +10A0C ; Right # Mn KHAROSHTHI VOWEL LENGTH MARK # Follows vowels and precedes vowel modifiers +11942 ; Right # Mc DIVES AKURU MEDIAL RA # Reduced from Bottom_And_Right + +# ================================================ + +# Indic_Positional_Category=Top +0F74 ; Top # Mn TIBETAN VOWEL SIGN U # Not really above, but need to override to fit into Universal model +1A18 ; Top # Mn BUGINESE VOWEL SIGN U # Workaround to allow below to occur before above by treating all below marks as above +AA35 ; Top # Mn CHAM CONSONANT SIGN +1112A..1112B ; Top # Mn [2] CHAKMA VOWEL SIGN U..CHAKMA VOWEL SIGN UU # see USE issue #25 +11131..11132 ; Top # Mn [2] CHAKMA O MARK..CHAKMA AU MARK # see USE issue #25 +1E4EC..1E4EF ; Top # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH # 1E4EE is below, but made to for ccc + +# ================================================ + +# Indic_Positional_Category=Top_And_Right +0E33 ; Top_And_Right # Lo THAI CHARACTER SARA AM # IPC has Right, which seems to be a mistake. +0EB3 ; Top_And_Right # Lo LAO VOWEL SIGN AM # IPC has Right, which seems to be a mistake. + +# ================================================ +# ================================================ +# VALUES NOT ASSIGNED IN Indic_Positional_Category +# ================================================ +# ================================================ + +# Indic_Positional_Category=Bottom +0859..085B ; Bottom # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK +18A9 ; Bottom # Mn MONGOLIAN LETTER ALI GALI DAGALGA +10AE5 ; Bottom # Mn MANICHAEAN ABBREVIATION MARK ABOVE # Overriden, ccc controls order +10AE6 ; Bottom # Mn MANICHAEAN ABBREVIATION MARK BELOW +10F46..10F47 ; Bottom # Mn [2] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING TWO DOTS BELOW +10F48..10F4A ; Bottom # Mn [3] SOGDIAN COMBINING DOT ABOVE..SOGDIAN COMBINING CURVE ABOVE # Overriden, ccc controls order +10F4B ; Bottom # Mn SOGDIAN COMBINING CURVE BELOW +10F4C ; Bottom # Mn SOGDIAN COMBINING HOOK ABOVE # Overriden, ccc controls order +10F4D..10F50 ; Bottom # Mn [4] SOGDIAN COMBINING HOOK BELOW..SOGDIAN COMBINING STROKE BELOW +10F82 ; Bottom # Mn OLD UYGHUR COMBINING DOT ABOVE # Overriden, ccc controls order +10F83 ; Bottom # Mn OLD UYGHUR COMBINING DOT BELOW +10F84 ; Bottom # Mn OLD UYGHUR COMBINING TWO DOTS ABOVE # Overriden, ccc controls order +10F85 ; Bottom # Mn OLD UYGHUR COMBINING TWO DOTS BELOW +16F4F ; Bottom # Mn MIAO SIGN CONSONANT MODIFIER BAR +16F51..16F87 ; Bottom # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI +16F8F..16F92 ; Bottom # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW +#HIEROGLYPHS defined here while ISC is being used as a proxy for dedicated Hieroglyph cluster +13440 ; Bottom # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY +13447..13455 ; Bottom # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED + +# ================================================ + +# Indic_Positional_Category=Left +103C ; Left # Mc MYANMAR CONSONANT SIGN MEDIAL RA + +# ================================================ + +# Indic_Positional_Category=Top +07EB..07F3 ; Top # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE +07FD ; Top # Mn NKO DANTAYALAN # Not really top, but assigned here to allow ccc to control mark order +1885..1886 ; Top # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA +1CF8..1CF9 ; Top # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE +10D24..10D27 ; Top # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI +10EAB..10EAC ; Top # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK +16B30..16B36 ; Top # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM +1E130..1E136 ; Top # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D +1E2AE ; Top # Mn TOTO SIGN RISING TONE +1E2EC..1E2EF ; Top # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI +1E944..1E94A ; Top # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA + +# ================================================ + +# Indic_Positional_Category=Overstruck +1BC9D..1BC9E ; Overstruck # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK + +# ================================================ +# ================================================ +# Deliberately suppressed +# ================================================ +# ================================================ + +# Indic_Positional_Category=NA +180B..180D ; NA # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE +180F ; NA # Mn MONGOLIAN FREE VARIATION SELECTOR FOUR +2D7F ; NA # Mn TIFINAGH CONSONANT JOINER diff --git a/gfx/harfbuzz/src/ms-use/IndicShapingInvalidCluster.txt b/gfx/harfbuzz/src/ms-use/IndicShapingInvalidCluster.txt new file mode 100644 index 0000000000..9e0edd34ca --- /dev/null +++ b/gfx/harfbuzz/src/ms-use/IndicShapingInvalidCluster.txt @@ -0,0 +1,113 @@ +# IndicShapingInvalidCluster.txt +# Date: 2015-03-12, 21:17:00 GMT [AG] +# Date: 2019-11-08, 23:22:00 GMT [AG] +# +# This file defines the following property: +# +# Indic_Shaping_Invalid_Cluster +# +# Scope: This file enumerates sequences of characters that should be treated as invalid clusters + + 0905 0946 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT E + 0905 093E ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AA + 0930 094D 0907 ; # DEVANAGARI LETTER RA, DEVANAGARI SIGN VIRAMA, DEVANAGARI LETTER I + 0909 0941 ; # DEVANAGARI LETTER U, DEVANAGARI VOWEL SIGN U + 090F 0945 ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN CANDRA E + 090F 0946 ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN SHORT E + 090F 0947 ; # DEVANAGARI LETTER E, DEVANAGARI VOWEL SIGN E + 0905 0949 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA O + 0906 0945 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN CANDRA E + 0905 094A ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN SHORT O + 0906 0946 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN SHORT E + 0905 094B ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN O + 0906 0947 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN E + 0905 094C ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AU + 0906 0948 ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN AI + 0905 0945 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN CANDRA E + 0905 093A ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OE + 0905 093B ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN OOE + 0906 093A ; # DEVANAGARI LETTER AA, DEVANAGARI VOWEL SIGN OE + 0905 094F ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN AW + 0905 0956 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UE + 0905 0957 ; # DEVANAGARI LETTER A, DEVANAGARI VOWEL SIGN UUE + 0985 09BE ; # BENGALI LETTER A, BENGALI VOWEL SIGN AA + 098B 09C3 ; # BENGALI LETTER VOCALIC R, BENGALI VOWEL SIGN VOCALIC R + 098C 09E2 ; # BENGALI LETTER VOCALIC L, BENGALI VOWEL SIGN VOCALIC L + 0A05 0A3E ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AA + 0A72 0A3F ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN I + 0A72 0A40 ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN II + 0A73 0A41 ; # GURMUKHI URA, GURMUKHI VOWEL SIGN U + 0A73 0A42 ; # GURMUKHI URA, GURMUKHI VOWEL SIGN UU + 0A72 0A47 ; # GURMUKHI IRI, GURMUKHI VOWEL SIGN EE + 0A05 0A48 ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AI + 0A73 0A4B ; # GURMUKHI URA, GURMUKHI VOWEL SIGN OO + 0A05 0A4C ; # GURMUKHI LETTER A, GURMUKHI VOWEL SIGN AU + 0A85 0ABE ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA + 0A85 0AC5 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA E + 0A85 0AC7 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN E + 0A85 0AC8 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AI + 0A85 0AC9 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN CANDRA O + 0A85 0ACB ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN O + 0A85 0ABE 0AC5 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN CANDRA E + 0A85 0ACC ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AU + 0A85 0ABE 0AC8 ; # GUJARATI LETTER A, GUJARATI VOWEL SIGN AA, GUJARATI VOWEL SIGN AI + 0AC5 0ABE ; # GUJARATI VOWEL SIGN CANDRA E, GUJARATI VOWEL SIGN AA + 0B05 0B3E ; # ORIYA LETTER A, ORIYA VOWEL SIGN AA + 0B0F 0B57 ; # ORIYA LETTER E, ORIYA AU LENGTH MARK + 0B13 0B57 ; # ORIYA LETTER O, ORIYA AU LENGTH MARK + 0B85 0BC2 ; # TAMIL LETTER A, TAMIL VOWEL SIGN UU + 0C12 0C55 ; # TELUGU LETTER O, TELUGU LENGTH MARK + 0C12 0C4C ; # TELUGU LETTER O, TELUGU VOWEL SIGN AU + 0C3F 0C55 ; # TELUGU VOWEL SIGN I, TELUGU LENGTH MARK + 0C46 0C55 ; # TELUGU VOWEL SIGN E, TELUGU LENGTH MARK + 0C4A 0C55 ; # TELUGU VOWEL SIGN O, TELUGU LENGTH MARK + 0C89 0CBE ; # KANNADA LETTER U, KANNADA VOWEL SIGN AA + 0C92 0CCC ; # KANNADA LETTER O, KANNADA VOWEL SIGN AU + 0C8B 0CBE ; # KANNADA LETTER VOCALIC R, KANNADA VOWEL SIGN AA + 0D07 0D57 ; # MALAYALAM LETTER I, MALAYALAM AU LENGTH MARK + 0D09 0D57 ; # MALAYALAM LETTER U, MALAYALAM AU LENGTH MARK + 0D0E 0D46 ; # MALAYALAM LETTER E, MALAYALAM VOWEL SIGN E + 0D12 0D3E ; # MALAYALAM LETTER O, MALAYALAM VOWEL SIGN AA + 0D12 0D57 ; # MALAYALAM LETTER O, MALAYALAM AU LENGTH MARK + 0D85 0DCF ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN AELA-PILLA + 0D85 0DD0 ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN KETTI AEDA-PILLA + 0D85 0DD1 ; # SINHALA LETTER AYANNA, SINHALA VOWEL SIGN DIGA AEDA-PILLA + 0D8B 0DDF ; # SINHALA LETTER UYANNA, SINHALA VOWEL SIGN GAYANUKITTA + 0D8D 0DD8 ; # SINHALA LETTER IRUYANNA, SINHALA VOWEL SIGN GAETTA-PILLA + 0D8F 0DDF ; # SINHALA LETTER ILUYANNA, SINHALA VOWEL SIGN GAYANUKITTA + 0D91 0DCA ; # SINHALA LETTER EYANNA, SINHALA SIGN AL-LAKUNA + 0D91 0DD9 ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA + 0D91 0DDA ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN DIGA KOMBUVA + 0D91 0DDC ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA + 0D91 0DDD ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA + 0D91 0DDE ; # SINHALA LETTER EYANNA, SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA + 0D94 0DDF ; # SINHALA LETTER OYANNA, SINHALA VOWEL SIGN GAYANUKITTA + 11005 11038 ; # BRAHMI LETTER A, BRAHMI VOWEL SIGN AA + 1100B 1103E ; # BRAHMI LETTER VOCALIC R, BRAHMI VOWEL SIGN VOCALIC R + 1100F 11042 ; # BRAHMI LETTER E, BRAHMI VOWEL SIGN E + 11680 116AD ; # TAKRI LETTER A, TAKRI VOWEL SIGN AA + 11686 116B2 ; # TAKRI LETTER E, TAKRI VOWEL SIGN E + 11680 116B4 ; # TAKRI LETTER A, TAKRI VOWEL SIGN O + 11680 116B5 ; # TAKRI LETTER A, TAKRI VOWEL SIGN AU + 11200 1122C ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AA + 11240 1122E ; # KHOJKI LETTER SHORT I, KHOJKI VOWEL SIGN II + 11206 1122C ; # KHOJKI LETTER O, KHOJKI VOWEL SIGN AA + 11200 11231 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AI + 11200 11233 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AU + 11200 1122C 11231 ; # KHOJKI LETTER A, KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN AI + 1122C 11230 ; # KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN E + 1122C 11231 ; # KHOJKI VOWEL SIGN AA, KHOJKI VOWEL SIGN AI + 112B0 112E0 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AA + 112B0 112E5 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN E + 112B0 112E6 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AI + 112B0 112E7 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN O + 112B0 112E8 ; # KHUDAWADI LETTER A, KHUDAWADI VOWEL SIGN AU + 11481 114B0 ; # TIRHUTA LETTER A, TIRHUTA VOWEL SIGN AA + 114AA 114B5 ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC R + 114AA 114B6 ; # TIRHUTA LETTER LA, TIRHUTA VOWEL SIGN VOCALIC RR + 1148B 114BA ; # TIRHUTA LETTER E, TIRHUTA VOWEL SIGN SHORT E + 1148D 114BA ; # TIRHUTA LETTER O, TIRHUTA VOWEL SIGN SHORT E + 11600 11639 ; # MODI LETTER A, MODI VOWEL SIGN E + 11600 1163A ; # MODI LETTER A, MODI VOWEL SIGN AI + 11601 11639 ; # MODI LETTER AA, MODI VOWEL SIGN E + 11601 1163A ; # MODI LETTER AA, MODI VOWEL SIGN AI diff --git a/gfx/harfbuzz/src/ms-use/IndicSyllabicCategory-Additional.txt b/gfx/harfbuzz/src/ms-use/IndicSyllabicCategory-Additional.txt new file mode 100644 index 0000000000..43326c12d1 --- /dev/null +++ b/gfx/harfbuzz/src/ms-use/IndicSyllabicCategory-Additional.txt @@ -0,0 +1,261 @@ +# Override values For Indic_Syllabic_Category +# Not derivable +# Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17 +# Updated for Unicode 10.0 by Andrew Glass 2017-07-25 +# Updated for Unicode 12.1 by Andrew Glass 2019-05-24 +# Updated for Unicode 13.0 by Andrew Glass 2020-07-28 +# Updated for Unicode 14.0 by Andrew Glass 2021-09-25 +# Updated for Unicode 15.0 by Andrew Glass 2022-09-16 +# Updated for Unicode 15.1 by Andrew Glass 2023-09-14 + +# ================================================ +# OVERRIDES TO ASSIGNED VALUES +# ================================================ + +# Indic_Syllabic_Category=Bindu +193A ; Bindu # Mn LIMBU SIGN KEMPHRENG +AA29 ; Bindu # Mn CHAM VOWEL SIGN AA +10A0D ; Bindu # Mn KHAROSHTHI SIGN DOUBLE RING BELOW + +# ================================================ + +# Indic_Syllabic_Category=Consonant +19C1..19C7 ; Consonant # Lo [7] NEW TAI LUE LETTER FINAL V..NEW TAI LUE LETTER FINAL B # Reassigned to avoid clustering with a base consonant +25CC ; Consonant # So DOTTED CIRCLE #Reassigned to allow it to cluster as a generic base + +# ================================================ + +# Indic_Syllabic_Category=Consonant_Dead +0F7F ; Consonant_Dead # Mc TIBETAN SIGN RNAM BCAD # reassigned so that visarga can form an independent cluster, but see #19 + +# ================================================ + +# Indic_Syllabic_Category=Consonant_Final_Modifier +1C36 ; Consonant_Final_Modifier # Mn LEPCHA SIGN RAN + +# ================================================ + +# Indic_Syllabic_Category=Gemination_Mark +11134 ; Gemination_Mark # Mc CHAKMA MAAYYAA + +# ================================================ + +# Indic_Syllabic_Category=Nukta +0F71 ; Nukta # Mn TIBETAN VOWEL SIGN AA # Reassigned to get this before an above vowel, but see #22 +1BF2..1BF3 ; Nukta # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN # see USE issue #20 + +# ================================================ + +# Indic_Syllabic_Category=Tone_Mark +1A7B..1A7C ; Tone_Mark # Mn [2] TAI THAM SIGN MAI SAM..TAI THAM SIGN KHUEN-LUE KARAN +1A7F ; Tone_Mark # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT + +# ================================================ + +# Indic_Syllabic_Category=Vowel_Independent +AAB1 ; Vowel_Independent # Lo TAI VIET VOWEL AA +AABA ; Vowel_Independent # Lo TAI VIET VOWEL UA +AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN + +# ================================================ +# ================================================ +# VALUES NOT ASSIGNED IN Indic_Syllabic_Category +# ================================================ +# ================================================ + +# Indic_Syllabic_Category=Consonant +0800..0815 ; Consonant # Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF +0840..0858 ; Consonant # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN +0F00..0F01 ; Consonant # Lo [2] TIBETAN SYLLABLE OM..TIBETAN MARK GTER YIG MGO TRUNCATED +0F04..0F06 ; Consonant # Po TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK CARET YIG MGO PHUR SHAD MA +1800 ; Consonant # Po MONGOLIAN BIRGA # Reassigned so that legacy Birga + MFVS sequences still work +1807 ; Consonant # Po MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER +180A ; Consonant # Po MONGOLIAN NIRUGU +1820..1878 ; Consonant # Lo [88] MONGOLIAN LETTER A..MONGOLIAN LETTER CHA WITH TWO DOTS +1843 ; Consonant # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN +2D30..2D67 ; Consonant # Lo [56] TIFINAGH LETTER YA..TIFINAGH LETTER YO +2D6F ; Consonant # Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK +10570..1057A ; Consonant # Lo [11] VITHKUQI CAPITAL LETTER A..VITHKUQI CAPITAL LETTER GA +1057C..1058A ; Consonant # Lo [15] VITHKUQI CAPITAL LETTER HA..VITHKUQI CAPITAL LETTER RE +1058C..10592 ; Consonant # Lo [7] VITHKUQI CAPITAL LETTER SE..VITHKUQI CAPITAL LETTER XE +10594..10595 ; Consonant # Lo [2] VITHKUQI CAPITAL LETTER Y..VITHKUQI CAPITAL LETTER ZE +10597..105A1 ; Consonant # Lo [11] VITHKUQI SMALL LETTER A..VITHKUQI SMALL LETTER GA +105A3..105B1 ; Consonant # Lo [15] VITHKUQI SMALL LETTER HA..VITHKUQI SMALL LETTER RE +105B3..105B9 ; Consonant # Lo [7] VITHKUQI SMALL LETTER SE..VITHKUQI SMALL LETTER XE +105BB..105BC ; Consonant # Lo [2] VITHKUQI SMALL LETTER Y..VITHKUQI SMALL LETTER ZE +10AC0..10AC7 ; Consonant # Lo [8] MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW +10AC9..10AE4 ; Consonant # Lo [28] MANICHAEAN LETTER ZAYIN..MANICHAEAN LETTER TAW +10D00..10D23 ; Consonant # Lo [36] HANIFI ROHINGYA LETTER A..HANIFI ROHINGYA MARK NA KHONNA +10E80..10EA9 ; Consonant # Lo [42] YEZIDI LETTER ELIF..YEZIDI LETTER ET +10EB0..10EB1 ; Consonant # Lo [2] YEZIDI LETTER LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE +10F30..10F45 ; Consonant # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN +10F70..10F81 ; Consonant # Lo [18] OLD UYGHUR LETTER ALEPH..OLD UYGHUR LETTER LESH +111DA ; Consonant # Lo SHARADA EKAM +16B00..16B2F ; Consonant # Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU +16F00..16F4A ; Consonant # Lo [75] MIAO LETTER PA..MIAO LETTER RTE +16FE4 ; Consonant # Mn KHITAN SMALL SCRIPT FILLER # Avoids Mn pushing this into VOWEL class +18B00..18CD5 ; Consonant # Lo [470] KHITAN SMALL SCRIPT CHARACTER-18B00..KHITAN SMALL SCRIPT CHARACTER-18CD5 +1BC00..1BC6A ; Consonant # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M +1BC70..1BC7C ; Consonant # Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK +1BC80..1BC88 ; Consonant # Lo [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL +1BC90..1BC99 ; Consonant # Lo [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW +1E100..1E12C ; Consonant # Lo [45] NYIAKENG PUACHUE HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W +1E137..1E13D ; Consonant # Lm [7] NYIAKENG PUACHUE HMONG SIGN FOR PERSON..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER +1E14E ; Consonant # Lo NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ +1E14F ; Consonant # So NYIAKENG PUACHUE HMONG CIRCLED CA +1E290..1E2AD ; Consonant # Lo [30] TOTO LETTER PA..TOTO LETTER A +1E2C0..1E2EB ; Consonant # Lo [44] WANCHO LETTER AA..WANCHO LETTER YIH +1E4D0..1E4EA ; Consonant # Lo [27] NAG MUNDARI LETTER O..NAG MUNDARI LETTER ELL +1E4EB ; Consonant # Lm NAG MUNDARI SIGN OJOD +1E900..1E921 ; Consonant # Lu [34] ADLAM CAPITAL LETTER ALIF..ADLAM CAPITAL LETTER SHA +1E922..1E943 ; Consonant # Ll [34] ADLAM SMALL LETTER ALIF..ADLAM SMALL LETTER SHA +1E94B ; Consonant # Lm ADLAM NASALIZATION MARK + +# ================================================ + +# Indic_Syllabic_Category=Consonant_Placeholder +1880..1884 ; Consonant_Placeholder # Lo [5] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI INVERTED UBADAMA + +# ================================================ + +# Indic_Syllabic_Category=Gemination_Mark +10D27 ; Gemination_Mark # Mn HANIFI ROHINGYA SIGN TASSI + +# ================================================ + +# Indic_Syllabic_Category=Modifying_Letter +FE00..FE0F ; Modifying_Letter # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16# Need to treat them as isolated bases so they don't merge with a cluster in invalid scenarios +16F50 ; Modifying_Letter # Lo MIAO LETTER NASALIZATION + +# ================================================ + +# Indic_Syllabic_Category=Nukta +0859..085B ; Nukta # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK +0F39 ; Nukta # Mn TIBETAN MARK TSA -PHRU # NOW IN UNICODE 10.0 +1885..1886 ; Nukta # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA +18A9 ; Nukta # Mn MONGOLIAN LETTER ALI GALI DAGALGA +10AE5..10AE6 ; Nukta # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW +16F4F ; Nukta # Mn MIAO SIGN CONSONANT MODIFIER BAR +1BC9D..1BC9E ; Nukta # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK +1E944..1E94A ; Nukta # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA +10F82..10F85 ; Nukta # Mn [4] OLD UYGHUR COMBINING DOT ABOVE..OLD UYGHUR COMBINING TWO DOTS BELOW + +# ================================================ + +# Indic_Syllabic_Category=Number +10D30..10D39 ; Number # Nd [10] HANIFI ROHINGYA DIGIT ZERO..HANIFI ROHINGYA DIGIT NINE +10F51..10F54 ; Number # No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED +16AC0..16AC9 ; Number # Nd [10] TANGSA DIGIT ZERO..TANGSA DIGIT NINE +1E140..1E149 ; Number # Nd [10] NYIAKENG PUACHUE HMONG DIGIT ZERO..NYIAKENG PUACHUE HMONG DIGIT NINE +1E2F0..1E2F9 ; Number # Nd [10] WANCHO DIGIT ZERO..WANCHO DIGIT NINE +1E4F0..1E4F9 ; Number # Nd [10] NAG MUNDARI DIGIT ZERO..NAG MUNDARI DIGIT NINE +1E950..1E959 ; Number # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE + +# ================================================ + +# Indic_Syllabic_Category=Tone_Mark +07EB..07F3 ; Tone_Mark # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE +07FD ; Tone_Mark # Mn NKO DANTAYALAN +0F86..0F87 ; Tone_Mark # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS +17CF ; Tone_Mark # Mn KHMER SIGN AHSDA +10D24..10D26 ; Tone_Mark # Mn [3] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TANA +10F46..10F50 ; Tone_Mark # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW +16B30..16B36 ; Tone_Mark # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM +16F8F..16F92 ; Tone_Mark # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW +1E130..1E136 ; Tone_Mark # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D +1E2AE ; Tone_Mark # Mn TOTO SIGN RISING TONE +1E2EC..1E2EF ; Tone_Mark # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI + +# ================================================ + +# Indic_Syllabic_Category=Virama +2D7F ; Virama # Mn TIFINAGH CONSONANT JOINER + +# ================================================ + +# Indic_Syllabic_Category=Vowel_Independent +AAB1 ; Vowel_Independent # Lo TAI VIET VOWEL AA +AABA ; Vowel_Independent # Lo TAI VIET VOWEL UA +AABD ; Vowel_Independent # Lo TAI VIET VOWEL AN + +# ================================================ + +# Indic_Syllabic_Category=Vowel_Dependent +0B55 ; Vowel_Dependent # Mn ORIYA SIGN OVERLINE +10EAB..10EAC ; Vowel_Dependent # Mn [2] YEZIDI COMBINING HAMZA MARK..YEZIDI COMBINING MADDA MARK +16F51..16F87 ; Vowel_Dependent # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI +1E4EC..1E4EF ; Vowel_Dependent # Mn [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI SIGN SUTUH + +# ================================================ + +# Indic_Syllabic_Category=Cantillation_Mark + +1CF8..1CF9 ; Cantillation_Mark # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE + +# ================================================ + +# Indic_Syllabic_Category=Symbol_Modifier +1B6B..1B73 ; Symbol_Modifier # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG + +# ================================================ +# ================================================ +# PROPERTIES NOT ASSIGNED IN Indic_Syllabic_Category +# ================================================ +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph +13000..1342F ; Hieroglyph # Lo [1072] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH V011D +1343C..1343F ; Hieroglyph # Cf [4] EGYPTIAN HIEROGLYPH BEGIN ENCLOSURE..END WALLED ENCLOSURE +13441..13446 ; Hieroglyph # Lo [6] EGYPTIAN HIEROGLYPH FULL BLANK..HIEROGLYPH WIDE LOST SIGN + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Joiner +13430..13436 ; Hieroglyph_Joiner # Cf [7] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH OVERLAY MIDDLE +13439..1343B ; Hieroglyph_Joiner # Cf [3] EGYPTIAN HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH INSERT AT BOTTOM + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Mark_Begin +005B ; Hieroglyph_Mark_Begin # Ps LEFT SQUARE BRACKET +007B ; Hieroglyph_Mark_Begin # Ps LEFT CURLY BRACKET +27E6 ; Hieroglyph_Mark_Begin # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET +27E8 ; Hieroglyph_Mark_Begin # Ps MATHEMATICAL LEFT ANGLE BRACKET +2E22 ; Hieroglyph_Mark_Begin # Ps TOP LEFT HALF BRACKET +2E24 ; Hieroglyph_Mark_Begin # Ps BOTTOM LEFT HALF BRACKET + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Mark_End +005D ; Hieroglyph_Mark_End # Pe RIGHT SQUARE BRACKET +007D ; Hieroglyph_Mark_End # Pe RIGHT CURLY BRACKET +27E7 ; Hieroglyph_Mark_End # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET +27E9 ; Hieroglyph_Mark_End # Pe MATHEMATICAL RIGHT ANGLE BRACKET +2E23 ; Hieroglyph_Mark_End # Pe TOP RIGHT HALF BRACKET +2E25 ; Hieroglyph_Mark_End # Pe BOTTOM RIGHT HALF BRACKET + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Segment_Begin +13437 ; Hieroglyph_Segment_Begin # Cf EGYPTIAN HIEROGLYPH BEGIN SEGMENT + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Segment_End +13438 ; Hieroglyph_Segment_End # Cf EGYPTIAN HIEROGLYPH END SEGMENT + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Mirror +13440 ; Hieroglyph_Mirror # Mn EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY + +# ================================================ + +# USE, Extended_Syllabic_Category=Hieroglyph_Modifier +13447..13455 ; Hieroglyph_Modifier # Mn [15] EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED + +# ================================================ + +# eof + + diff --git a/gfx/harfbuzz/src/relative_to.py b/gfx/harfbuzz/src/relative_to.py new file mode 100755 index 0000000000..b660d27935 --- /dev/null +++ b/gfx/harfbuzz/src/relative_to.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 + +import sys +from os import path + +print(path.relpath(sys.argv[1], sys.argv[2])) diff --git a/gfx/harfbuzz/src/sample.py b/gfx/harfbuzz/src/sample.py new file mode 100755 index 0000000000..5d04e803f2 --- /dev/null +++ b/gfx/harfbuzz/src/sample.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +import sys +import array +import gi +gi.require_version('HarfBuzz', '0.0') +from gi.repository import HarfBuzz as hb +from gi.repository import GLib + +fontdata = open (sys.argv[1], 'rb').read () +text = sys.argv[2] +# Need to create GLib.Bytes explicitly until this bug is fixed: +# https://bugzilla.gnome.org/show_bug.cgi?id=729541 +blob = hb.glib_blob_create (GLib.Bytes.new (fontdata)) +face = hb.face_create (blob, 0) +del blob +font = hb.font_create (face) +upem = hb.face_get_upem (face) +del face +hb.font_set_scale (font, upem, upem) +#hb.ft_font_set_funcs (font) +hb.ot_font_set_funcs (font) + +buf = hb.buffer_create () +class Debugger (object): + def message (self, buf, font, msg, data, _x_what_is_this): + print (msg) + return True +debugger = Debugger () +hb.buffer_set_message_func (buf, debugger.message, 1, 0) + +## +## Add text to buffer +## +# +# See https://github.com/harfbuzz/harfbuzz/pull/271 +# +# If you do not care about cluster values reflecting Python +# string indices, then this is quickest way to add text to +# buffer: +# hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1) +# Otherwise, then following handles both narrow and wide +# Python builds (the first item in the array is BOM, so we skip it): +if sys.maxunicode == 0x10FFFF: + hb.buffer_add_utf32 (buf, array.array ('I', text.encode ('utf-32'))[1:], 0, -1) +else: + hb.buffer_add_utf16 (buf, array.array ('H', text.encode ('utf-16'))[1:], 0, -1) + + +hb.buffer_guess_segment_properties (buf) + +hb.shape (font, buf, []) +del font + +infos = hb.buffer_get_glyph_infos (buf) +positions = hb.buffer_get_glyph_positions (buf) + +for info, pos in zip (infos, positions): + gid = info.codepoint + cluster = info.cluster + x_advance = pos.x_advance + x_offset = pos.x_offset + y_offset = pos.y_offset + + print ("gid%d=%d@%d,%d+%d" % (gid, cluster, x_advance, x_offset, y_offset)) diff --git a/gfx/harfbuzz/src/update-unicode-tables.make b/gfx/harfbuzz/src/update-unicode-tables.make new file mode 100755 index 0000000000..6f5b490f70 --- /dev/null +++ b/gfx/harfbuzz/src/update-unicode-tables.make @@ -0,0 +1,49 @@ +#!/usr/bin/env -S make -f + +all: packtab \ + hb-ot-shaper-arabic-joining-list.hh \ + hb-ot-shaper-arabic-table.hh hb-unicode-emoji-table.hh \ + hb-ot-shaper-indic-table.cc hb-ot-tag-table.hh \ + hb-ucd-table.hh hb-ot-shaper-use-table.hh \ + hb-ot-shaper-vowel-constraints.cc + +.PHONY: all clean packtab + +hb-ot-shaper-arabic-joining-list.hh: gen-arabic-joining-list.py ArabicShaping.txt Scripts.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-arabic-table.hh: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt Blocks.txt + ./$^ > $@ || ($(RM) $@; false) +hb-unicode-emoji-table.hh: gen-emoji-table.py emoji-data.txt emoji-test.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-indic-table.cc: gen-indic-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt Blocks.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-tag-table.hh: gen-tag-table.py languagetags language-subtag-registry + ./$^ > $@ || ($(RM) $@; false) +hb-ucd-table.hh: gen-ucd-table.py ucd.nounihan.grouped.zip hb-common.h + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-use-table.hh: gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt ms-use/IndicSyllabicCategory-Additional.txt ms-use/IndicPositionalCategory-Additional.txt + ./$^ > $@ || ($(RM) $@; false) +hb-ot-shaper-vowel-constraints.cc: gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt + ./$^ > $@ || ($(RM) $@; false) + +packtab: + /usr/bin/env python3 -c "import packTab" 2>/dev/null || /usr/bin/env python3 -m pip install git+https://github.com/harfbuzz/packtab + +ArabicShaping.txt Blocks.txt DerivedCoreProperties.txt IndicPositionalCategory.txt IndicSyllabicCategory.txt Scripts.txt UnicodeData.txt: + curl -O https://unicode.org/Public/UCD/latest/ucd/$@ +emoji-data.txt: + curl -O https://www.unicode.org/Public/UCD/latest/ucd/emoji/emoji-data.txt +emoji-test.txt: + curl -O https://www.unicode.org/Public/emoji/latest/emoji-test.txt +languagetags: + curl -O https://learn.microsoft.com/en-us/typography/opentype/spec/languagetags +language-subtag-registry: + curl -O https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry +ucd.nounihan.grouped.zip: + curl -O https://unicode.org/Public/UCD/latest/ucdxml/ucd.nounihan.grouped.zip + +clean: + $(RM) \ + ArabicShaping.txt UnicodeData.txt Blocks.txt emoji-data.txt \ + IndicSyllabicCategory.txt IndicPositionalCategory.txt \ + languagetags language-subtag-registry ucd.nounihan.grouped.zip Scripts.txt diff --git a/gfx/harfbuzz/src/wasm/graphite/Makefile b/gfx/harfbuzz/src/wasm/graphite/Makefile new file mode 100644 index 0000000000..cdd44dcb33 --- /dev/null +++ b/gfx/harfbuzz/src/wasm/graphite/Makefile @@ -0,0 +1,28 @@ +FONTS = CharisSIL-R.wasm.ttf Scheherazade-R.wasm.ttf AwamiNastaliq-Regular.wasm.ttf +ADD_TABLE = ../../addTable.py + +all: $(FONTS) + +%.wasm: %.cc ../../hb-wasm-api.h + emcc \ + -I ../.. \ + -I ~/graphite/include/ \ + -fvisibility=hidden \ + -Wl,--allow-undefined \ + -Wl,--no-entry \ + -Wl,--strip-all \ + -sERROR_ON_UNDEFINED_SYMBOLS=0 \ + -Wl,--export=malloc -Wl,--export=free \ + ~/graphite/src/libgraphite2.a \ + ~/wasm/wasi-sdk-19.0/share/wasi-sysroot/lib/wasm32-wasi/libc.a \ + $< \ + -o $@ + + +%.wasm.ttf: %.ttf shape.wasm $(ADD_TABLE) + python $(ADD_TABLE) $< $@ shape.wasm + +clean: + $(RM) shape.wasm $(FONTS) + +.PRECIOUS: shape.wasm diff --git a/gfx/harfbuzz/src/wasm/graphite/shape.cc b/gfx/harfbuzz/src/wasm/graphite/shape.cc new file mode 100644 index 0000000000..f445049a48 --- /dev/null +++ b/gfx/harfbuzz/src/wasm/graphite/shape.cc @@ -0,0 +1,250 @@ +#define HB_WASM_INTERFACE(ret_t, name) __attribute__((export_name(#name))) ret_t name + +#include <hb-wasm-api.h> + +#include <graphite2/Segment.h> + +#include <stdlib.h> +#include <string.h> + +void debugprint1 (char *s, int32_t); +void debugprint2 (char *s, int32_t, int32_t); + +static const void *copy_table (const void *data, unsigned int tag, size_t *len) +{ + face_t *face = (face_t *) data; + blob_t blob = BLOB_INIT; + if (!face_copy_table (face, tag, &blob)) + abort (); + + *len = blob.length; + return blob.data; +} + +static void free_table (const void *data, const void *table_data) +{ + blob_t blob; + blob.length = 0; // Doesn't matter + blob.data = (char *) table_data; + blob_free (&blob); +} + +void * +shape_plan_create (face_t *face) +{ + const gr_face_ops ops = {sizeof (gr_face_ops), ©_table, &free_table}; + gr_face *grface = gr_make_face_with_ops (face, &ops, gr_face_preloadAll); + return grface; +} + +void +shape_plan_destroy (void *data) +{ + gr_face_destroy ((gr_face *) data); +} + +bool_t +shape (void *shape_plan, + font_t *font, + buffer_t *buffer, + const feature_t *features, + uint32_t num_features) +{ + face_t *face = font_get_face (font); + gr_face *grface = (gr_face *) shape_plan; + + direction_t direction = buffer_get_direction (buffer); + direction_t horiz_dir = script_get_horizontal_direction (buffer_get_script (buffer)); + /* 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 ((DIRECTION_IS_HORIZONTAL (direction) && + direction != horiz_dir && horiz_dir != DIRECTION_INVALID) || + (DIRECTION_IS_VERTICAL (direction) && + direction != DIRECTION_TTB)) + { + buffer_reverse_clusters (buffer); + direction = DIRECTION_REVERSE (direction); + } + + buffer_contents_t contents = BUFFER_CONTENTS_INIT; + if (!buffer_copy_contents (buffer, &contents)) + return false; + + gr_segment *seg = nullptr; + const gr_slot *is; + unsigned int ci = 0, ic = 0; + unsigned int curradvx = 0, curradvy = 0; + unsigned length = contents.length; + + uint32_t *chars = (uint32_t *) malloc (length * sizeof (uint32_t)); + if (!chars) + return false; + for (unsigned int i = 0; i < contents.length; ++i) + chars[i] = contents.info[i].codepoint; + + seg = gr_make_seg (nullptr, grface, + 0, // https://github.com/harfbuzz/harfbuzz/issues/3439#issuecomment-1442650148 + nullptr, + gr_utf32, chars, contents.length, + 2 | (direction == DIRECTION_RTL ? 1 : 0)); + + free (chars); + + if (!seg) + return false; + + unsigned int glyph_count = gr_seg_n_slots (seg); + + struct cluster_t { + unsigned int base_char; + unsigned int num_chars; + unsigned int base_glyph; + unsigned int num_glyphs; + unsigned int cluster; + int advance; + }; + + length = glyph_count; + if (!buffer_contents_realloc (&contents, length)) + return false; + cluster_t *clusters = (cluster_t *) malloc (length * sizeof (cluster_t)); + uint32_t *gids = (uint32_t *) malloc (length * sizeof (uint32_t)); + if (!clusters || !gids) + { + free (clusters); + free (gids); + return false; + } + + memset (clusters, 0, sizeof (clusters[0]) * length); + codepoint_t *pg = gids; + clusters[0].cluster = contents.info[0].cluster; + unsigned int upem = face_get_upem (face); + int32_t font_x_scale, font_y_scale; + font_get_scale (font, &font_x_scale, &font_y_scale); + float xscale = (float) font_x_scale / upem; + float yscale = (float) font_y_scale / upem; + yscale *= yscale / xscale; + unsigned int curradv = 0; + if (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) + { + cluster_t *c = clusters + ci + 1; + c->base_char = clusters[ci].base_char + clusters[ci].num_chars; + c->cluster = contents.info[c->base_char].cluster; + c->num_chars = before - c->base_char; + c->base_glyph = ic; + c->num_glyphs = 0; + if (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 (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) + { + glyph_info_t *info = &contents.info[clusters[i].base_glyph + j]; + info->codepoint = gids[clusters[i].base_glyph + j]; + info->cluster = clusters[i].cluster; + info->var1 = (unsigned) clusters[i].advance; // all glyphs in the cluster get the same advance + } + } + contents.length = glyph_count; + + /* Positioning. */ + unsigned int currclus = 0xFFFFFFFF; + const glyph_info_t *info = contents.info; + glyph_position_t *pPos = contents.pos; + if (!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 = (int) info->var1; + 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; + } + buffer_set_contents (buffer, &contents); + } + 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 = (int) info->var1; + 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 - (int) info->var1 - curradvx + pPos->x_advance; + pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; + } + buffer_set_contents (buffer, &contents); + buffer_reverse_clusters (buffer); + } + + gr_seg_destroy (seg); + free (clusters); + free (gids); + + bool ret = glyph_count; + + return ret; +} diff --git a/gfx/harfbuzz/src/wasm/rust/harfbuzz-wasm/Cargo.toml b/gfx/harfbuzz/src/wasm/rust/harfbuzz-wasm/Cargo.toml new file mode 100644 index 0000000000..fa17de36be --- /dev/null +++ b/gfx/harfbuzz/src/wasm/rust/harfbuzz-wasm/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "harfbuzz-wasm" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +kurbo = { version = "0.9.0", optional = true } diff --git a/gfx/harfbuzz/src/wasm/rust/harfbuzz-wasm/src/lib.rs b/gfx/harfbuzz/src/wasm/rust/harfbuzz-wasm/src/lib.rs new file mode 100644 index 0000000000..ea20ce507f --- /dev/null +++ b/gfx/harfbuzz/src/wasm/rust/harfbuzz-wasm/src/lib.rs @@ -0,0 +1,464 @@ +#![warn(missing_docs)] +#![allow(dead_code)] +//! Interface to Harfbuzz's WASM exports +//! +//! This crate is designed to make it easier to write a +//! WASM shaper for your font using Rust. It binds the +//! functions exported by Harfbuzz into the WASM runtime, +//! and wraps them in an ergonomic interface using Rust +//! structures. For example, here is a basic shaping engine: +//! +//! +//! ```rust +//! #[wasm_bindgen] +//! pub fn shape(font_ref: u32, buf_ref: u32) -> i32 { +//! let font = Font::from_ref(font_ref); +//! let mut buffer = GlyphBuffer::from_ref(buf_ref); +//! for mut item in buffer.glyphs.iter_mut() { +//! // Map character to glyph +//! item.codepoint = font.get_glyph(codepoint, 0); +//! // Set advance width +//! item.h_advance = font.get_glyph_h_advance(item.codepoint); +//! } +//! 1 +//! } +//! ``` +use std::ffi::{c_int, CStr, CString}; + +#[cfg(feature = "kurbo")] +use kurbo::BezPath; + +// We don't use #[wasm_bindgen] here because that makes +// assumptions about Javascript calling conventions. We +// really do just want to import some C symbols and run +// them in unsafe-land! +extern "C" { + fn face_get_upem(face: u32) -> u32; + fn font_get_face(font: u32) -> u32; + fn font_get_glyph(font: u32, unicode: u32, uvs: u32) -> u32; + fn font_get_scale(font: u32, x_scale: *mut i32, y_scale: *mut i32); + fn font_get_glyph_extents(font: u32, glyph: u32, extents: *mut CGlyphExtents) -> bool; + fn font_glyph_to_string(font: u32, glyph: u32, str: *const u8, len: u32); + fn font_get_glyph_h_advance(font: u32, glyph: u32) -> i32; + fn font_get_glyph_v_advance(font: u32, glyph: u32) -> i32; + fn font_copy_glyph_outline(font: u32, glyph: u32, outline: *mut CGlyphOutline) -> bool; + fn face_copy_table(font: u32, tag: u32, blob: *mut Blob) -> bool; + fn buffer_copy_contents(buffer: u32, cbuffer: *mut CBufferContents) -> bool; + fn buffer_set_contents(buffer: u32, cbuffer: &CBufferContents) -> bool; + fn debugprint(s: *const u8); + fn shape_with( + font: u32, + buffer: u32, + features: u32, + num_features: u32, + shaper: *const u8, + ) -> i32; +} + +/// An opaque reference to a font at a given size and +/// variation. It is equivalent to the `hb_font_t` pointer +/// in Harfbuzz. +#[derive(Debug)] +pub struct Font(u32); + +impl Font { + /// Initialize a `Font` struct from the reference provided + /// by Harfbuzz to the `shape` function. + pub fn from_ref(ptr: u32) -> Self { + Self(ptr) + } + /// Call the given Harfbuzz shaper on a buffer reference. + /// + /// For example, `font.shape_with(buffer_ref, "ot")` will + /// run standard OpenType shaping, allowing you to modify + /// the buffer contents after glyph mapping, substitution + /// and positioning has taken place. + pub fn shape_with(&self, buffer_ref: u32, shaper: &str) { + let c_shaper = CString::new(shaper).unwrap(); + unsafe { + shape_with(self.0, buffer_ref, 0, 0, c_shaper.as_ptr() as *const u8); + } + } + + /// Return the font face object that this font belongs to. + pub fn get_face(&self) -> Face { + Face(unsafe { font_get_face(self.0) }) + } + + /// Map a Unicode codepoint to a glyph ID. + /// + /// The `uvs` parameter specifies a Unicode Variation + /// Selector codepoint which is used in conjunction with + /// [format 14 cmap tables](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences) + /// to provide alternate glyph mappings for characters with + /// Unicode Variation Sequences. Generally you will pass + /// `0`. + pub fn get_glyph(&self, unicode: u32, uvs: u32) -> u32 { + unsafe { font_get_glyph(self.0, unicode, uvs) } + } + + /// Get the extents for a given glyph ID, in its design position. + pub fn get_glyph_extents(&self, glyph: u32) -> CGlyphExtents { + let mut extents = CGlyphExtents::default(); + unsafe { + font_get_glyph_extents(self.0, glyph, &mut extents); + } + extents + } + + /// Get the default advance width for a given glyph ID. + pub fn get_glyph_h_advance(&self, glyph: u32) -> i32 { + unsafe { font_get_glyph_h_advance(self.0, glyph) } + } + + /// Get the default vertical advance for a given glyph ID. + fn get_glyph_v_advance(&self, glyph: u32) -> i32 { + unsafe { font_get_glyph_v_advance(self.0, glyph) } + } + + /// Get the name of a glyph. + /// + /// If no names are provided by the font, names of the form + /// `gidXXX` are constructed. + pub fn get_glyph_name(&self, glyph: u32) -> String { + let mut s = [1u8; 32]; + unsafe { + font_glyph_to_string(self.0, glyph, s.as_mut_ptr(), 32); + } + unsafe { CStr::from_ptr(s.as_ptr() as *const _) } + .to_str() + .unwrap() + .to_string() + } + + /// Get the X and Y scale factor applied to this font. + /// + /// This should be divided by the units per em value to + /// provide a scale factor mapping from design units to + /// user units. (See [`Face::get_upem`].) + pub fn get_scale(&self) -> (i32, i32) { + let mut x_scale: i32 = 0; + let mut y_scale: i32 = 0; + unsafe { + font_get_scale( + self.0, + &mut x_scale as *mut c_int, + &mut y_scale as *mut c_int, + ) + }; + (x_scale, y_scale) + } + + #[cfg(feature = "kurbo")] + /// Get the outline of a glyph as a vector of bezier paths + pub fn get_outline(&self, glyph: u32) -> Vec<BezPath> { + let mut outline = CGlyphOutline { + n_points: 0, + points: std::ptr::null_mut(), + n_contours: 0, + contours: std::ptr::null_mut(), + }; + let end_pts_of_contours: &[usize] = unsafe { + font_copy_glyph_outline(self.0, glyph, &mut outline); + std::slice::from_raw_parts(outline.contours, outline.n_contours as usize) + }; + let points: &[CGlyphOutlinePoint] = + unsafe { std::slice::from_raw_parts(outline.points, outline.n_points as usize) }; + let mut results: Vec<BezPath> = vec![]; + let mut start_pt: usize = 0; + for end_pt in end_pts_of_contours { + let this_contour = &points[start_pt..*end_pt]; + start_pt = *end_pt; + let mut path = BezPath::new(); + let mut ix = 0; + while ix < this_contour.len() { + let point = &this_contour[ix]; + match point.pointtype { + PointType::MoveTo => path.move_to((point.x as f64, point.y as f64)), + PointType::LineTo => path.line_to((point.x as f64, point.y as f64)), + PointType::QuadraticTo => { + ix += 1; + let end_pt = &this_contour[ix]; + path.quad_to( + (point.x as f64, point.y as f64), + (end_pt.x as f64, end_pt.y as f64), + ); + } + PointType::CubicTo => { + ix += 1; + let mid_pt = &this_contour[ix]; + ix += 1; + let end_pt = &this_contour[ix]; + path.curve_to( + (point.x as f64, point.y as f64), + (mid_pt.x as f64, mid_pt.y as f64), + (end_pt.x as f64, end_pt.y as f64), + ); + } + } + ix += 1; + } + path.close_path(); + results.push(path); + } + results + } +} + +/// An opaque reference to a font face, equivalent to the `hb_face_t` pointer +/// in Harfbuzz. +/// +/// This is generally returned from [`Font::get_face`]. +#[derive(Debug)] +pub struct Face(u32); + +impl Face { + /// Get a blob containing the contents of the given binary font table. + pub fn reference_table(&self, tag: &str) -> Blob { + let mut tag_u: u32 = 0; + let mut chars = tag.chars(); + tag_u |= (chars.next().unwrap() as u32) << 24; + tag_u |= (chars.next().unwrap() as u32) << 16; + tag_u |= (chars.next().unwrap() as u32) << 8; + tag_u |= chars.next().unwrap() as u32; + let mut blob = Blob { + data: std::ptr::null_mut(), + length: 0, + }; + unsafe { + face_copy_table(self.0, tag_u, &mut blob); + } + blob + } + + /// Get the face's design units per em. + pub fn get_upem(&self) -> u32 { + unsafe { face_get_upem(self.0) } + } +} + +/// Trait implemented by custom structs representing buffer items +pub trait BufferItem { + /// Construct an item in your preferred representation out of the info and position data provided by Harfbuzz. + fn from_c(info: CGlyphInfo, position: CGlyphPosition) -> Self; + /// Return info and position data to Harfbuzz. + fn to_c(self) -> (CGlyphInfo, CGlyphPosition); +} + +/// Generic representation of a Harfbuzz buffer item. +/// +/// By making this generic, we allow you to implement your own +/// representations of buffer items; for example, in your shaper, +/// you may want certain fields to keep track of the glyph's name, +/// extents, or shape, so you would want a custom struct to represent +/// buffer items. If you don't care about any of them, use the +/// supplied `GlyphBuffer` struct. +#[derive(Debug)] +pub struct Buffer<T: BufferItem> { + _ptr: u32, + /// Glyphs in the buffer + pub glyphs: Vec<T>, +} + +impl<T: BufferItem> Buffer<T> { + /// Construct a buffer from the pointer Harfbuzz provides to the WASM. + /// + /// The `Buffer` struct implements Drop, meaning that when the shaping + /// function is finished, the buffer contents are sent back to Harfbuzz. + pub fn from_ref(ptr: u32) -> Self { + let mut c_contents = CBufferContents { + info: std::ptr::null_mut(), + position: std::ptr::null_mut(), + length: 0, + }; + + unsafe { + buffer_copy_contents(ptr, &mut c_contents) || panic!("Couldn't copy buffer contents") + }; + let positions: Vec<CGlyphPosition> = unsafe { + std::slice::from_raw_parts(c_contents.position, c_contents.length as usize).to_vec() + }; + let infos: Vec<CGlyphInfo> = unsafe { + std::slice::from_raw_parts(c_contents.info, c_contents.length as usize).to_vec() + }; + Buffer { + glyphs: infos + .into_iter() + .zip(positions.into_iter()) + .map(|(i, p)| T::from_c(i, p)) + .collect(), + _ptr: ptr, + } + } +} + +impl<T: BufferItem> Drop for Buffer<T> { + fn drop(&mut self) { + let mut positions: Vec<CGlyphPosition>; + let mut infos: Vec<CGlyphInfo>; + let glyphs = std::mem::take(&mut self.glyphs); + (infos, positions) = glyphs.into_iter().map(|g| g.to_c()).unzip(); + let c_contents = CBufferContents { + length: positions.len() as u32, + info: infos[..].as_mut_ptr(), + position: positions[..].as_mut_ptr(), + }; + unsafe { + if !buffer_set_contents(self._ptr, &c_contents) { + panic!("Couldn't set buffer contents"); + } + } + } +} + +/// Some data provided by Harfbuzz. +#[derive(Debug)] +#[repr(C)] +pub struct Blob { + /// Length of the blob in bytes + pub length: u32, + /// A raw pointer to the contents + pub data: *mut u8, +} + +/// Glyph information in a buffer item provided by Harfbuzz +/// +/// You'll only need to interact with this if you're writing +/// your own buffer item structure. +#[repr(C)] +#[derive(Debug, Clone)] +pub struct CGlyphInfo { + pub codepoint: u32, + pub mask: u32, + pub cluster: u32, + pub var1: u32, + pub var2: u32, +} + +/// Glyph positioning information in a buffer item provided by Harfbuzz +/// +/// You'll only need to interact with this if you're writing +/// your own buffer item structure. +#[derive(Debug, Clone)] +#[repr(C)] +pub struct CGlyphPosition { + pub x_advance: i32, + pub y_advance: i32, + pub x_offset: i32, + pub y_offset: i32, + pub var: u32, +} + +/// Glyph extents +#[derive(Debug, Clone, Default)] +#[repr(C)] +pub struct CGlyphExtents { + /// The scaled left side bearing of the glyph + pub x_bearing: i32, + /// The scaled coordinate of the top of the glyph + pub y_bearing: i32, + /// The width of the glyph + pub width: i32, + /// The height of the glyph + pub height: i32, +} + +#[derive(Debug)] +#[repr(C)] +struct CBufferContents { + length: u32, + info: *mut CGlyphInfo, + position: *mut CGlyphPosition, +} + +/// Ergonomic representation of a Harfbuzz buffer item +/// +/// Harfbuzz buffers are normally split into two arrays, +/// one representing glyph information and the other +/// representing glyph positioning. In Rust, this would +/// require lots of zipping and unzipping, so we zip them +/// together into a single structure for you. +#[derive(Debug, Clone, Copy)] +pub struct Glyph { + /// The Unicode codepoint or glyph ID of the item + pub codepoint: u32, + /// The index of the cluster in the input text where this came from + pub cluster: u32, + /// The horizontal advance of the glyph + pub x_advance: i32, + /// The vertical advance of the glyph + pub y_advance: i32, + /// The horizontal offset of the glyph + pub x_offset: i32, + /// The vertical offset of the glyph + pub y_offset: i32, + /// You can use this for whatever you like + pub flags: u32, +} +impl BufferItem for Glyph { + fn from_c(info: CGlyphInfo, pos: CGlyphPosition) -> Self { + Self { + codepoint: info.codepoint, + cluster: info.cluster, + x_advance: pos.x_advance, + y_advance: pos.y_advance, + x_offset: pos.x_offset, + y_offset: pos.y_offset, + flags: 0, + } + } + fn to_c(self) -> (CGlyphInfo, CGlyphPosition) { + let info = CGlyphInfo { + codepoint: self.codepoint, + cluster: self.cluster, + mask: 0, + var1: 0, + var2: 0, + }; + let pos = CGlyphPosition { + x_advance: self.x_advance, + y_advance: self.y_advance, + x_offset: self.x_offset, + y_offset: self.y_offset, + var: 0, + }; + (info, pos) + } +} + +#[repr(C)] +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Debug)] +enum PointType { + MoveTo, + LineTo, + QuadraticTo, + CubicTo, +} + +#[repr(C)] +#[derive(Clone, Debug)] +struct CGlyphOutlinePoint { + x: f32, + y: f32, + pointtype: PointType, +} + +#[repr(C)] +struct CGlyphOutline { + n_points: usize, + points: *mut CGlyphOutlinePoint, + n_contours: usize, + contours: *mut usize, +} + +/// Our default buffer item struct. See also [`Glyph`]. +pub type GlyphBuffer = Buffer<Glyph>; + +/// Write a string to the Harfbuzz debug log. +pub fn debug(s: &str) { + let c_s = CString::new(s).unwrap(); + unsafe { + debugprint(c_s.as_ptr() as *const u8); + }; +} diff --git a/gfx/harfbuzz/src/wasm/sample/c/Makefile b/gfx/harfbuzz/src/wasm/sample/c/Makefile new file mode 100644 index 0000000000..4ee073b645 --- /dev/null +++ b/gfx/harfbuzz/src/wasm/sample/c/Makefile @@ -0,0 +1,25 @@ +ADD_TABLE = ../../../addTable.py + +all: test-fallback.wasm.ttf test-ot.wasm.ttf + +%.wasm: %.cc ../../../hb-wasm-api.h + clang \ + --target=wasm32-unknown-wasi \ + -Wl,--no-entry \ + -fvisibility=hidden \ + -Wl,--allow-undefined \ + -nostdlib \ + -I ../../.. \ + $< \ + -o $@ + +test-fallback.wasm.ttf: test.ttf shape-fallback.wasm $(ADD_TABLE) + python $(ADD_TABLE) $< $@ shape-fallback.wasm + +test-ot.wasm.ttf: test.ttf shape-ot.wasm $(ADD_TABLE) + python $(ADD_TABLE) $< $@ shape-ot.wasm + +clean: + $(RM) test-fallback.wasm.ttf test-ot.wasm.ttf shape-fallback.wasm shape-ot.wasm + +.PRECIOUS: *.wasm diff --git a/gfx/harfbuzz/src/wasm/sample/c/shape-fallback.cc b/gfx/harfbuzz/src/wasm/sample/c/shape-fallback.cc new file mode 100644 index 0000000000..7787bbae7a --- /dev/null +++ b/gfx/harfbuzz/src/wasm/sample/c/shape-fallback.cc @@ -0,0 +1,60 @@ +#define HB_WASM_INTERFACE(ret_t, name) __attribute__((export_name(#name))) ret_t name + +#include <hb-wasm-api.h> + +extern "C" { +void debugprint (const char *s); +void debugprint1 (const char *s, int32_t); +void debugprint2 (const char *s, int32_t, int32_t); +} + +bool_t +shape (void *shape_plan, + font_t *font, + buffer_t *buffer, + const feature_t *features, + uint32_t num_features) +{ + face_t *face = font_get_face (font); + + blob_t blob = BLOB_INIT; + face_copy_table (face, TAG ('c','m','a','p'), &blob); + + debugprint1 ("cmap length", blob.length); + + blob_free (&blob); + + buffer_contents_t contents = BUFFER_CONTENTS_INIT; + if (!buffer_copy_contents (buffer, &contents)) + return false; + + debugprint1 ("buffer length", contents.length); + + glyph_outline_t outline = GLYPH_OUTLINE_INIT; + + for (unsigned i = 0; i < contents.length; i++) + { + char name[64]; + + debugprint1 ("glyph at", i); + + font_glyph_to_string (font, contents.info[i].codepoint, name, sizeof (name)); + + debugprint (name); + + contents.info[i].codepoint = font_get_glyph (font, contents.info[i].codepoint, 0); + contents.pos[i].x_advance = font_get_glyph_h_advance (font, contents.info[i].codepoint); + + font_copy_glyph_outline (font, contents.info[i].codepoint, &outline); + debugprint1 ("num outline points", outline.n_points); + debugprint1 ("num outline contours", outline.n_contours); + } + + glyph_outline_free (&outline); + + bool_t ret = buffer_set_contents (buffer, &contents); + + buffer_contents_free (&contents); + + return ret; +} diff --git a/gfx/harfbuzz/src/wasm/sample/c/shape-ot.cc b/gfx/harfbuzz/src/wasm/sample/c/shape-ot.cc new file mode 100644 index 0000000000..9081cfebcc --- /dev/null +++ b/gfx/harfbuzz/src/wasm/sample/c/shape-ot.cc @@ -0,0 +1,18 @@ +#define HB_WASM_INTERFACE(ret_t, name) __attribute__((export_name(#name))) ret_t name + +#include <hb-wasm-api.h> + +extern "C" { +void debugprint1 (const char *s, int32_t); +void debugprint2 (const char *s, int32_t, int32_t); +} + +bool_t +shape (void *shape_plan, + font_t *font, + buffer_t *buffer, + const feature_t *features, + uint32_t num_features) +{ + return shape_with (font, buffer, features, num_features, "ot"); +} diff --git a/gfx/harfbuzz/src/wasm/sample/c/test.ttf b/gfx/harfbuzz/src/wasm/sample/c/test.ttf Binary files differnew file mode 100644 index 0000000000..2ba04f611b --- /dev/null +++ b/gfx/harfbuzz/src/wasm/sample/c/test.ttf diff --git a/gfx/harfbuzz/src/wasm/sample/rust/hello-wasm/Cargo.toml b/gfx/harfbuzz/src/wasm/sample/rust/hello-wasm/Cargo.toml new file mode 100644 index 0000000000..53357ea908 --- /dev/null +++ b/gfx/harfbuzz/src/wasm/sample/rust/hello-wasm/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "hello-wasm" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +#externref = "0.1.0" +wasm-bindgen = "0.2.0" +tiny-rng = "0.2.0" +harfbuzz-wasm = { path="../../../rust/harfbuzz-wasm"} diff --git a/gfx/harfbuzz/src/wasm/sample/rust/hello-wasm/src/lib.rs b/gfx/harfbuzz/src/wasm/sample/rust/hello-wasm/src/lib.rs new file mode 100644 index 0000000000..ab8f153684 --- /dev/null +++ b/gfx/harfbuzz/src/wasm/sample/rust/hello-wasm/src/lib.rs @@ -0,0 +1,24 @@ +use harfbuzz_wasm::{Font, GlyphBuffer}; +use tiny_rng::{Rand, Rng}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn shape( + _shape_plan: u32, + font_ref: u32, + buf_ref: u32, + _features: u32, + _num_features: u32, +) -> i32 { + let mut rng = Rng::from_seed(123456); + let font = Font::from_ref(font_ref); + font.shape_with(buf_ref, "ot"); + let mut buffer = GlyphBuffer::from_ref(buf_ref); + for mut item in buffer.glyphs.iter_mut() { + // Randomize it! + item.x_offset = ((rng.rand_u32() as i32) >> 24) - 120; + item.y_offset = ((rng.rand_u32() as i32) >> 24) - 120; + } + // Buffer is written back to HB on drop + 1 +} |