summaryrefslogtreecommitdiff
path: root/thirdparty/harfbuzz/src
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/harfbuzz/src')
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh98
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh158
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-common.hh840
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh222
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh417
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh999
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh1157
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh173
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh230
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout.cc382
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout.h486
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-layout.hh75
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-ltag-table.hh92
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-map.cc102
-rw-r--r--thirdparty/harfbuzz/src/hb-aat-map.hh96
-rw-r--r--thirdparty/harfbuzz/src/hb-aat.h38
-rw-r--r--thirdparty/harfbuzz/src/hb-algs.hh1127
-rw-r--r--thirdparty/harfbuzz/src/hb-array.hh408
-rw-r--r--thirdparty/harfbuzz/src/hb-atomic.hh295
-rw-r--r--thirdparty/harfbuzz/src/hb-bimap.hh166
-rw-r--r--thirdparty/harfbuzz/src/hb-blob.cc717
-rw-r--r--thirdparty/harfbuzz/src/hb-blob.h131
-rw-r--r--thirdparty/harfbuzz/src/hb-blob.hh97
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh643
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh571
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer-serialize.cc474
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer.cc2004
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer.h586
-rw-r--r--thirdparty/harfbuzz/src/hb-buffer.hh451
-rw-r--r--thirdparty/harfbuzz/src/hb-cache.hh80
-rw-r--r--thirdparty/harfbuzz/src/hb-cff-interp-common.hh688
-rw-r--r--thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh911
-rw-r--r--thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh201
-rw-r--r--thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh161
-rw-r--r--thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh272
-rw-r--r--thirdparty/harfbuzz/src/hb-common.cc1098
-rw-r--r--thirdparty/harfbuzz/src/hb-common.h513
-rw-r--r--thirdparty/harfbuzz/src/hb-config.hh163
-rw-r--r--thirdparty/harfbuzz/src/hb-coretext.cc1194
-rw-r--r--thirdparty/harfbuzz/src/hb-coretext.h96
-rw-r--r--thirdparty/harfbuzz/src/hb-debug.hh459
-rw-r--r--thirdparty/harfbuzz/src/hb-deprecated.h195
-rw-r--r--thirdparty/harfbuzz/src/hb-directwrite.cc979
-rw-r--r--thirdparty/harfbuzz/src/hb-directwrite.h40
-rw-r--r--thirdparty/harfbuzz/src/hb-dispatch.hh61
-rw-r--r--thirdparty/harfbuzz/src/hb-draw.cc261
-rw-r--r--thirdparty/harfbuzz/src/hb-draw.h98
-rw-r--r--thirdparty/harfbuzz/src/hb-draw.hh139
-rw-r--r--thirdparty/harfbuzz/src/hb-face.cc733
-rw-r--r--thirdparty/harfbuzz/src/hb-face.h158
-rw-r--r--thirdparty/harfbuzz/src/hb-face.hh109
-rw-r--r--thirdparty/harfbuzz/src/hb-fallback-shape.cc125
-rw-r--r--thirdparty/harfbuzz/src/hb-font.cc2186
-rw-r--r--thirdparty/harfbuzz/src/hb-font.h735
-rw-r--r--thirdparty/harfbuzz/src/hb-font.hh632
-rw-r--r--thirdparty/harfbuzz/src/hb-ft.cc1042
-rw-r--r--thirdparty/harfbuzz/src/hb-ft.h138
-rw-r--r--thirdparty/harfbuzz/src/hb-gdi.cc73
-rw-r--r--thirdparty/harfbuzz/src/hb-gdi.h39
-rw-r--r--thirdparty/harfbuzz/src/hb-glib.cc307
-rw-r--r--thirdparty/harfbuzz/src/hb-glib.h56
-rw-r--r--thirdparty/harfbuzz/src/hb-gobject-structs.cc110
-rw-r--r--thirdparty/harfbuzz/src/hb-gobject-structs.h142
-rw-r--r--thirdparty/harfbuzz/src/hb-gobject.h40
-rw-r--r--thirdparty/harfbuzz/src/hb-graphite2.cc442
-rw-r--r--thirdparty/harfbuzz/src/hb-graphite2.h60
-rw-r--r--thirdparty/harfbuzz/src/hb-icu.cc296
-rw-r--r--thirdparty/harfbuzz/src/hb-icu.h52
-rw-r--r--thirdparty/harfbuzz/src/hb-iter.hh939
-rw-r--r--thirdparty/harfbuzz/src/hb-kern.hh138
-rw-r--r--thirdparty/harfbuzz/src/hb-machinery.hh307
-rw-r--r--thirdparty/harfbuzz/src/hb-map.cc268
-rw-r--r--thirdparty/harfbuzz/src/hb-map.h104
-rw-r--r--thirdparty/harfbuzz/src/hb-map.hh326
-rw-r--r--thirdparty/harfbuzz/src/hb-meta.hh410
-rw-r--r--thirdparty/harfbuzz/src/hb-mutex.hh133
-rw-r--r--thirdparty/harfbuzz/src/hb-null.hh184
-rw-r--r--thirdparty/harfbuzz/src/hb-number-parser.hh237
-rw-r--r--thirdparty/harfbuzz/src/hb-number.cc80
-rw-r--r--thirdparty/harfbuzz/src/hb-number.hh41
-rw-r--r--thirdparty/harfbuzz/src/hb-object.hh342
-rw-r--r--thirdparty/harfbuzz/src/hb-open-file.hh521
-rw-r--r--thirdparty/harfbuzz/src/hb-open-type.hh1078
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff-common.hh622
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff1-std-str.hh425
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff1-table.cc620
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff1-table.hh1403
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff2-table.cc215
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cff2-table.hh531
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-cmap-table.hh1711
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh985
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh278
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh190
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh414
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh124
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color.cc321
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-color.h139
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-deprecated.h111
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-face-table-list.hh138
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-face.cc58
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-face.hh74
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-font.cc336
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-font.h45
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-gasp-table.hh84
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-glyf-table.hh1261
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh177
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-head-table.hh179
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-hhea-table.hh104
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh340
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-kern-table.hh359
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh509
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-common.hh3178
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh725
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh2740
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh1627
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh3422
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout-jstf-table.hh235
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.cc1993
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.h462
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-layout.hh627
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-map.cc342
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-map.hh284
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-math-table.hh728
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-math.cc293
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-math.h230
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-maxp-table.hh142
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-meta-table.hh127
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-meta.cc77
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-meta.h71
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-metrics.cc231
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-metrics.h122
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-metrics.hh35
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-name-language-static.hh456
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-name-language.hh40
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-name-table.hh376
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-name.cc228
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-name.h129
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-os2-table.hh316
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-os2-unicode-ranges.hh231
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-post-macroman.hh294
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-post-table.hh298
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh348
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-joining-list.hh46
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh433
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh323
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc716
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.hh50
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-default.cc73
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc439
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-hebrew.cc185
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh574
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-table.cc501
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.cc1615
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.hh436
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.hh372
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.rl113
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.cc457
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.hh113
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh430
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.cc387
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.hh171
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc394
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-use-machine.hh562
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-use-table.cc873
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-use.cc569
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-use.hh105
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc464
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.hh39
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-complex.hh402
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc596
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-fallback.hh54
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc478
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape-normalize.hh70
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape.cc1223
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape.h53
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-shape.hh170
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-stat-table.hh404
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-tag-table.hh2176
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-tag.cc567
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh169
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh327
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh701
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh488
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh119
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var.cc220
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-var.h146
-rw-r--r--thirdparty/harfbuzz/src/hb-ot-vorg-table.hh136
-rw-r--r--thirdparty/harfbuzz/src/hb-ot.h49
-rw-r--r--thirdparty/harfbuzz/src/hb-pool.hh100
-rw-r--r--thirdparty/harfbuzz/src/hb-sanitize.hh412
-rw-r--r--thirdparty/harfbuzz/src/hb-serialize.hh553
-rw-r--r--thirdparty/harfbuzz/src/hb-set-digest.hh174
-rw-r--r--thirdparty/harfbuzz/src/hb-set.cc541
-rw-r--r--thirdparty/harfbuzz/src/hb-set.h167
-rw-r--r--thirdparty/harfbuzz/src/hb-set.hh884
-rw-r--r--thirdparty/harfbuzz/src/hb-shape-plan.cc513
-rw-r--r--thirdparty/harfbuzz/src/hb-shape-plan.h108
-rw-r--r--thirdparty/harfbuzz/src/hb-shape-plan.hh76
-rw-r--r--thirdparty/harfbuzz/src/hb-shape.cc172
-rw-r--r--thirdparty/harfbuzz/src/hb-shape.h62
-rw-r--r--thirdparty/harfbuzz/src/hb-shaper-impl.hh38
-rw-r--r--thirdparty/harfbuzz/src/hb-shaper-list.hh60
-rw-r--r--thirdparty/harfbuzz/src/hb-shaper.cc108
-rw-r--r--thirdparty/harfbuzz/src/hb-shaper.hh134
-rw-r--r--thirdparty/harfbuzz/src/hb-static.cc112
-rw-r--r--thirdparty/harfbuzz/src/hb-string-array.hh85
-rw-r--r--thirdparty/harfbuzz/src/hb-style.cc135
-rw-r--r--thirdparty/harfbuzz/src/hb-style.h43
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff-common.cc227
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff-common.hh989
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff1.cc940
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff1.hh37
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff2.cc488
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-cff2.hh37
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-input.cc229
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-input.hh61
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-plan.cc395
-rw-r--r--thirdparty/harfbuzz/src/hb-subset-plan.hh194
-rw-r--r--thirdparty/harfbuzz/src/hb-subset.cc269
-rw-r--r--thirdparty/harfbuzz/src/hb-subset.h97
-rw-r--r--thirdparty/harfbuzz/src/hb-subset.hh73
-rw-r--r--thirdparty/harfbuzz/src/hb-ucd-table.hh6780
-rw-r--r--thirdparty/harfbuzz/src/hb-ucd.cc248
-rw-r--r--thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh78
-rw-r--r--thirdparty/harfbuzz/src/hb-unicode.cc586
-rw-r--r--thirdparty/harfbuzz/src/hb-unicode.h404
-rw-r--r--thirdparty/harfbuzz/src/hb-unicode.hh398
-rw-r--r--thirdparty/harfbuzz/src/hb-uniscribe.cc1047
-rw-r--r--thirdparty/harfbuzz/src/hb-uniscribe.h46
-rw-r--r--thirdparty/harfbuzz/src/hb-utf.hh453
-rw-r--r--thirdparty/harfbuzz/src/hb-vector.hh313
-rw-r--r--thirdparty/harfbuzz/src/hb-version.h66
-rw-r--r--thirdparty/harfbuzz/src/hb.h50
-rw-r--r--thirdparty/harfbuzz/src/hb.hh634
234 files changed, 104191 insertions, 0 deletions
diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh
new file mode 100644
index 0000000000..f2785a6f58
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LAYOUT_ANKR_TABLE_HH
+#define HB_AAT_LAYOUT_ANKR_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+
+/*
+ * ankr -- Anchor Point
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ankr.html
+ */
+#define HB_AAT_TAG_ankr HB_TAG('a','n','k','r')
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+struct Anchor
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ public:
+ FWORD xCoordinate;
+ FWORD yCoordinate;
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+
+typedef LArrayOf<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 NNOffsetTo<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) &&
+ 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) */
+ LOffsetTo<Lookup<NNOffsetTo<GlyphAnchors>>>
+ lookupTable; /* Offset to the table's lookup table */
+ LNNOffsetTo<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/thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh
new file mode 100644
index 0000000000..cd36fc8953
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh
@@ -0,0 +1,158 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LAYOUT_BSLN_TABLE_HH
+#define HB_AAT_LAYOUT_BSLN_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+
+/*
+ * bsln -- Baseline
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
+ */
+#define HB_AAT_TAG_bsln HB_TAG('b','s','l','n')
+
+
+namespace AAT {
+
+
+struct BaselineTableFormat0Part
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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 (likely (c->check_struct (this)));
+ }
+
+ protected:
+ HBGlyphID 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:
+ HBGlyphID 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);
+
+ 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/thirdparty/harfbuzz/src/hb-aat-layout-common.hh b/thirdparty/harfbuzz/src/hb-aat-layout-common.hh
new file mode 100644
index 0000000000..75d523f5fc
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-common.hh
@@ -0,0 +1,840 @@
+/*
+ * 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-open-type.hh"
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+/*
+ * 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));
+ }
+
+ HBGlyphID last; /* Last GlyphID in this segment */
+ HBGlyphID 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) &&
+ 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) &&
+ first <= last &&
+ valuesZ.sanitize (c, base, last - first + 1, hb_forward<Ts> (ds)...));
+ }
+
+ HBGlyphID last; /* Last GlyphID in this segment */
+ HBGlyphID first; /* First GlyphID in this segment */
+ NNOffsetTo<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));
+ }
+
+ HBGlyphID 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 */
+ HBGlyphID 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) &&
+ valueSize <= 4 &&
+ valueArrayZ.sanitize (c, glyphCount * valueSize));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 8 */
+ HBUINT16 valueSize; /* Byte size of each value. */
+ HBGlyphID 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);
+ switch (u.format) {
+ case 0: return_trace (u.format0.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+ case 4: return_trace (u.format4.sanitize (c));
+ case 6: return_trace (u.format6.sanitize (c));
+ case 8: return_trace (u.format8.sanitize (c));
+ case 10: return_trace (u.format10.sanitize (c));
+ default:return_trace (true);
+ }
+ }
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.format.sanitize (c)) return_trace (false);
+ switch (u.format) {
+ case 0: return_trace (u.format0.sanitize (c, base));
+ case 2: return_trace (u.format2.sanitize (c, base));
+ case 4: return_trace (u.format4.sanitize (c, base));
+ case 6: return_trace (u.format6.sanitize (c, base));
+ case 8: return_trace (u.format8.sanitize (c, base));
+ case 10: return_trace (false); /* We don't support format10 here currently. */
+ default:return_trace (true);
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ LookupFormat0<T> format0;
+ LookupFormat2<T> format2;
+ LookupFormat4<T> format4;
+ LookupFormat6<T> format6;
+ LookupFormat8<T> format8;
+ LookupFormat10<T> format10;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format);
+};
+/* Lookup 0 has unbounded size (dependant on num_glyphs). So we need to defined
+ * special NULL objects for Lookup<> objects, but since it's template our macros
+ * don't work. So we have to hand-code them here. UGLY. */
+} /* Close namespace. */
+/* Ugly hand-coded null objects for template Lookup<> :(. */
+extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2];
+template <typename T>
+struct Null<AAT::Lookup<T>> {
+ static AAT::Lookup<T> const & get_null ()
+ { return *reinterpret_cast<const AAT::Lookup<T> *> (_hb_Null_AAT_Lookup); }
+};
+namespace AAT {
+
+enum { DELETED_GLYPH = 0xFFFF };
+
+/*
+ * (Extended) State Table
+ */
+
+template <typename T>
+struct Entry
+{
+ bool sanitize (hb_sanitize_context_t *c, unsigned int count) 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>
+{
+ bool sanitize (hb_sanitize_context_t *c, unsigned int count /*XXX Unused?*/) 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<Types, Entry<Extra>>::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) &&
+ 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) + 1);
+ 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 + 1);
+ 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:
+ HBGlyphID firstGlyph; /* First glyph index included in the trimmed array. */
+ ArrayOf<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)
+ {
+ return (offset - ((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
+{
+ StateTableDriver (const StateTable<Types, EntryData> &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)
+ {
+ if (!c->in_place)
+ buffer->clear_output ();
+
+ int state = StateTable<Types, EntryData>::STATE_START_OF_TEXT;
+ for (buffer->idx = 0; buffer->successful;)
+ {
+ unsigned int klass = buffer->idx < buffer->len ?
+ machine.get_class (buffer->info[buffer->idx].codepoint, num_glyphs) :
+ (unsigned) StateTable<Types, EntryData>::CLASS_END_OF_TEXT;
+ DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
+ const Entry<EntryData> &entry = machine.get_entry (state, klass);
+
+ /* Unsafe-to-break before this if not in state 0, as things might
+ * go differently if we start from state 0 here.
+ *
+ * Ugh. The indexing here is ugly... */
+ if (state && buffer->backtrack_len () && buffer->idx < buffer->len)
+ {
+ /* If there's no action and we're just epsilon-transitioning to state 0,
+ * safe to break. */
+ if (c->is_actionable (this, entry) ||
+ !(entry.newState == StateTable<Types, EntryData>::STATE_START_OF_TEXT &&
+ entry.flags == context_t::DontAdvance))
+ buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
+ }
+
+ /* Unsafe-to-break if end-of-text would kick in here. */
+ if (buffer->idx + 2 <= buffer->len)
+ {
+ const Entry<EntryData> &end_entry = machine.get_entry (state, StateTable<Types, EntryData>::CLASS_END_OF_TEXT);
+ if (c->is_actionable (this, end_entry))
+ buffer->unsafe_to_break (buffer->idx, buffer->idx + 2);
+ }
+
+ c->transition (this, entry);
+
+ state = machine.new_state (entry.newState);
+ DEBUG_MSG (APPLY, nullptr, "s%d", state);
+
+ if (buffer->idx == buffer->len)
+ break;
+
+ if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0)
+ buffer->next_glyph ();
+ }
+
+ if (!c->in_place)
+ {
+ for (; buffer->successful && buffer->idx < buffer->len;)
+ buffer->next_glyph ();
+ buffer->swap_buffers ();
+ }
+ }
+
+ public:
+ const StateTable<Types, EntryData> &machine;
+ hb_buffer_t *buffer;
+ unsigned int num_glyphs;
+};
+
+
+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;
+
+ /* 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; }
+};
+
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_COMMON_HH */
diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh
new file mode 100644
index 0000000000..359e859cfc
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh
@@ -0,0 +1,222 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LAYOUT_FEAT_TABLE_HH
+#define HB_AAT_LAYOUT_FEAT_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+
+/*
+ * feat -- Feature Name
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html
+ */
+#define HB_AAT_TAG_feat HB_TAG('f','e','a','t')
+
+
+namespace AAT {
+
+
+struct SettingName
+{
+ friend struct FeatureName;
+
+ int cmp (hb_aat_layout_feature_selector_t key) const
+ { return (int) key - (int) setting; }
+
+ hb_aat_layout_feature_selector_t get_selector () const
+ { return (hb_aat_layout_feature_selector_t) (unsigned) setting; }
+
+ hb_aat_layout_feature_selector_info_t get_info (hb_aat_layout_feature_selector_t default_selector) const
+ {
+ return {
+ nameIndex,
+ (hb_aat_layout_feature_selector_t) (unsigned int) setting,
+ default_selector == HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID
+ ? (hb_aat_layout_feature_selector_t) (setting + 1)
+ : default_selector,
+ 0
+ };
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this)));
+ }
+
+ protected:
+ HBUINT16 setting; /* The setting. */
+ NameID nameIndex; /* The name table index for the setting's name. */
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+DECLARE_NULL_NAMESPACE_BYTES (AAT, SettingName);
+
+struct feat;
+
+struct FeatureName
+{
+ int cmp (hb_aat_layout_feature_type_t key) const
+ { return (int) key - (int) feature; }
+
+ enum {
+ Exclusive = 0x8000, /* If set, the feature settings are mutually exclusive. */
+ NotDefault = 0x4000, /* If clear, then the setting with an index of 0 in
+ * the setting name array for this feature should
+ * be taken as the default for the feature
+ * (if one is required). If set, then bits 0-15 of this
+ * featureFlags field contain the index of the setting
+ * which is to be taken as the default. */
+ IndexMask = 0x00FF /* If bits 30 and 31 are set, then these sixteen bits
+ * indicate the index of the setting in the setting name
+ * array for this feature which should be taken
+ * as the default. */
+ };
+
+ unsigned int get_selector_infos (unsigned int start_offset,
+ unsigned int *selectors_count, /* IN/OUT. May be NULL. */
+ hb_aat_layout_feature_selector_info_t *selectors, /* OUT. May be NULL. */
+ unsigned int *pdefault_index, /* OUT. May be NULL. */
+ const void *base) const
+ {
+ hb_array_t< const SettingName> settings_table = (base+settingTableZ).as_array (nSettings);
+
+ static_assert (Index::NOT_FOUND_INDEX == HB_AAT_LAYOUT_NO_SELECTOR_INDEX, "");
+
+ hb_aat_layout_feature_selector_t default_selector = HB_AAT_LAYOUT_FEATURE_SELECTOR_INVALID;
+ unsigned int default_index = Index::NOT_FOUND_INDEX;
+ if (featureFlags & Exclusive)
+ {
+ default_index = (featureFlags & NotDefault) ? featureFlags & IndexMask : 0;
+ default_selector = settings_table[default_index].get_selector ();
+ }
+ if (pdefault_index)
+ *pdefault_index = default_index;
+
+ if (selectors_count)
+ {
+ + settings_table.sub_array (start_offset, selectors_count)
+ | hb_map ([=] (const SettingName& setting) { return setting.get_info (default_selector); })
+ | hb_sink (hb_array (selectors, *selectors_count))
+ ;
+ }
+ return settings_table.length;
+ }
+
+ hb_aat_layout_feature_type_t get_feature_type () const
+ { return (hb_aat_layout_feature_type_t) (unsigned int) feature; }
+
+ hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
+
+ bool is_exclusive () const { return featureFlags & Exclusive; }
+
+ /* A FeatureName with no settings is meaningless */
+ bool has_data () const { return nSettings; }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ (base+settingTableZ).sanitize (c, nSettings)));
+ }
+
+ protected:
+ HBUINT16 feature; /* Feature type. */
+ HBUINT16 nSettings; /* The number of records in the setting name array. */
+ LNNOffsetTo<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) &&
+ 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/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh
new file mode 100644
index 0000000000..49506e9f5a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh
@@ -0,0 +1,417 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LAYOUT_JUST_TABLE_HH
+#define HB_AAT_LAYOUT_JUST_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout.hh"
+#include "hb-open-type.hh"
+
+#include "hb-aat-layout-morx-table.hh"
+
+/*
+ * just -- Justification
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6just.html
+ */
+#define HB_AAT_TAG_just HB_TAG('j','u','s','t')
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+struct ActionSubrecordHeader
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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 (likely (c->check_struct (this)));
+ }
+
+ ActionSubrecordHeader
+ header;
+ HBFixed lowerLimit; /* If the distance factor is less than this value,
+ * then the ligature is decomposed. */
+ HBFixed 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. */
+ ArrayOf<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;
+ HBGlyphID 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 (likely (c->check_struct (this)));
+ }
+
+ protected:
+ ActionSubrecordHeader
+ header;
+ HBFixed substThreshold; /* Distance growth factor (in ems) at which
+ * this glyph is replaced and the growth factor
+ * recalculated. */
+ HBGlyphID 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. */
+ HBGlyphID 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 (likely (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. */
+ HBFixed minimumLimit; /* The lowest value for the ductility axis tha
+ * still yields an acceptable appearance. Normally
+ * this will be 1.0. */
+ HBFixed noStretchValue; /* This is the default value that corresponds to
+ * no change in appearance. Normally, this will
+ * be 1.0. */
+ HBFixed 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 (likely (c->check_struct (this)));
+ }
+
+ protected:
+ ActionSubrecordHeader
+ header;
+ HBUINT16 flags; /* Currently unused; set to 0. */
+ HBGlyphID glyph; /* Glyph that should be added if the distance factor
+ * is growing. */
+ public:
+ DEFINE_SIZE_STATIC (10);
+};
+
+struct ActionSubrecord
+{
+ unsigned int get_length () const { return u.header.actionLength; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+
+ switch (u.header.actionType)
+ {
+ case 0: return_trace (u.decompositionAction.sanitize (c));
+ case 1: return_trace (u.unconditionalAddGlyphAction.sanitize (c));
+ case 2: return_trace (u.conditionalAddGlyphAction.sanitize (c));
+ // case 3: return_trace (u.stretchGlyphAction.sanitize (c));
+ case 4: return_trace (u.decompositionAction.sanitize (c));
+ case 5: return_trace (u.decompositionAction.sanitize (c));
+ default: return_trace (true);
+ }
+ }
+
+ protected:
+ union {
+ ActionSubrecordHeader header;
+ DecompositionAction decompositionAction;
+ UnconditionalAddGlyphAction unconditionalAddGlyphAction;
+ ConditionalAddGlyphAction conditionalAddGlyphAction;
+ /* StretchGlyphAction stretchGlyphAction; -- Not supported by CoreText */
+ DuctileGlyphAction ductileGlyphAction;
+ RepeatedAddGlyphAction repeatedAddGlyphAction;
+ } u; /* Data. The format of this data depends on
+ * the value of the actionType field. */
+ public:
+ DEFINE_SIZE_UNION (6, header);
+};
+
+struct PostcompensationActionChain
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+
+ unsigned int offset = min_size;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ const ActionSubrecord& subrecord = StructAtOffset<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:
+ HBFixed beforeGrowLimit;/* The ratio by which the advance width of the
+ * glyph is permitted to grow on the left or top side. */
+ HBFixed beforeShrinkLimit;
+ /* The ratio by which the advance width of the
+ * glyph is permitted to shrink on the left or top side. */
+ HBFixed afterGrowLimit; /* The ratio by which the advance width of the glyph
+ * is permitted to shrink on the left or top side. */
+ HBFixed 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 (likely (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::LArrayOf<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:
+ OffsetTo<JustificationCategory>
+ justClassTable; /* Offset to the justification category state table. */
+ OffsetTo<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. */
+ OffsetTo<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<OffsetTo<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) &&
+ 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). */
+ OffsetTo<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. */
+ OffsetTo<JustificationHeader>
+ vertData; /* ditto, vertical */
+
+ public:
+ DEFINE_SIZE_STATIC (10);
+};
+
+} /* namespace AAT */
+
+
+#endif /* HB_AAT_LAYOUT_JUST_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh
new file mode 100644
index 0000000000..1cd412164e
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh
@@ -0,0 +1,999 @@
+/*
+ * 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;
+ 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:
+ HBGlyphID left;
+ HBGlyphID 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_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() = 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() = 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);
+
+ 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;
+
+ 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) &&
+ 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;
+ unsigned int markControlPoint = *data++;
+ unsigned int currControlPoint = *data++;
+ hb_position_t markX = 0;
+ hb_position_t markY = 0;
+ hb_position_t currX = 0;
+ hb_position_t currY = 0;
+ if (!c->font->get_glyph_contour_point_for_origin (c->buffer->info[mark].codepoint,
+ markControlPoint,
+ HB_DIRECTION_LTR /*XXX*/,
+ &markX, &markY) ||
+ !c->font->get_glyph_contour_point_for_origin (c->buffer->cur ().codepoint,
+ currControlPoint,
+ HB_DIRECTION_LTR /*XXX*/,
+ &currX, &currY))
+ return;
+
+ o.x_offset = markX - currX;
+ o.y_offset = markY - currY;
+ }
+ break;
+
+ case 1: /* Anchor Point Actions. */
+ {
+ /* Indexed into 'ankr' table. */
+ /* Each action (record in ankrData) contains two 16-bit fields, so we must
+ double the ankrActionIndex to get the correct offset here. */
+ const HBUINT16 *data = &ankrData[entry.data.ankrActionIndex * 2];
+ if (!c->sanitizer.check_array (data, 2)) return;
+ unsigned int markAnchorPoint = *data++;
+ unsigned int currAnchorPoint = *data++;
+ const Anchor &markAnchor = c->ankr_table->get_anchor (c->buffer->info[mark].codepoint,
+ markAnchorPoint,
+ c->sanitizer.get_num_glyphs ());
+ const Anchor &currAnchor = c->ankr_table->get_anchor (c->buffer->cur ().codepoint,
+ currAnchorPoint,
+ c->sanitizer.get_num_glyphs ());
+
+ o.x_offset = c->font->em_scale_x (markAnchor.xCoordinate) - c->font->em_scale_x (currAnchor.xCoordinate);
+ o.y_offset = c->font->em_scale_y (markAnchor.yCoordinate) - c->font->em_scale_y (currAnchor.yCoordinate);
+ }
+ break;
+
+ case 2: /* Control Point Coordinate Actions. */
+ {
+ /* Each action contains four 16-bit fields, so we multiply the ankrActionIndex
+ by 4 to get the correct offset for the given action. */
+ const FWORD *data = (const FWORD *) &ankrData[entry.data.ankrActionIndex * 4];
+ if (!c->sanitizer.check_array (data, 4)) return;
+ int markX = *data++;
+ int markY = *data++;
+ int currX = *data++;
+ int currY = *data++;
+
+ o.x_offset = c->font->em_scale_x (markX) - c->font->em_scale_x (currX);
+ o.y_offset = c->font->em_scale_y (markY) - c->font->em_scale_y (currY);
+ }
+ break;
+ }
+ o.attach_type() = 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);
+
+ 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;
+ 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;
+ 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) &&
+ (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
+ {
+ LNNOffsetTo<Lookup<HBUINT32>> rowIndexTable;
+ LNNOffsetTo<Lookup<HBUINT32>> columnIndexTable;
+ LNNOffsetTo<UnsizedArrayOf<FWORD32>> array;
+ } l;
+ struct Short
+ {
+ LNNOffsetTo<Lookup<HBUINT16>> rowIndexTable;
+ LNNOffsetTo<Lookup<HBUINT16>> columnIndexTable;
+ LNNOffsetTo<UnsizedArrayOf<FWORD>> array;
+ } s;
+ } u;
+ LNNOffsetTo<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 (likely (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, hb_forward<Ts> (ds)...));
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+ case 4: return_trace (c->dispatch (u.format4, hb_forward<Ts> (ds)...));
+ case 6: return_trace (c->dispatch (u.format6, hb_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) ||
+ 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
+ {
+ 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 %d", 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() = 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 %d", 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) ||
+ (unsigned) thiz()->version < (unsigned) T::minVersion ||
+ !thiz()->tableCount.sanitize (c)))
+ return_trace (false);
+
+ typedef typename T::SubTable SubTable;
+
+ const SubTable *st = &thiz()->firstSubTable;
+ unsigned int count = thiz()->tableCount;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ if (unlikely (!st->u.header.sanitize (c)))
+ return_trace (false);
+ /* OpenType kern table has 2-byte subtable lengths. That's limiting.
+ * MS implementation also only supports one subtable, of format 0,
+ * anyway. Certain versions of some fonts, like Calibry, contain
+ * kern subtable that exceeds 64kb. Looks like, the subtable length
+ * is simply ignored. Which makes sense. It's only needed if you
+ * have multiple subtables. To handle such fonts, we just ignore
+ * the length for the last subtable. */
+ hb_sanitize_with_object_t with (c, i < count - 1 ? st : (const SubTable *) nullptr);
+
+ if (unlikely (!st->sanitize (c)))
+ return_trace (false);
+
+ st = &StructAfter<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/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh
new file mode 100644
index 0000000000..04027a61be
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh
@@ -0,0 +1,1157 @@
+/*
+ * 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-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)
+ {
+ 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];
+
+ memcpy (buf, info + start, l * sizeof (buf[0]));
+ 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]));
+
+ memcpy (info + start, buf + 2, r * sizeof (buf[0]));
+ 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);
+
+ 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_),
+ mark_set (false),
+ 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 HBGlyphID *replacement;
+
+ replacement = nullptr;
+ if (Types::extended)
+ {
+ if (entry.data.markIndex != 0xFFFF)
+ {
+ const Lookup<HBGlyphID> &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<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
+ replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
+ if (!replacement->sanitize (&c->sanitizer) || !*replacement)
+ replacement = nullptr;
+ }
+ if (replacement)
+ {
+ buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
+ buffer->info[mark].codepoint = *replacement;
+ ret = true;
+ }
+
+ replacement = nullptr;
+ unsigned int idx = hb_min (buffer->idx, buffer->len - 1);
+ if (Types::extended)
+ {
+ if (entry.data.currentIndex != 0xFFFF)
+ {
+ const Lookup<HBGlyphID> &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<HBGlyphID> &subs_old = (const UnsizedArrayOf<HBGlyphID> &) subs;
+ replacement = &subs_old[Types::wordOffsetToIndex (offset, table, subs_old.arrayZ)];
+ if (!replacement->sanitize (&c->sanitizer) || !*replacement)
+ replacement = nullptr;
+ }
+ if (replacement)
+ {
+ buffer->info[idx].codepoint = *replacement;
+ ret = true;
+ }
+
+ if (entry.flags & SetMark)
+ {
+ mark_set = true;
+ mark = buffer->idx;
+ }
+ }
+
+ public:
+ bool ret;
+ private:
+ hb_aat_apply_context_t *c;
+ bool mark_set;
+ unsigned int mark;
+ const ContextualSubtable *table;
+ const UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, 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);
+
+ return_trace (dc.ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+
+ unsigned int num_entries = 0;
+ if (unlikely (!machine.sanitize (c, &num_entries))) return_trace (false);
+
+ if (!Types::extended)
+ return_trace (substitutionTables.sanitize (c, this, 0));
+
+ unsigned int num_lookups = 0;
+
+ const Entry<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, 1 + data.markIndex);
+ if (data.currentIndex != 0xFFFF)
+ num_lookups = hb_max (num_lookups, 1 + data.currentIndex);
+ }
+
+ return_trace (substitutionTables.sanitize (c, this, num_lookups));
+ }
+
+ protected:
+ StateTable<Types, EntryData>
+ machine;
+ NNOffsetTo<UnsizedOffsetListOf<Lookup<HBGlyphID>, HBUINT, 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);
+ buffer->move_to (match_positions[--cursor % ARRAY_LENGTH (match_positions)]);
+
+ if (unlikely (!actionData->sanitize (&c->sanitizer))) break;
+ action = *actionData;
+
+ uint32_t uoffset = action & LigActionOffset;
+ if (uoffset & 0x20000000)
+ uoffset |= 0xC0000000; /* Sign-extend. */
+ int32_t offset = (int32_t) uoffset;
+ unsigned int component_idx = buffer->cur().codepoint + offset;
+ component_idx = Types::wordOffsetToIndex (component_idx, table, component.arrayZ);
+ const HBUINT16 &componentData = component[component_idx];
+ if (unlikely (!componentData.sanitize (&c->sanitizer))) break;
+ ligature_idx += componentData;
+
+ DEBUG_MSG (APPLY, nullptr, "Action store %u last %u",
+ bool (action & LigActionStore),
+ bool (action & LigActionLast));
+ if (action & (LigActionStore | LigActionLast))
+ {
+ ligature_idx = Types::offsetToIndex (ligature_idx, table, ligature.arrayZ);
+ const HBGlyphID &ligatureData = ligature[ligature_idx];
+ if (unlikely (!ligatureData.sanitize (&c->sanitizer))) break;
+ hb_codepoint_t lig = ligatureData;
+
+ DEBUG_MSG (APPLY, nullptr, "Produced ligature %u", lig);
+ buffer->replace_glyph (lig);
+
+ 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");
+ buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]);
+ buffer->replace_glyph (DELETED_GLYPH);
+ }
+
+ buffer->move_to (lig_end);
+ buffer->merge_out_clusters (match_positions[cursor % ARRAY_LENGTH (match_positions)], buffer->out_len);
+ }
+
+ actionData++;
+ }
+ while (!(action & LigActionLast));
+ buffer->move_to (end);
+ }
+ }
+
+ public:
+ bool ret;
+ private:
+ hb_aat_apply_context_t *c;
+ const LigatureSubtable *table;
+ const UnsizedArrayOf<HBUINT32> &ligAction;
+ const UnsizedArrayOf<HBUINT16> &component;
+ const UnsizedArrayOf<HBGlyphID> &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);
+
+ return_trace (dc.ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ /* The rest of array sanitizations are done at run-time. */
+ return_trace (c->check_struct (this) && machine.sanitize (c) &&
+ ligAction && component && ligature);
+ }
+
+ protected:
+ StateTable<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<HBGlyphID>, 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);
+
+ 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;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ const HBGlyphID *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
+ if (replacement)
+ {
+ info[i].codepoint = *replacement;
+ ret = true;
+ }
+ }
+
+ return_trace (ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (substitute.sanitize (c));
+ }
+
+ protected:
+ Lookup<HBGlyphID> 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 HBGlyphID *glyphs = &insertionAction[start];
+ if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
+
+ bool before = flags & MarkedInsertBefore;
+
+ unsigned int end = buffer->out_len;
+ buffer->move_to (mark);
+
+ if (buffer->idx < buffer->len && !before)
+ buffer->copy_glyph ();
+ /* TODO We ignore KashidaLike setting. */
+ for (unsigned int i = 0; i < count; i++)
+ buffer->output_glyph (glyphs[i]);
+ if (buffer->idx < buffer->len && !before)
+ buffer->skip_glyph ();
+
+ buffer->move_to (end + count);
+
+ 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 HBGlyphID *glyphs = &insertionAction[start];
+ if (unlikely (!c->sanitizer.check_array (glyphs, count))) count = 0;
+
+ bool before = flags & CurrentInsertBefore;
+
+ unsigned int end = buffer->out_len;
+
+ if (buffer->idx < buffer->len && !before)
+ buffer->copy_glyph ();
+ /* TODO We ignore KashidaLike setting. */
+ for (unsigned int i = 0; i < count; i++)
+ buffer->output_glyph (glyphs[i]);
+ 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
+ */
+ buffer->move_to ((flags & DontAdvance) ? end : end + count);
+ }
+ }
+
+ public:
+ bool ret;
+ private:
+ hb_aat_apply_context_t *c;
+ unsigned int mark;
+ const UnsizedArrayOf<HBGlyphID> &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);
+
+ return_trace (dc.ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ /* The rest of array sanitizations are done at run-time. */
+ return_trace (c->check_struct (this) && machine.sanitize (c) &&
+ insertionAction);
+ }
+
+ protected:
+ StateTable<Types, EntryData>
+ machine;
+ NNOffsetTo<UnsizedArrayOf<HBGlyphID>, 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, hb_forward<Ts> (ds)...));
+ case Contextual: return_trace (c->dispatch (u.contextual, hb_forward<Ts> (ds)...));
+ case Ligature: return_trace (c->dispatch (u.ligature, hb_forward<Ts> (ds)...));
+ case Noncontextual: return_trace (c->dispatch (u.noncontextual, hb_forward<Ts> (ds)...));
+ case Insertion: return_trace (c->dispatch (u.insertion, hb_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) ||
+ 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->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;
+ }
+ }
+ }
+ return flags;
+ }
+
+ void apply (hb_aat_apply_context_t *c,
+ hb_mask_t flags) 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 (!(subtable->subFeatureFlags & flags))
+ goto skip;
+
+ 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 %d", 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 %d", 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) ||
+ 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);
+ 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;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ map->chain_flags.push (chain->compile_flags (mapper));
+ chain = &StructAfter<Chain<Types>> (*chain);
+ }
+ }
+
+ void apply (hb_aat_apply_context_t *c) const
+ {
+ if (unlikely (!c->buffer->successful)) return;
+ c->set_lookup_index (0);
+ const Chain<Types> *chain = &firstChain;
+ unsigned int count = chainCount;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ chain->apply (c, c->plan->aat_map.chain_flags[i]);
+ 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) || !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);
+ 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/thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh
new file mode 100644
index 0000000000..8c04a6482f
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh
@@ -0,0 +1,173 @@
+/*
+ * Copyright © 2019 Ebrahim Byagowi
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LAYOUT_OPBD_TABLE_HH
+#define HB_AAT_LAYOUT_OPBD_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+#include "hb-open-type.hh"
+
+/*
+ * opbd -- Optical Bounds
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
+ */
+#define HB_AAT_TAG_opbd HB_TAG('o','p','b','d')
+
+
+namespace AAT {
+
+struct OpticalBounds
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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 OffsetTo<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<OffsetTo<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 OffsetTo<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<OffsetTo<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);
+
+ 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/thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh b/thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh
new file mode 100644
index 0000000000..baa1c72020
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh
@@ -0,0 +1,230 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ * Copyright © 2018 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH
+#define HB_AAT_LAYOUT_TRAK_TABLE_HH
+
+#include "hb-aat-layout-common.hh"
+#include "hb-ot-layout.hh"
+#include "hb-open-type.hh"
+
+/*
+ * trak -- Tracking
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6trak.html
+ */
+#define HB_AAT_TAG_trak HB_TAG('t','r','a','k')
+
+
+namespace AAT {
+
+
+struct TrackTableEntry
+{
+ friend struct TrackData;
+
+ float get_track_value () const { return track.to_float (); }
+
+ int get_value (const void *base, unsigned int index,
+ unsigned int table_size) const
+ { return (base+valuesZ).as_array (table_size)[index]; }
+
+ public:
+ bool sanitize (hb_sanitize_context_t *c, const void *base,
+ unsigned int table_size) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ (valuesZ.sanitize (c, base, table_size))));
+ }
+
+ protected:
+ HBFixed 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") */
+ NNOffsetTo<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 HBFixed> 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 HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
+ unsigned int size_index;
+ for (size_index = 0; size_index < sizes - 1; size_index++)
+ if (size_table[size_index].to_float () >= ptem)
+ break;
+
+ return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
+ *trackTableEntry, base));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ sizeTable.sanitize (c, base, nSizes) &&
+ trackTable.sanitize (c, nTracks, base, nSizes)));
+ }
+
+ protected:
+ HBUINT16 nTracks; /* Number of separate tracks included in this table. */
+ HBUINT16 nSizes; /* Number of point sizes included in this table. */
+ LNNOffsetTo<UnsizedArrayOf<HBFixed>>
+ 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) &&
+ 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). */
+ OffsetTo<TrackData>
+ horizData; /* Offset from start of tracking table to TrackData
+ * for horizontal text (or 0 if none). */
+ OffsetTo<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/thirdparty/harfbuzz/src/hb-aat-layout.cc b/thirdparty/harfbuzz/src/hb-aat-layout.cc
new file mode 100644
index 0000000000..fac510e9e6
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout.cc
@@ -0,0 +1,382 @@
+/*
+ * 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)),
+ 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 OpenType Layout features in the font face.
+ **/
+
+
+#if !defined(HB_NO_AAT) || defined(HAVE_CORETEXT)
+
+/* 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_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_HISTORICAL_LIGATURES_OFF},
+ {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','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 ('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},
+};
+
+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:
+ *
+ * Returns:
+ * 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)
+{
+ 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);
+ morx.apply (&c);
+ 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);
+ mort.apply (&c);
+ 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)
+{
+ hb_ot_layout_delete_glyphs_inplace (buffer, is_deleted_glyph);
+}
+
+/*
+ * hb_aat_layout_has_positioning:
+ * @face:
+ *
+ * Returns:
+ * 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);
+ c.set_ankr_table (font->face->table.ankr.get ());
+ kerx.apply (&c);
+}
+
+
+/*
+ * hb_aat_layout_has_tracking:
+ * @face:
+ *
+ * Returns:
+ * 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: a face object
+ * @start_offset: iteration's start offset
+ * @feature_count:(inout) (allow-none): buffer size as input, filled size as output
+ * @features: (out caller-allocates) (array length=feature_count): features buffer
+ *
+ * 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: a face object
+ * @feature_type: feature id
+ *
+ * Return value: Name ID index
+ *
+ * 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_selectors:
+ * @face: a face object
+ * @feature_type: feature id
+ * @start_offset: iteration's start offset
+ * @selector_count: (inout) (allow-none): buffer size as input, filled size as output
+ * @selectors: (out caller-allocates) (array length=selector_count): settings buffer
+ * @default_index: (out) (allow-none): index of default selector if any
+ *
+ * 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/thirdparty/harfbuzz/src/hb-aat-layout.h b/thirdparty/harfbuzz/src/hb-aat-layout.h
new file mode 100644
index 0000000000..b617e8b703
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout.h
@@ -0,0 +1,486 @@
+/*
+ * 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_IN
+#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:
+ *
+ *
+ * Since: 2.2.0
+ */
+typedef enum
+{
+ HB_AAT_LAYOUT_FEATURE_TYPE_INVALID = 0xFFFF,
+
+ HB_AAT_LAYOUT_FEATURE_TYPE_ALL_TYPOGRAPHIC = 0,
+ HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES = 1,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CURISVE_CONNECTION = 2,
+ HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE = 3,
+ HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_SUBSTITUTION = 4,
+ HB_AAT_LAYOUT_FEATURE_TYPE_LINGUISTIC_REARRANGEMENT = 5,
+ HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING = 6,
+ HB_AAT_LAYOUT_FEATURE_TYPE_SMART_SWASH_TYPE = 8,
+ HB_AAT_LAYOUT_FEATURE_TYPE_DIACRITICS_TYPE = 9,
+ HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION = 10,
+ HB_AAT_LAYOUT_FEATURE_TYPE_FRACTIONS = 11,
+ HB_AAT_LAYOUT_FEATURE_TYPE_OVERLAPPING_CHARACTERS_TYPE = 13,
+ HB_AAT_LAYOUT_FEATURE_TYPE_TYPOGRAPHIC_EXTRAS = 14,
+ HB_AAT_LAYOUT_FEATURE_TYPE_MATHEMATICAL_EXTRAS = 15,
+ HB_AAT_LAYOUT_FEATURE_TYPE_ORNAMENT_SETS_TYPE = 16,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES = 17,
+ HB_AAT_LAYOUT_FEATURE_TYPE_DESIGN_COMPLEXITY_TYPE = 18,
+ HB_AAT_LAYOUT_FEATURE_TYPE_STYLE_OPTIONS = 19,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_SHAPE = 20,
+ HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_CASE = 21,
+ HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING = 22,
+ HB_AAT_LAYOUT_FEATURE_TYPE_TRANSLITERATION = 23,
+ HB_AAT_LAYOUT_FEATURE_TYPE_ANNOTATION_TYPE = 24,
+ HB_AAT_LAYOUT_FEATURE_TYPE_KANA_SPACING_TYPE = 25,
+ HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_SPACING_TYPE = 26,
+ HB_AAT_LAYOUT_FEATURE_TYPE_UNICODE_DECOMPOSITION_TYPE = 27,
+ HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA = 28,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CJK_SYMBOL_ALTERNATIVES_TYPE = 29,
+ HB_AAT_LAYOUT_FEATURE_TYPE_IDEOGRAPHIC_ALTERNATIVES_TYPE = 30,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CJK_VERTICAL_ROMAN_PLACEMENT_TYPE = 31,
+ HB_AAT_LAYOUT_FEATURE_TYPE_ITALIC_CJK_ROMAN = 32,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CASE_SENSITIVE_LAYOUT = 33,
+ HB_AAT_LAYOUT_FEATURE_TYPE_ALTERNATE_KANA = 34,
+ HB_AAT_LAYOUT_FEATURE_TYPE_STYLISTIC_ALTERNATIVES = 35,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CONTEXTUAL_ALTERNATIVES = 36,
+ HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE = 37,
+ HB_AAT_LAYOUT_FEATURE_TYPE_UPPER_CASE = 38,
+ HB_AAT_LAYOUT_FEATURE_TYPE_LANGUAGE_TAG_TYPE = 39,
+ HB_AAT_LAYOUT_FEATURE_TYPE_CJK_ROMAN_SPACING_TYPE = 103,
+
+ _HB_AAT_LAYOUT_FEATURE_TYPE_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
+} hb_aat_layout_feature_type_t;
+
+/**
+ * hb_aat_layout_feature_selector_t:
+ *
+ *
+ * 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,
+
+ _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);
+
+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;
+
+#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/thirdparty/harfbuzz/src/hb-aat-layout.hh b/thirdparty/harfbuzz/src/hb-aat-layout.hh
new file mode 100644
index 0000000000..5e4e3bda15
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-layout.hh
@@ -0,0 +1,75 @@
+/*
+ * 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);
+
+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/thirdparty/harfbuzz/src/hb-aat-ltag-table.hh b/thirdparty/harfbuzz/src/hb-aat-ltag-table.hh
new file mode 100644
index 0000000000..711f9aa6c1
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-ltag-table.hh
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2018 Ebrahim Byagowi
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
+
+#ifndef HB_AAT_LTAG_TABLE_HH
+#define HB_AAT_LTAG_TABLE_HH
+
+#include "hb-open-type.hh"
+
+/*
+ * ltag -- Language Tag
+ * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6ltag.html
+ */
+#define HB_AAT_TAG_ltag HB_TAG('l','t','a','g')
+
+
+namespace AAT {
+
+using namespace OT;
+
+
+struct FTStringRange
+{
+ friend struct ltag;
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && (base+tag).sanitize (c, length));
+ }
+
+ protected:
+ NNOffsetTo<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) &&
+ version >= 1 &&
+ tagRanges.sanitize (c, this)));
+ }
+
+ protected:
+ HBUINT32 version; /* Table version; currently 1 */
+ HBUINT32 flags; /* Table flags; currently none defined */
+ LArrayOf<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/thirdparty/harfbuzz/src/hb-aat-map.cc b/thirdparty/harfbuzz/src/hb-aat-map.cc
new file mode 100644
index 0000000000..2c38c35029
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-map.cc
@@ -0,0 +1,102 @@
+/*
+ * 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 (hb_tag_t tag, unsigned value)
+{
+ if (!face->table.feat->has_data ()) return;
+
+ if (tag == HB_TAG ('a','a','l','t'))
+ {
+ if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
+ return;
+ feature_info_t *info = features.push();
+ info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
+ info->setting = (hb_aat_layout_feature_selector_t) value;
+ info->seq = features.length;
+ info->is_exclusive = true;
+ return;
+ }
+
+ const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
+ if (!mapping) return;
+
+ const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
+ if (!feature->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 = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
+ if (!feature->has_data ()) return;
+ }
+ else return;
+ }
+
+ feature_info_t *info = features.push();
+ info->type = mapping->aatFeatureType;
+ info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
+ info->seq = features.length;
+ info->is_exclusive = feature->is_exclusive ();
+}
+
+void
+hb_aat_map_builder_t::compile (hb_aat_map_t &m)
+{
+ /* Sort features and merge duplicates */
+ if (features.length)
+ {
+ features.qsort ();
+ unsigned int j = 0;
+ for (unsigned int i = 1; i < features.length; i++)
+ if (features[i].type != 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. */
+ (!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1))))
+ features[++j] = features[i];
+ features.shrink (j + 1);
+ }
+
+ hb_aat_layout_compile_map (this, &m);
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-aat-map.hh b/thirdparty/harfbuzz/src/hb-aat-map.hh
new file mode 100644
index 0000000000..5a0fa70544
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-aat-map.hh
@@ -0,0 +1,96 @@
+/*
+ * 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:
+
+ void init ()
+ {
+ memset (this, 0, sizeof (*this));
+ chain_flags.init ();
+ }
+ void fini () { chain_flags.fini (); }
+
+ public:
+ hb_vector_t<hb_mask_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_ HB_UNUSED) :
+ face (face_) {}
+
+ HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1);
+
+ 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, not is_exclusive flag or seq number */
+ 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;
+ }
+ };
+
+ public:
+ hb_face_t *face;
+
+ public:
+ hb_sorted_vector_t<feature_info_t> features;
+};
+
+
+#endif /* HB_AAT_MAP_HH */
diff --git a/thirdparty/harfbuzz/src/hb-aat.h b/thirdparty/harfbuzz/src/hb-aat.h
new file mode 100644
index 0000000000..c14313d1e2
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-algs.hh b/thirdparty/harfbuzz/src/hb-algs.hh
new file mode 100644
index 0000000000..30b5812e12
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-algs.hh
@@ -0,0 +1,1127 @@
+/*
+ * 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"
+
+
+/* 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 ( hb_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 (hb_forward<T> (v)); }
+}
+HB_FUNCOBJ (hb_bool);
+
+struct
+{
+ private:
+
+ template <typename T> constexpr auto
+ impl (const T& v, hb_priority<1>) const HB_RETURN (uint32_t, hb_deref (v).hash ())
+
+ template <typename T,
+ hb_enable_if (hb_is_integral (T))> constexpr auto
+ impl (const T& v, hb_priority<0>) const HB_AUTO_RETURN
+ (
+ /* Knuth's multiplicative method: */
+ (uint32_t) v * 2654435761u
+ )
+
+ 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 (hb_forward<T> (v)).*hb_forward<Appl> (a)) (hb_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 (hb_forward<T> (v))).*hb_forward<Appl> (a))
+
+ /* Operator(). */
+ template <typename Appl, typename ...Ts> auto
+ impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
+ (hb_deref (hb_forward<Appl> (a)) (hb_forward<Ts> (ds)...))
+
+ public:
+
+ template <typename Appl, typename ...Ts> auto
+ operator () (Appl&& a, Ts&&... ds) const HB_AUTO_RETURN
+ (
+ impl (hb_forward<Appl> (a),
+ hb_prioritize,
+ hb_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 (hb_forward<Appl> (a),
+ hb_forward<V> (v),
+ hb_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 (hb_forward<Appl> (a),
+ hb_forward<T0> (d0),
+ hb_forward<V> (v),
+ hb_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, hb_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, hb_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 (hb_forward<Pred> (p)).has (hb_forward<Val> (v)))
+
+ template <typename Pred, typename Val> auto
+ impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
+ (
+ hb_invoke (hb_forward<Pred> (p),
+ hb_forward<Val> (v))
+ )
+
+ public:
+
+ template <typename Pred, typename Val> auto
+ operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
+ impl (hb_forward<Pred> (p),
+ hb_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 (hb_forward<Pred> (p),
+ hb_forward<Val> (v))
+ )
+
+ template <typename Pred, typename Val> auto
+ impl (Pred&& p, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
+ (
+ hb_forward<Pred> (p) == hb_forward<Val> (v)
+ )
+
+ public:
+
+ template <typename Pred, typename Val> auto
+ operator () (Pred&& p, Val &&v) const HB_RETURN (bool,
+ impl (hb_forward<Pred> (p),
+ hb_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 (hb_forward<Proj> (f)).get (hb_forward<Val> (v)))
+
+ template <typename Proj, typename Val> auto
+ impl (Proj&& f, Val &&v, hb_priority<1>) const HB_AUTO_RETURN
+ (
+ hb_invoke (hb_forward<Proj> (f),
+ hb_forward<Val> (v))
+ )
+
+ template <typename Proj, typename Val> auto
+ impl (Proj&& f, Val &&v, hb_priority<0>) const HB_AUTO_RETURN
+ (
+ hb_forward<Proj> (f)[hb_forward<Val> (v)]
+ )
+
+ public:
+
+ template <typename Proj, typename Val> auto
+ operator () (Proj&& f, Val &&v) const HB_AUTO_RETURN
+ (
+ impl (hb_forward<Proj> (f),
+ hb_forward<Val> (v),
+ hb_prioritize)
+ )
+}
+HB_FUNCOBJ (hb_get);
+
+
+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;
+
+ hb_pair_t (T1 a, T2 b) : first (a), second (b) {}
+
+ template <typename Q1, typename Q2,
+ hb_enable_if (hb_is_convertible (T1, Q1) &&
+ hb_is_convertible (T2, T2))>
+ 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); }
+
+ T1 first;
+ T2 second;
+};
+#define hb_pair_t(T1,T2) hb_pair_t<T1, T2>
+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); }
+
+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
+ (hb_forward<T> (a) <= hb_forward<T2> (b) ? hb_forward<T> (a) : hb_forward<T2> (b))
+}
+HB_FUNCOBJ (hb_min);
+struct
+{
+ template <typename T, typename T2> constexpr auto
+ operator () (T&& a, T2&& b) const HB_AUTO_RETURN
+ (hb_forward<T> (a) >= hb_forward<T2> (b) ? hb_forward<T> (a) : hb_forward<T2> (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 (hb_forward<T> (x), hb_forward<T2> (min)), hb_forward<T3> (max)))
+}
+HB_FUNCOBJ (hb_clamp);
+
+
+/*
+ * Bithacks.
+ */
+
+/* Return the number of 1 bits in v. */
+template <typename T>
+static inline HB_CONST_FUNC unsigned int
+hb_popcount (T v)
+{
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+ if (sizeof (T) <= sizeof (unsigned int))
+ return __builtin_popcount (v);
+
+ if (sizeof (T) <= sizeof (unsigned long))
+ return __builtin_popcountl (v);
+
+ 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)
+ {
+ unsigned int shift = 32;
+ return hb_popcount<uint32_t> ((uint32_t) v) + hb_popcount ((uint32_t) (v >> shift));
+ }
+
+ 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 HB_CONST_FUNC unsigned int
+hb_bit_storage (T v)
+{
+ if (unlikely (!v)) return 0;
+
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+ if (sizeof (T) <= sizeof (unsigned int))
+ return sizeof (unsigned int) * 8 - __builtin_clz (v);
+
+ if (sizeof (T) <= sizeof (unsigned long))
+ return sizeof (unsigned long) * 8 - __builtin_clzl (v);
+
+ 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 HB_CONST_FUNC unsigned int
+hb_ctz (T v)
+{
+ if (unlikely (!v)) return 8 * sizeof (T);
+
+#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)
+ if (sizeof (T) <= sizeof (unsigned int))
+ return __builtin_ctz (v);
+
+ if (sizeof (T) <= sizeof (unsigned long))
+ return __builtin_ctzl (v);
+
+ 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 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 0;
+ 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 (!hb_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, T lo2, T hi2)
+{
+ return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2);
+}
+template <typename T> static inline bool
+hb_in_ranges (T u, T lo1, T hi1, T lo2, T hi2, T lo3, T hi3)
+{
+ return hb_in_range (u, lo1, hi1) || hb_in_range (u, lo2, hi2) || hb_in_range (u, lo3, hi3);
+}
+
+
+/*
+ * Overflow checking.
+ */
+
+/* Consider __builtin_mul_overflow use here also */
+static inline bool
+hb_unsigned_mul_overflows (unsigned int count, unsigned int 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 *) hb_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) ((tmp) = (a), (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> static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T2 *, const T2 *), T3 *array2)
+{
+ 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;
+ }
+ }
+}
+
+template <typename T> static inline void
+hb_stable_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
+{
+ hb_stable_sort (array, len, compar, (int *) nullptr);
+}
+
+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_bitwise_and
+{ HB_PARTIALIZE(2);
+ static constexpr bool passthru_left = false;
+ static constexpr bool passthru_right = false;
+ 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_bitwise_or
+{ HB_PARTIALIZE(2);
+ static constexpr bool passthru_left = true;
+ static constexpr bool passthru_right = true;
+ 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_bitwise_xor
+{ HB_PARTIALIZE(2);
+ static constexpr bool passthru_left = true;
+ static constexpr bool passthru_right = true;
+ 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_bitwise_sub
+{ HB_PARTIALIZE(2);
+ static constexpr bool passthru_left = true;
+ static constexpr bool passthru_right = false;
+ template <typename T> constexpr auto
+ operator () (const T &a, const T &b) const HB_AUTO_RETURN (a & ~b)
+}
+HB_FUNCOBJ (hb_bitwise_sub);
+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 (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);
+
+
+/* Compiler-assisted vectorization. */
+
+/* Type behaving similar to vectorized vars defined using __attribute__((vector_size(...))),
+ * basically a fixed-size bitset. */
+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 clear (unsigned char v = 0) { memset (this, v, sizeof (*this)); }
+
+ 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); }
+
+ private:
+ static_assert (0 == byte_size % sizeof (elt_t), "");
+ elt_t v[byte_size / sizeof (elt_t)];
+};
+
+
+#endif /* HB_ALGS_HH */
diff --git a/thirdparty/harfbuzz/src/hb-array.hh b/thirdparty/harfbuzz/src/hb-array.hh
new file mode 100644
index 0000000000..568cd02c79
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-array.hh
@@ -0,0 +1,408 @@
+/*
+ * 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;
+
+template <typename Type>
+struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
+{
+ /*
+ * Constructors.
+ */
+ hb_array_t () : arrayZ (nullptr), length (0), backwards_length (0) {}
+ hb_array_t (Type *array_, unsigned int length_) : arrayZ (array_), length (length_), backwards_length (0) {}
+ template <unsigned int length_>
+ hb_array_t (Type (&array_)[length_]) : arrayZ (array_), length (length_), backwards_length (0) {}
+
+ template <typename U,
+ hb_enable_if (hb_is_cr_convertible(U, Type))>
+ 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;
+ Type& __item_at__ (unsigned i) const
+ {
+ if (unlikely (i >= length)) return CrapOrNull (Type);
+ return arrayZ[i];
+ }
+ void __forward__ (unsigned n)
+ {
+ if (unlikely (n > length))
+ n = length;
+ length -= n;
+ backwards_length += n;
+ arrayZ += n;
+ }
+ 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. No need to compare length, as we
+ * assume we're only compared to .end(). */
+ bool operator != (const hb_array_t& o) const
+ { return arrayZ != o.arrayZ; }
+
+ /* 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 {
+ uint32_t current = 0;
+ for (unsigned int i = 0; i < this->length; i++) {
+ current = current * 31 + hb_hash (this->arrayZ[i]);
+ }
+ 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) const
+ {
+ for (unsigned i = 0; i < length; ++i)
+ if (!this->arrayZ[i].cmp (x))
+ {
+ if (pos)
+ *pos = i;
+ return true;
+ }
+
+ return false;
+ }
+
+ hb_sorted_array_t<Type> qsort (int (*cmp_)(const void*, const void*))
+ {
+ 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 ()
+ {
+ if (likely (length))
+ hb_qsort (arrayZ, length, this->get_item_size (), Type::cmp);
+ return hb_sorted_array_t<Type> (*this);
+ }
+ void qsort (unsigned int start, unsigned int end)
+ {
+ end = hb_min (end, length);
+ assert (start <= end);
+ if (likely (start < end))
+ hb_qsort (arrayZ + start, end - start, this->get_item_size (), Type::cmp);
+ }
+
+ /*
+ * 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--) {
+ Type temp = arrayZ[rhs];
+ arrayZ[rhs] = arrayZ[lhs];
+ arrayZ[lhs] = temp;
+ }
+ }
+
+ 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_null_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 malloc() or similar. */
+ void free ()
+ { ::free ((void *) arrayZ); arrayZ = nullptr; length = 0; }
+
+ template <typename hb_serialize_context_t>
+ 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 ()))) 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_sanitize_context_t>
+ bool sanitize (hb_sanitize_context_t *c) const
+ { return c->check_array (arrayZ, length); }
+
+ /*
+ * Members
+ */
+
+ public:
+ Type *arrayZ;
+ unsigned int length;
+ unsigned int backwards_length;
+};
+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_); }
+
+enum hb_bfind_not_found_t
+{
+ HB_BFIND_NOT_FOUND_DONT_STORE,
+ HB_BFIND_NOT_FOUND_STORE,
+ HB_BFIND_NOT_FOUND_STORE_CLOSEST,
+};
+
+template <typename Type>
+struct hb_sorted_array_t :
+ hb_iter_t<hb_sorted_array_t<Type>, Type&>,
+ hb_array_t<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;
+
+ hb_sorted_array_t () : hb_array_t<Type> () {}
+ hb_sorted_array_t (Type *array_, unsigned int length_) : hb_array_t<Type> (array_, length_) {}
+ template <unsigned int length_>
+ hb_sorted_array_t (Type (&array_)[length_]) : hb_array_t<Type> (array_) {}
+
+ template <typename U,
+ hb_enable_if (hb_is_cr_convertible(U, Type))>
+ hb_sorted_array_t (const hb_array_t<U> &o) :
+ hb_iter_t<hb_sorted_array_t, Type&> (),
+ hb_array_t<Type> (o) {}
+ 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. */
+ bool operator != (const hb_sorted_array_t& o) const
+ { return this->arrayZ != o.arrayZ || this->length != o.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_bfind_not_found_t not_found = HB_BFIND_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_BFIND_NOT_FOUND_DONT_STORE:
+ break;
+
+ case HB_BFIND_NOT_FOUND_STORE:
+ *i = to_store;
+ break;
+
+ case HB_BFIND_NOT_FOUND_STORE_CLOSEST:
+ *i = pos;
+ break;
+ }
+ }
+ return false;
+ }
+ template <typename T>
+ bool bsearch_impl (const T &x, unsigned *pos) const
+ {
+ return hb_bsearch_impl (pos,
+ x,
+ this->arrayZ,
+ this->length,
+ sizeof (Type),
+ _hb_cmp_method<T, Type>);
+ }
+};
+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>
+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;
+}
+
+/* TODO Specialize opeator== for hb_bytes_t and hb_ubytes_t. */
+
+template <>
+inline uint32_t hb_array_t<const char>::hash () const {
+ uint32_t current = 0;
+ for (unsigned int i = 0; i < this->length; i++)
+ current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
+ return current;
+}
+
+template <>
+inline uint32_t hb_array_t<const unsigned char>::hash () const {
+ uint32_t current = 0;
+ for (unsigned int i = 0; i < this->length; i++)
+ current = current * 31 + (uint32_t) (this->arrayZ[i] * 2654435761u);
+ return current;
+}
+
+
+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/thirdparty/harfbuzz/src/hb-atomic.hh b/thirdparty/harfbuzz/src/hb-atomic.hh
new file mode 100644
index 0000000000..b3fb296b4e
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-atomic.hh
@@ -0,0 +1,295 @@
+/*
+ * 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. */
+
+#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) && __cplusplus >= 201103L
+
+/* 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<int> *> (AI)->fetch_add ((V), std::memory_order_acq_rel))
+#define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_relaxed))
+#define hb_atomic_int_impl_set(AI, V) (reinterpret_cast<std::atomic<int> *> (AI)->store ((V), std::memory_order_release))
+#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast<std::atomic<int> const *> (AI)->load (std::memory_order_relaxed))
+#define hb_atomic_int_impl_get(AI) (reinterpret_cast<std::atomic<int> 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))
+
+
+#elif !defined(HB_NO_MT) && defined(_WIN32)
+
+#include <windows.h>
+
+static inline void _hb_memory_barrier ()
+{
+#if !defined(MemoryBarrier) && !defined(__MINGW32_VERSION)
+ /* MinGW has a convoluted history of supporting MemoryBarrier. */
+ LONG dummy = 0;
+ InterlockedExchange (&dummy, 1);
+#else
+ MemoryBarrier ();
+#endif
+}
+#define _hb_memory_barrier() _hb_memory_barrier ()
+
+#define hb_atomic_int_impl_add(AI, V) InterlockedExchangeAdd ((LONG *) (AI), (V))
+static_assert ((sizeof (LONG) == sizeof (int)), "");
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N) (InterlockedCompareExchangePointer ((P), (N), (O)) == (O))
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
+
+#define _hb_memory_barrier() __sync_synchronize ()
+
+#define hb_atomic_int_impl_add(AI, V) __sync_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N) __sync_bool_compare_and_swap ((P), (O), (N))
+
+
+#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS)
+
+#include <atomic.h>
+#include <mbarrier.h>
+
+#define _hb_memory_r_barrier() __machine_r_barrier ()
+#define _hb_memory_w_barrier() __machine_w_barrier ()
+#define _hb_memory_barrier() __machine_rw_barrier ()
+
+static inline int _hb_fetch_and_add (int *AI, int V)
+{
+ _hb_memory_w_barrier ();
+ int result = atomic_add_int_nv ((uint_t *) AI, V) - V;
+ _hb_memory_r_barrier ();
+ return result;
+}
+static inline bool _hb_compare_and_swap_ptr (void **P, void *O, void *N)
+{
+ _hb_memory_w_barrier ();
+ bool result = atomic_cas_ptr (P, O, N) == O;
+ _hb_memory_r_barrier ();
+ return result;
+}
+
+#define hb_atomic_int_impl_add(AI, V) _hb_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_compare_and_swap_ptr ((P), (O), (N))
+
+
+#elif !defined(HB_NO_MT) && defined(__APPLE__)
+
+#include <libkern/OSAtomic.h>
+#ifdef __MAC_OS_X_MIN_REQUIRED
+#include <AvailabilityMacros.h>
+#elif defined(__IPHONE_OS_MIN_REQUIRED)
+#include <Availability.h>
+#endif
+
+#define _hb_memory_barrier() OSMemoryBarrier ()
+
+#define hb_atomic_int_impl_add(AI, V) (OSAtomicAdd32Barrier ((V), (AI)) - (V))
+
+#if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100)
+#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwapPtrBarrier ((O), (N), (P))
+#else
+#if __ppc64__ || __x86_64__ || __aarch64__
+#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
+#else
+#define hb_atomic_ptr_impl_cmpexch(P,O,N) OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
+#endif
+#endif
+
+
+#elif !defined(HB_NO_MT) && defined(_AIX) && (defined(__IBMCPP__) || defined(__ibmxl__))
+
+#include <builtins.h>
+
+#define _hb_memory_barrier() __lwsync ()
+
+static inline int _hb_fetch_and_add (int *AI, int V)
+{
+ _hb_memory_barrier ();
+ int result = __fetch_and_add (AI, V);
+ _hb_memory_barrier ();
+ return result;
+}
+static inline bool _hb_compare_and_swaplp (long *P, long O, long N)
+{
+ _hb_memory_barrier ();
+ bool result = __compare_and_swaplp (P, &O, N);
+ _hb_memory_barrier ();
+ return result;
+}
+
+#define hb_atomic_int_impl_add(AI, V) _hb_fetch_and_add ((AI), (V))
+
+#define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_compare_and_swaplp ((long *) (P), (long) (O), (long) (N))
+static_assert ((sizeof (long) == sizeof (void *)), "");
+
+
+#elif 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)
+
+
+#else
+
+#error "Could not find any system to define atomic_int macros."
+#error "Check hb-atomic.hh for possible resolutions."
+
+#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; }
+#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; }
+#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
+
+
+#define HB_ATOMIC_INT_INIT(V) {V}
+struct hb_atomic_int_t
+{
+ void set_relaxed (int v_) { hb_atomic_int_impl_set_relaxed (&v, v_); }
+ void set (int v_) { hb_atomic_int_impl_set (&v, v_); }
+ int get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); }
+ int get () 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;
+};
+
+
+#define HB_ATOMIC_PTR_INIT(V) {V}
+template <typename P>
+struct hb_atomic_ptr_t
+{
+ typedef hb_remove_pointer<P> T;
+
+ 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 () 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 (); }
+ template <typename C> operator C * () const { return get (); }
+
+ T *v;
+};
+
+
+#endif /* HB_ATOMIC_HH */
diff --git a/thirdparty/harfbuzz/src/hb-bimap.hh b/thirdparty/harfbuzz/src/hb-bimap.hh
new file mode 100644
index 0000000000..e9f3a6a52d
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-bimap.hh
@@ -0,0 +1,166 @@
+/*
+ * 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
+{
+ hb_bimap_t () { init (); }
+ ~hb_bimap_t () { fini (); }
+
+ void init ()
+ {
+ forw_map.init ();
+ back_map.init ();
+ }
+
+ void fini ()
+ {
+ forw_map.fini ();
+ back_map.fini ();
+ }
+
+ void reset ()
+ {
+ forw_map.reset ();
+ back_map.reset ();
+ }
+
+ bool in_error () const { return forw_map.in_error () || back_map.in_error (); }
+
+ void set (hb_codepoint_t lhs, hb_codepoint_t rhs)
+ {
+ if (unlikely (lhs == HB_MAP_VALUE_INVALID)) return;
+ if (unlikely (rhs == HB_MAP_VALUE_INVALID)) { del (lhs); return; }
+ forw_map.set (lhs, rhs);
+ back_map.set (rhs, 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, hb_codepoint_t *vp = nullptr) const { return forw_map.has (lhs, vp); }
+
+ 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 get_population () == 0; }
+
+ unsigned int get_population () const { return forw_map.get_population (); }
+
+ protected:
+ hb_map_t forw_map;
+ hb_map_t back_map;
+};
+
+/* Inremental bimap: only lhs is given, rhs is incrementally assigned */
+struct hb_inc_bimap_t : hb_bimap_t
+{
+ hb_inc_bimap_t () { init (); }
+
+ void init ()
+ {
+ hb_bimap_t::init ();
+ next_value = 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 = next_value++;
+ set (lhs, rhs);
+ }
+ return rhs;
+ }
+
+ hb_codepoint_t skip ()
+ { return next_value++; }
+
+ hb_codepoint_t get_next_value () const
+ { return next_value; }
+
+ void add_set (const hb_set_t *set)
+ {
+ hb_codepoint_t i = HB_SET_VALUE_INVALID;
+ while (hb_set_next (set, &i)) add (i);
+ }
+
+ /* Create an identity map. */
+ bool identity (unsigned int size)
+ {
+ clear ();
+ for (hb_codepoint_t i = 0; i < size; i++) set (i, 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;
+ work.resize (count);
+
+ for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
+ work[rhs] = back_map[rhs];
+
+ work.qsort (cmp_id);
+
+ clear ();
+ for (hb_codepoint_t rhs = 0; rhs < count; rhs++)
+ set (work[rhs], rhs);
+ }
+
+ protected:
+ unsigned int next_value;
+};
+
+#endif /* HB_BIMAP_HH */
diff --git a/thirdparty/harfbuzz/src/hb-blob.cc b/thirdparty/harfbuzz/src/hb-blob.cc
new file mode 100644
index 0000000000..94ed50fd3c
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-blob.cc
@@ -0,0 +1,717 @@
+/*
+ * 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 */
+
+#include <stdio.h>
+#include <stdlib.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: 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)
+{
+ hb_blob_t *blob;
+
+ if (!length ||
+ length >= 1u << 31 ||
+ !(blob = hb_object_create<hb_blob_t> ())) {
+ if (destroy)
+ destroy (user_data);
+ return hb_blob_get_empty ();
+ }
+
+ 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 hb_blob_get_empty ();
+ }
+ }
+
+ 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: 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;
+
+ blob->fini_shallow ();
+
+ free (blob);
+}
+
+/**
+ * hb_blob_set_user_data: (skip)
+ * @blob: a blob.
+ * @key: key for data to set.
+ * @data: data to set.
+ * @destroy: callback to call when @data is not needed anymore.
+ * @replace: whether to replace an existing data with the same key.
+ *
+ * Return value:
+ *
+ * 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: key for data to get.
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.2
+ **/
+void *
+hb_blob_get_user_data (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.
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * Return value: TODO
+ *
+ * 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.
+ *
+ *
+ *
+ * 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):
+ *
+ *
+ *
+ * Returns: (transfer none) (array length=length):
+ *
+ * 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 (!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 (hb_object_is_immutable (this))
+ return false;
+
+ 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 *) malloc (this->length);
+ if (unlikely (!new_data))
+ return false;
+
+ DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data);
+
+ 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 = 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
+
+ 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 *) 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) - 1);
+
+ int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0);
+ 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: font filename.
+ *
+ * Returns: A hb_blob_t pointer with the content of the file
+ *
+ * Since: 1.7.7
+ **/
+hb_blob_t *
+hb_blob_create_from_file (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 *) calloc (1, sizeof (hb_mapped_file_t));
+ if (unlikely (!file)) return hb_blob_get_empty ();
+
+ 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 (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:
+ free (file);
+
+#elif defined(_WIN32) && !defined(HB_NO_MMAP)
+ hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
+ if (unlikely (!file)) return hb_blob_get_empty ();
+
+ HANDLE fd;
+ unsigned int size = strlen (file_name) + 1;
+ wchar_t * wchar_file_name = (wchar_t *) 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)
+ {
+ CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };
+ ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
+ ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF;
+ ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000;
+ ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000;
+ ceparams.lpSecurityAttributes = nullptr;
+ ceparams.hTemplateFile = nullptr;
+ fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,
+ OPEN_EXISTING, &ceparams);
+ }
+#else
+ fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
+ nullptr);
+#endif
+ free (wchar_file_name);
+
+ if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
+
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ {
+ 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)
+ 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 (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:
+ 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 *) malloc (allocated);
+ if (unlikely (!data)) return hb_blob_get_empty ();
+
+ 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 *) 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 (data, len, HB_MEMORY_MODE_WRITABLE, data,
+ (hb_destroy_func_t) free);
+
+fread_fail:
+ fclose (fp);
+fread_fail_without_close:
+ free (data);
+ return hb_blob_get_empty ();
+}
+#endif /* !HB_NO_OPEN */
diff --git a/thirdparty/harfbuzz/src/hb-blob.h b/thirdparty/harfbuzz/src/hb-blob.h
new file mode 100644
index 0000000000..f80e9af2d9
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-blob.h
@@ -0,0 +1,131 @@
+/*
+ * 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_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_BLOB_H
+#define HB_BLOB_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * Note re various memory-modes:
+ *
+ * - In no case shall the HarfBuzz client modify memory
+ * that is passed to HarfBuzz in a blob. If there is
+ * any such possibility, MODE_DUPLICATE should be used
+ * such that HarfBuzz makes a copy immediately,
+ *
+ * - Use MODE_READONLY otherwise, unless you really really
+ * really know what you are doing,
+ *
+ * - MODE_WRITABLE is appropriate if you really made a
+ * copy of data solely for the purpose of passing to
+ * HarfBuzz and doing that just once (no reuse!),
+ *
+ * - If the font is mmap()ed, it's ok to use
+ * READONLY_MAY_MAKE_WRITABLE, however, using that mode
+ * correctly is very tricky. Use MODE_READONLY instead.
+ */
+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;
+
+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_from_file (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 (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/thirdparty/harfbuzz/src/hb-blob.hh b/thirdparty/harfbuzz/src/hb-blob.hh
new file mode 100644
index 0000000000..d85bd823b0
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-blob.hh
@@ -0,0 +1,97 @@
+/*
+ * 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
+{
+ void fini_shallow () { 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;
+ unsigned int length;
+ hb_memory_mode_t mode;
+
+ void *user_data;
+ hb_destroy_func_t destroy;
+};
+
+
+/*
+ * 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 ()); b = nullptr; }
+
+ hb_nonnull_ptr_t<hb_blob_t> b;
+};
+
+
+#endif /* HB_BLOB_HH */
diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh
new file mode 100644
index 0000000000..1f9e2e91db
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh
@@ -0,0 +1,643 @@
+
+#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, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u,
+ 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u,
+ 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u,
+ 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u,
+ 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u,
+ 65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0
+};
+
+static const char _deserialize_json_key_spans[] = {
+ 0, 115, 26, 7, 2, 1, 50, 49,
+ 10, 117, 117, 117, 1, 50, 49, 10,
+ 117, 117, 1, 1, 50, 49, 117, 117,
+ 2, 1, 50, 49, 10, 117, 117, 1,
+ 50, 49, 10, 117, 117, 1, 50, 49,
+ 58, 89, 117, 117, 85, 115, 0
+};
+
+static const short _deserialize_json_index_offsets[] = {
+ 0, 0, 116, 143, 151, 154, 156, 207,
+ 257, 268, 386, 504, 622, 624, 675, 725,
+ 736, 854, 972, 974, 976, 1027, 1077, 1195,
+ 1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666,
+ 1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069,
+ 2119, 2178, 2268, 2386, 2504, 2590, 2706
+};
+
+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, 1, 8, 1,
+ 9, 10, 1, 11, 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, 12, 1,
+ 12, 12, 12, 12, 12, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 12,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 13, 1, 1, 14,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 1, 16, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 1, 18, 18, 18,
+ 18, 18, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 18, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 19, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 20, 1, 21, 21, 21, 21, 21,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 21, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 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, 18, 18, 18, 18, 18, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 18, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 19, 1, 1, 1,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 20, 1, 23,
+ 1, 23, 23, 23, 23, 23, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 23, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 24, 1, 24, 24, 24, 24,
+ 24, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 24, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 25, 1, 1, 26, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 1, 28, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29,
+ 1, 30, 30, 30, 30, 30, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 30, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 31, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 32, 1, 30,
+ 30, 30, 30, 30, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 30, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 31, 1, 1, 1, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 32, 1, 33, 1, 34,
+ 1, 34, 34, 34, 34, 34, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 34, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 35, 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, 1,
+ 1, 1, 1, 36, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 1, 38, 38,
+ 38, 38, 38, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 38, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 39, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 40, 1, 38, 38, 38, 38,
+ 38, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 38, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 39,
+ 1, 1, 1, 41, 41, 41, 41, 41,
+ 41, 41, 41, 41, 41, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 40, 1, 42, 43, 1, 44, 1, 44,
+ 44, 44, 44, 44, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 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,
+ 45, 1, 45, 45, 45, 45, 45, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 45, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 46, 1,
+ 1, 47, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 1, 49, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 1, 51,
+ 51, 51, 51, 51, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 51, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 52, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 53, 1, 51, 51, 51,
+ 51, 51, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 51, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 52, 1, 1, 1, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 53, 1, 54, 1, 54, 54, 54,
+ 54, 54, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 54, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 55, 1,
+ 55, 55, 55, 55, 55, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 55,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 56, 1, 1, 57,
+ 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 1, 59, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 1, 61, 61, 61,
+ 61, 61, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 61, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 62, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 63, 1, 61, 61, 61, 61, 61,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 61, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 62, 1,
+ 1, 1, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 63,
+ 1, 64, 1, 64, 64, 64, 64, 64,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 64, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 65, 1, 65, 65,
+ 65, 65, 65, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 65, 1, 66,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 67, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 1,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 1, 1, 1, 1, 1, 1,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 1, 70, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 71, 71,
+ 1, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 1, 1, 1, 1, 1,
+ 1, 1, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 1, 1, 1, 1,
+ 71, 1, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 1, 72, 72, 72,
+ 72, 72, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 72, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 73, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 74, 1, 72, 72, 72, 72, 72,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 72, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 73, 1,
+ 1, 1, 75, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 74,
+ 1, 76, 76, 76, 76, 76, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 76, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 77, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 78, 1, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 0
+};
+
+static const char _deserialize_json_trans_targs[] = {
+ 1, 0, 2, 2, 3, 4, 18, 24,
+ 37, 5, 12, 6, 7, 8, 9, 11,
+ 9, 11, 10, 2, 44, 10, 44, 13,
+ 14, 15, 16, 17, 16, 17, 10, 2,
+ 44, 19, 20, 21, 22, 23, 10, 2,
+ 44, 23, 25, 31, 26, 27, 28, 29,
+ 30, 29, 30, 10, 2, 44, 32, 33,
+ 34, 35, 36, 35, 36, 10, 2, 44,
+ 38, 39, 40, 42, 43, 41, 10, 41,
+ 10, 2, 44, 43, 44, 45, 46
+};
+
+static const char _deserialize_json_trans_actions[] = {
+ 0, 0, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 2, 2,
+ 0, 0, 3, 3, 4, 0, 5, 0,
+ 0, 2, 2, 2, 0, 0, 6, 6,
+ 7, 0, 0, 0, 2, 2, 8, 8,
+ 9, 0, 0, 0, 0, 0, 2, 2,
+ 2, 0, 0, 10, 10, 11, 0, 0,
+ 2, 2, 2, 0, 0, 12, 12, 13,
+ 0, 0, 0, 2, 2, 2, 14, 0,
+ 15, 15, 16, 0, 0, 0, 0
+};
+
+static const int deserialize_json_start = 1;
+static const int deserialize_json_first_final = 44;
+static const int deserialize_json_error = 0;
+
+static const int deserialize_json_en_main = 1;
+
+
+#line 97 "hb-buffer-deserialize-json.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_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 466 "hb-buffer-deserialize-json.hh"
+ {
+ cs = deserialize_json_start;
+ }
+
+#line 471 "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"
+ {
+ memset (&info, 0, sizeof (info));
+ 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 14:
+#line 55 "hb-buffer-deserialize-json.rl"
+ {
+ if (!hb_font_glyph_from_string (font,
+ tok, p - tok,
+ &info.codepoint))
+ return false;
+}
+ break;
+ case 15:
+#line 62 "hb-buffer-deserialize-json.rl"
+ { if (!parse_uint (tok, p, &info.codepoint)) return false; }
+ break;
+ case 8:
+#line 63 "hb-buffer-deserialize-json.rl"
+ { if (!parse_uint (tok, p, &info.cluster )) return false; }
+ break;
+ case 10:
+#line 64 "hb-buffer-deserialize-json.rl"
+ { if (!parse_int (tok, p, &pos.x_offset )) return false; }
+ break;
+ case 12:
+#line 65 "hb-buffer-deserialize-json.rl"
+ { if (!parse_int (tok, p, &pos.y_offset )) return false; }
+ break;
+ case 3:
+#line 66 "hb-buffer-deserialize-json.rl"
+ { if (!parse_int (tok, p, &pos.x_advance)) return false; }
+ break;
+ case 6:
+#line 67 "hb-buffer-deserialize-json.rl"
+ { if (!parse_int (tok, p, &pos.y_advance)) return false; }
+ break;
+ case 16:
+#line 62 "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 63 "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 64 "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 65 "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 66 "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 67 "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;
+#line 624 "hb-buffer-deserialize-json.hh"
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ _out: {}
+ }
+
+#line 125 "hb-buffer-deserialize-json.rl"
+
+
+ *end_ptr = p;
+
+ return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */
diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh
new file mode 100644
index 0000000000..67f0a1252f
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text.hh
@@ -0,0 +1,571 @@
+
+#line 1 "hb-buffer-deserialize-text.rl"
+/*
+ * Copyright © 2013 Google, Inc.
+ *
+ * This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BUFFER_DESERIALIZE_TEXT_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_HH
+
+#include "hb.hh"
+
+
+#line 36 "hb-buffer-deserialize-text.hh"
+static const unsigned char _deserialize_text_trans_keys[] = {
+ 0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u,
+ 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u,
+ 9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u,
+ 9u, 124u, 9u, 124u, 9u, 124u, 0
+};
+
+static const char _deserialize_text_key_spans[] = {
+ 0, 114, 13, 10, 13, 10, 10, 13,
+ 10, 1, 13, 10, 14, 116, 116, 0,
+ 114, 116, 116, 116, 116, 116, 116, 116,
+ 116, 116, 116
+};
+
+static const short _deserialize_text_index_offsets[] = {
+ 0, 0, 115, 129, 140, 154, 165, 176,
+ 190, 201, 203, 217, 228, 243, 360, 477,
+ 478, 593, 710, 827, 944, 1061, 1178, 1295,
+ 1412, 1529, 1646
+};
+
+static const char _deserialize_text_indicies[] = {
+ 0, 0, 0, 0, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 1, 1, 1, 1, 1, 1,
+ 1, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 1, 1, 1, 1, 1,
+ 1, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 1, 5, 1, 1, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 1, 8, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 1, 10, 1, 1,
+ 11, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 1, 13, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 1, 15, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 1, 17, 1, 1, 18, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 1, 20,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 1, 22, 1, 23, 1, 1, 24,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 1, 26, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 1, 22, 1, 1,
+ 1, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 1, 28, 28, 28, 28,
+ 28, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 28, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 29, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 30, 1, 1, 31, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 32, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 33,
+ 1, 34, 34, 34, 34, 34, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 34, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 35, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 36, 1, 1, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 1, 1, 1, 1, 1, 1, 1, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 1, 1, 1, 1, 1, 1, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 1, 28, 28, 28, 28, 28, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 28, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 29, 1, 1, 1,
+ 1, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 1, 1, 1, 30, 1,
+ 1, 31, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 32, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 33, 1, 38,
+ 38, 38, 38, 38, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 38, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 39, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 40, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 41, 1, 42, 42, 42, 42,
+ 42, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 42, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 43, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 44,
+ 1, 42, 42, 42, 42, 42, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 42, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 43, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 44, 1, 38, 38,
+ 38, 38, 38, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 38, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 39, 1, 1, 1, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 40, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 41, 1, 45, 45, 45, 45, 45,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 45, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 46, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 47, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 48,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 49, 1,
+ 50, 50, 50, 50, 50, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 50,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 51, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 52, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 53, 1, 50, 50, 50,
+ 50, 50, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 50, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 51,
+ 1, 1, 1, 1, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 52, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 53, 1, 45, 45, 45, 45, 45, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 45, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 46, 1, 1, 1,
+ 1, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 1, 1, 1, 1, 1,
+ 1, 47, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 48, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 49, 1, 28,
+ 28, 28, 28, 28, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 28, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 29, 1, 55, 55, 1, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 1, 1, 1, 30, 1, 1, 31, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 1, 1, 32, 1, 55, 1, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 1, 33, 1, 0
+};
+
+static const char _deserialize_text_trans_targs[] = {
+ 1, 0, 13, 17, 26, 3, 18, 21,
+ 18, 21, 5, 19, 20, 19, 20, 22,
+ 25, 8, 9, 12, 9, 12, 10, 11,
+ 23, 24, 23, 24, 14, 2, 6, 7,
+ 15, 16, 14, 15, 16, 17, 14, 4,
+ 15, 16, 14, 15, 16, 14, 2, 7,
+ 15, 16, 14, 2, 15, 16, 25, 26
+};
+
+static const char _deserialize_text_trans_actions[] = {
+ 0, 0, 1, 1, 1, 2, 2, 2,
+ 0, 0, 2, 2, 2, 0, 0, 2,
+ 2, 2, 2, 2, 0, 0, 3, 2,
+ 2, 2, 0, 0, 4, 5, 5, 5,
+ 4, 4, 0, 0, 0, 0, 6, 7,
+ 6, 6, 8, 8, 8, 9, 10, 10,
+ 9, 9, 11, 12, 11, 11, 0, 0
+};
+
+static const char _deserialize_text_eof_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 0, 0,
+ 0, 4, 6, 8, 8, 6, 9, 11,
+ 11, 9, 4
+};
+
+static const int deserialize_text_start = 1;
+static const int deserialize_text_first_final = 13;
+static const int deserialize_text_error = 0;
+
+static const int deserialize_text_en_main = 1;
+
+
+#line 91 "hb-buffer-deserialize-text.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer,
+ const char *buf,
+ unsigned int buf_len,
+ const char **end_ptr,
+ hb_font_t *font)
+{
+ const char *p = buf, *pe = buf + buf_len;
+
+ /* Ensure we have positions. */
+ (void) hb_buffer_get_glyph_positions (buffer, nullptr);
+
+ while (p < pe && ISSPACE (*p))
+ p++;
+ if (p < pe && *p == (buffer->len ? '|' : '['))
+ {
+ *end_ptr = ++p;
+ }
+
+ const char *eof = pe, *tok = nullptr;
+ int cs;
+ hb_glyph_info_t info = {0};
+ hb_glyph_position_t pos = {0};
+
+#line 343 "hb-buffer-deserialize-text.hh"
+ {
+ cs = deserialize_text_start;
+ }
+
+#line 348 "hb-buffer-deserialize-text.hh"
+ {
+ int _slen;
+ int _trans;
+ const unsigned char *_keys;
+ const char *_inds;
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _keys = _deserialize_text_trans_keys + (cs<<1);
+ _inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs];
+
+ _slen = _deserialize_text_key_spans[cs];
+ _trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+ (*p) <= _keys[1] ?
+ (*p) - _keys[0] : _slen ];
+
+ cs = _deserialize_text_trans_targs[_trans];
+
+ if ( _deserialize_text_trans_actions[_trans] == 0 )
+ goto _again;
+
+ switch ( _deserialize_text_trans_actions[_trans] ) {
+ case 2:
+#line 51 "hb-buffer-deserialize-text.rl"
+ {
+ tok = p;
+}
+ break;
+ case 5:
+#line 55 "hb-buffer-deserialize-text.rl"
+ {
+ if (!hb_font_glyph_from_string (font,
+ tok, p - tok,
+ &info.codepoint))
+ return false;
+}
+ break;
+ case 10:
+#line 62 "hb-buffer-deserialize-text.rl"
+ { if (!parse_uint (tok, p, &info.cluster )) return false; }
+ break;
+ case 3:
+#line 63 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.x_offset )) return false; }
+ break;
+ case 12:
+#line 64 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.y_offset )) return false; }
+ break;
+ case 7:
+#line 65 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.x_advance)) return false; }
+ break;
+ case 1:
+#line 38 "hb-buffer-deserialize-text.rl"
+ {
+ memset (&info, 0, sizeof (info));
+ memset (&pos , 0, sizeof (pos ));
+}
+#line 51 "hb-buffer-deserialize-text.rl"
+ {
+ tok = p;
+}
+ break;
+ case 4:
+#line 55 "hb-buffer-deserialize-text.rl"
+ {
+ if (!hb_font_glyph_from_string (font,
+ tok, p - tok,
+ &info.codepoint))
+ return false;
+}
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 9:
+#line 62 "hb-buffer-deserialize-text.rl"
+ { if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 11:
+#line 64 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 6:
+#line 65 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 8:
+#line 66 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+#line 480 "hb-buffer-deserialize-text.hh"
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ switch ( _deserialize_text_eof_actions[cs] ) {
+ case 4:
+#line 55 "hb-buffer-deserialize-text.rl"
+ {
+ if (!hb_font_glyph_from_string (font,
+ tok, p - tok,
+ &info.codepoint))
+ return false;
+}
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 9:
+#line 62 "hb-buffer-deserialize-text.rl"
+ { if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 11:
+#line 64 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 6:
+#line 65 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+ case 8:
+#line 66 "hb-buffer-deserialize-text.rl"
+ { if (!parse_int (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+ {
+ buffer->add_info (info);
+ if (unlikely (!buffer->successful))
+ return false;
+ buffer->pos[buffer->len - 1] = pos;
+ *end_ptr = p;
+}
+ break;
+#line 557 "hb-buffer-deserialize-text.hh"
+ }
+ }
+
+ _out: {}
+ }
+
+#line 119 "hb-buffer-deserialize-text.rl"
+
+
+ *end_ptr = p;
+
+ return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */
diff --git a/thirdparty/harfbuzz/src/hb-buffer-serialize.cc b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc
new file mode 100644
index 0000000000..bc6c978b38
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc
@@ -0,0 +1,474 @@
+/*
+ * 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 *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 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 serialize_formats[0];
+ case HB_BUFFER_SERIALIZE_FORMAT_JSON: return 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++ = ',';
+
+ *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 (*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++ = '}';
+
+ unsigned int l = p - b;
+ if (buf_size > l)
+ {
+ 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_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++ = '|';
+
+ if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
+ {
+ 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));
+ }
+
+ unsigned int l = p - b;
+ if (buf_size > l)
+ {
+ 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;
+}
+
+/**
+ * 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) (allow-none): if not %NULL, will be set to the number of byes written into @buf.
+ * @font: (allow-none): the #hb_font_t used to shape this buffer, needed to
+ * read glyph names and extents. If %NULL, and empty font will be used.
+ * @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
+ * `&lt;x_bearing,y_bearing,width,height&gt;`
+ *
+ * ## json
+ * TODO.
+ *
+ * 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)
+{
+ assert (start <= end && end <= buffer->len);
+
+ unsigned int sconsumed;
+ if (!buf_consumed)
+ buf_consumed = &sconsumed;
+ *buf_consumed = 0;
+ if (buf_size)
+ *buf = '\0';
+
+ assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
+ (buffer->content_type == HB_BUFFER_CONTENT_TYPE_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;
+
+ }
+}
+
+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;
+}
+
+#include "hb-buffer-deserialize-json.hh"
+#include "hb-buffer-deserialize-text.hh"
+
+/**
+ * hb_buffer_deserialize_glyphs:
+ * @buffer: an #hb_buffer_t buffer.
+ * @buf: (array length=buf_len):
+ * @buf_len:
+ * @end_ptr: (out):
+ * @font:
+ * @format:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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;
+
+ assert ((!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)) ||
+ (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS));
+
+ 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_glyphs_text (buffer,
+ buf, buf_len, end_ptr,
+ font);
+
+ case HB_BUFFER_SERIALIZE_FORMAT_JSON:
+ return _hb_buffer_deserialize_glyphs_json (buffer,
+ buf, buf_len, end_ptr,
+ font);
+
+ default:
+ case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
+ return false;
+
+ }
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-buffer.cc b/thirdparty/harfbuzz/src/hb-buffer.cc
new file mode 100644
index 0000000000..4fadbb78d2
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-buffer.cc
@@ -0,0 +1,2004 @@
+/*
+ * 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 dual role in HarfBuzz; they hold the input characters that are
+ * passed to hb_shape(), and after shaping they hold the output glyphs.
+ **/
+
+
+/**
+ * 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 ^
+ (unsigned int) p->script ^
+ (intptr_t) (p->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, swap_buffers() is 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. swap_buffers() then
+ * switches info and out_info.
+ */
+
+
+
+/* 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;
+
+ static_assert ((sizeof (info[0]) == sizeof (pos[0])), "");
+ if (unlikely (hb_unsigned_mul_overflows (new_allocated, sizeof (info[0]))))
+ goto done;
+
+ new_pos = (hb_glyph_position_t *) realloc (pos, new_allocated * sizeof (pos[0]));
+ new_info = (hb_glyph_info_t *) realloc (info, new_allocated * sizeof (info[0]));
+
+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;
+ 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... */
+ 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::reset ()
+{
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+
+ hb_unicode_funcs_destroy (unicode);
+ unicode = hb_unicode_funcs_reference (hb_unicode_funcs_get_default ());
+ flags = HB_BUFFER_FLAG_DEFAULT;
+ replacement = HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT;
+ invisible = 0;
+
+ clear ();
+}
+
+void
+hb_buffer_t::clear ()
+{
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+
+ hb_segment_properties_t default_props = HB_SEGMENT_PROPERTIES_DEFAULT;
+ props = default_props;
+ scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
+
+ content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
+ successful = true;
+ have_output = false;
+ have_positions = false;
+
+ idx = 0;
+ len = 0;
+ out_len = 0;
+ out_info = info;
+
+ serial = 0;
+
+ memset (context, 0, sizeof context);
+ memset (context_len, 0, sizeof context_len);
+
+ deallocate_var_all ();
+}
+
+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];
+
+ 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::remove_output ()
+{
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+
+ have_output = false;
+ have_positions = false;
+
+ out_len = 0;
+ out_info = info;
+}
+
+void
+hb_buffer_t::clear_output ()
+{
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+
+ have_output = true;
+ have_positions = false;
+
+ out_len = 0;
+ out_info = info;
+}
+
+void
+hb_buffer_t::clear_positions ()
+{
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+
+ have_output = false;
+ have_positions = true;
+
+ out_len = 0;
+ out_info = info;
+
+ hb_memset (pos, 0, sizeof (pos[0]) * len);
+}
+
+void
+hb_buffer_t::swap_buffers ()
+{
+ if (unlikely (!successful)) return;
+
+ assert (have_output);
+ have_output = false;
+
+ if (out_info != info)
+ {
+ hb_glyph_info_t *tmp_string;
+ tmp_string = info;
+ info = out_info;
+ out_info = tmp_string;
+ pos = (hb_glyph_position_t *) out_info;
+ }
+
+ unsigned int tmp;
+ tmp = len;
+ len = out_len;
+ out_len = tmp;
+
+ idx = 0;
+}
+
+
+void
+hb_buffer_t::replace_glyphs (unsigned int num_in,
+ unsigned int num_out,
+ const uint32_t *glyph_data)
+{
+ if (unlikely (!make_room_for (num_in, num_out))) return;
+
+ assert (idx + num_in <= len);
+
+ merge_clusters (idx, idx + num_in);
+
+ hb_glyph_info_t orig_info = info[idx];
+ hb_glyph_info_t *pinfo = &out_info[out_len];
+ for (unsigned int i = 0; i < num_out; i++)
+ {
+ *pinfo = orig_info;
+ pinfo->codepoint = glyph_data[i];
+ pinfo++;
+ }
+
+ idx += num_in;
+ out_len += num_out;
+}
+
+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, instead of the 0 below.
+ * But that would leave empty slots in the buffer in case of allocation
+ * failures. Setting to zero for now to avoid other problems (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 + 0))) 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)
+{
+ hb_mask_t not_mask = ~mask;
+ value &= mask;
+
+ if (!mask)
+ return;
+
+ 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::reverse_range (unsigned int start,
+ unsigned int end)
+{
+ if (end - start < 2)
+ return;
+
+ 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
+hb_buffer_t::reverse ()
+{
+ if (unlikely (!len))
+ return;
+
+ reverse_range (0, len);
+}
+
+void
+hb_buffer_t::reverse_clusters ()
+{
+ unsigned int i, start, count, last_cluster;
+
+ if (unlikely (!len))
+ return;
+
+ reverse ();
+
+ count = len;
+ start = 0;
+ last_cluster = info[0].cluster;
+ for (i = 1; i < count; i++) {
+ if (last_cluster != info[i].cluster) {
+ reverse_range (start, i);
+ start = i;
+ last_cluster = info[i].cluster;
+ }
+ }
+ reverse_range (start, i);
+}
+
+void
+hb_buffer_t::merge_clusters_impl (unsigned int start,
+ unsigned int end)
+{
+ 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 */
+ while (end < len && info[end - 1].cluster == info[end].cluster)
+ end++;
+
+ /* Extend start */
+ while (idx < start && info[start - 1].cluster == info[start].cluster)
+ start--;
+
+ /* If we hit the start of buffer, continue in out-buffer. */
+ if (idx == start)
+ 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)
+ {
+ /* 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::unsafe_to_break_impl (unsigned int start, unsigned int end)
+{
+ unsigned int cluster = UINT_MAX;
+ cluster = _unsafe_to_break_find_min_cluster (info, start, end, cluster);
+ _unsafe_to_break_set_mask (info, start, end, cluster);
+}
+void
+hb_buffer_t::unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end)
+{
+ if (!have_output)
+ {
+ unsafe_to_break_impl (start, end);
+ return;
+ }
+
+ assert (start <= out_len);
+ assert (idx <= end);
+
+ unsigned int cluster = UINT_MAX;
+ cluster = _unsafe_to_break_find_min_cluster (out_info, start, out_len, cluster);
+ cluster = _unsafe_to_break_find_min_cluster (info, idx, end, cluster);
+ _unsafe_to_break_set_mask (out_info, start, out_len, cluster);
+ _unsafe_to_break_set_mask (info, idx, end, cluster);
+}
+
+void
+hb_buffer_t::guess_segment_properties ()
+{
+ assert ((content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) ||
+ (!len && (content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+
+ /* 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 */
+ HB_BUFFER_SCRATCH_FLAG_DEFAULT,
+ HB_BUFFER_MAX_LEN_DEFAULT,
+ HB_BUFFER_MAX_OPS_DEFAULT,
+
+ HB_BUFFER_CONTENT_TYPE_INVALID,
+ HB_SEGMENT_PROPERTIES_DEFAULT,
+ false, /* successful */
+ true, /* have_output */
+ true /* have_positions */
+
+ /* Zero is good enough for everything else. */
+};
+
+
+/**
+ * hb_buffer_create: (Xconstructor)
+ *
+ * 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_get_empty:
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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);
+
+ free (buffer->info);
+ free (buffer->pos);
+#ifndef HB_NO_BUFFER_MESSAGE
+ if (buffer->message_destroy)
+ buffer->message_destroy (buffer->message_data);
+#endif
+
+ free (buffer);
+}
+
+/**
+ * hb_buffer_set_user_data: (skip)
+ * @buffer: an #hb_buffer_t.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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:
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 0.9.2
+ **/
+void *
+hb_buffer_get_user_data (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 glyphs (the result of shaping).
+ *
+ * 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.
+ *
+ * see hb_buffer_set_content_type().
+ *
+ * Return value:
+ * The type of @buffer contents.
+ *
+ * Since: 0.9.5
+ **/
+hb_buffer_content_type_t
+hb_buffer_get_content_type (hb_buffer_t *buffer)
+{
+ return buffer->content_type;
+}
+
+
+/**
+ * hb_buffer_set_unicode_funcs:
+ * @buffer: an #hb_buffer_t.
+ * @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.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 0.9.2
+ **/
+hb_unicode_funcs_t *
+hb_buffer_get_unicode_funcs (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 (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.
+ *
+ * See hb_buffer_set_script().
+ *
+ * Return value:
+ * The #hb_script_t of the @buffer.
+ *
+ * Since: 0.9.2
+ **/
+hb_script_t
+hb_buffer_get_script (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 (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 (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.
+ *
+ * See hb_buffer_set_flags().
+ *
+ * Return value:
+ * The @buffer flags.
+ *
+ * Since: 0.9.7
+ **/
+hb_buffer_flags_t
+hb_buffer_get_flags (hb_buffer_t *buffer)
+{
+ return buffer->flags;
+}
+
+/**
+ * hb_buffer_set_cluster_level:
+ * @buffer: an #hb_buffer_t.
+ * @cluster_level:
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 0.9.42
+ **/
+hb_buffer_cluster_level_t
+hb_buffer_get_cluster_level (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.
+ *
+ * See hb_buffer_set_replacement_codepoint().
+ *
+ * Return value:
+ * The @buffer replacement #hb_codepoint_t.
+ *
+ * Since: 0.9.31
+ **/
+hb_codepoint_t
+hb_buffer_get_replacement_codepoint (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 (hb_buffer_t *buffer)
+{
+ return buffer->invisible;
+}
+
+
+/**
+ * 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)
+{
+ buffer->reset ();
+}
+
+/**
+ * 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)
+{
+ 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 (!buffer->ensure (length))
+ return false;
+
+ /* Wipe the new space */
+ if (length > buffer->len) {
+ memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
+ if (buffer->have_positions)
+ 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 (hb_buffer_t *buffer)
+{
+ return buffer->len;
+}
+
+/**
+ * hb_buffer_get_glyph_infos:
+ * @buffer: an #hb_buffer_t.
+ * @length: (out): 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): output length.
+ *
+ * Returns @buffer glyph position array. Returned pointer
+ * is valid as long as @buffer contents are not modified.
+ *
+ * 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 (!buffer->have_positions)
+ buffer->clear_positions ();
+
+ if (length)
+ *length = buffer->len;
+
+ return (hb_glyph_position_t *) buffer->pos;
+}
+
+/**
+ * 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 to 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;
+
+ assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE) ||
+ (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+
+ 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;
+
+ buffer->ensure (buffer->len + item_length * sizeof (T) / 4);
+
+ /* 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 code points.
+ *
+ * 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,
+ 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)
+ buffer->content_type = source->content_type;
+ if (!buffer->have_positions && source->have_positions)
+ buffer->clear_positions ();
+
+ if (buffer->len + (end - start) < buffer->len) /* Overflows. */
+ {
+ buffer->successful = false;
+ return;
+ }
+
+ unsigned int orig_len = buffer->len;
+ hb_buffer_set_length (buffer, buffer->len + (end - start));
+ if (unlikely (!buffer->successful))
+ return;
+
+ memcpy (buffer->info + orig_len, source->info + start, (end - start) * sizeof (buffer->info[0]));
+ if (buffer->have_positions)
+ memcpy (buffer->pos + orig_len, source->pos + start, (end - start) * sizeof (buffer->pos[0]));
+}
+
+
+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);
+ assert ((buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS) ||
+ (!buffer->len && (buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID)));
+
+ 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_codepont_t) -1.
+ * @position_fuzz: allowed absolute difference in position values.
+ *
+ * If dottedcircle_glyph is (hb_codepoint_t) -1 then %HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+ * and %HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT are never returned. This should be used by most
+ * 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):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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 (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)
+{
+ char buf[100];
+ vsnprintf (buf, sizeof (buf), fmt, ap);
+ return (bool) this->message_func (this, font, buf, this->message_data);
+}
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-buffer.h b/thirdparty/harfbuzz/src/hb-buffer.h
new file mode 100644
index 0000000000..2f581f3c73
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-buffer.h
@@ -0,0 +1,586 @@
+/*
+ * 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
+ */
+
+#ifndef HB_H_IN
+#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's safe to break the glyph-run at the
+ * beginning of this cluster, and the two sides
+ * 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, or limiting
+ * the reshaping to a small piece around the
+ * breaking point only.
+ * @HB_GLYPH_FLAG_DEFINED: All the currently defined flags.
+ *
+ * Since: 1.5.0
+ */
+typedef enum { /*< flags >*/
+ HB_GLYPH_FLAG_UNSAFE_TO_BREAK = 0x00000001,
+
+ HB_GLYPH_FLAG_DEFINED = 0x00000001 /* 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;
+
+#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_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_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 (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).
+ */
+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 (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 (hb_buffer_t *buffer);
+
+HB_EXTERN void
+hb_buffer_set_direction (hb_buffer_t *buffer,
+ hb_direction_t direction);
+
+HB_EXTERN hb_direction_t
+hb_buffer_get_direction (hb_buffer_t *buffer);
+
+HB_EXTERN void
+hb_buffer_set_script (hb_buffer_t *buffer,
+ hb_script_t script);
+
+HB_EXTERN hb_script_t
+hb_buffer_get_script (hb_buffer_t *buffer);
+
+HB_EXTERN void
+hb_buffer_set_language (hb_buffer_t *buffer,
+ hb_language_t language);
+
+
+HB_EXTERN hb_language_t
+hb_buffer_get_language (hb_buffer_t *buffer);
+
+HB_EXTERN void
+hb_buffer_set_segment_properties (hb_buffer_t *buffer,
+ const hb_segment_properties_t *props);
+
+HB_EXTERN void
+hb_buffer_get_segment_properties (hb_buffer_t *buffer,
+ hb_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
+ *
+ * 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_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 (hb_buffer_t *buffer);
+
+/**
+ * hb_buffer_cluster_level_t:
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES: Return cluster values grouped by graphemes into
+ * monotone order.
+ * @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS: Return cluster values grouped into monotone order.
+ * @HB_BUFFER_CLUSTER_LEVEL_CHARACTERS: Don't group cluster values.
+ * @HB_BUFFER_CLUSTER_LEVEL_DEFAULT: Default cluster level,
+ * equal to @HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES.
+ *
+ * Since: 0.9.42
+ */
+typedef enum {
+ 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 (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 (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 (hb_buffer_t *buffer);
+
+
+HB_EXTERN void
+hb_buffer_reset (hb_buffer_t *buffer);
+
+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,
+ 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 (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 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
+ *
+ * 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_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 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);
+
+
+/*
+ * Compare buffers
+ */
+
+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);
+
+
+/*
+ * Debugging.
+ */
+
+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/thirdparty/harfbuzz/src/hb-buffer.hh b/thirdparty/harfbuzz/src/hb-buffer.hh
new file mode 100644
index 0000000000..3420ba434a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-buffer.hh
@@ -0,0 +1,451 @@
+/*
+ * 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"
+
+
+#ifndef HB_BUFFER_MAX_LEN_FACTOR
+#define HB_BUFFER_MAX_LEN_FACTOR 32
+#endif
+#ifndef HB_BUFFER_MAX_LEN_MIN
+#define HB_BUFFER_MAX_LEN_MIN 8192
+#endif
+#ifndef HB_BUFFER_MAX_LEN_DEFAULT
+#define HB_BUFFER_MAX_LEN_DEFAULT 0x3FFFFFFF /* Shaping more than a billion chars? Let us know! */
+#endif
+
+#ifndef HB_BUFFER_MAX_OPS_FACTOR
+#define HB_BUFFER_MAX_OPS_FACTOR 64
+#endif
+#ifndef HB_BUFFER_MAX_OPS_MIN
+#define HB_BUFFER_MAX_OPS_MIN 1024
+#endif
+#ifndef HB_BUFFER_MAX_OPS_DEFAULT
+#define HB_BUFFER_MAX_OPS_DEFAULT 0x1FFFFFFF /* Shaping more than a billion operations? Let us know! */
+#endif
+
+static_assert ((sizeof (hb_glyph_info_t) == 20), "");
+static_assert ((sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)), "");
+
+HB_MARK_AS_FLAG_T (hb_buffer_flags_t);
+HB_MARK_AS_FLAG_T (hb_buffer_serialize_flags_t);
+HB_MARK_AS_FLAG_T (hb_buffer_diff_flags_t);
+
+enum hb_buffer_scratch_flags_t {
+ HB_BUFFER_SCRATCH_FLAG_DEFAULT = 0x00000000u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII = 0x00000001u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES = 0x00000002u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK = 0x00000004u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_GPOS_ATTACHMENT = 0x00000008u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK = 0x00000010u,
+ HB_BUFFER_SCRATCH_FLAG_HAS_CGJ = 0x00000020u,
+
+ /* Reserved for complex shapers' internal use. */
+ HB_BUFFER_SCRATCH_FLAG_COMPLEX0 = 0x01000000u,
+ HB_BUFFER_SCRATCH_FLAG_COMPLEX1 = 0x02000000u,
+ HB_BUFFER_SCRATCH_FLAG_COMPLEX2 = 0x04000000u,
+ HB_BUFFER_SCRATCH_FLAG_COMPLEX3 = 0x08000000u,
+};
+HB_MARK_AS_FLAG_T (hb_buffer_scratch_flags_t);
+
+
+/*
+ * hb_buffer_t
+ */
+
+struct hb_buffer_t
+{
+ hb_object_header_t header;
+
+ /* 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_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
+ unsigned int max_len; /* Maximum allowed len. */
+ int max_ops; /* Maximum allowed operations. */
+
+ /* Buffer contents */
+ hb_buffer_content_type_t content_type;
+ hb_segment_properties_t props; /* Script, language, direction */
+
+ bool successful; /* Allocations successful */
+ bool have_output; /* Whether we have an output buffer going on */
+ bool have_positions; /* Whether we have positions */
+
+ unsigned int idx; /* Cursor into ->info and ->pos arrays */
+ unsigned int len; /* Length of ->info and ->pos arrays */
+ unsigned int out_len; /* Length of ->out array if have_output */
+
+ unsigned int allocated; /* Length of allocated arrays */
+ hb_glyph_info_t *info;
+ hb_glyph_info_t *out_info;
+ hb_glyph_position_t *pos;
+
+ unsigned int serial;
+
+ /* Text before / after the main buffer contents.
+ * Always in Unicode, and ordered outward.
+ * Index 0 is for "pre-context", 1 for "post-context". */
+ static constexpr unsigned CONTEXT_LENGTH = 5u;
+ hb_codepoint_t context[2][CONTEXT_LENGTH];
+ unsigned int context_len[2];
+
+ /* Debugging API */
+#ifndef HB_NO_BUFFER_MESSAGE
+ hb_buffer_message_func_t message_func;
+ void *message_data;
+ hb_destroy_func_t message_destroy;
+#endif
+
+ /* Internal debugging. */
+ /* The bits here reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
+#ifndef HB_NDEBUG
+ uint8_t allocated_var_bits;
+#endif
+
+
+ /* Methods */
+
+ bool in_error () const { return !successful; }
+
+ void allocate_var (unsigned int start, unsigned int count)
+ {
+#ifndef HB_NDEBUG
+ 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;
+#endif
+ }
+ void deallocate_var (unsigned int start, unsigned int count)
+ {
+#ifndef HB_NDEBUG
+ 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;
+#endif
+ }
+ void assert_var (unsigned int start, unsigned int count)
+ {
+#ifndef HB_NDEBUG
+ unsigned int end = start + count;
+ assert (end <= 8);
+ unsigned int bits = (1u<<end) - (1u<<start);
+ assert (bits == (allocated_var_bits & bits));
+#endif
+ }
+ void deallocate_var_all ()
+ {
+#ifndef HB_NDEBUG
+ allocated_var_bits = 0;
+#endif
+ }
+
+ 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]; }
+
+ bool has_separate_output () const { return info != out_info; }
+
+
+ HB_INTERNAL void reset ();
+ HB_INTERNAL void clear ();
+
+ unsigned int backtrack_len () const { return have_output? out_len : idx; }
+ unsigned int lookahead_len () const { return len - idx; }
+ unsigned int next_serial () { return serial++; }
+
+ HB_INTERNAL void add (hb_codepoint_t codepoint,
+ unsigned int cluster);
+ HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info);
+
+ HB_INTERNAL void reverse_range (unsigned int start, unsigned int end);
+ HB_INTERNAL void reverse ();
+ HB_INTERNAL void reverse_clusters ();
+ HB_INTERNAL void guess_segment_properties ();
+
+ HB_INTERNAL void swap_buffers ();
+ HB_INTERNAL void remove_output ();
+ HB_INTERNAL void clear_output ();
+ HB_INTERNAL void clear_positions ();
+
+ HB_INTERNAL void replace_glyphs (unsigned int num_in,
+ unsigned int num_out,
+ const hb_codepoint_t *glyph_data);
+
+ void replace_glyph (hb_codepoint_t glyph_index)
+ {
+ if (unlikely (out_info != info || out_len != idx)) {
+ if (unlikely (!make_room_for (1, 1))) return;
+ out_info[out_len] = info[idx];
+ }
+ out_info[out_len].codepoint = glyph_index;
+
+ idx++;
+ out_len++;
+ }
+ /* Makes a copy of the glyph at idx to output and replace glyph_index */
+ hb_glyph_info_t & output_glyph (hb_codepoint_t glyph_index)
+ {
+ if (unlikely (!make_room_for (0, 1))) return Crap (hb_glyph_info_t);
+
+ if (unlikely (idx == len && !out_len))
+ return Crap (hb_glyph_info_t);
+
+ out_info[out_len] = idx < len ? info[idx] : out_info[out_len - 1];
+ out_info[out_len].codepoint = glyph_index;
+
+ out_len++;
+
+ return out_info[out_len - 1];
+ }
+ void output_info (const hb_glyph_info_t &glyph_info)
+ {
+ if (unlikely (!make_room_for (0, 1))) return;
+
+ out_info[out_len] = glyph_info;
+
+ out_len++;
+ }
+ /* Copies glyph at idx to output but doesn't advance idx */
+ void copy_glyph ()
+ {
+ if (unlikely (!make_room_for (0, 1))) return;
+
+ out_info[out_len] = info[idx];
+
+ out_len++;
+ }
+ /* Copies glyph at idx to output and advance idx.
+ * If there's no output, just advance idx. */
+ void
+ next_glyph ()
+ {
+ if (have_output)
+ {
+ if (out_info != info || out_len != idx)
+ {
+ if (unlikely (!make_room_for (1, 1))) return;
+ out_info[out_len] = info[idx];
+ }
+ out_len++;
+ }
+
+ idx++;
+ }
+ /* Copies n glyphs at idx to output and advance idx.
+ * If there's no output, just advance idx. */
+ void
+ next_glyphs (unsigned int n)
+ {
+ if (have_output)
+ {
+ if (out_info != info || out_len != idx)
+ {
+ if (unlikely (!make_room_for (n, n))) return;
+ memmove (out_info + out_len, info + idx, n * sizeof (out_info[0]));
+ }
+ out_len += n;
+ }
+
+ idx += n;
+ }
+ /* 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 ();
+
+ void unsafe_to_break (unsigned int start,
+ unsigned int end)
+ {
+ if (end - start < 2)
+ return;
+ unsafe_to_break_impl (start, end);
+ }
+ HB_INTERNAL void unsafe_to_break_impl (unsigned int start, unsigned int end);
+ HB_INTERNAL void unsafe_to_break_from_outbuffer (unsigned int start, unsigned int end);
+
+
+ /* Internal methods */
+ HB_INTERNAL bool move_to (unsigned int i); /* i is output-buffer index. */
+
+ HB_INTERNAL bool enlarge (unsigned int size);
+
+ bool ensure (unsigned int size)
+ { return likely (!size || size < allocated) ? true : enlarge (size); }
+
+ bool ensure_inplace (unsigned int size)
+ { return likely (!size || size < allocated); }
+
+ HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
+ 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 (!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)
+ {
+ if (mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+ inf.mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+ else
+ inf.mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+ }
+ inf.cluster = cluster;
+ }
+
+ unsigned int
+ _unsafe_to_break_find_min_cluster (const hb_glyph_info_t *infos,
+ unsigned int start, unsigned int end,
+ unsigned int cluster) const
+ {
+ for (unsigned int i = start; i < end; i++)
+ cluster = hb_min (cluster, infos[i].cluster);
+ return cluster;
+ }
+ void
+ _unsafe_to_break_set_mask (hb_glyph_info_t *infos,
+ unsigned int start, unsigned int end,
+ unsigned int cluster)
+ {
+ for (unsigned int i = start; i < end; i++)
+ if (cluster != infos[i].cluster)
+ {
+ scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK;
+ infos[i].mask |= HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+ }
+ }
+
+ void unsafe_to_break_all () { unsafe_to_break_impl (0, len); }
+ void safe_to_break_all ()
+ {
+ for (unsigned int i = 0; i < len; i++)
+ info[i].mask &= ~HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+ }
+};
+DECLARE_NULL_INSTANCE (hb_buffer_t);
+
+
+/* Loop over clusters. Duplicated in foreach_syllable(). */
+#define foreach_cluster(buffer, start, end) \
+ for (unsigned int \
+ _count = buffer->len, \
+ start = 0, end = _count ? _next_cluster (buffer, 0) : 0; \
+ start < _count; \
+ start = end, end = _next_cluster (buffer, start))
+
+static inline unsigned int
+_next_cluster (hb_buffer_t *buffer, unsigned int start)
+{
+ hb_glyph_info_t *info = buffer->info;
+ unsigned int count = buffer->len;
+
+ unsigned int cluster = info[start].cluster;
+ while (++start < count && cluster == info[start].cluster)
+ ;
+
+ return start;
+}
+
+
+#define HB_BUFFER_XALLOCATE_VAR(b, func, var) \
+ b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
+ sizeof (b->info[0].var))
+#define HB_BUFFER_ALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var ())
+#define HB_BUFFER_DEALLOCATE_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var ())
+#define HB_BUFFER_ASSERT_VAR(b, var) HB_BUFFER_XALLOCATE_VAR (b, assert_var, var ())
+
+
+#endif /* HB_BUFFER_HH */
diff --git a/thirdparty/harfbuzz/src/hb-cache.hh b/thirdparty/harfbuzz/src/hb-cache.hh
new file mode 100644
index 0000000000..bf26d96be4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-cache.hh
@@ -0,0 +1,80 @@
+/*
+ * 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 lock-free cache for int->int functions. */
+
+template <unsigned int key_bits, unsigned int value_bits, unsigned int cache_bits>
+struct hb_cache_t
+{
+ static_assert ((key_bits >= cache_bits), "");
+ static_assert ((key_bits + value_bits - cache_bits <= 8 * sizeof (hb_atomic_int_t)), "");
+ static_assert (sizeof (hb_atomic_int_t) == sizeof (unsigned int), "");
+
+ void init () { clear (); }
+ void fini () {}
+
+ void clear ()
+ {
+ for (unsigned i = 0; i < ARRAY_LENGTH (values); i++)
+ values[i].set_relaxed (-1);
+ }
+
+ bool get (unsigned int key, unsigned int *value) const
+ {
+ unsigned int k = key & ((1u<<cache_bits)-1);
+ unsigned int v = values[k].get_relaxed ();
+ if ((key_bits + value_bits - cache_bits == 8 * sizeof (hb_atomic_int_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].set_relaxed (v);
+ return true;
+ }
+
+ private:
+ hb_atomic_int_t values[1u<<cache_bits];
+};
+
+typedef hb_cache_t<21, 16, 8> hb_cmap_cache_t;
+typedef hb_cache_t<16, 24, 8> hb_advance_cache_t;
+
+
+#endif /* HB_CACHE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
new file mode 100644
index 0000000000..91a9b7d0d1
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh
@@ -0,0 +1,688 @@
+/*
+ * 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
+
+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 init () { set_real (0.0); }
+ void fini () {}
+
+ 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;
+};
+
+/* byte string */
+struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
+{
+ // 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));
+ }
+
+ 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.
+ * byte_str_t, a wrapper struct pairing a byte pointer along with its length, always
+ * checks the length before access. A Null pointer is used as the initial pointer
+ * along with zero length by the default ctor.
+ */
+ DEFINE_SIZE_MIN(0);
+};
+
+/* Holder of a section of byte string within a CFFIndex entry */
+struct byte_str_t : hb_ubytes_t
+{
+ byte_str_t ()
+ : hb_ubytes_t () {}
+ byte_str_t (const UnsizedByteStr& s, unsigned int l)
+ : hb_ubytes_t ((const unsigned char*)&s, l) {}
+ byte_str_t (const unsigned char *s, unsigned int l)
+ : hb_ubytes_t (s, l) {}
+ byte_str_t (const hb_ubytes_t &ub) /* conversion from hb_ubytes_t */
+ : hb_ubytes_t (ub) {}
+
+ /* sub-string */
+ byte_str_t sub_str (unsigned int offset, unsigned int len_) const
+ { return byte_str_t (hb_ubytes_t::sub_array (offset, len_)); }
+
+ bool check_limit (unsigned int offset, unsigned int count) const
+ { return (offset + count <= length); }
+};
+
+/* A byte string associated with the current offset and an error condition */
+struct byte_str_ref_t
+{
+ byte_str_ref_t () { init (); }
+
+ void init ()
+ {
+ str = byte_str_t ();
+ offset = 0;
+ error = false;
+ }
+
+ void fini () {}
+
+ byte_str_ref_t (const byte_str_t &str_, unsigned int offset_ = 0)
+ : str (str_), offset (offset_), error (false) {}
+
+ void reset (const byte_str_t &str_, unsigned int offset_ = 0)
+ {
+ str = str_;
+ offset = offset_;
+ error = false;
+ }
+
+ const unsigned char& operator [] (int i) {
+ if (unlikely ((unsigned int) (offset + i) >= str.length))
+ {
+ set_error ();
+ return Null (unsigned char);
+ }
+ return str[offset + i];
+ }
+
+ /* Conversion to byte_str_t */
+ operator byte_str_t () const { return str.sub_str (offset, str.length - offset); }
+
+ byte_str_t sub_str (unsigned int offset_, unsigned int len_) const
+ { return str.sub_str (offset_, len_); }
+
+ bool avail (unsigned int count=1) const
+ { return (!in_error () && str.check_limit (offset, count)); }
+ void inc (unsigned int count=1)
+ {
+ if (likely (!in_error () && (offset <= str.length) && (offset + count <= str.length)))
+ {
+ offset += count;
+ }
+ else
+ {
+ offset = str.length;
+ set_error ();
+ }
+ }
+
+ void set_error () { error = true; }
+ bool in_error () const { return error; }
+
+ byte_str_t str;
+ unsigned int offset; /* beginning of the sub-string within str */
+
+ protected:
+ bool error;
+};
+
+typedef hb_vector_t<byte_str_t> byte_str_array_t;
+
+/* stack */
+template <typename ELEM, int LIMIT>
+struct cff_stack_t
+{
+ void init ()
+ {
+ error = false;
+ count = 0;
+ elements.init ();
+ elements.resize (kSizeLimit);
+ for (unsigned int i = 0; i < elements.length; i++)
+ elements[i].init ();
+ }
+ void fini () { elements.fini_deep (); }
+
+ ELEM& operator [] (unsigned int i)
+ {
+ if (unlikely (i >= count)) set_error ();
+ return elements[i];
+ }
+
+ void push (const ELEM &v)
+ {
+ if (likely (count < elements.length))
+ elements[count++] = v;
+ else
+ set_error ();
+ }
+ ELEM &push ()
+ {
+ if (likely (count < elements.length))
+ 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 < elements.length))
+ count++;
+ else
+ set_error ();
+ }
+
+ void clear () { count = 0; }
+
+ bool in_error () const { return (error || elements.in_error ()); }
+ void set_error () { error = true; }
+
+ unsigned int get_count () const { return count; }
+ bool is_empty () const { return !count; }
+
+ static constexpr unsigned kSizeLimit = LIMIT;
+
+ protected:
+ bool error;
+ unsigned int count;
+ hb_vector_t<ELEM> elements;
+};
+
+/* 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;
+ }
+
+ hb_array_t<const ARG> get_subarray (unsigned int start) const
+ { return S::elements.sub_array (start); }
+
+ private:
+ typedef cff_stack_t<ARG, 513> S;
+};
+
+/* an operator prefixed by its operands in a byte string */
+struct op_str_t
+{
+ void init () {}
+ void fini () {}
+
+ op_code_t op;
+ byte_str_t str;
+};
+
+/* 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);
+
+ HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length);
+ if (unlikely (!d)) return_trace (false);
+ memcpy (d, &opstr.str[0], opstr.str.length);
+ return_trace (true);
+ }
+};
+
+template <typename VAL>
+struct parsed_values_t
+{
+ void init ()
+ {
+ opStart = 0;
+ values.init ();
+ }
+ void fini () { values.fini_deep (); }
+
+ void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t ())
+ {
+ VAL *val = values.push ();
+ val->op = op;
+ val->str = str_ref.str.sub_str (opStart, str_ref.offset - opStart);
+ opStart = str_ref.offset;
+ }
+
+ void add_op (op_code_t op, const byte_str_ref_t& str_ref, const VAL &v)
+ {
+ VAL *val = values.push (v);
+ val->op = op;
+ val->str = str_ref.sub_str ( opStart, str_ref.offset - opStart);
+ opStart = str_ref.offset;
+ }
+
+ bool has_op (op_code_t op) const
+ {
+ for (unsigned int i = 0; i < get_count (); i++)
+ if (get_value (i).op == op) return true;
+ return false;
+ }
+
+ unsigned get_count () const { return values.length; }
+ const VAL &get_value (unsigned int i) const { return values[i]; }
+ const VAL &operator [] (unsigned int i) const { return get_value (i); }
+
+ unsigned int opStart;
+ hb_vector_t<VAL> values;
+};
+
+template <typename ARG=number_t>
+struct interp_env_t
+{
+ void init (const byte_str_t &str_)
+ {
+ str_ref.reset (str_);
+ argStack.init ();
+ error = false;
+ }
+ void fini () { argStack.fini (); }
+
+ bool in_error () const
+ { return error || str_ref.in_error () || argStack.in_error (); }
+
+ void set_error () { error = true; }
+
+ op_code_t fetch_op ()
+ {
+ op_code_t op = OpCode_Invalid;
+ if (unlikely (!str_ref.avail ()))
+ return OpCode_Invalid;
+ op = (op_code_t)(unsigned char)str_ref[0];
+ if (op == OpCode_escape) {
+ if (unlikely (!str_ref.avail ()))
+ return OpCode_Invalid;
+ op = Make_OpCode_ESC(str_ref[1]);
+ str_ref.inc ();
+ }
+ 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;
+ protected:
+ bool error;
+};
+
+typedef interp_env_t<> num_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() { fini (); }
+
+ void fini () { env.fini (); }
+
+ ENV env;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_COMMON_HH */
diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
new file mode 100644
index 0000000000..52d778ffe2
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-cff-interp-cs-common.hh
@@ -0,0 +1,911 @@
+/*
+ * 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; }
+
+ byte_str_t operator [] (unsigned int index) const
+ {
+ if (unlikely (!subrs || index >= subrs->count))
+ return Null (byte_str_t);
+ else
+ return (*subrs)[index];
+ }
+
+ protected:
+ unsigned int bias;
+ const SUBRS *subrs;
+};
+
+struct point_t
+{
+ void init ()
+ {
+ x.init ();
+ y.init ();
+ }
+
+ 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>
+{
+ void init (const byte_str_t &str, const SUBRS *globalSubrs_, const SUBRS *localSubrs_)
+ {
+ interp_env_t<ARG>::init (str);
+
+ context.init (str, CSType_CharString);
+ seen_moveto = true;
+ seen_hintmask = false;
+ hstem_count = 0;
+ vstem_count = 0;
+ hintmask_size = 0;
+ pt.init ();
+ callStack.init ();
+ globalSubrs.init (globalSubrs_);
+ localSubrs.init (localSubrs_);
+ }
+ void fini ()
+ {
+ interp_env_t<ARG>::fini ();
+
+ callStack.fini ();
+ 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;
+ d.init ();
+ 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>
+{
+ bool interpret (PARAM& param)
+ {
+ SUPER::env.set_endchar (false);
+
+ for (;;) {
+ OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
+ if (unlikely (SUPER::env.in_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/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-dict-common.hh
new file mode 100644
index 0000000000..a520ca3bce
--- /dev/null
+++ b/thirdparty/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 () { single_val.set_int (0); }
+ void fini () {}
+
+ number_t single_val;
+};
+
+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>
+{
+ 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/thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh
new file mode 100644
index 0000000000..1c8762c172
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-cff1-interp-cs.hh
@@ -0,0 +1,161 @@
+/*
+ * 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>
+ void init (const byte_str_t &str, ACC &acc, unsigned int fd)
+ {
+ SUPER::init (str, acc.globalSubrs, acc.privateDicts[fd].localSubrs);
+ processed_width = false;
+ has_width = false;
+ arg_start = 0;
+ in_seac = false;
+ }
+
+ void fini () { SUPER::fini (); }
+
+ 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>
+struct 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/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh
new file mode 100644
index 0000000000..332ece31cd
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh
@@ -0,0 +1,272 @@
+/*
+ * 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 init ()
+ {
+ number_t::init ();
+ deltas.init ();
+ }
+
+ void fini ()
+ {
+ number_t::fini ();
+ deltas.fini_deep ();
+ }
+
+ 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_,
+ unsigned int numBlends, hb_array_t<const blend_arg_t> blends_)
+ {
+ numValues = numValues_;
+ valueIndex = valueIndex_;
+ deltas.resize (numBlends);
+ for (unsigned int i = 0; i < numBlends; i++)
+ deltas[i] = blends_[i];
+ }
+
+ bool blending () const { return deltas.length > 0; }
+ void reset_blends ()
+ {
+ numValues = valueIndex = 0;
+ deltas.resize (0);
+ }
+
+ unsigned int numValues;
+ unsigned int valueIndex;
+ hb_vector_t<number_t> deltas;
+};
+
+typedef interp_env_t<blend_arg_t> BlendInterpEnv;
+typedef biased_subrs_t<CFF2Subrs> cff2_biased_subrs_t;
+
+struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs>
+{
+ template <typename ACC>
+ void init (const byte_str_t &str, ACC &acc, unsigned int fd,
+ const int *coords_=nullptr, unsigned int num_coords_=0)
+ {
+ SUPER::init (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 blend_arg_t& eval_arg (unsigned int i)
+ {
+ blend_arg_t &arg = argStack[i];
+ blend_arg (arg);
+ return arg;
+ }
+
+ const blend_arg_t& pop_arg ()
+ {
+ blend_arg_t &arg = argStack.pop ();
+ blend_arg (arg);
+ return arg;
+ }
+
+ void process_blend ()
+ {
+ if (!seen_blend)
+ {
+ region_count = varStore->varStore.get_region_index_count (get_ivs ());
+ if (do_blend)
+ {
+ if (unlikely (!scalars.resize (region_count)))
+ set_error ();
+ else
+ varStore->varStore.get_scalars (get_ivs (), coords, num_coords,
+ &scalars[0], region_count);
+ }
+ seen_blend = true;
+ }
+ }
+
+ void process_vsindex ()
+ {
+ unsigned int index = argStack.pop_uint ();
+ if (unlikely (seen_vsindex () || seen_blend))
+ {
+ 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_; }
+
+ protected:
+ void blend_arg (blend_arg_t &arg)
+ {
+ if (do_blend && arg.blending ())
+ {
+ if (likely (scalars.length == arg.deltas.length))
+ {
+ double v = arg.to_real ();
+ for (unsigned int i = 0; i < scalars.length; i++)
+ {
+ v += (double)scalars[i] * arg.deltas[i].to_real ();
+ }
+ arg.set_real (v);
+ arg.deltas.resize (0);
+ }
+ }
+ }
+
+ 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<blend_arg_t, CFF2Subrs> SUPER;
+};
+template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t, PARAM>>
+struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH>
+{
+ static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param)
+ {
+ switch (op) {
+ case OpCode_callsubr:
+ case OpCode_callgsubr:
+ /* a subroutine number shoudln't be a blended value */
+ if (unlikely (env.argStack.peek ().blending ()))
+ {
+ env.set_error ();
+ break;
+ }
+ SUPER::process_op (op, env, param);
+ break;
+
+ case OpCode_blendcs:
+ OPSET::process_blend (env, param);
+ break;
+
+ case OpCode_vsindexcs:
+ if (unlikely (env.argStack.peek ().blending ()))
+ {
+ env.set_error ();
+ break;
+ }
+ OPSET::process_vsindex (env, param);
+ break;
+
+ default:
+ SUPER::process_op (op, env, param);
+ }
+ }
+
+ static void process_blend (cff2_cs_interp_env_t &env, PARAM& param)
+ {
+ unsigned int n, k;
+
+ env.process_blend ();
+ k = env.get_region_count ();
+ n = env.argStack.pop_uint ();
+ /* copy the blend values into blend array of the default values */
+ unsigned int start = env.argStack.get_count () - ((k+1) * n);
+ /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
+ if (unlikely (start > env.argStack.get_count ()))
+ {
+ env.set_error ();
+ return;
+ }
+ for (unsigned int i = 0; i < n; i++)
+ {
+ const hb_array_t<const blend_arg_t> blends = env.argStack.get_subarray (start + n + (i * k));
+ env.argStack[start + i].set_blends (n, i, k, blends);
+ }
+
+ /* pop off blend values leaving default values now adorned with blend values */
+ env.argStack.pop (k * n);
+ }
+
+ static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param)
+ {
+ env.process_vsindex ();
+ env.clear_args ();
+ }
+
+ private:
+ typedef cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH> SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct cff2_cs_interpreter_t : cs_interpreter_t<cff2_cs_interp_env_t, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF2_INTERP_CS_HH */
diff --git a/thirdparty/harfbuzz/src/hb-common.cc b/thirdparty/harfbuzz/src/hb-common.cc
new file mode 100644
index 0000000000..5acfa78431
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-common.cc
@@ -0,0 +1,1098 @@
+/*
+ * 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"
+
+#include <locale.h>
+
+#ifdef HB_NO_SETLOCALE
+#define setlocale(Category, Locale) "C"
+#endif
+
+/**
+ * SECTION:hb-common
+ * @title: hb-common
+ * @short_description: Common data types
+ * @include: hb.h
+ *
+ * Common data types used across HarfBuzz are defined here.
+ **/
+
+
+/* hb_options_t */
+
+hb_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.set_relaxed (u.i);
+}
+
+
+/* hb_tag_t */
+
+/**
+ * hb_tag_from_string:
+ * @str: (array length=len) (element-type uint8_t):
+ * @len:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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:
+ * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
+ *
+ *
+ *
+ * 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 */
+
+const char direction_strings[][4] = {
+ "ltr",
+ "rtl",
+ "ttb",
+ "btt"
+};
+
+/**
+ * hb_direction_from_string:
+ * @str: (array length=len) (element-type uint8_t):
+ * @len:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * 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) {
+ /* If a custom allocated is used calling strdup() pairs
+ badly with a call to the custom free() in fini() below.
+ Therefore don't call strdup(), implement its behavior.
+ */
+ size_t len = strlen(s) + 1;
+ lang = (hb_language_t) malloc(len);
+ if (likely (lang))
+ {
+ memcpy((unsigned char *) lang, s, len);
+ for (unsigned char *p = (unsigned char *) lang; *p; p++)
+ *p = canon_map[*p];
+ }
+
+ return *this;
+ }
+
+ void fini () { free ((void *) lang); }
+};
+
+
+/* Thread-safe lock-free language list */
+
+static hb_atomic_ptr_t <hb_language_item_t> langs;
+
+#if HB_USE_ATEXIT
+static 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 ();
+ free (first_lang);
+ first_lang = next;
+ }
+}
+#endif
+
+static hb_language_item_t *
+lang_find_or_insert (const char *key)
+{
+retry:
+ hb_language_item_t *first_lang = langs;
+
+ for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
+ if (*lang == key)
+ return lang;
+
+ /* Not found; allocate one. */
+ hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
+ if (unlikely (!lang))
+ return nullptr;
+ lang->next = first_lang;
+ *lang = key;
+ if (unlikely (!lang->lang))
+ {
+ free (lang);
+ return nullptr;
+ }
+
+ if (unlikely (!langs.cmpexch (first_lang, lang)))
+ {
+ lang->fini ();
+ free (lang);
+ goto retry;
+ }
+
+#if HB_USE_ATEXIT
+ if (!first_lang)
+ atexit (free_langs); /* First person registers atexit() callback. */
+#endif
+
+ 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);
+ 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: an #hb_language_t to convert.
+ *
+ * See hb_language_from_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:
+ *
+ * Get default language from current locale.
+ *
+ * Note that the first time this function is called, it calls
+ * "setlocale (LC_CTYPE, nullptr)" to fetch current locale. The underlying
+ * setlocale function is, in many implementations, NOT threadsafe. To avoid
+ * problems, call this function once before multiple threads can call it.
+ * This function is only used from hb_buffer_guess_segment_properties() by
+ * HarfBuzz itself.
+ *
+ * Return value: (transfer none):
+ *
+ * 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 (setlocale (LC_CTYPE, nullptr), -1);
+ (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
+ }
+
+ return language;
+}
+
+
+/* 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('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
+ 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.
+ *
+ * See hb_script_from_iso15924_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:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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:
+
+ 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:
+
+ 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:
+ * @minor:
+ * @micro:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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 && (ISALNUM(**pp) || **pp == '_'))
+ (*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)
+ 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);
+ 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:
+ *
+ * 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)
+ memset (variation, 0, sizeof (*variation));
+ return false;
+}
+
+/**
+ * hb_variation_to_string:
+ *
+ * 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++] = '=';
+ len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
+
+ assert (len < ARRAY_LENGTH (s));
+ len = hb_min (len, size - 1);
+ memcpy (buf, s, len);
+ buf[len] = '\0';
+}
+
+/**
+ * hb_color_get_alpha:
+ * color: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Alpha channel value of the given color
+ *
+ * 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: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Red channel value of the given color
+ *
+ * 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: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Green channel value of the given color
+ *
+ * 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: a #hb_color_t we are interested in its channels.
+ *
+ * Return value: Blue channel value of the given color
+ *
+ * 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/thirdparty/harfbuzz/src/hb-common.h b/thirdparty/harfbuzz/src/hb-common.h
new file mode 100644
index 0000000000..a97a5f5a04
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-common.h
@@ -0,0 +1,513 @@
+/*
+ * 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_H_IN
+#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
+
+
+typedef int hb_bool_t;
+
+typedef uint32_t hb_codepoint_t;
+typedef int32_t hb_position_t;
+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;
+
+
+/* hb_tag_t */
+
+typedef uint32_t hb_tag_t;
+
+#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)))
+#define HB_UNTAG(tag) (uint8_t)(((tag)>>24)&0xFF), (uint8_t)(((tag)>>16)&0xFF), (uint8_t)(((tag)>>8)&0xFF), (uint8_t)((tag)&0xFF)
+
+#define HB_TAG_NONE HB_TAG(0,0,0,0)
+#define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff)
+#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.
+ */
+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);
+
+#define HB_DIRECTION_IS_VALID(dir) ((((unsigned int) (dir)) & ~3U) == 4)
+/* Direction must be valid for the following */
+#define HB_DIRECTION_IS_HORIZONTAL(dir) ((((unsigned int) (dir)) & ~1U) == 4)
+#define HB_DIRECTION_IS_VERTICAL(dir) ((((unsigned int) (dir)) & ~1U) == 6)
+#define HB_DIRECTION_IS_FORWARD(dir) ((((unsigned int) (dir)) & ~2U) == 4)
+#define HB_DIRECTION_IS_BACKWARD(dir) ((((unsigned int) (dir)) & ~2U) == 5)
+#define HB_DIRECTION_REVERSE(dir) ((hb_direction_t) (((unsigned int) (dir)) ^ 1))
+
+
+/* hb_language_t */
+
+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);
+
+#define HB_LANGUAGE_INVALID ((hb_language_t) 0)
+
+HB_EXTERN hb_language_t
+hb_language_get_default (void);
+
+
+/* hb_script_t */
+
+/* https://unicode.org/iso15924/ */
+/* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
+/* Unicode Character Database property: Script (sc) */
+typedef enum
+{
+ /*1.1*/ HB_SCRIPT_COMMON = HB_TAG ('Z','y','y','y'),
+ /*1.1*/ HB_SCRIPT_INHERITED = HB_TAG ('Z','i','n','h'),
+ /*5.0*/ HB_SCRIPT_UNKNOWN = HB_TAG ('Z','z','z','z'),
+
+ /*1.1*/ HB_SCRIPT_ARABIC = HB_TAG ('A','r','a','b'),
+ /*1.1*/ HB_SCRIPT_ARMENIAN = HB_TAG ('A','r','m','n'),
+ /*1.1*/ HB_SCRIPT_BENGALI = HB_TAG ('B','e','n','g'),
+ /*1.1*/ HB_SCRIPT_CYRILLIC = HB_TAG ('C','y','r','l'),
+ /*1.1*/ HB_SCRIPT_DEVANAGARI = HB_TAG ('D','e','v','a'),
+ /*1.1*/ HB_SCRIPT_GEORGIAN = HB_TAG ('G','e','o','r'),
+ /*1.1*/ HB_SCRIPT_GREEK = HB_TAG ('G','r','e','k'),
+ /*1.1*/ HB_SCRIPT_GUJARATI = HB_TAG ('G','u','j','r'),
+ /*1.1*/ HB_SCRIPT_GURMUKHI = HB_TAG ('G','u','r','u'),
+ /*1.1*/ HB_SCRIPT_HANGUL = HB_TAG ('H','a','n','g'),
+ /*1.1*/ HB_SCRIPT_HAN = HB_TAG ('H','a','n','i'),
+ /*1.1*/ HB_SCRIPT_HEBREW = HB_TAG ('H','e','b','r'),
+ /*1.1*/ HB_SCRIPT_HIRAGANA = HB_TAG ('H','i','r','a'),
+ /*1.1*/ HB_SCRIPT_KANNADA = HB_TAG ('K','n','d','a'),
+ /*1.1*/ HB_SCRIPT_KATAKANA = HB_TAG ('K','a','n','a'),
+ /*1.1*/ HB_SCRIPT_LAO = HB_TAG ('L','a','o','o'),
+ /*1.1*/ HB_SCRIPT_LATIN = HB_TAG ('L','a','t','n'),
+ /*1.1*/ HB_SCRIPT_MALAYALAM = HB_TAG ('M','l','y','m'),
+ /*1.1*/ HB_SCRIPT_ORIYA = HB_TAG ('O','r','y','a'),
+ /*1.1*/ HB_SCRIPT_TAMIL = HB_TAG ('T','a','m','l'),
+ /*1.1*/ HB_SCRIPT_TELUGU = HB_TAG ('T','e','l','u'),
+ /*1.1*/ HB_SCRIPT_THAI = HB_TAG ('T','h','a','i'),
+
+ /*2.0*/ HB_SCRIPT_TIBETAN = HB_TAG ('T','i','b','t'),
+
+ /*3.0*/ HB_SCRIPT_BOPOMOFO = HB_TAG ('B','o','p','o'),
+ /*3.0*/ HB_SCRIPT_BRAILLE = HB_TAG ('B','r','a','i'),
+ /*3.0*/ HB_SCRIPT_CANADIAN_SYLLABICS = HB_TAG ('C','a','n','s'),
+ /*3.0*/ HB_SCRIPT_CHEROKEE = HB_TAG ('C','h','e','r'),
+ /*3.0*/ HB_SCRIPT_ETHIOPIC = HB_TAG ('E','t','h','i'),
+ /*3.0*/ HB_SCRIPT_KHMER = HB_TAG ('K','h','m','r'),
+ /*3.0*/ HB_SCRIPT_MONGOLIAN = HB_TAG ('M','o','n','g'),
+ /*3.0*/ HB_SCRIPT_MYANMAR = HB_TAG ('M','y','m','r'),
+ /*3.0*/ HB_SCRIPT_OGHAM = HB_TAG ('O','g','a','m'),
+ /*3.0*/ HB_SCRIPT_RUNIC = HB_TAG ('R','u','n','r'),
+ /*3.0*/ HB_SCRIPT_SINHALA = HB_TAG ('S','i','n','h'),
+ /*3.0*/ HB_SCRIPT_SYRIAC = HB_TAG ('S','y','r','c'),
+ /*3.0*/ HB_SCRIPT_THAANA = HB_TAG ('T','h','a','a'),
+ /*3.0*/ HB_SCRIPT_YI = HB_TAG ('Y','i','i','i'),
+
+ /*3.1*/ HB_SCRIPT_DESERET = HB_TAG ('D','s','r','t'),
+ /*3.1*/ HB_SCRIPT_GOTHIC = HB_TAG ('G','o','t','h'),
+ /*3.1*/ HB_SCRIPT_OLD_ITALIC = HB_TAG ('I','t','a','l'),
+
+ /*3.2*/ HB_SCRIPT_BUHID = HB_TAG ('B','u','h','d'),
+ /*3.2*/ HB_SCRIPT_HANUNOO = HB_TAG ('H','a','n','o'),
+ /*3.2*/ HB_SCRIPT_TAGALOG = HB_TAG ('T','g','l','g'),
+ /*3.2*/ HB_SCRIPT_TAGBANWA = HB_TAG ('T','a','g','b'),
+
+ /*4.0*/ HB_SCRIPT_CYPRIOT = HB_TAG ('C','p','r','t'),
+ /*4.0*/ HB_SCRIPT_LIMBU = HB_TAG ('L','i','m','b'),
+ /*4.0*/ HB_SCRIPT_LINEAR_B = HB_TAG ('L','i','n','b'),
+ /*4.0*/ HB_SCRIPT_OSMANYA = HB_TAG ('O','s','m','a'),
+ /*4.0*/ HB_SCRIPT_SHAVIAN = HB_TAG ('S','h','a','w'),
+ /*4.0*/ HB_SCRIPT_TAI_LE = HB_TAG ('T','a','l','e'),
+ /*4.0*/ HB_SCRIPT_UGARITIC = HB_TAG ('U','g','a','r'),
+
+ /*4.1*/ HB_SCRIPT_BUGINESE = HB_TAG ('B','u','g','i'),
+ /*4.1*/ HB_SCRIPT_COPTIC = HB_TAG ('C','o','p','t'),
+ /*4.1*/ HB_SCRIPT_GLAGOLITIC = HB_TAG ('G','l','a','g'),
+ /*4.1*/ HB_SCRIPT_KHAROSHTHI = HB_TAG ('K','h','a','r'),
+ /*4.1*/ HB_SCRIPT_NEW_TAI_LUE = HB_TAG ('T','a','l','u'),
+ /*4.1*/ HB_SCRIPT_OLD_PERSIAN = HB_TAG ('X','p','e','o'),
+ /*4.1*/ HB_SCRIPT_SYLOTI_NAGRI = HB_TAG ('S','y','l','o'),
+ /*4.1*/ HB_SCRIPT_TIFINAGH = HB_TAG ('T','f','n','g'),
+
+ /*5.0*/ HB_SCRIPT_BALINESE = HB_TAG ('B','a','l','i'),
+ /*5.0*/ HB_SCRIPT_CUNEIFORM = HB_TAG ('X','s','u','x'),
+ /*5.0*/ HB_SCRIPT_NKO = HB_TAG ('N','k','o','o'),
+ /*5.0*/ HB_SCRIPT_PHAGS_PA = HB_TAG ('P','h','a','g'),
+ /*5.0*/ HB_SCRIPT_PHOENICIAN = HB_TAG ('P','h','n','x'),
+
+ /*5.1*/ HB_SCRIPT_CARIAN = HB_TAG ('C','a','r','i'),
+ /*5.1*/ HB_SCRIPT_CHAM = HB_TAG ('C','h','a','m'),
+ /*5.1*/ HB_SCRIPT_KAYAH_LI = HB_TAG ('K','a','l','i'),
+ /*5.1*/ HB_SCRIPT_LEPCHA = HB_TAG ('L','e','p','c'),
+ /*5.1*/ HB_SCRIPT_LYCIAN = HB_TAG ('L','y','c','i'),
+ /*5.1*/ HB_SCRIPT_LYDIAN = HB_TAG ('L','y','d','i'),
+ /*5.1*/ HB_SCRIPT_OL_CHIKI = HB_TAG ('O','l','c','k'),
+ /*5.1*/ HB_SCRIPT_REJANG = HB_TAG ('R','j','n','g'),
+ /*5.1*/ HB_SCRIPT_SAURASHTRA = HB_TAG ('S','a','u','r'),
+ /*5.1*/ HB_SCRIPT_SUNDANESE = HB_TAG ('S','u','n','d'),
+ /*5.1*/ HB_SCRIPT_VAI = HB_TAG ('V','a','i','i'),
+
+ /*5.2*/ HB_SCRIPT_AVESTAN = HB_TAG ('A','v','s','t'),
+ /*5.2*/ HB_SCRIPT_BAMUM = HB_TAG ('B','a','m','u'),
+ /*5.2*/ HB_SCRIPT_EGYPTIAN_HIEROGLYPHS = HB_TAG ('E','g','y','p'),
+ /*5.2*/ HB_SCRIPT_IMPERIAL_ARAMAIC = HB_TAG ('A','r','m','i'),
+ /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PAHLAVI = HB_TAG ('P','h','l','i'),
+ /*5.2*/ HB_SCRIPT_INSCRIPTIONAL_PARTHIAN = HB_TAG ('P','r','t','i'),
+ /*5.2*/ HB_SCRIPT_JAVANESE = HB_TAG ('J','a','v','a'),
+ /*5.2*/ HB_SCRIPT_KAITHI = HB_TAG ('K','t','h','i'),
+ /*5.2*/ HB_SCRIPT_LISU = HB_TAG ('L','i','s','u'),
+ /*5.2*/ HB_SCRIPT_MEETEI_MAYEK = HB_TAG ('M','t','e','i'),
+ /*5.2*/ HB_SCRIPT_OLD_SOUTH_ARABIAN = HB_TAG ('S','a','r','b'),
+ /*5.2*/ HB_SCRIPT_OLD_TURKIC = HB_TAG ('O','r','k','h'),
+ /*5.2*/ HB_SCRIPT_SAMARITAN = HB_TAG ('S','a','m','r'),
+ /*5.2*/ HB_SCRIPT_TAI_THAM = HB_TAG ('L','a','n','a'),
+ /*5.2*/ HB_SCRIPT_TAI_VIET = HB_TAG ('T','a','v','t'),
+
+ /*6.0*/ HB_SCRIPT_BATAK = HB_TAG ('B','a','t','k'),
+ /*6.0*/ HB_SCRIPT_BRAHMI = HB_TAG ('B','r','a','h'),
+ /*6.0*/ HB_SCRIPT_MANDAIC = HB_TAG ('M','a','n','d'),
+
+ /*6.1*/ HB_SCRIPT_CHAKMA = HB_TAG ('C','a','k','m'),
+ /*6.1*/ HB_SCRIPT_MEROITIC_CURSIVE = HB_TAG ('M','e','r','c'),
+ /*6.1*/ HB_SCRIPT_MEROITIC_HIEROGLYPHS = HB_TAG ('M','e','r','o'),
+ /*6.1*/ HB_SCRIPT_MIAO = HB_TAG ('P','l','r','d'),
+ /*6.1*/ HB_SCRIPT_SHARADA = HB_TAG ('S','h','r','d'),
+ /*6.1*/ HB_SCRIPT_SORA_SOMPENG = HB_TAG ('S','o','r','a'),
+ /*6.1*/ HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'),
+
+ /*
+ * Since: 0.9.30
+ */
+ /*7.0*/ HB_SCRIPT_BASSA_VAH = HB_TAG ('B','a','s','s'),
+ /*7.0*/ HB_SCRIPT_CAUCASIAN_ALBANIAN = HB_TAG ('A','g','h','b'),
+ /*7.0*/ HB_SCRIPT_DUPLOYAN = HB_TAG ('D','u','p','l'),
+ /*7.0*/ HB_SCRIPT_ELBASAN = HB_TAG ('E','l','b','a'),
+ /*7.0*/ HB_SCRIPT_GRANTHA = HB_TAG ('G','r','a','n'),
+ /*7.0*/ HB_SCRIPT_KHOJKI = HB_TAG ('K','h','o','j'),
+ /*7.0*/ HB_SCRIPT_KHUDAWADI = HB_TAG ('S','i','n','d'),
+ /*7.0*/ HB_SCRIPT_LINEAR_A = HB_TAG ('L','i','n','a'),
+ /*7.0*/ HB_SCRIPT_MAHAJANI = HB_TAG ('M','a','h','j'),
+ /*7.0*/ HB_SCRIPT_MANICHAEAN = HB_TAG ('M','a','n','i'),
+ /*7.0*/ HB_SCRIPT_MENDE_KIKAKUI = HB_TAG ('M','e','n','d'),
+ /*7.0*/ HB_SCRIPT_MODI = HB_TAG ('M','o','d','i'),
+ /*7.0*/ HB_SCRIPT_MRO = HB_TAG ('M','r','o','o'),
+ /*7.0*/ HB_SCRIPT_NABATAEAN = HB_TAG ('N','b','a','t'),
+ /*7.0*/ HB_SCRIPT_OLD_NORTH_ARABIAN = HB_TAG ('N','a','r','b'),
+ /*7.0*/ HB_SCRIPT_OLD_PERMIC = HB_TAG ('P','e','r','m'),
+ /*7.0*/ HB_SCRIPT_PAHAWH_HMONG = HB_TAG ('H','m','n','g'),
+ /*7.0*/ HB_SCRIPT_PALMYRENE = HB_TAG ('P','a','l','m'),
+ /*7.0*/ HB_SCRIPT_PAU_CIN_HAU = HB_TAG ('P','a','u','c'),
+ /*7.0*/ HB_SCRIPT_PSALTER_PAHLAVI = HB_TAG ('P','h','l','p'),
+ /*7.0*/ HB_SCRIPT_SIDDHAM = HB_TAG ('S','i','d','d'),
+ /*7.0*/ HB_SCRIPT_TIRHUTA = HB_TAG ('T','i','r','h'),
+ /*7.0*/ HB_SCRIPT_WARANG_CITI = HB_TAG ('W','a','r','a'),
+
+ /*8.0*/ HB_SCRIPT_AHOM = HB_TAG ('A','h','o','m'),
+ /*8.0*/ HB_SCRIPT_ANATOLIAN_HIEROGLYPHS = HB_TAG ('H','l','u','w'),
+ /*8.0*/ HB_SCRIPT_HATRAN = HB_TAG ('H','a','t','r'),
+ /*8.0*/ HB_SCRIPT_MULTANI = HB_TAG ('M','u','l','t'),
+ /*8.0*/ HB_SCRIPT_OLD_HUNGARIAN = HB_TAG ('H','u','n','g'),
+ /*8.0*/ HB_SCRIPT_SIGNWRITING = HB_TAG ('S','g','n','w'),
+
+ /*
+ * Since 1.3.0
+ */
+ /*9.0*/ HB_SCRIPT_ADLAM = HB_TAG ('A','d','l','m'),
+ /*9.0*/ HB_SCRIPT_BHAIKSUKI = HB_TAG ('B','h','k','s'),
+ /*9.0*/ HB_SCRIPT_MARCHEN = HB_TAG ('M','a','r','c'),
+ /*9.0*/ HB_SCRIPT_OSAGE = HB_TAG ('O','s','g','e'),
+ /*9.0*/ HB_SCRIPT_TANGUT = HB_TAG ('T','a','n','g'),
+ /*9.0*/ HB_SCRIPT_NEWA = HB_TAG ('N','e','w','a'),
+
+ /*
+ * Since 1.6.0
+ */
+ /*10.0*/HB_SCRIPT_MASARAM_GONDI = HB_TAG ('G','o','n','m'),
+ /*10.0*/HB_SCRIPT_NUSHU = HB_TAG ('N','s','h','u'),
+ /*10.0*/HB_SCRIPT_SOYOMBO = HB_TAG ('S','o','y','o'),
+ /*10.0*/HB_SCRIPT_ZANABAZAR_SQUARE = HB_TAG ('Z','a','n','b'),
+
+ /*
+ * Since 1.8.0
+ */
+ /*11.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'),
+
+ /*
+ * Since 2.4.0
+ */
+ /*12.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'),
+
+ /*
+ * Since 2.6.7
+ */
+ /*13.0*/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'),
+
+ /* No script set. */
+ HB_SCRIPT_INVALID = HB_TAG_NONE,
+
+ /* 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 */
+
+typedef struct hb_user_data_key_t {
+ /*< private >*/
+ char unused;
+} hb_user_data_key_t;
+
+typedef void (*hb_destroy_func_t) (void *user_data);
+
+
+/* Font features and variations. */
+
+/**
+ * HB_FEATURE_GLOBAL_START
+ *
+ * Since: 2.0.0
+ */
+#define HB_FEATURE_GLOBAL_START 0
+/**
+ * HB_FEATURE_GLOBAL_END
+ *
+ * Since: 2.0.0
+ */
+#define HB_FEATURE_GLOBAL_END ((unsigned int) -1)
+
+/**
+ * hb_feature_t:
+ * @tag: a feature tag
+ * @value: 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:
+ *
+ * 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.
+ *
+ * Since: 2.1.0
+ */
+typedef uint32_t hb_color_t;
+
+#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_END_DECLS
+
+#endif /* HB_COMMON_H */
diff --git a/thirdparty/harfbuzz/src/hb-config.hh b/thirdparty/harfbuzz/src/hb-config.hh
new file mode 100644
index 0000000000..fc8d424bfb
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-config.hh
@@ -0,0 +1,163 @@
+/*
+ * 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
+
+
+#ifdef HB_TINY
+#define HB_LEAN
+#define HB_MINI
+#define HB_NO_MT
+#define HB_NO_UCD_UNASSIGNED
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+#ifndef __OPTIMIZE_SIZE__
+#define __OPTIMIZE_SIZE__
+#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_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_PRIVATE_SUBTAG
+#define HB_NO_LAYOUT_FEATURE_PARAMS
+#define HB_NO_LAYOUT_COLLECT_GLYPHS
+#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_SETLOCALE
+#define HB_NO_OT_FONT_GLYPH_NAMES
+#define HB_NO_OT_SHAPE_FRACTIONS
+#define HB_NO_STYLE
+#define HB_NO_SUBSET_LAYOUT
+#define HB_NO_VAR
+#endif
+
+#ifdef HB_MINI
+#define HB_NO_AAT
+#define HB_NO_LEGACY
+#endif
+
+
+/* Closure of options. */
+
+#ifdef HB_DISABLE_DEPRECATED
+#define HB_IF_NOT_DEPRECATED(x)
+#else
+#define HB_IF_NOT_DEPRECATED(x) x
+#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_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_BLACKLIST
+#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_SHAPE_COMPLEX_ARABIC_FALLBACK
+#define HB_NO_OT_SHAPE_COMPLEX_HEBREW_FALLBACK
+#define HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK
+#define HB_NO_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS
+#endif
+
+#ifdef NDEBUG
+#ifndef HB_NDEBUG
+#define HB_NDEBUG
+#endif
+#endif
+
+#ifdef __OPTIMIZE_SIZE__
+#ifndef HB_OPTIMIZE_SIZE
+#define HB_OPTIMIZE_SIZE
+#endif
+#endif
+
+#ifdef HAVE_CONFIG_OVERRIDE_H
+#include "config-override.h"
+#endif
+
+
+#endif /* HB_CONFIG_HH */
diff --git a/thirdparty/harfbuzz/src/hb-coretext.cc b/thirdparty/harfbuzz/src/hb-coretext.cc
new file mode 100644
index 0000000000..7b6b2bd5ef
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-coretext.cc
@@ -0,0 +1,1194 @@
+/*
+ * 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"
+#include <math.h>
+
+
+/**
+ * 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
+ if (&CTGetCoreTextVersion != nullptr && CTGetCoreTextVersion() < 0x00070000) {
+ 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;
+ }
+
+ return (hb_coretext_font_data_t *) ct_font;
+}
+
+void
+_hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
+{
+ CFRelease ((CTFontRef) data);
+}
+
+static const hb_coretext_font_data_t *
+hb_coretext_font_data_sync (hb_font_t *font)
+{
+retry:
+ const hb_coretext_font_data_t *data = font->data.coretext;
+ if (unlikely (!data)) return nullptr;
+
+ if (fabs (CTFontGetSize ((CTFontRef) data) - (CGFloat) font->ptem) > .5)
+ {
+ /* XXX-MT-bug
+ * Note that evaluating condition above can be dangerous if another thread
+ * got here first and destructed data. That's, as always, bad use pattern.
+ * If you modify the font (change font size), other threads must not be
+ * using it at the same time. However, since this check is delayed to
+ * when one actually tries to shape something, this is a XXX race condition
+ * (and the only one we have that I know of) right now. Ie. you modify the
+ * font size in one thread, then (supposedly safely) try to use it from two
+ * or more threads and BOOM! I'm not sure how to fix this. We want RCU.
+ */
+
+ /* Drop and recreate. */
+ /* If someone dropped it in the mean time, throw it away and don't touch it.
+ * Otherwise, destruct it. */
+ if (likely (font->data.coretext.cmpexch (const_cast<hb_coretext_font_data_t *> (data), nullptr)))
+ _hb_coretext_shaper_font_data_destroy (const_cast<hb_coretext_font_data_t *> (data));
+ else
+ goto retry;
+ }
+ return font->data.coretext;
+}
+
+/**
+ * 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_face_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)
+{
+ const hb_coretext_font_data_t *data = hb_coretext_font_data_sync (font);
+ return data ? (CTFontRef) data : 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) {
+ 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) hb_coretext_font_data_sync (font);
+
+ 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<feature_record_t> feature_records;
+ 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.find (&event->feature);
+ if (feature)
+ active_features.remove (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_and = ~0, status_or = 0;
+ double 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;
+ status_and &= run_status;
+ DEBUG_MSG (CORETEXT, run, "CTRunStatus: %x", run_status);
+ double run_advance = CTRunGetTypographicBounds (run, range_all, nullptr, nullptr, nullptr);
+ if (HB_DIRECTION_IS_VERTICAL (buffer->props.direction))
+ run_advance = -run_advance;
+ DEBUG_MSG (CORETEXT, run, "Run advance: %g", run_advance);
+
+ /* 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 = (positions[0].x - advances_so_far) * x_mult;
+ for (unsigned int j = 0; j < num_glyphs; j++)
+ {
+ double advance;
+ if (likely (j + 1 < num_glyphs))
+ advance = positions[j + 1].x - positions[j].x;
+ else /* last glyph */
+ advance = run_advance - (positions[j].x - positions[0].x);
+ info->mask = advance * x_mult;
+ info->var1.i32 = x_offset;
+ info->var2.i32 = positions[j].y * y_mult;
+ info++;
+ }
+ }
+ else
+ {
+ hb_position_t y_offset = (positions[0].y - advances_so_far) * y_mult;
+ for (unsigned int j = 0; j < num_glyphs; j++)
+ {
+ double advance;
+ if (likely (j + 1 < num_glyphs))
+ advance = positions[j + 1].y - positions[j].y;
+ else /* last glyph */
+ advance = run_advance - (positions[j].y - positions[0].y);
+ info->mask = advance * y_mult;
+ info->var1.i32 = positions[j].x * x_mult;
+ 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;
+ }
+
+ /* Mac OS 10.6 doesn't have kCTTypesetterOptionForcedEmbeddingLevel,
+ * or if it does, it doesn't respect it. So we get runs with wrong
+ * directions. As such, disable the assert... It wouldn't crash, but
+ * cursoring will be off...
+ *
+ * https://crbug.com/419769
+ */
+ if (false)
+ {
+ /* Make sure all runs had the expected direction. */
+ HB_UNUSED bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+ assert (bool (status_and & kCTRunStatusRightToLeft) == backward);
+ assert (bool (status_or & kCTRunStatusRightToLeft) == backward);
+ }
+
+ buffer->clear_positions ();
+
+ unsigned int count = buffer->len;
+ 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))
+ {
+ 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;
+ }
+ }
+ }
+ }
+
+ buffer->unsafe_to_break_all ();
+
+#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/thirdparty/harfbuzz/src/hb-coretext.h b/thirdparty/harfbuzz/src/hb-coretext.h
new file mode 100644
index 0000000000..e53dbaf2c7
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-debug.hh b/thirdparty/harfbuzz/src/hb-debug.hh
new file mode 100644
index 0000000000..ec3a1ff211
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-debug.hh
@@ -0,0 +1,459 @@
+/*
+ * 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.get_relaxed ();
+
+ if (unlikely (!u.i))
+ {
+ _hb_options_init ();
+ u.i = _hb_options.get_relaxed ();
+ }
+
+ 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", 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) ", (unsigned int) (2 * sizeof (void *)), obj);
+ else
+ fprintf (stderr, " %*s ", (unsigned 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 <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 hb_forward<T> (v);
+ }
+
+ _hb_debug_msg<max_level> (what, obj, func, true, plevel ? *plevel : 1, -1,
+ "return %s (line %d)",
+ hb_printer_t<decltype (v)>().print (v), line);
+ if (plevel) --*plevel;
+ plevel = nullptr;
+ returned = true;
+ return hb_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 hb_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 hb_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_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
+
+/*
+ * 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 %d 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_DISPATCH
+#define HB_DEBUG_DISPATCH ( \
+ HB_DEBUG_APPLY + \
+ HB_DEBUG_SANITIZE + \
+ HB_DEBUG_SERIALIZE + \
+ HB_DEBUG_SUBSET + \
+ 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 %d", (int) format)
+#else
+#define TRACE_DISPATCH(this, format) hb_no_trace_t<typename context_t::return_t> trace
+#endif
+
+
+#endif /* HB_DEBUG_HH */
diff --git a/thirdparty/harfbuzz/src/hb-deprecated.h b/thirdparty/harfbuzz/src/hb-deprecated.h
new file mode 100644
index 0000000000..43f89a4c4e
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-deprecated.h
@@ -0,0 +1,195 @@
+/*
+ * 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_H_IN
+#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
+
+
+#define HB_SCRIPT_CANADIAN_ABORIGINAL HB_SCRIPT_CANADIAN_SYLLABICS
+
+#define HB_BUFFER_FLAGS_DEFAULT HB_BUFFER_FLAG_DEFAULT
+#define HB_BUFFER_SERIALIZE_FLAGS_DEFAULT HB_BUFFER_SERIALIZE_FLAG_DEFAULT
+
+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_EXTERN HB_DEPRECATED_FOR(hb_font_funcs_set_nominal_glyph_func and hb_font_funcs_set_variation_glyph_func) void
+hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
+ hb_font_get_glyph_func_t func,
+ void *user_data, hb_destroy_func_t destroy);
+
+HB_EXTERN HB_DEPRECATED void
+hb_set_invert (hb_set_t *set);
+
+/**
+ * hb_unicode_eastasian_width_func_t:
+ *
+ * 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):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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:
+ *
+ * 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 function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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);
+
+
+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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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);
+
+#endif
+
+HB_END_DECLS
+
+#endif /* HB_DEPRECATED_H */
diff --git a/thirdparty/harfbuzz/src/hb-directwrite.cc b/thirdparty/harfbuzz/src/hb-directwrite.cc
new file mode 100644
index 0000000000..f2fce073e0
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-directwrite.cc
@@ -0,0 +1,979 @@
+/*
+ * 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"
+
+
+/* Declare object creator for dynamic support of DWRITE */
+typedef HRESULT (* WINAPI t_DWriteCreateFactory)(
+ DWRITE_FACTORY_TYPE factoryType,
+ REFIID iid,
+ IUnknown **factory
+);
+
+/*
+ * hb-directwrite uses new/delete syntatically but as we let users
+ * to override malloc/free, we will redefine new/delete so users
+ * won't need to do that by their own.
+ */
+void* operator new (size_t size) { return malloc (size); }
+void* operator new [] (size_t size) { return malloc (size); }
+void operator delete (void* pointer) { free (pointer); }
+void operator delete [] (void* pointer) { free (pointer); }
+
+
+/*
+ * 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 ();
+ }
+ if (data->fontFileLoader)
+ delete data->fontFileLoader;
+ if (data->fontFileStream)
+ delete data->fontFileStream;
+ if (data->faceBlob)
+ hb_blob_destroy (data->faceBlob);
+ if (data->dwrite_dll)
+ FreeLibrary (data->dwrite_dll);
+ if (data)
+ 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)
+{
+ hb_directwrite_font_data_t *data = new hb_directwrite_font_data_t;
+ if (unlikely (!data))
+ return nullptr;
+
+ return data;
+}
+
+void
+_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
+{
+ delete 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
+ */
+
+static hb_bool_t
+_hb_directwrite_shape_full (hb_shape_plan_t *shape_plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer,
+ const hb_feature_t *features,
+ unsigned int num_features,
+ float lineWidth)
+{
+ 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. */
+ }
+
+ // TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
+
+ 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 = buffer->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);
+
+ // TODO: it does work but doesn't care about ranges
+ DWRITE_TYPOGRAPHIC_FEATURES typographic_features;
+ typographic_features.featureCount = num_features;
+ if (num_features)
+ {
+ typographic_features.features = new DWRITE_FONT_FEATURE[num_features];
+ for (unsigned int i = 0; i < num_features; ++i)
+ {
+ typographic_features.features[i].nameTag = (DWRITE_FONT_FEATURE_TAG)
+ hb_uint32_swap (features[i].tag);
+ typographic_features.features[i].parameter = features[i].value;
+ }
+ }
+ const DWRITE_TYPOGRAPHIC_FEATURES* dwFeatures;
+ dwFeatures = (const DWRITE_TYPOGRAPHIC_FEATURES*) &typographic_features;
+ const uint32_t featureRangeLengths[] = { textLength };
+ //
+
+ 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, textLength, fontFace, false,
+ isRightToLeft, &runHead->mScript, localeName,
+ nullptr, &dwFeatures, featureRangeLengths, 1,
+ 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,
+ textLength, glyphIndices, glyphProperties,
+ glyphCount, fontFace, fontEmSize,
+ false, isRightToLeft, &runHead->mScript, localeName,
+ &dwFeatures, featureRangeLengths, 1,
+ glyphAdvances, glyphOffsets);
+
+ if (FAILED (hr))
+ FAIL ("Analyzer failed to get glyph placements.");
+
+ IDWriteTextAnalyzer1* analyzer1;
+ analyzer->QueryInterface (&analyzer1);
+
+ if (analyzer1 && lineWidth)
+ {
+ DWRITE_JUSTIFICATION_OPPORTUNITY* justificationOpportunities =
+ new DWRITE_JUSTIFICATION_OPPORTUNITY[maxGlyphCount];
+ hr = analyzer1->GetJustificationOpportunities (fontFace, fontEmSize, runHead->mScript,
+ textLength, glyphCount, textString,
+ clusterMap, glyphProperties,
+ justificationOpportunities);
+
+ if (FAILED (hr))
+ FAIL ("Analyzer failed to get justification opportunities.");
+
+ float* justifiedGlyphAdvances = new float[maxGlyphCount];
+ DWRITE_GLYPH_OFFSET* justifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[glyphCount];
+ hr = analyzer1->JustifyGlyphAdvances (lineWidth, glyphCount, justificationOpportunities,
+ glyphAdvances, glyphOffsets, justifiedGlyphAdvances,
+ justifiedGlyphOffsets);
+
+ if (FAILED (hr)) FAIL ("Analyzer failed to get justify glyph advances.");
+
+ DWRITE_SCRIPT_PROPERTIES scriptProperties;
+ hr = analyzer1->GetScriptProperties (runHead->mScript, &scriptProperties);
+ if (FAILED (hr)) FAIL ("Analyzer failed to get script properties.");
+ uint32_t justificationCharacter = scriptProperties.justificationCharacter;
+
+ // if a script justificationCharacter is not space, it can have GetJustifiedGlyphs
+ if (justificationCharacter != 32)
+ {
+ uint16_t* modifiedClusterMap = new uint16_t[textLength];
+ retry_getjustifiedglyphs:
+ uint16_t* modifiedGlyphIndices = new uint16_t[maxGlyphCount];
+ float* modifiedGlyphAdvances = new float[maxGlyphCount];
+ DWRITE_GLYPH_OFFSET* modifiedGlyphOffsets = new DWRITE_GLYPH_OFFSET[maxGlyphCount];
+ uint32_t actualGlyphsCount;
+ hr = analyzer1->GetJustifiedGlyphs (fontFace, fontEmSize, runHead->mScript,
+ textLength, glyphCount, maxGlyphCount,
+ clusterMap, glyphIndices, glyphAdvances,
+ justifiedGlyphAdvances, justifiedGlyphOffsets,
+ glyphProperties, &actualGlyphsCount,
+ modifiedClusterMap, modifiedGlyphIndices,
+ modifiedGlyphAdvances, modifiedGlyphOffsets);
+
+ if (hr == HRESULT_FROM_WIN32 (ERROR_INSUFFICIENT_BUFFER))
+ {
+ maxGlyphCount = actualGlyphsCount;
+ delete [] modifiedGlyphIndices;
+ delete [] modifiedGlyphAdvances;
+ delete [] modifiedGlyphOffsets;
+
+ maxGlyphCount = actualGlyphsCount;
+
+ goto retry_getjustifiedglyphs;
+ }
+ if (FAILED (hr))
+ FAIL ("Analyzer failed to get justified glyphs.");
+
+ delete [] clusterMap;
+ delete [] glyphIndices;
+ delete [] glyphAdvances;
+ delete [] glyphOffsets;
+
+ glyphCount = actualGlyphsCount;
+ clusterMap = modifiedClusterMap;
+ glyphIndices = modifiedGlyphIndices;
+ glyphAdvances = modifiedGlyphAdvances;
+ glyphOffsets = modifiedGlyphOffsets;
+
+ delete [] justifiedGlyphAdvances;
+ delete [] justifiedGlyphOffsets;
+ }
+ else
+ {
+ delete [] glyphAdvances;
+ delete [] glyphOffsets;
+
+ glyphAdvances = justifiedGlyphAdvances;
+ glyphOffsets = justifiedGlyphOffsets;
+ }
+
+ delete [] justificationOpportunities;
+ }
+
+ /* 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);
+
+ delete [] clusterMap;
+ delete [] glyphIndices;
+ delete [] textProperties;
+ delete [] glyphProperties;
+ delete [] glyphAdvances;
+ delete [] glyphOffsets;
+
+ if (num_features)
+ delete [] typographic_features.features;
+
+ /* Wow, done! */
+ return true;
+}
+
+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)
+{
+ return _hb_directwrite_shape_full (shape_plan, font, buffer,
+ features, num_features, 0);
+}
+
+HB_UNUSED static bool
+_hb_directwrite_shape_experimental_width (hb_font_t *font,
+ hb_buffer_t *buffer,
+ const hb_feature_t *features,
+ unsigned int num_features,
+ float width)
+{
+ static const char *shapers = "directwrite";
+ hb_shape_plan_t *shape_plan;
+ shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
+ features, num_features, &shapers);
+ hb_bool_t res = _hb_directwrite_shape_full (shape_plan, font, buffer,
+ features, num_features, width);
+
+ buffer->unsafe_to_break_all ();
+
+ return res;
+}
+
+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);
+ delete 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 = new _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.
+ *
+ * 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
+*
+* 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/thirdparty/harfbuzz/src/hb-directwrite.h b/thirdparty/harfbuzz/src/hb-directwrite.h
new file mode 100644
index 0000000000..f837627a28
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-dispatch.hh b/thirdparty/harfbuzz/src/hb-dispatch.hh
new file mode 100644
index 0000000000..7eace86e54
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-dispatch.hh
@@ -0,0 +1,61 @@
+/*
+ * 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
+{
+ hb_dispatch_context_t () : debug_depth (0) {}
+ 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 (), hb_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;
+};
+
+
+#endif /* HB_DISPATCH_HH */
diff --git a/thirdparty/harfbuzz/src/hb-draw.cc b/thirdparty/harfbuzz/src/hb-draw.cc
new file mode 100644
index 0000000000..1a5f9c8c6b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-draw.cc
@@ -0,0 +1,261 @@
+/*
+ * 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
+#ifdef HB_EXPERIMENTAL_API
+
+#include "hb-draw.hh"
+#include "hb-ot.h"
+#include "hb-ot-glyf-table.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-ot-cff2-table.hh"
+
+/**
+ * hb_draw_funcs_set_move_to_func:
+ * @funcs: draw functions object
+ * @move_to: move-to callback
+ *
+ * Sets move-to callback to the draw functions object.
+ *
+ * Since: EXPERIMENTAL
+ **/
+void
+hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_move_to_func_t move_to)
+{
+ if (unlikely (hb_object_is_immutable (funcs))) return;
+ funcs->move_to = move_to;
+}
+
+/**
+ * hb_draw_funcs_set_line_to_func:
+ * @funcs: draw functions object
+ * @line_to: line-to callback
+ *
+ * Sets line-to callback to the draw functions object.
+ *
+ * Since: EXPERIMENTAL
+ **/
+void
+hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_line_to_func_t line_to)
+{
+ if (unlikely (hb_object_is_immutable (funcs))) return;
+ funcs->line_to = line_to;
+}
+
+/**
+ * hb_draw_funcs_set_quadratic_to_func:
+ * @funcs: draw functions object
+ * @move_to: quadratic-to callback
+ *
+ * Sets quadratic-to callback to the draw functions object.
+ *
+ * Since: EXPERIMENTAL
+ **/
+void
+hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_quadratic_to_func_t quadratic_to)
+{
+ if (unlikely (hb_object_is_immutable (funcs))) return;
+ funcs->quadratic_to = quadratic_to;
+ funcs->is_quadratic_to_set = true;
+}
+
+/**
+ * hb_draw_funcs_set_cubic_to_func:
+ * @funcs: draw functions
+ * @cubic_to: cubic-to callback
+ *
+ * Sets cubic-to callback to the draw functions object.
+ *
+ * Since: EXPERIMENTAL
+ **/
+void
+hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_cubic_to_func_t cubic_to)
+{
+ if (unlikely (hb_object_is_immutable (funcs))) return;
+ funcs->cubic_to = cubic_to;
+}
+
+/**
+ * hb_draw_funcs_set_close_path_func:
+ * @funcs: draw functions object
+ * @close_path: close-path callback
+ *
+ * Sets close-path callback to the draw functions object.
+ *
+ * Since: EXPERIMENTAL
+ **/
+void
+hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs,
+ hb_draw_close_path_func_t close_path)
+{
+ if (unlikely (hb_object_is_immutable (funcs))) return;
+ funcs->close_path = close_path;
+}
+
+static void
+_move_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {}
+
+static void
+_line_to_nil (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {}
+
+static void
+_quadratic_to_nil (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED,
+ hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED,
+ void *user_data HB_UNUSED) {}
+
+static void
+_cubic_to_nil (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED,
+ hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED,
+ hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED,
+ void *user_data HB_UNUSED) {}
+
+static void
+_close_path_nil (void *user_data HB_UNUSED) {}
+
+/**
+ * hb_draw_funcs_create:
+ *
+ * Creates a new draw callbacks object.
+ *
+ * Since: EXPERIMENTAL
+ **/
+hb_draw_funcs_t *
+hb_draw_funcs_create ()
+{
+ hb_draw_funcs_t *funcs;
+ if (unlikely (!(funcs = hb_object_create<hb_draw_funcs_t> ())))
+ return const_cast<hb_draw_funcs_t *> (&Null (hb_draw_funcs_t));
+
+ funcs->move_to = (hb_draw_move_to_func_t) _move_to_nil;
+ funcs->line_to = (hb_draw_line_to_func_t) _line_to_nil;
+ funcs->quadratic_to = (hb_draw_quadratic_to_func_t) _quadratic_to_nil;
+ funcs->is_quadratic_to_set = false;
+ funcs->cubic_to = (hb_draw_cubic_to_func_t) _cubic_to_nil;
+ funcs->close_path = (hb_draw_close_path_func_t) _close_path_nil;
+ return funcs;
+}
+
+/**
+ * hb_draw_funcs_reference:
+ * @funcs: draw functions
+ *
+ * Add to callbacks object refcount.
+ *
+ * Returns: The same object.
+ * Since: EXPERIMENTAL
+ **/
+hb_draw_funcs_t *
+hb_draw_funcs_reference (hb_draw_funcs_t *funcs)
+{
+ return hb_object_reference (funcs);
+}
+
+/**
+ * hb_draw_funcs_destroy:
+ * @funcs: draw functions
+ *
+ * Decreases refcount of callbacks object and deletes the object if it reaches
+ * to zero.
+ *
+ * Since: EXPERIMENTAL
+ **/
+void
+hb_draw_funcs_destroy (hb_draw_funcs_t *funcs)
+{
+ if (!hb_object_destroy (funcs)) return;
+
+ free (funcs);
+}
+
+/**
+ * hb_draw_funcs_make_immutable:
+ * @funcs: draw functions
+ *
+ * Makes funcs object immutable.
+ *
+ * Since: EXPERIMENTAL
+ **/
+void
+hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs)
+{
+ if (hb_object_is_immutable (funcs))
+ return;
+
+ hb_object_make_immutable (funcs);
+}
+
+/**
+ * hb_draw_funcs_is_immutable:
+ * @funcs: draw functions
+ *
+ * Checks whether funcs is immutable.
+ *
+ * Returns: If is immutable.
+ * Since: EXPERIMENTAL
+ **/
+hb_bool_t
+hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs)
+{
+ return hb_object_is_immutable (funcs);
+}
+
+/**
+ * hb_font_draw_glyph:
+ * @font: a font object
+ * @glyph: a glyph id
+ * @funcs: draw callbacks object
+ * @user_data: parameter you like be passed to the callbacks when are called
+ *
+ * Draw a glyph.
+ *
+ * Returns: Whether the font had the glyph and the operation completed successfully.
+ * Since: EXPERIMENTAL
+ **/
+hb_bool_t
+hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph,
+ const hb_draw_funcs_t *funcs,
+ void *user_data)
+{
+ if (unlikely (funcs == &Null (hb_draw_funcs_t) ||
+ glyph >= font->face->get_num_glyphs ()))
+ return false;
+
+ draw_helper_t draw_helper (funcs, user_data);
+ if (font->face->table.glyf->get_path (font, glyph, draw_helper)) return true;
+#ifndef HB_NO_CFF
+ if (font->face->table.cff1->get_path (font, glyph, draw_helper)) return true;
+ if (font->face->table.cff2->get_path (font, glyph, draw_helper)) return true;
+#endif
+
+ return false;
+}
+
+#endif
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-draw.h b/thirdparty/harfbuzz/src/hb-draw.h
new file mode 100644
index 0000000000..98eccf4c0c
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-draw.h
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_DRAW_H
+#define HB_DRAW_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+#ifdef HB_EXPERIMENTAL_API
+typedef void (*hb_draw_move_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data);
+typedef void (*hb_draw_line_to_func_t) (hb_position_t to_x, hb_position_t to_y, void *user_data);
+typedef void (*hb_draw_quadratic_to_func_t) (hb_position_t control_x, hb_position_t control_y,
+ hb_position_t to_x, hb_position_t to_y,
+ void *user_data);
+typedef void (*hb_draw_cubic_to_func_t) (hb_position_t control1_x, hb_position_t control1_y,
+ hb_position_t control2_x, hb_position_t control2_y,
+ hb_position_t to_x, hb_position_t to_y,
+ void *user_data);
+typedef void (*hb_draw_close_path_func_t) (void *user_data);
+
+/**
+ * hb_draw_funcs_t:
+ *
+ * Glyph draw callbacks.
+ *
+ * _move_to, _line_to and _cubic_to calls are nessecary to be defined but we
+ * translate _quadratic_to calls to _cubic_to if the callback isn't defined.
+ *
+ * Since: EXPERIMENTAL
+ **/
+typedef struct hb_draw_funcs_t hb_draw_funcs_t;
+
+HB_EXTERN void
+hb_draw_funcs_set_move_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_move_to_func_t move_to);
+
+HB_EXTERN void
+hb_draw_funcs_set_line_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_line_to_func_t line_to);
+
+HB_EXTERN void
+hb_draw_funcs_set_quadratic_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_quadratic_to_func_t quadratic_to);
+
+HB_EXTERN void
+hb_draw_funcs_set_cubic_to_func (hb_draw_funcs_t *funcs,
+ hb_draw_cubic_to_func_t cubic_to);
+
+HB_EXTERN void
+hb_draw_funcs_set_close_path_func (hb_draw_funcs_t *funcs,
+ hb_draw_close_path_func_t close_path);
+
+HB_EXTERN hb_draw_funcs_t *
+hb_draw_funcs_create (void);
+
+HB_EXTERN hb_draw_funcs_t *
+hb_draw_funcs_reference (hb_draw_funcs_t *funcs);
+
+HB_EXTERN void
+hb_draw_funcs_destroy (hb_draw_funcs_t *funcs);
+
+HB_EXTERN void
+hb_draw_funcs_make_immutable (hb_draw_funcs_t *funcs);
+
+HB_EXTERN hb_bool_t
+hb_draw_funcs_is_immutable (hb_draw_funcs_t *funcs);
+#endif
+
+HB_END_DECLS
+
+#endif /* HB_DRAW_H */
diff --git a/thirdparty/harfbuzz/src/hb-draw.hh b/thirdparty/harfbuzz/src/hb-draw.hh
new file mode 100644
index 0000000000..2aa0a5b4db
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-draw.hh
@@ -0,0 +1,139 @@
+/*
+ * 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"
+
+#ifdef HB_EXPERIMENTAL_API
+struct hb_draw_funcs_t
+{
+ hb_object_header_t header;
+
+ hb_draw_move_to_func_t move_to;
+ hb_draw_line_to_func_t line_to;
+ hb_draw_quadratic_to_func_t quadratic_to;
+ bool is_quadratic_to_set;
+ hb_draw_cubic_to_func_t cubic_to;
+ hb_draw_close_path_func_t close_path;
+};
+
+struct draw_helper_t
+{
+ draw_helper_t (const hb_draw_funcs_t *funcs_, void *user_data_)
+ {
+ funcs = funcs_;
+ user_data = user_data_;
+ path_open = false;
+ path_start_x = current_x = path_start_y = current_y = 0;
+ }
+ ~draw_helper_t () { end_path (); }
+
+ void move_to (hb_position_t x, hb_position_t y)
+ {
+ if (path_open) end_path ();
+ current_x = path_start_x = x;
+ current_y = path_start_y = y;
+ }
+
+ void line_to (hb_position_t x, hb_position_t y)
+ {
+ if (equal_to_current (x, y)) return;
+ if (!path_open) start_path ();
+ funcs->line_to (x, y, user_data);
+ current_x = x;
+ current_y = y;
+ }
+
+ void
+ quadratic_to (hb_position_t control_x, hb_position_t control_y,
+ hb_position_t to_x, hb_position_t to_y)
+ {
+ if (equal_to_current (control_x, control_y) && equal_to_current (to_x, to_y))
+ return;
+ if (!path_open) start_path ();
+ if (funcs->is_quadratic_to_set)
+ funcs->quadratic_to (control_x, control_y, to_x, to_y, user_data);
+ else
+ funcs->cubic_to (roundf ((current_x + 2.f * control_x) / 3.f),
+ roundf ((current_y + 2.f * control_y) / 3.f),
+ roundf ((to_x + 2.f * control_x) / 3.f),
+ roundf ((to_y + 2.f * control_y) / 3.f),
+ to_x, to_y, user_data);
+ current_x = to_x;
+ current_y = to_y;
+ }
+
+ void
+ cubic_to (hb_position_t control1_x, hb_position_t control1_y,
+ hb_position_t control2_x, hb_position_t control2_y,
+ hb_position_t to_x, hb_position_t to_y)
+ {
+ if (equal_to_current (control1_x, control1_y) &&
+ equal_to_current (control2_x, control2_y) &&
+ equal_to_current (to_x, to_y))
+ return;
+ if (!path_open) start_path ();
+ funcs->cubic_to (control1_x, control1_y, control2_x, control2_y, to_x, to_y, user_data);
+ current_x = to_x;
+ current_y = to_y;
+ }
+
+ void end_path ()
+ {
+ if (path_open)
+ {
+ if ((path_start_x != current_x) || (path_start_y != current_y))
+ funcs->line_to (path_start_x, path_start_y, user_data);
+ funcs->close_path (user_data);
+ }
+ path_open = false;
+ path_start_x = current_x = path_start_y = current_y = 0;
+ }
+
+ protected:
+ bool equal_to_current (hb_position_t x, hb_position_t y)
+ { return current_x == x && current_y == y; }
+
+ void start_path ()
+ {
+ if (path_open) end_path ();
+ path_open = true;
+ funcs->move_to (path_start_x, path_start_y, user_data);
+ }
+
+ hb_position_t path_start_x;
+ hb_position_t path_start_y;
+
+ hb_position_t current_x;
+ hb_position_t current_y;
+
+ bool path_open;
+ const hb_draw_funcs_t *funcs;
+ void *user_data;
+};
+#endif
+
+#endif /* HB_DRAW_HH */
diff --git a/thirdparty/harfbuzz/src/hb-face.cc b/thirdparty/harfbuzz/src/hb-face.cc
new file mode 100644
index 0000000000..7bde50df5b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-face.cc
@@ -0,0 +1,733 @@
+/*
+ * 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
+ *
+ * Font face is objects represent a single face in a font family.
+ * More exactly, 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.
+ **/
+
+
+/**
+ * hb_face_count:
+ * @blob: a blob.
+ *
+ * Get 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 */
+ HB_ATOMIC_INT_INIT (1000), /* upem */
+ HB_ATOMIC_INT_INIT (0), /* num_glyphs */
+
+ /* Zero for the rest is fine. */
+};
+
+
+/**
+ * hb_face_create_for_tables:
+ * @reference_table_func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Return value: (transfer full)
+ *
+ * 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.set_relaxed (-1);
+
+ face->data.init0 (face);
+ face->table.init0 (face);
+
+ return face;
+}
+
+
+typedef struct hb_face_for_data_closure_t {
+ hb_blob_t *blob;
+ unsigned int 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 *) calloc (1, sizeof (hb_face_for_data_closure_t));
+ if (unlikely (!closure))
+ return nullptr;
+
+ closure->blob = blob;
+ closure->index = index;
+
+ 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);
+ 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: (Xconstructor)
+ * @blob:
+ * @index:
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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:
+ *
+ *
+ *
+ * Return value: (transfer full)
+ *
+ * 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.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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.
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_face_destroy (hb_face_t *face)
+{
+ if (!hb_object_destroy (face)) return;
+
+ for (hb_face_t::plan_node_t *node = face->shape_plans; node; )
+ {
+ hb_face_t::plan_node_t *next = node->next;
+ hb_shape_plan_destroy (node->shape_plan);
+ free (node);
+ node = next;
+ }
+
+ face->data.fini ();
+ face->table.fini ();
+
+ if (face->destroy)
+ face->destroy (face->user_data);
+
+ free (face);
+}
+
+/**
+ * hb_face_set_user_data: (skip)
+ * @face: a face.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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.
+ * @key:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * 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.
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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.
+ * @tag:
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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.
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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.
+ * @index:
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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.
+ * @upem:
+ *
+ *
+ *
+ * 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.set_relaxed (upem);
+}
+
+/**
+ * hb_face_get_upem:
+ * @face: a face.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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.
+ * @glyph_count:
+ *
+ *
+ *
+ * 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.set_relaxed (glyph_count);
+}
+
+/**
+ * hb_face_get_glyph_count:
+ * @face: a face.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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.
+ * @start_offset: index of first tag to return.
+ * @table_count: input length of @table_tags array, output number of items written.
+ * @table_tags: array to write tags into.
+ *
+ * Retrieves table tags for a face, if possible.
+ *
+ * Return value: total number of tables, or 0 if 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: font face.
+ * @out: set to add Unicode characters covered by @face to.
+ *
+ * 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_variation_selectors:
+ * @face: font face.
+ * @out: set to add Variation Selector characters covered by @face to.
+ *
+ *
+ *
+ * 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: font face.
+ * @out: set to add Unicode characters for @variation_selector covered by @face to.
+ *
+ *
+ *
+ * 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
+
+
+/*
+ * face-builder: A face that has add_table().
+ */
+
+struct hb_face_builder_data_t
+{
+ struct table_entry_t
+ {
+ int cmp (hb_tag_t t) const
+ {
+ if (t < tag) return -1;
+ if (t > tag) return -1;
+ return 0;
+ }
+
+ hb_tag_t tag;
+ hb_blob_t *blob;
+ };
+
+ hb_vector_t<table_entry_t> tables;
+};
+
+static hb_face_builder_data_t *
+_hb_face_builder_data_create ()
+{
+ hb_face_builder_data_t *data = (hb_face_builder_data_t *) 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 (unsigned int i = 0; i < data->tables.length; i++)
+ hb_blob_destroy (data->tables[i].blob);
+
+ data->tables.fini ();
+
+ free (data);
+}
+
+static hb_blob_t *
+_hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
+{
+
+ unsigned int table_count = data->tables.length;
+ unsigned int face_length = table_count * 16 + 12;
+
+ for (unsigned int i = 0; i < table_count; i++)
+ face_length += hb_ceil_to_4 (hb_blob_get_length (data->tables[i].blob));
+
+ char *buf = (char *) 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.lsearch (HB_TAG ('C','F','F',' ')) || data->tables.lsearch (HB_TAG ('C','F','F','2'));
+ hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
+
+ bool ret = f->serialize_single (&c, sfnt_tag, data->tables.as_array ());
+
+ c.end_serialize ();
+
+ if (unlikely (!ret))
+ {
+ free (buf);
+ return nullptr;
+ }
+
+ return hb_blob_create (buf, face_length, HB_MEMORY_MODE_WRITABLE, buf, 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);
+
+ hb_face_builder_data_t::table_entry_t *entry = data->tables.lsearch (tag);
+ if (entry)
+ return hb_blob_reference (entry->blob);
+
+ return nullptr;
+}
+
+
+/**
+ * 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:
+ *
+ * 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;
+
+ hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
+
+ hb_face_builder_data_t::table_entry_t *entry = data->tables.push ();
+ if (data->tables.in_error())
+ return false;
+
+ entry->tag = tag;
+ entry->blob = hb_blob_reference (blob);
+
+ return true;
+}
diff --git a/thirdparty/harfbuzz/src/hb-face.h b/thirdparty/harfbuzz/src/hb-face.h
new file mode 100644
index 0000000000..e8ff090d55
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-face.h
@@ -0,0 +1,158 @@
+/*
+ * 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_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_FACE_H
+#define HB_FACE_H
+
+#include "hb-common.h"
+#include "hb-blob.h"
+#include "hb-set.h"
+
+HB_BEGIN_DECLS
+
+
+HB_EXTERN unsigned int
+hb_face_count (hb_blob_t *blob);
+
+
+/*
+ * hb_face_t
+ */
+
+typedef struct hb_face_t hb_face_t;
+
+HB_EXTERN hb_face_t *
+hb_face_create (hb_blob_t *blob,
+ unsigned int index);
+
+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_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_END_DECLS
+
+#endif /* HB_FACE_H */
diff --git a/thirdparty/harfbuzz/src/hb-face.hh b/thirdparty/harfbuzz/src/hb-face.hh
new file mode 100644
index 0000000000..f1b472ccf3
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-face.hh
@@ -0,0 +1,109 @@
+/*
+ * 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;
+ };
+ hb_atomic_ptr_t<plan_node_t> shape_plans;
+
+ hb_blob_t *reference_table (hb_tag_t tag) const
+ {
+ hb_blob_t *blob;
+
+ if (unlikely (!reference_table_func))
+ return hb_blob_get_empty ();
+
+ blob = reference_table_func (/*XXX*/const_cast<hb_face_t *> (this), tag, user_data);
+ if (unlikely (!blob))
+ return hb_blob_get_empty ();
+
+ return blob;
+ }
+
+ HB_PURE_FUNC unsigned int get_upem () const
+ {
+ unsigned int ret = upem.get_relaxed ();
+ if (unlikely (!ret))
+ {
+ return load_upem ();
+ }
+ return ret;
+ }
+
+ unsigned int get_num_glyphs () const
+ {
+ unsigned int ret = num_glyphs.get_relaxed ();
+ 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/thirdparty/harfbuzz/src/hb-fallback-shape.cc b/thirdparty/harfbuzz/src/hb-fallback-shape.cc
new file mode 100644
index 0000000000..c5b7c2c230
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-fallback-shape.cc
@@ -0,0 +1,125 @@
+/*
+ * 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)
+{
+ /* TODO
+ *
+ * - Apply fallback kern.
+ * - Handle Variation Selectors?
+ * - Apply normalization?
+ *
+ * This will make the fallback shaper into a dumb "TrueType"
+ * shaper which many people unfortunately still request.
+ */
+
+ hb_codepoint_t space;
+ bool has_space = (bool) font->get_nominal_glyph (' ', &space);
+
+ 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->safe_to_break_all ();
+
+ return true;
+}
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-font.cc b/thirdparty/harfbuzz/src/hb-font.cc
new file mode 100644
index 0000000000..27959487dc
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-font.cc
@@ -0,0 +1,2186 @@
+/*
+ * 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-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
+ *
+ * Font objects represent a font face at a certain size and other
+ * parameters (pixels per EM, points per EM, variation settings.)
+ * Fonts are created from font faces, and are used as input to
+ * hb_shape() among other things.
+ **/
+
+
+/*
+ * 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)
+{
+ 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)
+{
+ 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)
+{
+ 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);
+}
+
+DEFINE_NULL_INSTANCE (hb_font_funcs_t) =
+{
+ HB_OBJECT_HEADER_STATIC,
+
+ {
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ },
+ {
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ },
+ {
+ {
+#define HB_FONT_FUNC_IMPLEMENT(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,
+
+ {
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ },
+ {
+#define HB_FONT_FUNC_IMPLEMENT(name) nullptr,
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ },
+ {
+ {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_default,
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ }
+ }
+};
+
+
+/**
+ * hb_font_funcs_create: (Xconstructor)
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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:
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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: font functions.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: font functions.
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
+{
+ if (!hb_object_destroy (ffuncs)) return;
+
+#define HB_FONT_FUNC_IMPLEMENT(name) if (ffuncs->destroy.name) \
+ ffuncs->destroy.name (ffuncs->user_data.name);
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+
+ free (ffuncs);
+}
+
+/**
+ * hb_font_funcs_set_user_data: (skip)
+ * @ffuncs: font functions.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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,
+ hb_bool_t replace)
+{
+ return hb_object_set_user_data (ffuncs, key, data, destroy, replace);
+}
+
+/**
+ * hb_font_funcs_get_user_data: (skip)
+ * @ffuncs: font functions.
+ * @key:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.2
+ **/
+void *
+hb_font_funcs_get_user_data (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: font functions.
+ *
+ *
+ *
+ * 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: font functions.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 0.9.2
+ **/
+hb_bool_t
+hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs)
+{
+ return hb_object_is_immutable (ffuncs);
+}
+
+
+#define HB_FONT_FUNC_IMPLEMENT(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_object_is_immutable (ffuncs)) \
+ { \
+ if (destroy) \
+ destroy (user_data); \
+ return; \
+ } \
+ \
+ if (ffuncs->destroy.name) \
+ ffuncs->destroy.name (ffuncs->user_data.name); \
+ \
+ if (func) { \
+ ffuncs->get.f.name = func; \
+ ffuncs->user_data.name = user_data; \
+ ffuncs->destroy.name = destroy; \
+ } else { \
+ ffuncs->get.f.name = hb_font_get_##name##_default; \
+ ffuncs->user_data.name = nullptr; \
+ ffuncs->destroy.name = nullptr; \
+ } \
+}
+
+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: a font.
+ * @extents: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @extents: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @unicode:
+ * @variation_selector:
+ * @glyph: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @unicode:
+ * @glyph: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 2.6.3
+ **/
+unsigned int
+hb_font_get_nominal_glyphs (hb_font_t *font,
+ unsigned int count,
+ const hb_codepoint_t *first_unicode,
+ unsigned int unicode_stride,
+ hb_codepoint_t *first_glyph,
+ unsigned int glyph_stride)
+{
+ return font->get_nominal_glyphs (count,
+ first_unicode, unicode_stride,
+ first_glyph, glyph_stride);
+}
+
+/**
+ * hb_font_get_variation_glyph:
+ * @font: a font.
+ * @unicode:
+ * @variation_selector:
+ * @glyph: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @glyph:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @glyph:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ *
+ *
+ *
+ * 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: a font.
+ *
+ *
+ *
+ * Since: 1.8.6
+ **/
+void
+hb_font_get_glyph_v_advances (hb_font_t* font,
+ unsigned int count,
+ const hb_codepoint_t *first_glyph,
+ unsigned glyph_stride,
+ hb_position_t *first_advance,
+ unsigned advance_stride)
+{
+ font->get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+
+/**
+ * hb_font_get_glyph_h_origin:
+ * @font: a font.
+ * @glyph:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @glyph:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @left_glyph:
+ * @right_glyph:
+ *
+ *
+ *
+ * Return 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: a font.
+ * @top_glyph:
+ * @bottom_glyph:
+ *
+ *
+ *
+ * Return 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: a font.
+ * @glyph:
+ * @extents: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @glyph:
+ * @point_index:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @glyph:
+ * @name: (array length=size):
+ * @size:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @name: (array length=len):
+ * @len:
+ * @glyph: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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);
+}
+
+
+/* A bit higher-level, and with fallback */
+
+/**
+ * hb_font_get_extents_for_direction:
+ * @font: a font.
+ * @direction:
+ * @extents: (out):
+ *
+ *
+ *
+ * Since: 1.1.3
+ **/
+void
+hb_font_get_extents_for_direction (hb_font_t *font,
+ hb_direction_t direction,
+ hb_font_extents_t *extents)
+{
+ return font->get_extents_for_direction (direction, extents);
+}
+/**
+ * hb_font_get_glyph_advance_for_direction:
+ * @font: a font.
+ * @glyph:
+ * @direction:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * 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)
+{
+ return font->get_glyph_advance_for_direction (glyph, direction, x, y);
+}
+/**
+ * hb_font_get_glyph_advances_for_direction:
+ * @font: a font.
+ * @direction:
+ *
+ *
+ *
+ * Since: 1.8.6
+ **/
+HB_EXTERN void
+hb_font_get_glyph_advances_for_direction (hb_font_t* font,
+ hb_direction_t direction,
+ unsigned int count,
+ const hb_codepoint_t *first_glyph,
+ unsigned glyph_stride,
+ hb_position_t *first_advance,
+ unsigned advance_stride)
+{
+ font->get_glyph_advances_for_direction (direction, count, first_glyph, glyph_stride, first_advance, advance_stride);
+}
+
+/**
+ * hb_font_get_glyph_origin_for_direction:
+ * @font: a font.
+ * @glyph:
+ * @direction:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * 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: a font.
+ * @glyph:
+ * @direction:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * 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: a font.
+ * @glyph:
+ * @direction:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * 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: a font.
+ * @first_glyph:
+ * @second_glyph:
+ * @direction:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * 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: a font.
+ * @glyph:
+ * @direction:
+ * @extents: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: a font.
+ * @glyph:
+ * @point_index:
+ * @direction:
+ * @x: (out):
+ * @y: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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);
+}
+
+/* Generates gidDDD if glyph has no name. */
+/**
+ * hb_font_glyph_to_string:
+ * @font: a font.
+ * @glyph:
+ * @s: (array length=size):
+ * @size:
+ *
+ *
+ *
+ * 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);
+}
+
+/* Parses gidDDD and uniUUUU strings automatically. */
+/**
+ * hb_font_glyph_from_string:
+ * @font: a font.
+ * @s: (array length=len) (element-type uint8_t):
+ * @len:
+ * @glyph: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 0.9.2
+ **/
+hb_bool_t
+hb_font_glyph_from_string (hb_font_t *font,
+ const char *s, int len, /* -1 means nul-terminated */
+ hb_codepoint_t *glyph)
+{
+ return font->glyph_from_string (s, len, glyph);
+}
+
+
+/*
+ * hb_font_t
+ */
+
+DEFINE_NULL_INSTANCE (hb_font_t) =
+{
+ HB_OBJECT_HEADER_STATIC,
+
+ nullptr, /* parent */
+ const_cast<hb_face_t *> (&_hb_Null_hb_face_t),
+
+ 1000, /* x_scale */
+ 1000, /* y_scale */
+ 1<<16, /* x_mult */
+ 1<<16, /* y_mult */
+
+ 0, /* x_ppem */
+ 0, /* y_ppem */
+ 0, /* ptem */
+
+ 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 = hb_face_get_upem (face);
+ font->x_mult = font->y_mult = 1 << 16;
+
+ return font;
+}
+
+/**
+ * hb_font_create: (Xconstructor)
+ * @face: a face.
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * Since: 0.9.2
+ **/
+hb_font_t *
+hb_font_create (hb_face_t *face)
+{
+ 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
+
+ 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)
+{
+ free (font->coords);
+ free (font->design_coords);
+
+ font->coords = coords;
+ font->design_coords = design_coords;
+ font->num_coords = coords_length;
+}
+
+/**
+ * hb_font_create_sub_font:
+ * @parent: parent font.
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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->mults_changed ();
+ 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 *) calloc (num_coords, sizeof (parent->coords[0]));
+ float *design_coords = (float *) calloc (num_coords, sizeof (parent->design_coords[0]));
+ if (likely (coords && design_coords))
+ {
+ memcpy (coords, parent->coords, num_coords * sizeof (parent->coords[0]));
+ 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
+ {
+ free (coords);
+ free (design_coords);
+ }
+ }
+
+ return font;
+}
+
+/**
+ * hb_font_get_empty:
+ *
+ *
+ *
+ * Return value: (transfer full)
+ *
+ * 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: a font.
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * Since: 0.9.2
+ **/
+hb_font_t *
+hb_font_reference (hb_font_t *font)
+{
+ return hb_object_reference (font);
+}
+
+/**
+ * hb_font_destroy: (skip)
+ * @font: a font.
+ *
+ *
+ *
+ * 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);
+
+ free (font->coords);
+ free (font->design_coords);
+
+ free (font);
+}
+
+/**
+ * hb_font_set_user_data: (skip)
+ * @font: a font.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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,
+ hb_bool_t replace)
+{
+ return hb_object_set_user_data (font, key, data, destroy, replace);
+}
+
+/**
+ * hb_font_get_user_data: (skip)
+ * @font: a font.
+ * @key:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.2
+ **/
+void *
+hb_font_get_user_data (hb_font_t *font,
+ hb_user_data_key_t *key)
+{
+ return hb_object_get_user_data (font, key);
+}
+
+/**
+ * hb_font_make_immutable:
+ * @font: a font.
+ *
+ *
+ *
+ * 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: a font.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 0.9.2
+ **/
+hb_bool_t
+hb_font_is_immutable (hb_font_t *font)
+{
+ return hb_object_is_immutable (font);
+}
+
+/**
+ * hb_font_set_parent:
+ * @font: a font.
+ * @parent: new parent.
+ *
+ * Sets 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)
+ 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: a font.
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.2
+ **/
+hb_font_t *
+hb_font_get_parent (hb_font_t *font)
+{
+ return font->parent;
+}
+
+/**
+ * hb_font_set_face:
+ * @font: a font.
+ * @face: new face.
+ *
+ * Sets font-face 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 (unlikely (!face))
+ face = hb_face_get_empty ();
+
+ hb_face_t *old = font->face;
+
+ hb_face_make_immutable (face);
+ font->face = hb_face_reference (face);
+ font->mults_changed ();
+
+ hb_face_destroy (old);
+}
+
+/**
+ * hb_font_get_face:
+ * @font: a font.
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.2
+ **/
+hb_face_t *
+hb_font_get_face (hb_font_t *font)
+{
+ return font->face;
+}
+
+
+/**
+ * hb_font_set_funcs:
+ * @font: a font.
+ * @klass: (closure font_data) (destroy destroy) (scope notified):
+ * @font_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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)
+{
+ if (hb_object_is_immutable (font))
+ {
+ if (destroy)
+ destroy (font_data);
+ return;
+ }
+
+ 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: a font.
+ * @font_data: (destroy destroy) (scope notified):
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_font_set_funcs_data (hb_font_t *font,
+ void *font_data,
+ hb_destroy_func_t destroy)
+{
+ /* Destroy user_data? */
+ if (hb_object_is_immutable (font))
+ {
+ if (destroy)
+ destroy (font_data);
+ return;
+ }
+
+ if (font->destroy)
+ font->destroy (font->user_data);
+
+ font->user_data = font_data;
+ font->destroy = destroy;
+}
+
+
+/**
+ * hb_font_set_scale:
+ * @font: a font.
+ * @x_scale:
+ * @y_scale:
+ *
+ *
+ *
+ * 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;
+
+ font->x_scale = x_scale;
+ font->y_scale = y_scale;
+ font->mults_changed ();
+}
+
+/**
+ * hb_font_get_scale:
+ * @font: a font.
+ * @x_scale: (out):
+ * @y_scale: (out):
+ *
+ *
+ *
+ * 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: a font.
+ * @x_ppem:
+ * @y_ppem:
+ *
+ *
+ *
+ * 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;
+
+ font->x_ppem = x_ppem;
+ font->y_ppem = y_ppem;
+}
+
+/**
+ * hb_font_get_ppem:
+ * @font: a font.
+ * @x_ppem: (out):
+ * @y_ppem: (out):
+ *
+ *
+ *
+ * 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: a font.
+ * @ptem: font size in points.
+ *
+ * Sets "point size" of the font. Set to 0 to unset.
+ *
+ * There are 72 points in an inch.
+ *
+ * Since: 1.6.0
+ **/
+void
+hb_font_set_ptem (hb_font_t *font, float ptem)
+{
+ if (hb_object_is_immutable (font))
+ return;
+
+ font->ptem = ptem;
+}
+
+/**
+ * hb_font_get_ptem:
+ * @font: a font.
+ *
+ * Gets the "point size" of the font. A value of 0 means unset.
+ *
+ * Return value: Point size.
+ *
+ * Since: 0.9.2
+ **/
+float
+hb_font_get_ptem (hb_font_t *font)
+{
+ return font->ptem;
+}
+
+#ifndef HB_NO_VAR
+/*
+ * Variations
+ */
+
+/**
+ * hb_font_set_variations:
+ *
+ * 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;
+
+ if (!variations_length)
+ {
+ hb_font_set_var_coords_normalized (font, nullptr, 0);
+ return;
+ }
+
+ unsigned int coords_length = hb_ot_var_get_axis_count (font->face);
+
+ int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr;
+ float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr;
+
+ if (unlikely (coords_length && !(normalized && design_coords)))
+ {
+ free (normalized);
+ free (design_coords);
+ return;
+ }
+
+ const OT::fvar &fvar = *font->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 (font->face, variations[i].tag, &info) &&
+ info.axis_index < coords_length)
+ {
+ float v = variations[i].value;
+ design_coords[info.axis_index] = v;
+ normalized[info.axis_index] = fvar.normalize_axis_value (info.axis_index, v);
+ }
+ }
+ font->face->table.avar->map_coords (normalized, coords_length);
+
+ _hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
+}
+
+/**
+ * hb_font_set_var_coords_design:
+ *
+ * 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;
+
+ int *normalized = coords_length ? (int *) calloc (coords_length, sizeof (int)) : nullptr;
+ float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr;
+
+ if (unlikely (coords_length && !(normalized && design_coords)))
+ {
+ free (normalized);
+ free (design_coords);
+ return;
+ }
+
+ if (coords_length)
+ 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 instance_index)
+{
+ if (hb_object_is_immutable (font))
+ return;
+
+ unsigned int coords_length = hb_ot_var_named_instance_get_design_coords (font->face, instance_index, nullptr, nullptr);
+
+ float *coords = coords_length ? (float *) calloc (coords_length, sizeof (float)) : nullptr;
+ if (unlikely (coords_length && !coords))
+ return;
+
+ hb_ot_var_named_instance_get_design_coords (font->face, instance_index, &coords_length, coords);
+ hb_font_set_var_coords_design (font, coords, coords_length);
+ free (coords);
+}
+
+/**
+ * hb_font_set_var_coords_normalized:
+ *
+ * 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;
+
+ int *copy = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr;
+ int *unmapped = coords_length ? (int *) calloc (coords_length, sizeof (coords[0])) : nullptr;
+ float *design_coords = coords_length ? (float *) calloc (coords_length, sizeof (design_coords[0])) : nullptr;
+
+ if (unlikely (coords_length && !(copy && unmapped && design_coords)))
+ {
+ free (copy);
+ free (unmapped);
+ free (design_coords);
+ return;
+ }
+
+ if (coords_length)
+ {
+ memcpy (copy, coords, coords_length * sizeof (coords[0]));
+ 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]);
+ free (unmapped);
+
+ _hb_font_adopt_var_coords (font, copy, design_coords, coords_length);
+}
+
+/**
+ * hb_font_get_var_coords_normalized:
+ *
+ * Return value is valid as long as variation coordinates of the font
+ * are not modified.
+ *
+ * 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;
+}
+
+#ifdef HB_EXPERIMENTAL_API
+/**
+ * hb_font_get_var_coords_design:
+ *
+ * Return value is valid as long as variation coordinates of the font
+ * are not modified.
+ *
+ * Since: EXPERIMENTAL
+ */
+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
+#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 *) 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);
+ 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified): callback function.
+ * @user_data: data to pass to @func.
+ * @destroy: function to call when @user_data is not needed anymore.
+ *
+ * Deprecated. Use hb_font_funcs_set_nominal_glyph_func() and
+ * hb_font_funcs_set_variation_glyph_func() instead.
+ *
+ * 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)
+{
+ 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;
+ }
+
+ hb_font_funcs_set_nominal_glyph_func (ffuncs,
+ hb_font_get_nominal_glyph_trampoline,
+ trampoline,
+ trampoline_destroy);
+
+ trampoline_reference (&trampoline->closure);
+ hb_font_funcs_set_variation_glyph_func (ffuncs,
+ hb_font_get_variation_glyph_trampoline,
+ trampoline,
+ trampoline_destroy);
+}
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-font.h b/thirdparty/harfbuzz/src/hb-font.h
new file mode 100644
index 0000000000..e1a5719f1d
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-font.h
@@ -0,0 +1,735 @@
+/*
+ * 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_IN
+#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"
+
+HB_BEGIN_DECLS
+
+
+typedef struct hb_font_t hb_font_t;
+
+
+/*
+ * hb_font_funcs_t
+ */
+
+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 (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 and glyph extents */
+
+/* Note that typically ascender is positive and descender negative in coordinate systems that grow up. */
+typedef struct hb_font_extents_t
+{
+ hb_position_t ascender; /* typographic ascender. */
+ hb_position_t descender; /* typographic descender. */
+ hb_position_t line_gap; /* suggested line spacing gap. */
+ /*< 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;
+
+/* Note that height is negative in coordinate systems that grow up. */
+typedef struct hb_glyph_extents_t
+{
+ hb_position_t x_bearing; /* left side of glyph from origin. */
+ hb_position_t y_bearing; /* top side of glyph from origin. */
+ hb_position_t width; /* distance from left to right side. */
+ hb_position_t height; /* distance from top to bottom side. */
+} hb_glyph_extents_t;
+
+/* func types */
+
+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);
+typedef hb_font_get_font_extents_func_t hb_font_get_font_h_extents_func_t;
+typedef hb_font_get_font_extents_func_t hb_font_get_font_v_extents_func_t;
+
+
+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);
+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);
+
+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);
+
+
+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);
+typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_h_advance_func_t;
+typedef hb_font_get_glyph_advance_func_t hb_font_get_glyph_v_advance_func_t;
+
+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);
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_h_advances_func_t;
+typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
+
+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);
+typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t;
+typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t;
+
+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);
+typedef hb_font_get_glyph_kerning_func_t hb_font_get_glyph_h_kerning_func_t;
+
+
+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);
+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);
+
+
+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);
+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);
+
+
+/* func setters */
+
+/**
+ * hb_font_funcs_set_font_h_extents_func:
+ * @ffuncs: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_v_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_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: font functions.
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN void
+hb_font_funcs_set_glyph_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);
+
+/* 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);
+
+
+/* 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 (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 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_variations (hb_font_t *font,
+ const hb_variation_t *variations,
+ unsigned int variations_length);
+
+HB_EXTERN void
+hb_font_set_var_coords_design (hb_font_t *font,
+ const float *coords,
+ unsigned int coords_length);
+
+#ifdef HB_EXPERIMENTAL_API
+HB_EXTERN const float *
+hb_font_get_var_coords_design (hb_font_t *font,
+ unsigned int *length);
+#endif
+
+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_EXTERN void
+hb_font_set_var_named_instance (hb_font_t *font,
+ unsigned instance_index);
+
+#ifdef HB_EXPERIMENTAL_API
+HB_EXTERN hb_bool_t
+hb_font_draw_glyph (hb_font_t *font, hb_codepoint_t glyph,
+ const hb_draw_funcs_t *funcs, void *user_data);
+#endif
+
+HB_END_DECLS
+
+#endif /* HB_FONT_H */
diff --git a/thirdparty/harfbuzz/src/hb-font.hh b/thirdparty/harfbuzz/src/hb-font.hh
new file mode 100644
index 0000000000..8fc7f44d44
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-font.hh
@@ -0,0 +1,632 @@
+/*
+ * 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 (font_h_extents) \
+ HB_FONT_FUNC_IMPLEMENT (font_v_extents) \
+ HB_FONT_FUNC_IMPLEMENT (nominal_glyph) \
+ HB_FONT_FUNC_IMPLEMENT (nominal_glyphs) \
+ HB_FONT_FUNC_IMPLEMENT (variation_glyph) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_h_advance) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_v_advance) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_h_advances) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_v_advances) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_h_origin) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_v_origin) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_h_kerning) \
+ HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (glyph_v_kerning)) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_extents) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_contour_point) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_name) \
+ HB_FONT_FUNC_IMPLEMENT (glyph_from_name) \
+ /* ^--- Add new callbacks here */
+
+struct hb_font_funcs_t
+{
+ hb_object_header_t header;
+
+ struct {
+#define HB_FONT_FUNC_IMPLEMENT(name) void *name;
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ } user_data;
+
+ struct {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_destroy_func_t name;
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ } destroy;
+
+ /* Don't access these directly. Call font->get_*() instead. */
+ union get_t {
+ struct get_funcs_t {
+#define HB_FONT_FUNC_IMPLEMENT(name) hb_font_get_##name##_func_t name;
+ HB_FONT_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_FONT_FUNC_IMPLEMENT
+ } f;
+ void (*array[0
+#define HB_FONT_FUNC_IMPLEMENT(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;
+
+ hb_font_t *parent;
+ hb_face_t *face;
+
+ int32_t x_scale;
+ int32_t y_scale;
+ int64_t x_mult;
+ int64_t y_mult;
+
+ unsigned int x_ppem;
+ unsigned int y_ppem;
+
+ float ptem;
+
+ /* Font variation coordinates. */
+ 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_scalef (v, x_scale); }
+ hb_position_t em_scalef_y (float v) { return em_scalef (v, y_scale); }
+ float em_fscale_x (int16_t v) { return em_fscale (v, x_scale); }
+ float em_fscale_y (int16_t v) { return em_fscale (v, y_scale); }
+ 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);
+ }
+
+
+ /* 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(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)
+ {
+ memset (extents, 0, sizeof (*extents));
+ return klass->get.f.font_h_extents (this, user_data,
+ extents,
+ klass->user_data.font_h_extents);
+ }
+ hb_bool_t get_font_v_extents (hb_font_extents_t *extents)
+ {
+ memset (extents, 0, sizeof (*extents));
+ return klass->get.f.font_v_extents (this, user_data,
+ extents,
+ klass->user_data.font_v_extents);
+ }
+
+ 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)
+ {
+ *glyph = 0;
+ return klass->get.f.nominal_glyph (this, user_data,
+ unicode, glyph,
+ 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.nominal_glyphs);
+ }
+
+ hb_bool_t get_variation_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector,
+ hb_codepoint_t *glyph)
+ {
+ *glyph = 0;
+ return klass->get.f.variation_glyph (this, user_data,
+ unicode, variation_selector, glyph,
+ klass->user_data.variation_glyph);
+ }
+
+ hb_position_t get_glyph_h_advance (hb_codepoint_t glyph)
+ {
+ return klass->get.f.glyph_h_advance (this, user_data,
+ glyph,
+ klass->user_data.glyph_h_advance);
+ }
+
+ hb_position_t get_glyph_v_advance (hb_codepoint_t glyph)
+ {
+ return klass->get.f.glyph_v_advance (this, user_data,
+ glyph,
+ klass->user_data.glyph_v_advance);
+ }
+
+ 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.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.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.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.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.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.glyph_v_kerning);
+#endif
+ }
+
+ hb_bool_t get_glyph_extents (hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents)
+ {
+ memset (extents, 0, sizeof (*extents));
+ return klass->get.f.glyph_extents (this, user_data,
+ glyph,
+ extents,
+ klass->user_data.glyph_extents);
+ }
+
+ hb_bool_t get_glyph_contour_point (hb_codepoint_t glyph, unsigned int point_index,
+ hb_position_t *x, hb_position_t *y)
+ {
+ *x = *y = 0;
+ return klass->get.f.glyph_contour_point (this, user_data,
+ glyph, point_index,
+ x, y,
+ klass->user_data.glyph_contour_point);
+ }
+
+ hb_bool_t get_glyph_name (hb_codepoint_t glyph,
+ char *name, unsigned int size)
+ {
+ if (size) *name = '\0';
+ return klass->get.f.glyph_name (this, user_data,
+ glyph,
+ name, size,
+ klass->user_data.glyph_name);
+ }
+
+ hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */
+ hb_codepoint_t *glyph)
+ {
+ *glyph = 0;
+ if (len == -1) len = strlen (name);
+ return klass->get.f.glyph_from_name (this, user_data,
+ name, len,
+ glyph,
+ klass->user_data.glyph_from_name);
+ }
+
+
+ /* A bit higher-level, and with fallback */
+
+ 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;
+
+ /* TODO cache this somehow?! */
+ 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 ()
+ {
+ signed upem = face->get_upem ();
+ x_mult = ((int64_t) x_scale << 16) / upem;
+ y_mult = ((int64_t) y_scale << 16) / upem;
+ }
+
+ hb_position_t em_mult (int16_t v, int64_t mult)
+ {
+ return (hb_position_t) ((v * mult) >> 16);
+ }
+ hb_position_t em_scalef (float v, int scale)
+ { return (hb_position_t) roundf (v * scale / face->get_upem ()); }
+ float em_fscale (int16_t v, int scale)
+ { return (float) v * scale / face->get_upem (); }
+};
+DECLARE_NULL_INSTANCE (hb_font_t);
+
+
+#endif /* HB_FONT_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ft.cc b/thirdparty/harfbuzz/src/hb-ft.cc
new file mode 100644
index 0000000000..2680873c27
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ft.cc
@@ -0,0 +1,1042 @@
+/*
+ * 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-font.hh"
+#include "hb-machinery.hh"
+#include "hb-cache.hh"
+
+#include FT_ADVANCES_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_TRUETYPE_TABLES_H
+
+
+/**
+ * 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?
+ */
+
+
+struct hb_ft_font_t
+{
+ mutable hb_mutex_t lock;
+ FT_Face ft_face;
+ int load_flags;
+ bool symbol; /* Whether selected cmap is symbol cmap. */
+ bool unref; /* Whether to destroy ft_face when done. */
+
+ mutable hb_atomic_int_t cached_x_scale;
+ mutable hb_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 *) 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_x_scale.set_relaxed (0);
+ ft_font->advance_cache.init ();
+
+ 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;
+
+ ft_font->advance_cache.fini ();
+
+ if (ft_font->unref)
+ _hb_ft_face_destroy (ft_font->ft_face);
+
+ ft_font->lock.fini ();
+
+ free (ft_font);
+}
+
+/**
+ * hb_ft_font_set_load_flags:
+ * @font: #hb_font_t to work upon
+ * @load_flags: The FreeType load flags to set
+ *
+ * Sets the FT_Load_Glyph load flags for the specified #hb_font_t.
+ *
+ * For more information, see
+ * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx
+ *
+ * Since: 1.0.5
+ **/
+void
+hb_ft_font_set_load_flags (hb_font_t *font, int load_flags)
+{
+ if (hb_object_is_immutable (font))
+ return;
+
+ if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
+ return;
+
+ hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
+
+ ft_font->load_flags = load_flags;
+}
+
+/**
+ * hb_ft_font_get_load_flags:
+ * @font: #hb_font_t to work upon
+ *
+ * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t.
+ *
+ * For more information, see
+ * https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx
+ *
+ * Return value: FT_Load_Glyph flags found
+ *
+ * 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_get_face:
+ * @font: #hb_font_t to work upon
+ *
+ * Fetches the FT_Face associated with the specified #hb_font_t
+ * font object.
+ *
+ * Return value: the FT_Face found
+ *
+ * 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:
+ * @font:
+ *
+ *
+ *
+ * Return value:
+ * 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:
+ * @font:
+ *
+ *
+ *
+ * Return value:
+ * 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 HB_UNUSED,
+ 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) && 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);
+ 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_lock_t lock (ft_font->lock);
+ FT_Face ft_face = ft_font->ft_face;
+ int load_flags = ft_font->load_flags;
+ int mult = font->x_scale < 0 ? -1 : +1;
+
+ if (font->x_scale != ft_font->cached_x_scale.get ())
+ {
+ ft_font->advance_cache.clear ();
+ ft_font->cached_x_scale.set (font->x_scale);
+ }
+
+ 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);
+ ft_font->advance_cache.set (glyph, v);
+ }
+
+ *first_advance = (v * mult + (1<<9)) >> 10;
+ first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+ first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+ }
+}
+
+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;
+
+ if (unlikely (FT_Get_Advance (ft_font->ft_face, glyph, ft_font->load_flags | FT_LOAD_VERTICAL_LAYOUT, &v)))
+ return 0;
+
+ if (font->y_scale < 0)
+ v = -v;
+
+ /* Note: FreeType's vertical metrics grows downward while other FreeType coordinates
+ * have a Y growing upward. Hence the extra negation. */
+ return (-v + (1<<9)) >> 10;
+}
+
+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;
+
+ 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);
+
+ if (font->x_scale < 0)
+ *x = -*x;
+ if (font->y_scale < 0)
+ *y = -*y;
+
+ return true;
+}
+
+#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;
+ 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;
+
+ if (unlikely (FT_Load_Glyph (ft_face, glyph, ft_font->load_flags)))
+ return false;
+
+ extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
+ extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
+ extents->width = ft_face->glyph->metrics.width;
+ extents->height = -ft_face->glyph->metrics.height;
+ if (font->x_scale < 0)
+ {
+ extents->x_bearing = -extents->x_bearing;
+ extents->width = -extents->width;
+ }
+ if (font->y_scale < 0)
+ {
+ extents->y_bearing = -extents->y_bearing;
+ extents->height = -extents->height;
+ }
+ 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;
+ 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);
+ if (font->y_scale < 0)
+ {
+ metrics->ascender = -metrics->ascender;
+ metrics->descender = -metrics->descender;
+ metrics->line_gap = -metrics->line_gap;
+ }
+ return true;
+}
+
+#if HB_USE_ATEXIT
+static void free_static_ft_funcs ();
+#endif
+
+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_font_h_extents_func (funcs, hb_ft_get_font_h_extents, nullptr, nullptr);
+ //hb_font_funcs_set_font_v_extents_func (funcs, hb_ft_get_font_v_extents, nullptr, nullptr);
+ 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_glyph_h_advances_func (funcs, hb_ft_get_glyph_h_advances, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_advance_func (funcs, hb_ft_get_glyph_v_advance, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ft_get_glyph_h_origin, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ft_get_glyph_v_origin, nullptr, nullptr);
+#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);
+
+ hb_font_funcs_make_immutable (funcs);
+
+#if HB_USE_ATEXIT
+ atexit (free_static_ft_funcs);
+#endif
+
+ return funcs;
+ }
+} static_ft_funcs;
+
+#if HB_USE_ATEXIT
+static
+void free_static_ft_funcs ()
+{
+ static_ft_funcs.free_instance ();
+}
+#endif
+
+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 *) malloc (length);
+ if (!buffer)
+ return nullptr;
+
+ error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
+ if (error)
+ {
+ free (buffer);
+ return nullptr;
+ }
+
+ return hb_blob_create ((const char *) buffer, length,
+ HB_MEMORY_MODE_WRITABLE,
+ buffer, free);
+}
+
+/**
+ * hb_ft_face_create:
+ * @ft_face: (destroy destroy) (scope notified): FT_Face to work upon
+ * @destroy: A callback to call when the face object is not needed anymore
+ *
+ * Creates an #hb_face_t face object from the specified FT_Face.
+ *
+ * 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.
+ *
+ * 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 (FT_Face ft_face)
+{
+ 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.
+ *
+ * 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 = (FT_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: (optional): 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. Otherwise, HarfBuzz will not pick up
+ * the face size.</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_has_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 *) calloc (mm_var->num_axis, sizeof (FT_Fixed));
+ int *coords = (int *) 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);
+ }
+ }
+ free (coords);
+ free (ft_coords);
+#ifdef HAVE_FT_DONE_MM_VAR
+ FT_Done_MM_Var (ft_face->glyph->library, mm_var);
+#else
+ free (mm_var);
+#endif
+ }
+#endif
+}
+
+/**
+ * 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_references() on it. Otherwise, HarfBuzz will not pick up
+ * the face size.</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);
+}
+
+#if HB_USE_ATEXIT
+static void free_static_ft_library ();
+#endif
+
+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;
+
+#if HB_USE_ATEXIT
+ atexit (free_static_ft_library);
+#endif
+
+ return l;
+ }
+ static void destroy (FT_Library l)
+ {
+ FT_Done_FreeType (l);
+ }
+ static FT_Library get_null ()
+ {
+ return nullptr;
+ }
+} static_ft_library;
+
+#if HB_USE_ATEXIT
+static
+void free_static_ft_library ()
+{
+ static_ft_library.free_instance ();
+}
+#endif
+
+static FT_Library
+get_ft_library ()
+{
+ return static_ft_library.get_unconst ();
+}
+
+static void
+_release_blob (FT_Face ft_face)
+{
+ 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_face_t face object created with hb_ft_face_create()
+ * is preconfigured for FreeType font functions and does not
+ * require this function to be used.
+ *
+ * <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_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
+ if (font->x_scale < 0 || font->y_scale < 0)
+ {
+ FT_Matrix matrix = { font->x_scale < 0 ? -1 : +1, 0,
+ 0, font->y_scale < 0 ? -1 : +1};
+ FT_Set_Transform (ft_face, &matrix, nullptr);
+ }
+
+#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
+ unsigned int num_coords;
+ const int *coords = hb_font_get_var_coords_normalized (font, &num_coords);
+ if (num_coords)
+ {
+ FT_Fixed *ft_coords = (FT_Fixed *) calloc (num_coords, sizeof (FT_Fixed));
+ if (ft_coords)
+ {
+ for (unsigned int i = 0; i < num_coords; i++)
+ ft_coords[i] = coords[i] * 4;
+ FT_Set_Var_Blend_Coordinates (ft_face, num_coords, ft_coords);
+ free (ft_coords);
+ }
+ }
+#endif
+
+ ft_face->generic.data = blob;
+ ft_face->generic.finalizer = (FT_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);
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ft.h b/thirdparty/harfbuzz/src/hb-ft.h
new file mode 100644
index 0000000000..bf07115ab9
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ft.h
@@ -0,0 +1,138 @@
+/*
+ * 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 change. */
+HB_EXTERN void
+hb_ft_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/thirdparty/harfbuzz/src/hb-gdi.cc b/thirdparty/harfbuzz/src/hb-gdi.cc
new file mode 100644
index 0000000000..f6306ef89f
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-gdi.cc
@@ -0,0 +1,73 @@
+/*
+ * 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"
+
+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 *) 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, free);
+
+fail_with_releasedc_and_free:
+ free (buffer);
+fail_with_releasedc:
+ ReleaseDC (nullptr, hdc);
+fail:
+ return hb_blob_get_empty ();
+}
+
+/**
+ * hb_gdi_face_create:
+ * @hfont: a HFONT object.
+ *
+ * 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/thirdparty/harfbuzz/src/hb-gdi.h b/thirdparty/harfbuzz/src/hb-gdi.h
new file mode 100644
index 0000000000..68cc43917e
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-glib.cc b/thirdparty/harfbuzz/src/hb-glib.cc
new file mode 100644
index 0000000000..f93bb8853c
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-glib.cc
@@ -0,0 +1,307 @@
+/*
+ * 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);
+#endif
+
+ /* We don't ifdef-out the fallback code such that compiler always
+ * sees it and makes sure it's compilable. */
+
+ gchar utf8[12];
+ gchar *normalized;
+ int len;
+ hb_bool_t ret;
+
+ len = g_unichar_to_utf8 (a, utf8);
+ len += g_unichar_to_utf8 (b, utf8 + len);
+ normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFC);
+ len = g_utf8_strlen (normalized, -1);
+ if (unlikely (!len))
+ return false;
+
+ if (len == 1) {
+ *ab = g_utf8_get_char (normalized);
+ ret = true;
+ } else {
+ ret = false;
+ }
+
+ g_free (normalized);
+ return ret;
+}
+
+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);
+#endif
+
+ /* We don't ifdef-out the fallback code such that compiler always
+ * sees it and makes sure it's compilable. */
+
+ gchar utf8[6];
+ gchar *normalized;
+ int len;
+ hb_bool_t ret;
+
+ len = g_unichar_to_utf8 (ab, utf8);
+ normalized = g_utf8_normalize (utf8, len, G_NORMALIZE_NFD);
+ len = g_utf8_strlen (normalized, -1);
+ if (unlikely (!len))
+ return false;
+
+ if (len == 1) {
+ *a = g_utf8_get_char (normalized);
+ *b = 0;
+ ret = *a != ab;
+ } else if (len == 2) {
+ *a = g_utf8_get_char (normalized);
+ *b = g_utf8_get_char (g_utf8_next_char (normalized));
+ /* Here's the ugly part: if ab decomposes to a single character and
+ * that character decomposes again, we have to detect that and undo
+ * the second part :-(. */
+ gchar *recomposed = g_utf8_normalize (normalized, -1, G_NORMALIZE_NFC);
+ hb_codepoint_t c = g_utf8_get_char (recomposed);
+ if (c != ab && c != *a) {
+ *a = c;
+ *b = 0;
+ }
+ g_free (recomposed);
+ ret = true;
+ } else {
+ /* If decomposed to more than two characters, take the last one,
+ * and recompose the rest to get the first component. */
+ gchar *end = g_utf8_offset_to_pointer (normalized, len - 1);
+ gchar *recomposed;
+ *b = g_utf8_get_char (end);
+ recomposed = g_utf8_normalize (normalized, end - normalized, G_NORMALIZE_NFC);
+ /* We expect that recomposed has exactly one character now. */
+ *a = g_utf8_get_char (recomposed);
+ g_free (recomposed);
+ ret = true;
+ }
+
+ g_free (normalized);
+ return ret;
+}
+
+
+#if HB_USE_ATEXIT
+static void free_static_glib_funcs ();
+#endif
+
+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);
+
+#if HB_USE_ATEXIT
+ atexit (free_static_glib_funcs);
+#endif
+
+ return funcs;
+ }
+} static_glib_funcs;
+
+#if HB_USE_ATEXIT
+static
+void free_static_glib_funcs ()
+{
+ static_glib_funcs.free_instance ();
+}
+#endif
+
+/**
+ * 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/thirdparty/harfbuzz/src/hb-glib.h b/thirdparty/harfbuzz/src/hb-glib.h
new file mode 100644
index 0000000000..5f04183ba1
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-gobject-structs.cc b/thirdparty/harfbuzz/src/hb-gobject-structs.cc
new file mode 100644
index 0000000000..7c46e26400
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-gobject-structs.cc
@@ -0,0 +1,110 @@
+/*
+ * 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 *) calloc (1, sizeof (hb_##name##_t)); \
+ if (unlikely (!c)) return nullptr; \
+ *c = *l; \
+ return c; \
+ } \
+ static void _hb_##name##_destroy (hb_##name##_t *l) { 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 (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 (user_data_key)
+
+HB_DEFINE_VALUE_TYPE (ot_math_glyph_variant)
+HB_DEFINE_VALUE_TYPE (ot_math_glyph_part)
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-gobject-structs.h b/thirdparty/harfbuzz/src/hb-gobject-structs.h
new file mode 100644
index 0000000000..6fad8d7019
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-gobject-structs.h
@@ -0,0 +1,142 @@
+/*
+ * 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_IN
+#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_gobject_blob_get_type:
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN GType
+hb_gobject_blob_get_type (void);
+#define HB_GOBJECT_TYPE_BLOB (hb_gobject_blob_get_type ())
+
+/**
+ * hb_gobject_buffer_get_type:
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN GType
+hb_gobject_buffer_get_type (void);
+#define HB_GOBJECT_TYPE_BUFFER (hb_gobject_buffer_get_type ())
+
+/**
+ * hb_gobject_face_get_type:
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN GType
+hb_gobject_face_get_type (void);
+#define HB_GOBJECT_TYPE_FACE (hb_gobject_face_get_type ())
+
+/**
+ * hb_gobject_font_get_type:
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN GType
+hb_gobject_font_get_type (void);
+#define HB_GOBJECT_TYPE_FONT (hb_gobject_font_get_type ())
+
+/**
+ * hb_gobject_font_funcs_get_type:
+ *
+ * Since: 0.9.2
+ **/
+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_gobject_unicode_funcs_get_type:
+ *
+ * Since: 0.9.2
+ **/
+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_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_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/thirdparty/harfbuzz/src/hb-gobject.h b/thirdparty/harfbuzz/src/hb-gobject.h
new file mode 100644
index 0000000000..8891aa0ee7
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-graphite2.cc b/thirdparty/harfbuzz/src/hb-graphite2.cc
new file mode 100644
index 0000000000..d8a72dc2f1
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-graphite2.cc
@@ -0,0 +1,442 @@
+/*
+ * 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 *) 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 *) calloc (1, sizeof (hb_graphite2_face_data_t));
+ if (unlikely (!data))
+ return nullptr;
+
+ data->face = face;
+ data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
+
+ if (unlikely (!data->grface)) {
+ 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;
+ free (old);
+ }
+
+ gr_face_destroy (data->grface);
+
+ free (data);
+}
+
+/**
+ * hb_graphite2_face_get_gr_face:
+ * @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:
+ *
+ * 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;
+ unsigned 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);
+ }
+
+ 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;
+
+ /* TODO ensure_native_direction. */
+
+ hb_tag_t script_tag[HB_OT_MAX_TAGS_PER_SCRIPT];
+ unsigned int count = HB_OT_MAX_TAGS_PER_SCRIPT;
+ hb_ot_tags_from_script_and_language (hb_buffer_get_script (buffer),
+ HB_LANGUAGE_INVALID,
+ &count,
+ script_tag,
+ nullptr, nullptr);
+
+ seg = gr_make_seg (nullptr, grface,
+ count ? script_tag[count - 1] : HB_OT_TAG_DEFAULT_SCRIPT,
+ feats,
+ gr_utf32, chars, buffer->len,
+ 2 | (hb_buffer_get_direction (buffer) == 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;
+ }
+
+ 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
+
+ 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(buffer->props.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(buffer->props.direction))
+ {
+ c->advance = curradv - gr_slot_origin_X(is) * xscale;
+ curradv -= c->advance;
+ }
+ else
+ {
+ c->advance = 0;
+ clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv;
+ curradv += clusters[ci].advance;
+ }
+ 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(buffer->props.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(buffer->props.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->unsafe_to_break_all ();
+
+ return true;
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-graphite2.h b/thirdparty/harfbuzz/src/hb-graphite2.h
new file mode 100644
index 0000000000..f299da9f71
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-graphite2.h
@@ -0,0 +1,60 @@
+/*
+ * 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_EXTERN HB_DEPRECATED_FOR (hb_graphite2_face_get_gr_face) gr_font *
+hb_graphite2_font_get_gr_font (hb_font_t *font);
+
+#endif
+
+
+HB_END_DECLS
+
+#endif /* HB_GRAPHITE2_H */
diff --git a/thirdparty/harfbuzz/src/hb-icu.cc b/thirdparty/harfbuzz/src/hb-icu.cc
new file mode 100644
index 0000000000..008a39e414
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-icu.cc
@@ -0,0 +1,296 @@
+/*
+ * 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;
+}
+
+
+#if HB_USE_ATEXIT
+static void free_static_icu_funcs ();
+#endif
+
+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);
+
+#if HB_USE_ATEXIT
+ atexit (free_static_icu_funcs);
+#endif
+
+ return funcs;
+ }
+} static_icu_funcs;
+
+#if HB_USE_ATEXIT
+static
+void free_static_icu_funcs ()
+{
+ static_icu_funcs.free_instance ();
+}
+#endif
+
+/**
+ * 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/thirdparty/harfbuzz/src/hb-icu.h b/thirdparty/harfbuzz/src/hb-icu.h
new file mode 100644
index 0000000000..2db6a7b679
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-iter.hh b/thirdparty/harfbuzz/src/hb-iter.hh
new file mode 100644
index 0000000000..981c5c218c
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-iter.hh
@@ -0,0 +1,939 @@
+/*
+ * 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.
+ *
+ * TODO Document more.
+ *
+ * If iterator implementation implements operator!=, then can be
+ * used in range-based for loop. That comes free 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.
+ * TODO When opting in for C++17, address this by changing return
+ * type of .end()?
+ */
+
+/*
+ * 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;
+
+ 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:
+
+ /* TODO:
+ * Port operators below to use hb_enable_if to sniff which method implements
+ * an operator and use it, and remove hb_iter_fallback_mixin_t completely. */
+
+ /* Operators. */
+ iter_t iter () const { return *thiz(); }
+ iter_t operator + () const { return *thiz(); }
+ iter_t begin () const { return *thiz(); }
+ iter_t end () const { return thiz()->__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.
+ * TODO Use a wrapper return type to fix for non-reference type. */
+ template <typename T = item_t,
+ hb_enable_if (hb_is_reference (T))>
+ hb_remove_reference<item_t>* operator -> () const { return hb_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::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 (hb_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> unsigned
+ operator () (T&& c) const
+ { return c.len (); }
+
+}
+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)
+
+/* 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<hb_add_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 (hb_forward<Rhs> (rhs) (hb_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;
+ 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;
+ hb_reference_wrapper<Pred> p;
+ 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_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 (hb_forward<S2> (s), hb_declval<T&> ()))>
+ { v = hb_invoke (hb_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 (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 (hb_forward<Pred> (p), hb_get (hb_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 (hb_forward<Pred> (p), hb_get (hb_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 (hb_forward<Pred> (p), hb_get (hb_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/thirdparty/harfbuzz/src/hb-kern.hh b/thirdparty/harfbuzz/src/hb-kern.hh
new file mode 100644
index 0000000000..3f952fe7fc
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-kern.hh
@@ -0,0 +1,138 @@
+/*
+ * 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
+ {
+ OT::hb_ot_apply_context_t c (1, font, buffer);
+ 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, 1);
+ if (!skippy_iter.next ())
+ {
+ 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;
+ }
+ }
+
+ const Driver &driver;
+ bool crossStream;
+};
+
+
+} /* namespace OT */
+
+
+#endif /* HB_KERN_HH */
diff --git a/thirdparty/harfbuzz/src/hb-machinery.hh b/thirdparty/harfbuzz/src/hb-machinery.hh
new file mode 100644
index 0000000000..54bc60d4c8
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-machinery.hh
@@ -0,0 +1,307 @@
+/*
+ * 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"
+#include "hb-serialize.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
+ */
+
+/* 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.
+ */
+
+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;
+
+ 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 ()); }
+
+ void free_instance ()
+ {
+ retry:
+ Stored *p = instance.get ();
+ 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 (); }
+ const Returned & 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 ();
+ 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 *must* be called when there are no other threads 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 *) calloc (1, sizeof (Stored));
+ if (likely (p))
+ p->init (data);
+ return p;
+ }
+ static Stored *create ()
+ {
+ Stored *p = (Stored *) calloc (1, sizeof (Stored));
+ if (likely (p))
+ p->init ();
+ return p;
+ }
+ static void destroy (Stored *p)
+ {
+ p->fini ();
+ 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> {};
+
+template <typename T, unsigned int WheresFace>
+struct hb_table_lazy_loader_t : hb_lazy_loader_t<T,
+ hb_table_lazy_loader_t<T, WheresFace>,
+ hb_face_t, WheresFace,
+ hb_blob_t>
+{
+ static hb_blob_t *create (hb_face_t *face)
+ { return hb_sanitize_context_t ().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 (); }
+};
+
+template <typename Subclass>
+struct hb_font_funcs_lazy_loader_t : hb_lazy_loader_t<hb_font_funcs_t, Subclass>
+{
+ static void destroy (hb_font_funcs_t *p)
+ { hb_font_funcs_destroy (p); }
+ static const hb_font_funcs_t *get_null ()
+ { return hb_font_funcs_get_empty (); }
+};
+template <typename Subclass>
+struct hb_unicode_funcs_lazy_loader_t : hb_lazy_loader_t<hb_unicode_funcs_t, Subclass>
+{
+ static void destroy (hb_unicode_funcs_t *p)
+ { hb_unicode_funcs_destroy (p); }
+ static const hb_unicode_funcs_t *get_null ()
+ { return hb_unicode_funcs_get_empty (); }
+};
+
+
+#endif /* HB_MACHINERY_HH */
diff --git a/thirdparty/harfbuzz/src/hb-map.cc b/thirdparty/harfbuzz/src/hb-map.cc
new file mode 100644
index 0000000000..191be14372
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-map.cc
@@ -0,0 +1,268 @@
+/*
+ * 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: (Xconstructor)
+ *
+ * Return value: (transfer full):
+ *
+ * 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 ();
+
+ map->init_shallow ();
+
+ return map;
+}
+
+/**
+ * hb_map_get_empty:
+ *
+ * Return value: (transfer full):
+ *
+ * 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.
+ *
+ * Return value: (transfer full):
+ *
+ * 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.
+ *
+ * Since: 1.7.7
+ **/
+void
+hb_map_destroy (hb_map_t *map)
+{
+ if (!hb_object_destroy (map)) return;
+
+ map->fini_shallow ();
+
+ free (map);
+}
+
+/**
+ * hb_map_set_user_data: (skip)
+ * @map: a map.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ * Return value:
+ *
+ * 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:
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 1.7.7
+ **/
+void *
+hb_map_get_user_data (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.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 1.7.7
+ **/
+hb_bool_t
+hb_map_allocation_successful (const hb_map_t *map)
+{
+ return map->successful;
+}
+
+
+/**
+ * hb_map_set:
+ * @map: a map.
+ * @key:
+ * @value:
+ *
+ *
+ *
+ * Since: 1.7.7
+ **/
+void
+hb_map_set (hb_map_t *map,
+ hb_codepoint_t key,
+ hb_codepoint_t value)
+{
+ map->set (key, value);
+}
+
+/**
+ * hb_map_get:
+ * @map: a map.
+ * @key:
+ *
+ *
+ *
+ * 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:
+ *
+ *
+ *
+ * Since: 1.7.7
+ **/
+void
+hb_map_del (hb_map_t *map,
+ hb_codepoint_t key)
+{
+ map->del (key);
+}
+
+/**
+ * hb_map_has:
+ * @map: a map.
+ * @key:
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * Since: 1.7.7
+ **/
+void
+hb_map_clear (hb_map_t *map)
+{
+ return map->clear ();
+}
+
+/**
+ * hb_map_is_empty:
+ * @map: a map.
+ *
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * Since: 1.7.7
+ **/
+unsigned int
+hb_map_get_population (const hb_map_t *map)
+{
+ return map->get_population ();
+}
diff --git a/thirdparty/harfbuzz/src/hb-map.h b/thirdparty/harfbuzz/src/hb-map.h
new file mode 100644
index 0000000000..b77843c2ba
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-map.h
@@ -0,0 +1,104 @@
+/*
+ * 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_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_MAP_H
+#define HB_MAP_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * Since: 1.7.7
+ */
+#define HB_MAP_VALUE_INVALID ((hb_codepoint_t) -1)
+
+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 (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 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 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_END_DECLS
+
+#endif /* HB_MAP_H */
diff --git a/thirdparty/harfbuzz/src/hb-map.hh b/thirdparty/harfbuzz/src/hb-map.hh
new file mode 100644
index 0000000000..92c1bd67e5
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-map.hh
@@ -0,0 +1,326 @@
+/*
+ * 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"
+
+
+/*
+ * hb_hashmap_t
+ */
+
+template <typename K, typename V,
+ K kINVALID = hb_is_pointer (K) ? 0 : hb_is_signed (K) ? hb_int_min (K) : (K) -1,
+ V vINVALID = hb_is_pointer (V) ? 0 : hb_is_signed (V) ? hb_int_min (V) : (V) -1>
+struct hb_hashmap_t
+{
+ HB_DELETE_COPY_ASSIGN (hb_hashmap_t);
+ hb_hashmap_t () { init (); }
+ ~hb_hashmap_t () { fini (); }
+
+ static_assert (hb_is_integral (K) || hb_is_pointer (K), "");
+ static_assert (hb_is_integral (V) || hb_is_pointer (V), "");
+
+ struct item_t
+ {
+ K key;
+ V value;
+ uint32_t hash;
+
+ void clear () { key = kINVALID; value = vINVALID; hash = 0; }
+
+ bool operator == (const K &o) { return hb_deref (key) == hb_deref (o); }
+ bool operator == (const item_t &o) { return *this == o.key; }
+ bool is_unused () const { return key == kINVALID; }
+ bool is_tombstone () const { return key != kINVALID && value == vINVALID; }
+ bool is_real () const { return key != kINVALID && value != vINVALID; }
+ hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); }
+ };
+
+ hb_object_header_t header;
+ bool successful; /* Allocations successful */
+ unsigned int population; /* Not including tombstones. */
+ unsigned int occupancy; /* Including tombstones. */
+ unsigned int mask;
+ unsigned int prime;
+ item_t *items;
+
+ void init_shallow ()
+ {
+ successful = true;
+ population = occupancy = 0;
+ mask = 0;
+ prime = 0;
+ items = nullptr;
+ }
+ void init ()
+ {
+ hb_object_init (this);
+ init_shallow ();
+ }
+ void fini_shallow ()
+ {
+ free (items);
+ items = nullptr;
+ population = occupancy = 0;
+ }
+ void fini ()
+ {
+ hb_object_fini (this);
+ fini_shallow ();
+ }
+
+ void reset ()
+ {
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+ successful = true;
+ clear ();
+ }
+
+ bool in_error () const { return !successful; }
+
+ bool resize ()
+ {
+ if (unlikely (!successful)) return false;
+
+ unsigned int power = hb_bit_storage (population * 2 + 8);
+ unsigned int new_size = 1u << power;
+ item_t *new_items = (item_t *) malloc ((size_t) new_size * sizeof (item_t));
+ if (unlikely (!new_items))
+ {
+ successful = false;
+ return false;
+ }
+ for (auto &_ : hb_iter (new_items, new_size))
+ _.clear ();
+
+ unsigned int old_size = mask + 1;
+ item_t *old_items = items;
+
+ /* Switch to new, empty, array. */
+ population = occupancy = 0;
+ mask = new_size - 1;
+ prime = prime_for (power);
+ items = new_items;
+
+ /* Insert back old items. */
+ if (old_items)
+ for (unsigned int i = 0; i < old_size; i++)
+ if (old_items[i].is_real ())
+ set_with_hash (old_items[i].key,
+ old_items[i].hash,
+ old_items[i].value);
+
+ free (old_items);
+
+ return true;
+ }
+
+ void set (K key, V value)
+ {
+ set_with_hash (key, hb_hash (key), value);
+ }
+
+ V get (K key) const
+ {
+ if (unlikely (!items)) return vINVALID;
+ unsigned int i = bucket_for (key);
+ return items[i].is_real () && items[i] == key ? items[i].value : vINVALID;
+ }
+
+ void del (K key) { set (key, vINVALID); }
+
+ /* Has interface. */
+ static constexpr V SENTINEL = vINVALID;
+ typedef V value_t;
+ value_t operator [] (K k) const { return get (k); }
+ bool has (K k, V *vp = nullptr) const
+ {
+ V v = (*this)[k];
+ if (vp) *vp = v;
+ return v != SENTINEL;
+ }
+ /* Projection. */
+ V operator () (K k) const { return get (k); }
+
+ void clear ()
+ {
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+ if (items)
+ for (auto &_ : hb_iter (items, mask + 1))
+ _.clear ();
+
+ population = occupancy = 0;
+ }
+
+ bool is_empty () const { return population == 0; }
+
+ unsigned int get_population () const { return population; }
+
+ /*
+ * Iterator
+ */
+ auto iter () const HB_AUTO_RETURN
+ (
+ + hb_array (items, mask ? mask + 1 : 0)
+ | hb_filter (&item_t::is_real)
+ | hb_map (&item_t::get_pair)
+ )
+ auto keys () const HB_AUTO_RETURN
+ (
+ + hb_array (items, mask ? mask + 1 : 0)
+ | hb_filter (&item_t::is_real)
+ | hb_map (&item_t::key)
+ | hb_map (hb_ridentity)
+ )
+ auto values () const HB_AUTO_RETURN
+ (
+ + hb_array (items, mask ? mask + 1 : 0)
+ | hb_filter (&item_t::is_real)
+ | hb_map (&item_t::value)
+ | hb_map (hb_ridentity)
+ )
+
+ /* Sink interface. */
+ hb_hashmap_t& operator << (const hb_pair_t<K, V>& v)
+ { set (v.first, v.second); return *this; }
+
+ protected:
+
+ void set_with_hash (K key, uint32_t hash, V value)
+ {
+ if (unlikely (!successful)) return;
+ if (unlikely (key == kINVALID)) return;
+ if ((occupancy + occupancy / 2) >= mask && !resize ()) return;
+ unsigned int i = bucket_for_hash (key, hash);
+
+ if (value == vINVALID && items[i].key != key)
+ return; /* Trying to delete non-existent key. */
+
+ if (!items[i].is_unused ())
+ {
+ occupancy--;
+ if (items[i].is_tombstone ())
+ population--;
+ }
+
+ items[i].key = key;
+ items[i].value = value;
+ items[i].hash = hash;
+
+ occupancy++;
+ if (!items[i].is_tombstone ())
+ population++;
+ }
+
+ unsigned int bucket_for (K key) const
+ {
+ return bucket_for_hash (key, hb_hash (key));
+ }
+
+ unsigned int bucket_for_hash (K key, uint32_t hash) const
+ {
+ unsigned int i = hash % prime;
+ unsigned int step = 0;
+ unsigned int tombstone = (unsigned) -1;
+ while (!items[i].is_unused ())
+ {
+ if (items[i].hash == hash && items[i] == key)
+ return i;
+ if (tombstone == (unsigned) -1 && items[i].is_tombstone ())
+ tombstone = i;
+ i = (i + ++step) & mask;
+ }
+ return tombstone == (unsigned) -1 ? i : tombstone;
+ }
+
+ 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,
+ HB_MAP_VALUE_INVALID,
+ HB_MAP_VALUE_INVALID> {};
+
+
+#endif /* HB_MAP_HH */
diff --git a/thirdparty/harfbuzz/src/hb-meta.hh b/thirdparty/harfbuzz/src/hb-meta.hh
new file mode 100644
index 0000000000..4c0898b1b7
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-meta.hh
@@ -0,0 +1,410 @@
+/*
+ * 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"
+
+
+/*
+ * 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>;
+
+
+/* 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;
+
+struct
+{
+ template <typename T> constexpr T*
+ operator () (T& arg) const
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-align"
+ /* https://en.cppreference.com/w/cpp/memory/addressof */
+ return reinterpret_cast<T*> (
+ &const_cast<char&> (
+ reinterpret_cast<const volatile char&> (arg)));
+#pragma GCC diagnostic pop
+ }
+}
+HB_FUNCOBJ (hb_addressof);
+
+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_bool_constant<false>{};
+template <typename T> struct hb_match_const<const T> : hb_type_identity_t<T>, hb_bool_constant<true> {};
+template <typename T> using hb_remove_const = typename hb_match_const<T>::type;
+template <typename T> using hb_add_const = const T;
+#define hb_is_const(T) hb_match_const<T>::value
+template <typename T> struct hb_match_reference : hb_type_identity_t<T>, hb_bool_constant<false>{};
+template <typename T> struct hb_match_reference<T &> : hb_type_identity_t<T>, hb_bool_constant<true> {};
+template <typename T> struct hb_match_reference<T &&> : hb_type_identity_t<T>, hb_bool_constant<true> {};
+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));
+#define hb_is_reference(T) hb_match_reference<T>::value
+template <typename T> struct hb_match_pointer : hb_type_identity_t<T>, hb_bool_constant<false>{};
+template <typename T> struct hb_match_pointer<T *> : hb_type_identity_t<T>, hb_bool_constant<true> {};
+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));
+#define hb_is_pointer(T) hb_match_pointer<T>::value
+
+
+/* TODO Add feature-parity to std::decay. */
+template <typename T> using hb_decay = hb_remove_const<hb_remove_reference<T>>;
+
+
+template<bool B, class T, class F>
+struct _hb_conditional { typedef T type; };
+template<class T, class F>
+struct _hb_conditional<false, T, F> { typedef F type; };
+template<bool B, class T, class F>
+using hb_conditional = typename _hb_conditional<B, T, F>::type;
+
+
+template <typename From, typename To>
+struct hb_is_convertible
+{
+ private:
+ static constexpr bool from_void = hb_is_same (void, hb_decay<From>);
+ static constexpr bool to_void = hb_is_same (void, hb_decay<To> );
+ static constexpr bool either_void = from_void || to_void;
+ static constexpr bool both_void = from_void && to_void;
+
+ static hb_true_type impl2 (hb_conditional<to_void, int, To>);
+
+ template <typename T>
+ static auto impl (hb_priority<1>) -> decltype (impl2 (hb_declval (T)));
+ template <typename T>
+ static hb_false_type impl (hb_priority<0>);
+ public:
+ static constexpr bool value = both_void ||
+ (!either_void &&
+ decltype (impl<hb_conditional<from_void, int, From>> (hb_prioritize))::value);
+};
+#define hb_is_convertible(From,To) hb_is_convertible<From, To>::value
+
+template <typename Base, typename Derived>
+using hb_is_base_of = hb_is_convertible<hb_decay<Derived> *, hb_decay<Base> *>;
+#define hb_is_base_of(Base,Derived) hb_is_base_of<Base, Derived>::value
+
+template <typename From, typename To>
+using hb_is_cr_convertible = hb_bool_constant<
+ hb_is_same (hb_decay<From>, hb_decay<To>) &&
+ (!hb_is_const (From) || hb_is_const (To)) &&
+ (!hb_is_reference (To) || hb_is_const (To) || hb_is_reference (To))
+>;
+#define hb_is_cr_convertible(From,To) hb_is_cr_convertible<From, To>::value
+
+/* std::move and std::forward */
+
+template <typename T>
+static constexpr hb_remove_reference<T>&& hb_move (T&& t) { return (hb_remove_reference<T>&&) (t); }
+
+template <typename T>
+static constexpr T&& hb_forward (hb_remove_reference<T>& t) { return (T&&) t; }
+template <typename T>
+static constexpr T&& hb_forward (hb_remove_reference<T>&& t) { return (T&&) t; }
+
+struct
+{
+ template <typename T> constexpr auto
+ operator () (T&& v) const HB_AUTO_RETURN (hb_forward<T> (v))
+
+ template <typename T> constexpr auto
+ operator () (T *v) const HB_AUTO_RETURN (*v)
+}
+HB_FUNCOBJ (hb_deref);
+
+struct
+{
+ template <typename T> constexpr auto
+ operator () (T&& v) const HB_AUTO_RETURN (hb_forward<T> (v))
+
+ template <typename T> constexpr auto
+ operator () (T& v) const HB_AUTO_RETURN (hb_addressof (v))
+}
+HB_FUNCOBJ (hb_ref);
+
+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 () const { return v; }
+ T get () const { return v; }
+ T v;
+};
+template <typename T>
+struct hb_reference_wrapper<T&>
+{
+ hb_reference_wrapper (T& v) : v (hb_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& () const { return *v; }
+ T& get () const { return *v; }
+ T* v;
+};
+
+
+template <typename T>
+using hb_is_integral = hb_bool_constant<
+ hb_is_same (hb_decay<T>, char) ||
+ hb_is_same (hb_decay<T>, signed char) ||
+ hb_is_same (hb_decay<T>, unsigned char) ||
+ hb_is_same (hb_decay<T>, signed int) ||
+ hb_is_same (hb_decay<T>, unsigned int) ||
+ hb_is_same (hb_decay<T>, signed short) ||
+ hb_is_same (hb_decay<T>, unsigned short) ||
+ hb_is_same (hb_decay<T>, signed long) ||
+ hb_is_same (hb_decay<T>, unsigned long) ||
+ hb_is_same (hb_decay<T>, signed long long) ||
+ hb_is_same (hb_decay<T>, unsigned long long) ||
+ false
+>;
+#define hb_is_integral(T) hb_is_integral<T>::value
+template <typename T>
+using hb_is_floating_point = hb_bool_constant<
+ hb_is_same (hb_decay<T>, float) ||
+ hb_is_same (hb_decay<T>, double) ||
+ hb_is_same (hb_decay<T>, long double) ||
+ false
+>;
+#define hb_is_floating_point(T) hb_is_floating_point<T>::value
+template <typename T>
+using hb_is_arithmetic = hb_bool_constant<
+ hb_is_integral (T) ||
+ hb_is_floating_point (T) ||
+ false
+>;
+#define hb_is_arithmetic(T) hb_is_arithmetic<T>::value
+
+
+template <typename T>
+using hb_is_signed = hb_conditional<hb_is_arithmetic (T),
+ hb_bool_constant<(T) -1 < (T) 0>,
+ hb_false_type>;
+#define hb_is_signed(T) hb_is_signed<T>::value
+template <typename T>
+using hb_is_unsigned = hb_conditional<hb_is_arithmetic (T),
+ hb_bool_constant<(T) 0 < (T) -1>,
+ hb_false_type>;
+#define hb_is_unsigned(T) hb_is_unsigned<T>::value
+
+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> {};
+#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
+
+
+
+template <typename T, typename>
+struct _hb_is_destructible : hb_false_type {};
+template <typename T>
+struct _hb_is_destructible<T, hb_void_t<decltype (hb_declval (T).~T ())>> : hb_true_type {};
+template <typename T>
+using hb_is_destructible = _hb_is_destructible<T, void>;
+#define hb_is_destructible(T) hb_is_destructible<T>::value
+
+template <typename T, typename, typename ...Ts>
+struct _hb_is_constructible : hb_false_type {};
+template <typename T, typename ...Ts>
+struct _hb_is_constructible<T, hb_void_t<decltype (T (hb_declval (Ts)...))>, Ts...> : hb_true_type {};
+template <typename T, typename ...Ts>
+using hb_is_constructible = _hb_is_constructible<T, void, Ts...>;
+#define hb_is_constructible(...) hb_is_constructible<__VA_ARGS__>::value
+
+template <typename T>
+using hb_is_default_constructible = hb_is_constructible<T>;
+#define hb_is_default_constructible(T) hb_is_default_constructible<T>::value
+
+template <typename T>
+using hb_is_copy_constructible = hb_is_constructible<T, hb_add_lvalue_reference<hb_add_const<T>>>;
+#define hb_is_copy_constructible(T) hb_is_copy_constructible<T>::value
+
+template <typename T>
+using hb_is_move_constructible = hb_is_constructible<T, hb_add_rvalue_reference<hb_add_const<T>>>;
+#define hb_is_move_constructible(T) hb_is_move_constructible<T>::value
+
+template <typename T, typename U, typename>
+struct _hb_is_assignable : hb_false_type {};
+template <typename T, typename U>
+struct _hb_is_assignable<T, U, hb_void_t<decltype (hb_declval (T) = hb_declval (U))>> : hb_true_type {};
+template <typename T, typename U>
+using hb_is_assignable = _hb_is_assignable<T, U, void>;
+#define hb_is_assignable(T,U) hb_is_assignable<T, U>::value
+
+template <typename T>
+using hb_is_copy_assignable = hb_is_assignable<hb_add_lvalue_reference<T>,
+ hb_add_lvalue_reference<hb_add_const<T>>>;
+#define hb_is_copy_assignable(T) hb_is_copy_assignable<T>::value
+
+template <typename T>
+using hb_is_move_assignable = hb_is_assignable<hb_add_lvalue_reference<T>,
+ hb_add_rvalue_reference<T>>;
+#define hb_is_move_assignable(T) hb_is_move_assignable<T>::value
+
+/* Trivial versions. */
+
+template <typename T> union hb_trivial { T value; };
+
+template <typename T>
+using hb_is_trivially_destructible= hb_is_destructible<hb_trivial<T>>;
+#define hb_is_trivially_destructible(T) hb_is_trivially_destructible<T>::value
+
+/* Don't know how to do the following. */
+//template <typename T, typename ...Ts>
+//using hb_is_trivially_constructible= hb_is_constructible<hb_trivial<T>, hb_trivial<Ts>...>;
+//#define hb_is_trivially_constructible(...) hb_is_trivially_constructible<__VA_ARGS__>::value
+
+template <typename T>
+using hb_is_trivially_default_constructible= hb_is_default_constructible<hb_trivial<T>>;
+#define hb_is_trivially_default_constructible(T) hb_is_trivially_default_constructible<T>::value
+
+template <typename T>
+using hb_is_trivially_copy_constructible= hb_is_copy_constructible<hb_trivial<T>>;
+#define hb_is_trivially_copy_constructible(T) hb_is_trivially_copy_constructible<T>::value
+
+template <typename T>
+using hb_is_trivially_move_constructible= hb_is_move_constructible<hb_trivial<T>>;
+#define hb_is_trivially_move_constructible(T) hb_is_trivially_move_constructible<T>::value
+
+/* Don't know how to do the following. */
+//template <typename T, typename U>
+//using hb_is_trivially_assignable= hb_is_assignable<hb_trivial<T>, hb_trivial<U>>;
+//#define hb_is_trivially_assignable(T,U) hb_is_trivially_assignable<T, U>::value
+
+template <typename T>
+using hb_is_trivially_copy_assignable= hb_is_copy_assignable<hb_trivial<T>>;
+#define hb_is_trivially_copy_assignable(T) hb_is_trivially_copy_assignable<T>::value
+
+template <typename T>
+using hb_is_trivially_move_assignable= hb_is_move_assignable<hb_trivial<T>>;
+#define hb_is_trivially_move_assignable(T) hb_is_trivially_move_assignable<T>::value
+
+template <typename T>
+using hb_is_trivially_copyable= hb_bool_constant<
+ hb_is_trivially_destructible (T) &&
+ (!hb_is_move_assignable (T) || hb_is_trivially_move_assignable (T)) &&
+ (!hb_is_move_constructible (T) || hb_is_trivially_move_constructible (T)) &&
+ (!hb_is_copy_assignable (T) || hb_is_trivially_copy_assignable (T)) &&
+ (!hb_is_copy_constructible (T) || hb_is_trivially_copy_constructible (T)) &&
+ true
+>;
+#define hb_is_trivially_copyable(T) hb_is_trivially_copyable<T>::value
+
+template <typename T>
+using hb_is_trivial= hb_bool_constant<
+ hb_is_trivially_copyable (T) &&
+ hb_is_trivially_default_constructible (T)
+>;
+#define hb_is_trivial(T) hb_is_trivial<T>::value
+
+/* 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/thirdparty/harfbuzz/src/hb-mutex.hh b/thirdparty/harfbuzz/src/hb-mutex.hh
new file mode 100644
index 0000000000..56392d049b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-mutex.hh
@@ -0,0 +1,133 @@
+/*
+ * 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_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(HAVE_PTHREAD) || defined(__APPLE__))
+
+#include <pthread.h>
+typedef pthread_mutex_t hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT PTHREAD_MUTEX_INITIALIZER
+#define hb_mutex_impl_init(M) pthread_mutex_init (M, nullptr)
+#define hb_mutex_impl_lock(M) pthread_mutex_lock (M)
+#define hb_mutex_impl_unlock(M) pthread_mutex_unlock (M)
+#define hb_mutex_impl_finish(M) pthread_mutex_destroy (M)
+
+
+#elif !defined(HB_NO_MT) && defined(_WIN32)
+
+typedef CRITICAL_SECTION hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT {0}
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#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) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
+
+#if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
+# include <sched.h>
+# define HB_SCHED_YIELD() sched_yield ()
+#else
+# define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
+#endif
+
+/* This actually is not a totally awful implementation. */
+typedef volatile int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT 0
+#define hb_mutex_impl_init(M) *(M) = 0
+#define hb_mutex_impl_lock(M) HB_STMT_START { while (__sync_lock_test_and_set((M), 1)) HB_SCHED_YIELD (); } HB_STMT_END
+#define hb_mutex_impl_unlock(M) __sync_lock_release (M)
+#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END
+
+
+#elif defined(HB_NO_MT)
+
+typedef int hb_mutex_impl_t;
+#define HB_MUTEX_IMPL_INIT 0
+#define hb_mutex_impl_init(M) HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_lock(M) HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_unlock(M) HB_STMT_START {} HB_STMT_END
+#define hb_mutex_impl_finish(M) HB_STMT_START {} HB_STMT_END
+
+
+#else
+
+#error "Could not find any system to define mutex macros."
+#error "Check hb-mutex.hh for possible resolutions."
+
+#endif
+
+
+#define HB_MUTEX_INIT {HB_MUTEX_IMPL_INIT}
+
+struct hb_mutex_t
+{
+ hb_mutex_impl_t m;
+
+ void init () { hb_mutex_impl_init (&m); }
+ void lock () { hb_mutex_impl_lock (&m); }
+ void unlock () { hb_mutex_impl_unlock (&m); }
+ void fini () { hb_mutex_impl_finish (&m); }
+};
+
+struct hb_lock_t
+{
+ hb_lock_t (hb_mutex_t &mutex_) : mutex (mutex_) { mutex.lock (); }
+ ~hb_lock_t () { mutex.unlock (); }
+ private:
+ hb_mutex_t &mutex;
+};
+
+
+#endif /* HB_MUTEX_HH */
diff --git a/thirdparty/harfbuzz/src/hb-null.hh b/thirdparty/harfbuzz/src/hb-null.hh
new file mode 100644
index 0000000000..9853939b07
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-null.hh
@@ -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
+ */
+
+#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 384
+
+/* Use SFINAE to sniff whether T has min_size; in which case return T::null_size,
+ * otherwise return sizeof(T). */
+
+/* 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, 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::min_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
+
+
+/*
+ * 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[Namespace::Type::null_size]; \
+ 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 DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \
+ const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]
+
+/* 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, &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 &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 *> (&Null (T)); }
+ T * get_raw () const { return v; }
+
+ T *v;
+};
+
+
+#endif /* HB_NULL_HH */
diff --git a/thirdparty/harfbuzz/src/hb-number-parser.hh b/thirdparty/harfbuzz/src/hb-number-parser.hh
new file mode 100644
index 0000000000..1a9dbba6dd
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-number.cc b/thirdparty/harfbuzz/src/hb-number.cc
new file mode 100644
index 0000000000..6e4f3f7ebd
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-number.cc
@@ -0,0 +1,80 @@
+/*
+ * 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-machinery.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/thirdparty/harfbuzz/src/hb-number.hh b/thirdparty/harfbuzz/src/hb-number.hh
new file mode 100644
index 0000000000..14d1260aa3
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-object.hh b/thirdparty/harfbuzz/src/hb-object.hh
new file mode 100644
index 0000000000..39845a70e7
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-object.hh
@@ -0,0 +1,342 @@
+/*
+ * 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.find (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 item;
+ }
+
+ template <typename T>
+ void remove (T v, lock_t &l)
+ {
+ l.lock ();
+ item_t *item = items.find (v);
+ if (item)
+ {
+ item_t old = *item;
+ *item = items[items.length - 1];
+ 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.find (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[items.length - 1];
+ items.pop ();
+ l.unlock ();
+ old.fini ();
+ l.lock ();
+ }
+ items.fini ();
+ l.unlock ();
+ }
+
+};
+
+
+/*
+ * Reference-count.
+ */
+
+#define HB_REFERENCE_COUNT_INERT_VALUE 0
+#define HB_REFERENCE_COUNT_POISON_VALUE -0x0000DEAD
+#define HB_REFERENCE_COUNT_INIT {HB_ATOMIC_INT_INIT (HB_REFERENCE_COUNT_INERT_VALUE)}
+
+struct hb_reference_count_t
+{
+ mutable hb_atomic_int_t ref_count;
+
+ void init (int v = 1) { ref_count.set_relaxed (v); }
+ int get_relaxed () const { return ref_count.get_relaxed (); }
+ int inc () const { return ref_count.inc (); }
+ int dec () const { return ref_count.dec (); }
+ void fini () { ref_count.set_relaxed (HB_REFERENCE_COUNT_POISON_VALUE); }
+
+ bool is_inert () const { return ref_count.get_relaxed () == HB_REFERENCE_COUNT_INERT_VALUE; }
+ bool is_valid () const { return ref_count.get_relaxed () > 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 (); }
+
+ HB_INTERNAL bool set (hb_user_data_key_t *key,
+ void * data,
+ hb_destroy_func_t destroy,
+ hb_bool_t replace);
+
+ HB_INTERNAL void *get (hb_user_data_key_t *key);
+
+ void fini () { items.fini (lock); lock.fini (); }
+};
+
+
+/*
+ * Object header
+ */
+
+struct hb_object_header_t
+{
+ hb_reference_count_t ref_count;
+ mutable hb_atomic_int_t writable;
+ hb_atomic_ptr_t<hb_user_data_array_t> user_data;
+};
+#define HB_OBJECT_HEADER_STATIC \
+ { \
+ HB_REFERENCE_COUNT_INIT, \
+ HB_ATOMIC_INT_INIT (false), \
+ HB_ATOMIC_PTR_INIT (nullptr) \
+ }
+
+
+/*
+ * 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>
+static inline Type *hb_object_create ()
+{
+ Type *obj = (Type *) calloc (1, sizeof (Type));
+
+ if (unlikely (!obj))
+ return obj;
+
+ hb_object_init (obj);
+ hb_object_trace (obj, HB_FUNC);
+ return obj;
+}
+template <typename Type>
+static inline void hb_object_init (Type *obj)
+{
+ obj->header.ref_count.init ();
+ obj->header.writable.set_relaxed (true);
+ obj->header.user_data.init ();
+}
+template <typename Type>
+static inline bool hb_object_is_inert (const Type *obj)
+{
+ return unlikely (obj->header.ref_count.is_inert ());
+}
+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.get_relaxed ();
+}
+template <typename Type>
+static inline void hb_object_make_immutable (const Type *obj)
+{
+ obj->header.writable.set_relaxed (false);
+}
+template <typename Type>
+static inline Type *hb_object_reference (Type *obj)
+{
+ hb_object_trace (obj, HB_FUNC);
+ if (unlikely (!obj || hb_object_is_inert (obj)))
+ return obj;
+ assert (hb_object_is_valid (obj));
+ obj->header.ref_count.inc ();
+ return obj;
+}
+template <typename Type>
+static inline bool hb_object_destroy (Type *obj)
+{
+ hb_object_trace (obj, HB_FUNC);
+ if (unlikely (!obj || hb_object_is_inert (obj)))
+ return false;
+ assert (hb_object_is_valid (obj));
+ if (obj->header.ref_count.dec () != 1)
+ return false;
+
+ hb_object_fini (obj);
+ 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 ();
+ if (user_data)
+ {
+ user_data->fini ();
+ free (user_data);
+ user_data = 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 || hb_object_is_inert (obj)))
+ return false;
+ assert (hb_object_is_valid (obj));
+
+retry:
+ hb_user_data_array_t *user_data = obj->header.user_data.get ();
+ if (unlikely (!user_data))
+ {
+ user_data = (hb_user_data_array_t *) 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 ();
+ 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 || hb_object_is_inert (obj)))
+ return nullptr;
+ assert (hb_object_is_valid (obj));
+ hb_user_data_array_t *user_data = obj->header.user_data.get ();
+ if (!user_data)
+ return nullptr;
+ return user_data->get (key);
+}
+
+
+#endif /* HB_OBJECT_HH */
diff --git a/thirdparty/harfbuzz/src/hb-open-file.hh b/thirdparty/harfbuzz/src/hb-open-file.hh
new file mode 100644
index 0000000000..ac13dd23c3
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-open-file.hh
@@ -0,0 +1,521 @@
+/*
+ * 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 OffsetTable;
+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 OffsetTable
+{
+ 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.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;
+ return tables.bfind (t, table_index, HB_BFIND_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 item_t>
+ bool serialize (hb_serialize_context_t *c,
+ hb_tag_t sfnt_tag,
+ hb_array_t<item_t> items)
+ {
+ 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. */
+ if (unlikely (!tables.serialize (c, items.length))) return_trace (false);
+
+ const char *dir_end = (const char *) c->head;
+ HBUINT32 *checksum_adjustment = nullptr;
+
+ /* Write OffsetTables, alloc for and write actual table blobs. */
+ for (unsigned int i = 0; i < tables.len; i++)
+ {
+ TableRecord &rec = tables.arrayZ[i];
+ hb_blob_t *blob = items[i].blob;
+ rec.tag = items[i].tag;
+ rec.length = blob->length;
+ rec.offset.serialize (c, this);
+
+ /* Allocate room for the table and copy it. */
+ char *start = (char *) c->allocate_size<void> (rec.length);
+ if (unlikely (!start)) return false;
+
+ if (likely (rec.length))
+ memcpy (start, blob->data, rec.length);
+
+ /* 4-byte alignment. */
+ c->align (4);
+ const char *end = (const char *) c->head;
+
+ if (items[i].tag == 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);
+ }
+
+ 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 < items.length; 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 */
+ LArrayOf<LOffsetTo<OffsetTable>>
+ table; /* Array of offsets to the OffsetTable for each font
+ * from the beginning of the file */
+ public:
+ DEFINE_SIZE_ARRAY (12, table);
+};
+
+struct TTCHeader
+{
+ friend struct OpenTypeFontFile;
+
+ private:
+
+ unsigned int get_face_count () const
+ {
+ switch (u.header.version.major) {
+ case 2: /* version 2 is compatible with version 1 */
+ case 1: return u.version1.get_face_count ();
+ default:return 0;
+ }
+ }
+ const OpenTypeFontFace& get_face (unsigned int i) const
+ {
+ switch (u.header.version.major) {
+ case 2: /* version 2 is compatible with version 1 */
+ case 1: return u.version1.get_face (i);
+ default:return Null (OpenTypeFontFace);
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!u.header.version.sanitize (c))) return_trace (false);
+ switch (u.header.version.major) {
+ case 2: /* version 2 is compatible with version 1 */
+ case 1: return_trace (u.version1.sanitize (c));
+ default:return_trace (true);
+ }
+ }
+
+ protected:
+ union {
+ struct {
+ Tag ttcTag; /* TrueType Collection ID string: 'ttcf' */
+ FixedVersion<>version; /* Version of the TTC Header (1.0 or 2.0),
+ * 0x00010000u or 0x00020000u */
+ } header;
+ TTCHeaderVersion1 version1;
+ } u;
+};
+
+/*
+ * Mac Resource Fork
+ *
+ * http://mirror.informatimago.com/next/developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html
+ */
+
+struct ResourceRecord
+{
+ const OpenTypeFontFace & get_face (const void *data_base) const
+ { return * reinterpret_cast<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) &&
+ 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 */
+ NNOffsetTo<LArrayOf<HBUINT8>, HBUINT24>
+ offset; /* Offset from beginning of data block to
+ * data for this resource */
+ HBUINT32 reserved; /* Reserved for handle to resource */
+ public:
+ DEFINE_SIZE_STATIC (12);
+};
+
+#define HB_TAG_sfnt HB_TAG ('s','f','n','t')
+
+struct ResourceTypeRecord
+{
+ unsigned int get_resource_count () const
+ { return tag == HB_TAG_sfnt ? resCountM1 + 1 : 0; }
+
+ bool is_sfnt () const { return tag == HB_TAG_sfnt; }
+
+ const ResourceRecord& get_resource_record (unsigned int i,
+ const void *type_base) const
+ { return (type_base+resourcesZ).as_array (get_resource_count ())[i]; }
+
+ bool sanitize (hb_sanitize_context_t *c,
+ const void *type_base,
+ const void *data_base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ resourcesZ.sanitize (c, type_base,
+ get_resource_count (),
+ data_base));
+ }
+
+ protected:
+ Tag tag; /* Resource type. */
+ HBUINT16 resCountM1; /* Number of resources minus 1. */
+ NNOffsetTo<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) &&
+ 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 */
+ NNOffsetTo<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) &&
+ data.sanitize (c, this, dataLen) &&
+ map.sanitize (c, this, &(this+data)));
+ }
+
+ protected:
+ LNNOffsetTo<UnsizedArrayOf<HBUINT8>>
+ data; /* Offset from beginning of resource fork
+ * to resource data */
+ LNNOffsetTo<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 item_t>
+ bool serialize_single (hb_serialize_context_t *c,
+ hb_tag_t sfnt_tag,
+ hb_array_t<item_t> items)
+ {
+ TRACE_SERIALIZE (this);
+ assert (sfnt_tag != TTCTag);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ return_trace (u.fontFace.serialize (c, sfnt_tag, items));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!u.tag.sanitize (c))) return_trace (false);
+ switch (u.tag) {
+ case CFFTag: /* All the non-collection tags */
+ case TrueTag:
+ case Typ1Tag:
+ case TrueTypeTag: return_trace (u.fontFace.sanitize (c));
+ case TTCTag: return_trace (u.ttcHeader.sanitize (c));
+ case DFontTag: return_trace (u.rfHeader.sanitize (c));
+ default: return_trace (true);
+ }
+ }
+
+ protected:
+ union {
+ Tag tag; /* 4-byte identifier. */
+ OpenTypeFontFace fontFace;
+ TTCHeader ttcHeader;
+ ResourceForkHeader rfHeader;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (4, tag);
+};
+
+
+} /* namespace OT */
+
+
+#endif /* HB_OPEN_FILE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-open-type.hh b/thirdparty/harfbuzz/src/hb-open-type.hh
new file mode 100644
index 0000000000..50558cf8d3
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-open-type.hh
@@ -0,0 +1,1078 @@
+/*
+ * 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-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>
+struct IntType
+{
+ typedef Type type;
+ typedef hb_conditional<hb_is_signed (Type), signed, unsigned> wide_type;
+
+ IntType& operator = (wide_type i) { v = i; return *this; }
+ operator wide_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>
+ int cmp (Type2 a) const
+ {
+ Type b = v;
+ if (sizeof (Type) < sizeof (int) && sizeof (Type2) < sizeof (int))
+ return (int) a - (int) b;
+ else
+ return a < b ? -1 : a == b ? 0 : +1;
+ }
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this)));
+ }
+ protected:
+ BEInt<Type, Size> v;
+ public:
+ DEFINE_SIZE_STATIC (Size);
+};
+
+typedef IntType<uint8_t, 1> HBUINT8; /* 8-bit unsigned integer. */
+typedef IntType<int8_t, 1> HBINT8; /* 8-bit signed integer. */
+typedef IntType<uint16_t, 2> HBUINT16; /* 16-bit unsigned integer. */
+typedef IntType<int16_t, 2> HBINT16; /* 16-bit signed integer. */
+typedef IntType<uint32_t, 4> HBUINT32; /* 32-bit unsigned integer. */
+typedef IntType<int32_t, 4> 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. */
+
+/* 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;
+
+/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
+struct F2DOT14 : HBINT16
+{
+ F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; }
+ // 16384 means 1<<14
+ float to_float () const { return ((int32_t) v) / 16384.f; }
+ void set_float (float f) { v = roundf (f * 16384.f); }
+ public:
+ DEFINE_SIZE_STATIC (2);
+};
+
+/* 32-bit signed fixed-point number (16.16). */
+struct HBFixed : HBINT32
+{
+ HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; }
+ // 65536 means 1<<16
+ float to_float () const { return ((int32_t) v) / 65536.f; }
+ void set_float (float f) { v = roundf (f * 65536.f); }
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+
+/* Date represented in number of seconds since 12:00 midnight, January 1,
+ * 1904. The value is represented as a signed 64-bit integer. */
+struct LONGDATETIME
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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->v); }
+ operator char* () { return reinterpret_cast<char *> (&this->v); }
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+
+/* Glyph index number, same as uint16 (length = 16 bits) */
+struct HBGlyphID : HBUINT16
+{
+ HBGlyphID& operator = (uint16_t i) { HBUINT16::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;
+
+/* 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; }
+
+ void *serialize (hb_serialize_context_t *c, const void *base)
+ {
+ void *t = c->start_embed<void> ();
+ c->check_assign (*this, (unsigned) ((char *) t - (char *) base));
+ return t;
+ }
+
+ public:
+ DEFINE_SIZE_STATIC (sizeof (Type));
+};
+
+typedef Offset<HBUINT16> Offset16;
+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=HBUINT16, bool has_null=true>
+struct OffsetTo : Offset<OffsetType, has_null>
+{
+ 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 void *))>
+ 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 void *))>
+ 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, void *))>
+ friend Type& operator + (Base &&base, OffsetTo &offset) { return offset ((void *) base); }
+ template <typename Base,
+ hb_enable_if (hb_is_convertible (Base, void *))>
+ friend Type& operator + (OffsetTo &offset, Base &&base) { return offset ((void *) base); }
+
+ Type& serialize (hb_serialize_context_t *c, const void *base)
+ {
+ return * (Type *) Offset<OffsetType>::serialize (c, base);
+ }
+
+ template <typename ...Ts>
+ bool serialize_subset (hb_subset_context_t *c, const OffsetTo& src,
+ const void *src_base, Ts&&... ds)
+ {
+ *this = 0;
+ if (src.is_null ())
+ return false;
+
+ auto *s = c->serializer;
+
+ s->push ();
+
+ bool ret = c->dispatch (src_base+src, hb_forward<Ts> (ds)...);
+
+ if (ret || !has_null)
+ s->add_link (*this, s->pop_pack ());
+ else
+ s->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, hb_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 void *base) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this))) return_trace (false);
+ if (unlikely (this->is_null ())) return_trace (true);
+ if (unlikely (!c->check_range (base, *this))) return_trace (false);
+ return_trace (true);
+ }
+
+ template <typename ...Ts>
+ bool sanitize (hb_sanitize_context_t *c, const void *base, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (sanitize_shallow (c, base) &&
+ (this->is_null () ||
+ c->dispatch (StructAtOffset<Type> (base, *this), hb_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, bool has_null=true>
+using LOffsetTo = OffsetTo<Type, HBUINT32, has_null>;
+template <typename Type, typename OffsetType=HBUINT16>
+using NNOffsetTo = OffsetTo<Type, OffsetType, false>;
+template <typename Type>
+using LNNOffsetTo = LOffsetTo<Type, 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 [] (int i_) const
+ {
+ unsigned int i = (unsigned int) i_;
+ const Type *p = &arrayZ[i];
+ if (unlikely (p < arrayZ)) return Null (Type); /* Overflowed. */
+ return *p;
+ }
+ Type& operator [] (int i_)
+ {
+ unsigned int i = (unsigned int) i_;
+ Type *p = &arrayZ[i];
+ if (unlikely (p < arrayZ)) return Crap (Type); /* Overflowed. */
+ return *p;
+ }
+
+ unsigned int get_size (unsigned int len) const
+ { 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); }
+ operator hb_array_t< Type> () { return as_array (); }
+ operator hb_array_t<const Type> () const { return as_array (); }
+
+ template <typename T>
+ Type &lsearch (unsigned int len, const T &x, Type &not_found = Crap (Type))
+ { return *as_array (len).lsearch (x, &not_found); }
+ template <typename T>
+ const Type &lsearch (unsigned int len, const T &x, const Type &not_found = Null (Type)) const
+ { return *as_array (len).lsearch (x, &not_found); }
+ template <typename T>
+ bool lfind (unsigned int len, const T &x, unsigned *pos = nullptr) const
+ { return as_array (len).lfind (x, pos); }
+
+ 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)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend (*this, items_len))) 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 = items.len ();
+ if (unlikely (!serialize (c, count))) 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>
+ bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
+ if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true);
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!c->dispatch (arrayZ[i], hb_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, bool has_null=true>
+using UnsizedOffsetArrayOf = UnsizedArrayOf<OffsetTo<Type, OffsetType, has_null>>;
+
+/* Unsized array of offsets relative to the beginning of the array itself. */
+template <typename Type, typename OffsetType, bool has_null=true>
+struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType, has_null>
+{
+ const Type& operator [] (int i_) const
+ {
+ unsigned int i = (unsigned int) i_;
+ const OffsetTo<Type, OffsetType, has_null> *p = &this->arrayZ[i];
+ if (unlikely (p < this->arrayZ)) return Null (Type); /* Overflowed. */
+ return this+*p;
+ }
+ Type& operator [] (int i_)
+ {
+ unsigned int i = (unsigned int) i_;
+ const OffsetTo<Type, OffsetType, has_null> *p = &this->arrayZ[i];
+ if (unlikely (p < this->arrayZ)) return Crap (Type); /* Overflowed. */
+ return this+*p;
+ }
+
+ template <typename ...Ts>
+ bool sanitize (hb_sanitize_context_t *c, unsigned int count, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace ((UnsizedOffsetArrayOf<Type, OffsetType, has_null>
+ ::sanitize (c, count, this, hb_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 &not_found = Crap (Type))
+ { return *as_array (len).bsearch (x, &not_found); }
+ template <typename T>
+ const Type &bsearch (unsigned int len, const T &x, const Type &not_found = Null (Type)) const
+ { return *as_array (len).bsearch (x, &not_found); }
+ template <typename T>
+ bool bfind (unsigned int len, const T &x, unsigned int *i = nullptr,
+ hb_bfind_not_found_t not_found = HB_BFIND_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=HBUINT16>
+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);
+ return arrayZ[i];
+ }
+ Type& operator [] (int i_)
+ {
+ unsigned int i = (unsigned int) i_;
+ if (unlikely (i >= len)) return Crap (Type);
+ 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 (); }
+
+ hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
+ { return as_array ().sub_array (start_offset, count); }
+ hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+ { return as_array ().sub_array (start_offset, count); }
+ hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
+ { return as_array ().sub_array (start_offset, count); }
+ hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+ { return as_array ().sub_array (start_offset, count); }
+
+ hb_success_t serialize (hb_serialize_context_t *c, unsigned items_len)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ c->check_assign (len, items_len);
+ if (unlikely (!c->extend (*this))) return_trace (false);
+ return_trace (true);
+ }
+ template <typename Iterator,
+ hb_requires (hb_is_source_of (Iterator, Type))>
+ hb_success_t serialize (hb_serialize_context_t *c, Iterator items)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned count = items.len ();
+ if (unlikely (!serialize (c, count))) 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);
+ if (unlikely (!as_array ().copy (c))) return_trace (nullptr);
+ return_trace (out);
+ }
+
+ template <typename ...Ts>
+ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!sanitize_shallow (c))) return_trace (false);
+ if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true);
+ unsigned int count = len;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!c->dispatch (arrayZ[i], hb_forward<Ts> (ds)...)))
+ return_trace (false);
+ return_trace (true);
+ }
+
+ template <typename T>
+ Type &lsearch (const T &x, Type &not_found = Crap (Type))
+ { return *as_array ().lsearch (x, &not_found); }
+ template <typename T>
+ const Type &lsearch (const T &x, const Type &not_found = Null (Type)) 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); }
+
+ void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
+ { as_array ().qsort (start, end); }
+
+ bool sanitize_shallow (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (len.sanitize (c) && c->check_array (arrayZ, len));
+ }
+
+ public:
+ LenType len;
+ Type arrayZ[HB_VAR_ARRAY];
+ public:
+ DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
+};
+template <typename Type>
+using LArrayOf = ArrayOf<Type, HBUINT32>;
+using PString = ArrayOf<HBUINT8, HBUINT8>;
+
+/* Array of Offset's */
+template <typename Type>
+using OffsetArrayOf = ArrayOf<OffsetTo<Type, HBUINT16>>;
+template <typename Type>
+using LOffsetArrayOf = ArrayOf<OffsetTo<Type, HBUINT32>>;
+template <typename Type>
+using LOffsetLArrayOf = ArrayOf<OffsetTo<Type, HBUINT32>, HBUINT32>;
+
+/* Array of offsets relative to the beginning of the array itself. */
+template <typename Type>
+struct OffsetListOf : OffsetArrayOf<Type>
+{
+ const Type& operator [] (int i_) const
+ {
+ unsigned int i = (unsigned int) i_;
+ if (unlikely (i >= this->len)) return Null (Type);
+ return this+this->arrayZ[i];
+ }
+ const Type& operator [] (int i_)
+ {
+ unsigned int i = (unsigned int) i_;
+ if (unlikely (i >= this->len)) return Crap (Type);
+ return this+this->arrayZ[i];
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ struct OffsetListOf<Type> *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 (OffsetArrayOf<Type>::sanitize (c, this, hb_forward<Ts> (ds)...));
+ }
+};
+
+/* An array starting at second element. */
+template <typename Type, typename LenType=HBUINT16>
+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);
+ return arrayZ[i-1];
+ }
+ Type& operator [] (int i_)
+ {
+ unsigned int i = (unsigned int) i_;
+ if (unlikely (i >= lenP1 || !i)) return Crap (Type);
+ 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 (); }
+
+ bool serialize (hb_serialize_context_t *c, unsigned int items_len)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ c->check_assign (lenP1, items_len + 1);
+ if (unlikely (!c->extend (*this))) 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 = items.len ();
+ if (unlikely (!serialize (c, count))) 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>
+ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!sanitize_shallow (c))) return_trace (false);
+ if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true);
+ unsigned int count = get_length ();
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!c->dispatch (arrayZ[i], hb_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) &&
+ (!lenP1 || c->check_array (arrayZ, lenP1 - 1)));
+ }
+
+ public:
+ LenType lenP1;
+ Type arrayZ[HB_VAR_ARRAY];
+ public:
+ DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ);
+};
+
+/* 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);
+ return arrayZ[i];
+ }
+ Type& operator [] (int i_)
+ {
+ unsigned int i = (unsigned int) i_;
+ if (unlikely (i > lenM1)) return Crap (Type);
+ return arrayZ[i];
+ }
+ unsigned int get_size () const
+ { return lenM1.static_size + (lenM1 + 1) * Type::static_size; }
+
+ template <typename ...Ts>
+ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!sanitize_shallow (c))) return_trace (false);
+ unsigned int count = lenM1 + 1;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!c->dispatch (arrayZ[i], hb_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) &&
+ (c->check_array (arrayZ, lenM1 + 1)));
+ }
+
+ 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=HBUINT16>
+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 (); }
+
+ hb_sorted_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
+ { return as_array ().sub_array (start_offset, count); }
+ hb_sorted_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+ { return as_array ().sub_array (start_offset, count); }
+ hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
+ { return as_array ().sub_array (start_offset, count); }
+ hb_sorted_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+ { return as_array ().sub_array (start_offset, count); }
+
+ 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 &not_found = Crap (Type))
+ { return *as_array ().bsearch (x, &not_found); }
+ template <typename T>
+ const Type &bsearch (const T &x, const Type &not_found = Null (Type)) const
+ { return *as_array ().bsearch (x, &not_found); }
+ template <typename T>
+ bool bfind (const T &x, unsigned int *i = nullptr,
+ hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+ unsigned int to_store = (unsigned int) -1) const
+ { return as_array ().bfind (x, i, not_found, to_store); }
+};
+
+/*
+ * 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);
+ 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);
+ 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>
+ bool sanitize (hb_sanitize_context_t *c, Ts&&... ds) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!sanitize_shallow (c))) return_trace (false);
+ if (!sizeof... (Ts) && hb_is_trivially_copyable (Type)) return_trace (true);
+ unsigned int count = get_length ();
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!(*this)[i].sanitize (c, hb_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) &&
+ 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/thirdparty/harfbuzz/src/hb-ot-cff-common.hh b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
new file mode 100644
index 0000000000..e5286cd792
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-cff-common.hh
@@ -0,0 +1,622 @@
+/*
+ * 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); }
+
+inline unsigned int calcOffSize (unsigned int dataSize)
+{
+ unsigned int size = 1;
+ unsigned int offset = dataSize + 1;
+ while (offset & ~0xFF)
+ {
+ size++;
+ offset >>= 8;
+ }
+ /* format does not support size > 4; caller should handle it as an error */
+ return size;
+}
+
+struct code_pair_t
+{
+ hb_codepoint_t code;
+ hb_codepoint_t glyph;
+};
+
+typedef hb_vector_t<unsigned char> str_buff_t;
+struct str_buff_vec_t : hb_vector_t<str_buff_t>
+{
+ void fini () { SUPER::fini_deep (); }
+
+ unsigned int total_size () const
+ {
+ unsigned int size = 0;
+ for (unsigned int i = 0; i < length; i++)
+ size += (*this)[i].length;
+ return size;
+ }
+
+ private:
+ typedef hb_vector_t<str_buff_t> SUPER;
+};
+
+/* CFF INDEX */
+template <typename COUNT>
+struct CFFIndex
+{
+ static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
+ { return offSize * (count + 1); }
+
+ unsigned int offset_array_size () const
+ { return calculate_offset_array_size (offSize, count); }
+
+ CFFIndex *copy (hb_serialize_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size = get_size ();
+ CFFIndex *out = c->allocate_size<CFFIndex> (size);
+ if (likely (out))
+ memcpy (out, this, size);
+ return_trace (out);
+ }
+
+ bool serialize (hb_serialize_context_t *c, const CFFIndex &src)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size = src.get_size ();
+ CFFIndex *dest = c->allocate_size<CFFIndex> (size);
+ if (unlikely (!dest)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ unsigned int offSize_,
+ const byte_str_array_t &byteArray)
+ {
+ TRACE_SERIALIZE (this);
+ if (byteArray.length == 0)
+ {
+ COUNT *dest = c->allocate_min<COUNT> ();
+ if (unlikely (!dest)) return_trace (false);
+ *dest = 0;
+ }
+ else
+ {
+ /* serialize CFFIndex header */
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ this->count = byteArray.length;
+ this->offSize = offSize_;
+ if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (byteArray.length + 1))))
+ return_trace (false);
+
+ /* serialize indices */
+ unsigned int offset = 1;
+ unsigned int i = 0;
+ for (; i < byteArray.length; i++)
+ {
+ set_offset_at (i, offset);
+ offset += byteArray[i].get_size ();
+ }
+ set_offset_at (i, offset);
+
+ /* serialize data */
+ for (unsigned int i = 0; i < byteArray.length; i++)
+ {
+ const byte_str_t &bs = byteArray[i];
+ unsigned char *dest = c->allocate_size<unsigned char> (bs.length);
+ if (unlikely (!dest)) return_trace (false);
+ memcpy (dest, &bs[0], bs.length);
+ }
+ }
+ return_trace (true);
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ unsigned int offSize_,
+ const str_buff_vec_t &buffArray)
+ {
+ byte_str_array_t byteArray;
+ byteArray.init ();
+ byteArray.resize (buffArray.length);
+ for (unsigned int i = 0; i < byteArray.length; i++)
+ byteArray[i] = byte_str_t (buffArray[i].arrayZ, buffArray[i].length);
+ bool result = this->serialize (c, offSize_, byteArray);
+ byteArray.fini ();
+ return result;
+ }
+
+ template <typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ if (it.len () == 0)
+ {
+ COUNT *dest = c->allocate_min<COUNT> ();
+ if (unlikely (!dest)) return_trace (false);
+ *dest = 0;
+ }
+ else
+ {
+ serialize_header(c, + it | hb_map ([] (const byte_str_t &_) { return _.length; }));
+ for (const byte_str_t &_ : +it)
+ _.copy (c);
+ }
+ return_trace (true);
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ const byte_str_array_t &byteArray)
+ { return serialize (c, + hb_iter (byteArray)); }
+
+ bool serialize (hb_serialize_context_t *c,
+ const str_buff_vec_t &buffArray)
+ {
+ auto it =
+ + hb_iter (buffArray)
+ | hb_map ([] (const str_buff_t &_) { return byte_str_t (_.arrayZ, _.length); })
+ ;
+ return serialize (c, it);
+ }
+
+ template <typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ bool serialize_header (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+
+ unsigned total = + it | hb_reduce (hb_add, 0);
+ unsigned off_size = calcOffSize (total);
+
+ /* serialize CFFIndex header */
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ this->count = it.len ();
+ this->offSize = off_size;
+ if (unlikely (!c->allocate_size<HBUINT8> (off_size * (it.len () + 1))))
+ return_trace (false);
+
+ /* serialize indices */
+ unsigned int offset = 1;
+ unsigned int i = 0;
+ for (unsigned _ : +it)
+ {
+ CFFIndex<COUNT>::set_offset_at (i++, offset);
+ offset += _;
+ }
+ CFFIndex<COUNT>::set_offset_at (i, offset);
+
+ return_trace (true);
+ }
+
+ void set_offset_at (unsigned int index, unsigned int offset)
+ {
+ HBUINT8 *p = offsets + offSize * index + offSize;
+ unsigned int size = offSize;
+ for (; size; size--)
+ {
+ --p;
+ *p = offset & 0xFF;
+ offset >>= 8;
+ }
+ }
+
+ unsigned int offset_at (unsigned int index) const
+ {
+ assert (index <= count);
+ const HBUINT8 *p = offsets + offSize * index;
+ unsigned int size = offSize;
+ unsigned int offset = 0;
+ for (; size; size--)
+ offset = (offset << 8) + *p++;
+ return offset;
+ }
+
+ unsigned int length_at (unsigned int index) const
+ {
+ if (unlikely ((offset_at (index + 1) < offset_at (index)) ||
+ (offset_at (index + 1) > offset_at (count))))
+ return 0;
+ return offset_at (index + 1) - offset_at (index);
+ }
+
+ const unsigned char *data_base () const
+ { return (const unsigned char *) this + min_size + offset_array_size (); }
+
+ unsigned int data_size () const { return HBINT8::static_size; }
+
+ byte_str_t operator [] (unsigned int index) const
+ {
+ if (unlikely (index >= count)) return Null (byte_str_t);
+ return byte_str_t (data_base () + offset_at (index) - 1, length_at (index));
+ }
+
+ unsigned int get_size () const
+ {
+ if (this == &Null (CFFIndex)) return 0;
+ if (count > 0)
+ return min_size + offset_array_size () + (offset_at (count) - 1);
+ return count.static_size; /* empty CFFIndex contains count only */
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely ((c->check_struct (this) && count == 0) || /* empty INDEX */
+ (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
+ c->check_array (offsets, offSize, count + 1) &&
+ c->check_array ((const HBUINT8*) data_base (), 1, max_offset () - 1))));
+ }
+
+ protected:
+ unsigned int max_offset () const
+ {
+ unsigned int max = 0;
+ for (unsigned int i = 0; i < count + 1u; i++)
+ {
+ unsigned int off = offset_at (i);
+ if (off > max) max = off;
+ }
+ return max;
+ }
+
+ public:
+ COUNT count; /* Number of object data. Note there are (count+1) offsets */
+ 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_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
+};
+
+template <typename COUNT, typename TYPE>
+struct CFFIndexOf : CFFIndex<COUNT>
+{
+ const byte_str_t operator [] (unsigned int index) const
+ {
+ if (likely (index < CFFIndex<COUNT>::count))
+ return byte_str_t (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
+ return Null (byte_str_t);
+ }
+
+ template <typename DATA, typename PARAM1, typename PARAM2>
+ bool serialize (hb_serialize_context_t *c,
+ unsigned int offSize_,
+ const DATA *dataArray,
+ unsigned int dataArrayLen,
+ const hb_vector_t<unsigned int> &dataSizeArray,
+ const PARAM1 &param1,
+ const PARAM2 &param2)
+ {
+ TRACE_SERIALIZE (this);
+ /* serialize CFFIndex header */
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ this->count = dataArrayLen;
+ this->offSize = offSize_;
+ if (unlikely (!c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
+ return_trace (false);
+
+ /* serialize indices */
+ unsigned int offset = 1;
+ unsigned int i = 0;
+ for (; i < dataArrayLen; i++)
+ {
+ CFFIndex<COUNT>::set_offset_at (i, offset);
+ offset += dataSizeArray[i];
+ }
+ CFFIndex<COUNT>::set_offset_at (i, offset);
+
+ /* serialize data */
+ for (unsigned int i = 0; i < dataArrayLen; i++)
+ {
+ TYPE *dest = c->start_embed<TYPE> ();
+ if (unlikely (!dest || !dest->serialize (c, dataArray[i], param1, param2)))
+ return_trace (false);
+ }
+ return_trace (true);
+ }
+};
+
+/* 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], hb_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)
+ {
+ // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
+ 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));
+ 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 : CFFIndexOf<COUNT, FontDict>
+{
+ 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;
+ c->push ();
+ + 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)
+ ;
+ c->pop_pack (false);
+
+ /* serialize INDEX header */
+ return_trace (CFFIndex<COUNT>::serialize_header (c, hb_iter (sizes)));
+ }
+};
+
+/* 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);
+ for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
+ if (unlikely (!fds[i].sanitize (c)))
+ return_trace (false);
+
+ return_trace (true);
+ }
+
+ hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ { return (hb_codepoint_t) fds[glyph]; }
+
+ 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 (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) ||
+ (nRanges () == 0) || ranges[0].first != 0))
+ return_trace (false);
+
+ for (unsigned int i = 1; i < nRanges (); i++)
+ if (unlikely (ranges[i - 1].first >= ranges[i].first))
+ return_trace (false);
+
+ if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
+ return_trace (false);
+
+ return_trace (true);
+ }
+
+ hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ {
+ unsigned int i;
+ for (i = 1; i < nRanges (); i++)
+ if (glyph < ranges[i].first)
+ break;
+
+ return (hb_codepoint_t) ranges[i - 1].fd;
+ }
+
+ 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);
+ if (unlikely (!dest)) return_trace (false);
+ 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;
+ }
+ }
+
+ hb_codepoint_t 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;
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+
+ switch (format)
+ {
+ case 0: return_trace (u.format0.sanitize (c, fdcount));
+ case 3: return_trace (u.format3.sanitize (c, fdcount));
+ default:return_trace (false);
+ }
+ }
+
+ HBUINT8 format;
+ union {
+ FDSelect0 format0;
+ FDSelect3 format3;
+ } u;
+ public:
+ DEFINE_SIZE_MIN (1);
+};
+
+template <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/thirdparty/harfbuzz/src/hb-ot-cff1-std-str.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-std-str.hh
new file mode 100644
index 0000000000..65d56ae18b
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc b/thirdparty/harfbuzz/src/hb-ot-cff1-table.cc
new file mode 100644
index 0000000000..66b9c8c907
--- /dev/null
+++ b/thirdparty/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
+{
+ void init (const OT::cff1::accelerator_t *_cff)
+ {
+ path_open = false;
+ 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;
+ 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);
+ cff1_cs_interpreter_t<cff1_cs_opset_extents_t, cff1_extents_param_t> interp;
+ const byte_str_t str = (*cff->charStrings)[glyph];
+ interp.env.init (str, *cff, fd);
+ interp.env.set_in_seac (in_seac);
+ cff1_extents_param_t param;
+ param.init (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 = font->em_scalef_x (bounds.min.x.to_real ());
+ extents->width = font->em_scalef_x (bounds.max.x.to_real () - bounds.min.x.to_real ());
+ }
+ if (bounds.min.y >= bounds.max.y)
+ {
+ extents->height = 0;
+ extents->y_bearing = 0;
+ }
+ else
+ {
+ extents->y_bearing = font->em_scalef_y (bounds.max.y.to_real ());
+ extents->height = font->em_scalef_y (bounds.min.y.to_real () - bounds.max.y.to_real ());
+ }
+
+ return true;
+}
+
+#ifdef HB_EXPERIMENTAL_API
+struct cff1_path_param_t
+{
+ cff1_path_param_t (const OT::cff1::accelerator_t *cff_, hb_font_t *font_,
+ draw_helper_t &draw_helper_, point_t *delta_)
+ {
+ draw_helper = &draw_helper_;
+ cff = cff_;
+ font = font_;
+ delta = delta_;
+ }
+
+ void move_to (const point_t &p)
+ {
+ point_t point = p;
+ if (delta) point.move (*delta);
+ draw_helper->move_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_y (point.y.to_real ()));
+ }
+
+ void line_to (const point_t &p)
+ {
+ point_t point = p;
+ if (delta) point.move (*delta);
+ draw_helper->line_to (font->em_scalef_x (point.x.to_real ()), font->em_scalef_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_helper->cubic_to (font->em_scalef_x (point1.x.to_real ()), font->em_scalef_y (point1.y.to_real ()),
+ font->em_scalef_x (point2.x.to_real ()), font->em_scalef_y (point2.y.to_real ()),
+ font->em_scalef_x (point3.x.to_real ()), font->em_scalef_y (point3.y.to_real ()));
+ }
+
+ void end_path () { draw_helper->end_path (); }
+
+ hb_font_t *font;
+ draw_helper_t *draw_helper;
+ 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 &param, const point_t &pt1)
+ {
+ param.line_to (pt1);
+ env.moveto (pt1);
+ }
+
+ static void curve (cff1_cs_interp_env_t &env, cff1_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);
+ }
+};
+
+static bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph,
+ draw_helper_t &draw_helper, 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_helper, true)
+ && _get_path (param.cff, param.font, accent, *param.draw_helper, true, &delta))))
+ env.set_error ();
+ }
+};
+
+bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoint_t glyph,
+ draw_helper_t &draw_helper, 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);
+ cff1_cs_interpreter_t<cff1_cs_opset_path_t, cff1_path_param_t> interp;
+ const byte_str_t str = (*cff->charStrings)[glyph];
+ interp.env.init (str, *cff, fd);
+ interp.env.set_in_seac (in_seac);
+ cff1_path_param_t param (cff, font, draw_helper, 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::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) 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_helper);
+}
+#endif
+
+struct get_seac_param_t
+{
+ void init (const OT::cff1::accelerator_t *_cff)
+ {
+ cff = _cff;
+ base = 0;
+ accent = 0;
+ }
+
+ bool has_seac () const { return base && accent; }
+
+ const OT::cff1::accelerator_t *cff;
+ hb_codepoint_t base;
+ hb_codepoint_t accent;
+};
+
+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_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);
+ cff1_cs_interpreter_t<cff1_cs_opset_seac_t, get_seac_param_t> interp;
+ const byte_str_t str = (*charStrings)[glyph];
+ interp.env.init (str, *this, fd);
+ get_seac_param_t param;
+ param.init (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/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
new file mode 100644
index 0000000000..7228f77727
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-cff1-table.hh
@@ -0,0 +1,1403 @@
+/*
+ * 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-cff1.hh"
+#include "hb-draw.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;
+template <typename Type> struct CFF1IndexOf : CFFIndexOf<HBUINT16, Type> {};
+
+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
+ {
+ 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);
+ unsigned int size = src.get_size ();
+ Encoding *dest = c->allocate_size<Encoding> (size);
+ if (unlikely (!dest)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ /* 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);
+
+ 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) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
+ }
+
+ hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+ {
+ if (glyph == 0)
+ return 0;
+ else
+ return sids[glyph - 1];
+ }
+
+ 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;
+ }
+
+ unsigned int get_size (unsigned int num_glyphs) const
+ {
+ assert (num_glyphs > 0);
+ return HBUINT16::static_size * (num_glyphs - 1);
+ }
+
+ HBUINT16 sids[HB_VAR_ARRAY];
+
+ 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) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+ num_glyphs--;
+ for (unsigned int i = 0; num_glyphs > 0; i++)
+ {
+ if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1)))
+ return_trace (false);
+ num_glyphs -= (ranges[i].nLeft + 1);
+ }
+ return_trace (true);
+ }
+
+ hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+ {
+ if (glyph == 0) return 0;
+ glyph--;
+ for (unsigned int i = 0;; i++)
+ {
+ if (glyph <= ranges[i].nLeft)
+ return (hb_codepoint_t)ranges[i].first + glyph;
+ glyph -= (ranges[i].nLeft + 1);
+ }
+
+ return 0;
+ }
+
+ 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
+ {
+ unsigned int size = HBUINT8::static_size;
+ int glyph = (int)num_glyphs;
+
+ assert (glyph > 0);
+ glyph--;
+ for (unsigned int i = 0; glyph > 0; i++)
+ {
+ glyph -= (ranges[i].nLeft + 1);
+ size += Charset_Range<TYPE>::static_size;
+ }
+
+ return size;
+ }
+
+ Charset_Range<TYPE> ranges[HB_VAR_ARRAY];
+
+ 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);
+ unsigned int size = src.get_size (num_glyphs);
+ Charset *dest = c->allocate_size<Charset> (size);
+ if (unlikely (!dest)) return_trace (false);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ /* 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::min_size + HBUINT16::static_size * (num_glyphs - 1));
+ 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[i].code;
+ for (int left = (int)sid_ranges[i].glyph; left >= 0; left--)
+ fmt0->sids[glyph++] = sid++;
+ }
+ }
+ break;
+
+ case 1:
+ {
+ Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::min_size + Charset1_Range::static_size * sid_ranges.length);
+ if (unlikely (!fmt1)) return_trace (false);
+ for (unsigned int i = 0; i < sid_ranges.length; i++)
+ {
+ if (unlikely (!(sid_ranges[i].glyph <= 0xFF)))
+ return_trace (false);
+ fmt1->ranges[i].first = sid_ranges[i].code;
+ fmt1->ranges[i].nLeft = sid_ranges[i].glyph;
+ }
+ }
+ break;
+
+ case 2:
+ {
+ Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::min_size + Charset2_Range::static_size * sid_ranges.length);
+ if (unlikely (!fmt2)) return_trace (false);
+ for (unsigned int i = 0; i < sid_ranges.length; i++)
+ {
+ if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF)))
+ return_trace (false);
+ fmt2->ranges[i].first = sid_ranges[i].code;
+ fmt2->ranges[i].nLeft = sid_ranges[i].glyph;
+ }
+ }
+ 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) const
+ {
+ if (unlikely (glyph >= num_glyphs)) return 0;
+ switch (format)
+ {
+ case 0: return u.format0.get_sid (glyph);
+ case 1: return u.format1.get_sid (glyph);
+ case 2: return u.format2.get_sid (glyph);
+ default:return 0;
+ }
+ }
+
+ 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) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+
+ switch (format)
+ {
+ case 0: return_trace (u.format0.sanitize (c, c->get_num_glyphs ()));
+ case 1: return_trace (u.format1.sanitize (c, c->get_num_glyphs ()));
+ case 2: return_trace (u.format2.sanitize (c, c->get_num_glyphs ()));
+ 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_inc_bimap_t &sidmap)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely ((strings.count == 0) || (sidmap.get_population () == 0)))
+ {
+ if (unlikely (!c->extend_min (this->count)))
+ return_trace (false);
+ count = 0;
+ return_trace (true);
+ }
+
+ byte_str_array_t bytesArray;
+ bytesArray.init ();
+ if (!bytesArray.resize (sidmap.get_population ()))
+ return_trace (false);
+ for (unsigned int i = 0; i < strings.count; i++)
+ {
+ hb_codepoint_t j = sidmap[i];
+ if (j != HB_MAP_VALUE_INVALID)
+ bytesArray[j] = strings[i];
+ }
+
+ bool result = CFF1Index::serialize (c, bytesArray);
+ bytesArray.fini ();
+ 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) {}
+
+ 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_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.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:
+ env.clear_args ();
+ break;
+ 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:
+ val.single_val = env.argStack.pop_num ();
+ 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 : 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 CFF1IndexOf<TopDict> 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) &&
+ likely (version.major == 1));
+ }
+
+ template <typename PRIVOPSET, typename PRIVDICTVAL>
+ struct accelerator_templ_t
+ {
+ void init (hb_face_t *face)
+ {
+ 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))
+ { fini (); return; }
+
+ nameIndex = &cff->nameIndex (cff);
+ if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc))
+ { fini (); return; }
+
+ topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ());
+ if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
+ { fini (); return; }
+
+ { /* parse top dict */
+ const byte_str_t topDictStr = (*topDictIndex)[0];
+ if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+ cff1_top_dict_interpreter_t top_interp;
+ top_interp.env.init (topDictStr);
+ topDict.init ();
+ if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+ }
+
+ if (is_predef_charset ())
+ charset = &Null (Charset);
+ else
+ {
+ charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset);
+ if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; }
+ }
+
+ 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)))
+ { fini (); return; }
+
+ fdCount = fdArray->count;
+ }
+ else
+ {
+ fdArray = &Null (CFF1FDArray);
+ fdSelect = &Null (CFF1FDSelect);
+ }
+
+ encoding = &Null (Encoding);
+ if (is_CID ())
+ {
+ if (unlikely (charset == &Null (Charset))) { fini (); return; }
+ }
+ else
+ {
+ if (!is_predef_encoding ())
+ {
+ encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset);
+ if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; }
+ }
+ }
+
+ stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ());
+ if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc))
+ { fini (); return; }
+
+ globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ());
+ if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc))
+ { fini (); return; }
+
+ charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset);
+
+ if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc)))
+ { fini (); return; }
+
+ num_glyphs = charStrings->count;
+ if (num_glyphs != sc.get_num_glyphs ())
+ { fini (); return; }
+
+ if (unlikely (!privateDicts.resize (fdCount)))
+ { fini (); return; }
+ 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++)
+ {
+ byte_str_t fontDictStr = (*fdArray)[i];
+ if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+ cff1_font_dict_values_t *font;
+ cff1_font_dict_interpreter_t font_interp;
+ font_interp.env.init (fontDictStr);
+ font = fontDicts.push ();
+ if (unlikely (font == &Crap (cff1_font_dict_values_t))) { fini (); return; }
+ font->init ();
+ if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+ PRIVDICTVAL *priv = &privateDicts[i];
+ const byte_str_t privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+ if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp;
+ priv_interp.env.init (privDictStr);
+ priv->init ();
+ if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+ priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset);
+ if (priv->localSubrs != &Null (CFF1Subrs) &&
+ unlikely (!priv->localSubrs->sanitize (&sc)))
+ { fini (); return; }
+ }
+ }
+ else /* non-CID */
+ {
+ cff1_top_dict_values_t *font = &topDict;
+ PRIVDICTVAL *priv = &privateDicts[0];
+
+ const byte_str_t privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+ if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp;
+ priv_interp.env.init (privDictStr);
+ priv->init ();
+ if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+ priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset);
+ if (priv->localSubrs != &Null (CFF1Subrs) &&
+ unlikely (!priv->localSubrs->sanitize (&sc)))
+ { fini (); return; }
+ }
+ }
+
+ void fini ()
+ {
+ sc.end_processing ();
+ topDict.fini ();
+ fontDicts.fini_deep ();
+ privateDicts.fini_deep ();
+ hb_blob_destroy (blob);
+ blob = nullptr;
+ }
+
+ 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) const
+ {
+ if (encoding != &Null (Encoding))
+ return encoding->get_code (glyph);
+ else
+ {
+ hb_codepoint_t sid = glyph_to_sid (glyph);
+ 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;
+ }
+ }
+
+ hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const
+ {
+ if (charset != &Null (Charset))
+ return charset->get_sid (glyph, num_glyphs);
+ 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_blob_t *blob;
+ hb_sanitize_context_t sc;
+
+ public:
+ const Encoding *encoding;
+ const Charset *charset;
+ const CFF1NameIndex *nameIndex;
+ const CFF1TopDictIndex *topDictIndex;
+ const CFF1StringIndex *stringIndex;
+ const CFF1Subrs *globalSubrs;
+ const CFF1CharStrings *charStrings;
+ const CFF1FDArray *fdArray;
+ const CFF1FDSelect *fdSelect;
+ unsigned int fdCount;
+
+ cff1_top_dict_values_t topDict;
+ hb_vector_t<cff1_font_dict_values_t>
+ fontDicts;
+ hb_vector_t<PRIVDICTVAL> privateDicts;
+
+ unsigned int num_glyphs;
+ };
+
+ struct accelerator_t : accelerator_templ_t<cff1_private_dict_opset_t, cff1_private_dict_values_t>
+ {
+ void init (hb_face_t *face)
+ {
+ SUPER::init (face);
+
+ if (!is_valid ()) return;
+ if (is_CID ()) return;
+
+ /* fill glyph_names */
+ for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
+ {
+ hb_codepoint_t sid = glyph_to_sid (gid);
+ gname_t gname;
+ gname.sid = sid;
+ if (sid < cff1_std_strings_length)
+ gname.name = cff1_std_strings (sid);
+ else
+ {
+ byte_str_t ustr = (*stringIndex)[sid - cff1_std_strings_length];
+ gname.name = hb_bytes_t ((const char*)ustr.arrayZ, ustr.length);
+ }
+ if (unlikely (!gname.name.arrayZ)) { fini (); return; }
+ glyph_names.push (gname);
+ }
+ glyph_names.qsort ();
+ }
+
+ void fini ()
+ {
+ glyph_names.fini ();
+
+ SUPER::fini ();
+ }
+
+ bool get_glyph_name (hb_codepoint_t glyph,
+ char *buf, unsigned int buf_len) const
+ {
+ if (!buf) return true;
+ if (unlikely (!is_valid ())) return false;
+ if (is_CID()) return false;
+ 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
+ {
+ byte_str_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 (len < 0) len = strlen (name);
+ if (unlikely (!len)) return false;
+
+ gname_t key = { hb_bytes_t (name, len), 0 };
+ const gname_t *gname = glyph_names.bsearch (key);
+ 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 get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const;
+#ifdef HB_EXPERIMENTAL_API
+ HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const;
+#endif
+
+ 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_;
+ int 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); }
+ };
+
+ 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, cff1_private_dict_values_subset_t> {};
+
+ bool subset (hb_subset_context_t *c) const { return hb_subset_cff1 (c); }
+
+ 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 */
+ OffsetTo<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 {};
+} /* namespace OT */
+
+#endif /* HB_OT_CFF1_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.cc b/thirdparty/harfbuzz/src/hb-ot-cff2-table.cc
new file mode 100644
index 0000000000..ac0feeee21
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.cc
@@ -0,0 +1,215 @@
+/*
+ * 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
+{
+ void init ()
+ {
+ path_open = false;
+ 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;
+ 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, cff2_extents_param_t>
+{
+ static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt)
+ {
+ param.end_path ();
+ env.moveto (pt);
+ }
+
+ static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1)
+ {
+ if (!param.is_path_open ())
+ {
+ param.start_path ();
+ param.update_bounds (env.get_pt ());
+ }
+ env.moveto (pt1);
+ param.update_bounds (env.get_pt ());
+ }
+
+ static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+ {
+ if (!param.is_path_open ())
+ {
+ param.start_path ();
+ param.update_bounds (env.get_pt ());
+ }
+ /* include control points */
+ param.update_bounds (pt1);
+ param.update_bounds (pt2);
+ env.moveto (pt3);
+ param.update_bounds (env.get_pt ());
+ }
+};
+
+struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_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);
+ cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t> interp;
+ const byte_str_t str = (*charStrings)[glyph];
+ interp.env.init (str, *this, fd, font->coords, font->num_coords);
+ cff2_extents_param_t param;
+ param.init ();
+ 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 = font->em_scalef_x (param.min_x.to_real ());
+ extents->width = font->em_scalef_x (param.max_x.to_real () - param.min_x.to_real ());
+ }
+ if (param.min_y >= param.max_y)
+ {
+ extents->height = 0;
+ extents->y_bearing = 0;
+ }
+ else
+ {
+ extents->y_bearing = font->em_scalef_y (param.max_y.to_real ());
+ extents->height = font->em_scalef_y (param.min_y.to_real () - param.max_y.to_real ());
+ }
+
+ return true;
+}
+
+#ifdef HB_EXPERIMENTAL_API
+struct cff2_path_param_t
+{
+ cff2_path_param_t (hb_font_t *font_, draw_helper_t &draw_helper_)
+ {
+ draw_helper = &draw_helper_;
+ font = font_;
+ }
+
+ void move_to (const point_t &p)
+ { draw_helper->move_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); }
+
+ void line_to (const point_t &p)
+ { draw_helper->line_to (font->em_scalef_x (p.x.to_real ()), font->em_scalef_y (p.y.to_real ())); }
+
+ void cubic_to (const point_t &p1, const point_t &p2, const point_t &p3)
+ {
+ draw_helper->cubic_to (font->em_scalef_x (p1.x.to_real ()), font->em_scalef_y (p1.y.to_real ()),
+ font->em_scalef_x (p2.x.to_real ()), font->em_scalef_y (p2.y.to_real ()),
+ font->em_scalef_x (p3.x.to_real ()), font->em_scalef_y (p3.y.to_real ()));
+ }
+
+ protected:
+ draw_helper_t *draw_helper;
+ hb_font_t *font;
+};
+
+struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_interp_env_t, cff2_path_param_t>
+{
+ static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt)
+ {
+ param.move_to (pt);
+ env.moveto (pt);
+ }
+
+ static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1)
+ {
+ param.line_to (pt1);
+ env.moveto (pt1);
+ }
+
+ static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
+ {
+ param.cubic_to (pt1, pt2, pt3);
+ env.moveto (pt3);
+ }
+};
+
+struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, cff2_path_procs_path_t> {};
+
+bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) 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);
+ cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t> interp;
+ const byte_str_t str = (*charStrings)[glyph];
+ interp.env.init (str, *this, fd, font->coords, font->num_coords);
+ cff2_path_param_t param (font, draw_helper);
+ if (unlikely (!interp.interpret (param))) return false;
+ return true;
+}
+#endif
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
new file mode 100644
index 0000000000..829217feaa
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-cff2-table.hh
@@ -0,0 +1,531 @@
+/*
+ * 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-cff2.hh"
+#include "hb-draw.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;
+template <typename Type> struct CFF2IndexOf : CFFIndexOf<HBUINT32, Type> {};
+
+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);
+ memcpy (dest, &src, size);
+ return_trace (true);
+ }
+
+ unsigned int get_size (unsigned int num_glyphs) const
+ {
+ switch (format)
+ {
+ case 0: return format.static_size + u.format0.get_size (num_glyphs);
+ case 3: return format.static_size + u.format3.get_size ();
+ case 4: return format.static_size + u.format4.get_size ();
+ default:return 0;
+ }
+ }
+
+ hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+ {
+ if (this == &Null (CFF2FDSelect))
+ return 0;
+
+ switch (format)
+ {
+ case 0: return u.format0.get_fd (glyph);
+ case 3: return u.format3.get_fd (glyph);
+ case 4: return u.format4.get_fd (glyph);
+ default:return 0;
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+
+ switch (format)
+ {
+ case 0: return_trace (u.format0.sanitize (c, fdcount));
+ case 3: return_trace (u.format3.sanitize (c, fdcount));
+ case 4: return_trace (u.format4.sanitize (c, fdcount));
+ default:return_trace (false);
+ }
+ }
+
+ HBUINT8 format;
+ union {
+ FDSelect0 format0;
+ FDSelect3 format3;
+ FDSelect4 format4;
+ } u;
+ public:
+ DEFINE_SIZE_MIN (2);
+};
+
+struct CFF2VariationStore
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this)) && c->check_range (&varStore, size) && varStore.sanitize (c));
+ }
+
+ bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int size_ = varStore->get_size ();
+ CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_);
+ if (unlikely (!dest)) return_trace (false);
+ 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
+{
+ void init (const byte_str_t &str)
+ {
+ num_interp_env_t::init (str);
+ ivs = 0;
+ seen_vsindex = false;
+ }
+
+ 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;
+ bool seen_vsindex;
+};
+
+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:
+ val.single_val = env.argStack.pop_num ();
+ env.clear_args ();
+ break;
+ 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) &&
+ likely (version.major == 2));
+ }
+
+ template <typename PRIVOPSET, typename PRIVDICTVAL>
+ struct accelerator_templ_t
+ {
+ void init (hb_face_t *face)
+ {
+ 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))
+ { fini (); return; }
+
+ { /* parse top dict */
+ byte_str_t topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+ if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+ cff2_top_dict_interpreter_t top_interp;
+ top_interp.env.init (topDictStr);
+ topDict.init ();
+ if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+ }
+
+ 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)) ||
+ (((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
+ { fini (); return; }
+
+ num_glyphs = charStrings->count;
+ if (num_glyphs != sc.get_num_glyphs ())
+ { fini (); return; }
+
+ fdCount = fdArray->count;
+ if (!privateDicts.resize (fdCount))
+ { fini (); return; }
+
+ /* parse font dicts and gather private dicts */
+ for (unsigned int i = 0; i < fdCount; i++)
+ {
+ const byte_str_t fontDictStr = (*fdArray)[i];
+ if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+ cff2_font_dict_values_t *font;
+ cff2_font_dict_interpreter_t font_interp;
+ font_interp.env.init (fontDictStr);
+ font = fontDicts.push ();
+ if (unlikely (font == &Crap (cff2_font_dict_values_t))) { fini (); return; }
+ font->init ();
+ if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+
+ const byte_str_t privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
+ if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+ dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp;
+ priv_interp.env.init(privDictStr);
+ privateDicts[i].init ();
+ if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
+
+ privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset);
+ if (privateDicts[i].localSubrs != &Null (CFF2Subrs) &&
+ unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
+ { fini (); return; }
+ }
+ }
+
+ void fini ()
+ {
+ sc.end_processing ();
+ topDict.fini ();
+ fontDicts.fini_deep ();
+ privateDicts.fini_deep ();
+ hb_blob_destroy (blob);
+ blob = nullptr;
+ }
+
+ bool is_valid () const { return blob; }
+
+ protected:
+ hb_blob_t *blob;
+ hb_sanitize_context_t sc;
+
+ public:
+ cff2_top_dict_values_t topDict;
+ const CFF2Subrs *globalSubrs;
+ const CFF2VariationStore *varStore;
+ const CFF2CharStrings *charStrings;
+ const CFF2FDArray *fdArray;
+ const CFF2FDSelect *fdSelect;
+ unsigned int fdCount;
+
+ hb_vector_t<cff2_font_dict_values_t> fontDicts;
+ hb_vector_t<PRIVDICTVAL> privateDicts;
+
+ unsigned int num_glyphs;
+ };
+
+ struct accelerator_t : accelerator_templ_t<cff2_private_dict_opset_t, cff2_private_dict_values_t>
+ {
+ HB_INTERNAL bool get_extents (hb_font_t *font,
+ hb_codepoint_t glyph,
+ hb_glyph_extents_t *extents) const;
+#ifdef HB_EXPERIMENTAL_API
+ HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, draw_helper_t &draw_helper) const;
+#endif
+ };
+
+ typedef accelerator_templ_t<cff2_private_dict_opset_subset_t, cff2_private_dict_values_subset_t> accelerator_subset_t;
+
+ bool subset (hb_subset_context_t *c) const { return hb_subset_cff2 (c); }
+
+ 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 {};
+} /* namespace OT */
+
+#endif /* HB_OT_CFF2_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
new file mode 100644
index 0000000000..cc48379bb8
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh
@@ -0,0 +1,1711 @@
+/*
+ * 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-open-type.hh"
+#include "hb-set.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 (!gid)
+ return false;
+ *glyph = gid;
+ return true;
+ }
+ 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,
+ hb_requires (hb_is_iterator (Iterator))>
+ HBUINT16* serialize_endcode_array (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ HBUINT16 *endCode = c->start_embed<HBUINT16> ();
+ hb_codepoint_t prev_endcp = 0xFFFF;
+
+ for (const hb_item_type<Iterator> _ : +it)
+ {
+ if (prev_endcp != 0xFFFF && prev_endcp + 1u != _.first)
+ {
+ HBUINT16 end_code;
+ end_code = prev_endcp;
+ c->copy<HBUINT16> (end_code);
+ }
+ prev_endcp = _.first;
+ }
+
+ {
+ // last endCode
+ HBUINT16 endcode;
+ endcode = prev_endcp;
+ if (unlikely (!c->copy<HBUINT16> (endcode))) return nullptr;
+ // There must be a final entry with end_code == 0xFFFF.
+ if (prev_endcp != 0xFFFF)
+ {
+ HBUINT16 finalcode;
+ finalcode = 0xFFFF;
+ if (unlikely (!c->copy<HBUINT16> (finalcode))) return nullptr;
+ }
+ }
+
+ return endCode;
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ HBUINT16* serialize_startcode_array (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ HBUINT16 *startCode = c->start_embed<HBUINT16> ();
+ hb_codepoint_t prev_cp = 0xFFFF;
+
+ for (const hb_item_type<Iterator> _ : +it)
+ {
+ if (prev_cp == 0xFFFF || prev_cp + 1u != _.first)
+ {
+ HBUINT16 start_code;
+ start_code = _.first;
+ c->copy<HBUINT16> (start_code);
+ }
+
+ prev_cp = _.first;
+ }
+
+ // There must be a final entry with end_code == 0xFFFF.
+ if (it.len () == 0 || prev_cp != 0xFFFF)
+ {
+ HBUINT16 finalcode;
+ finalcode = 0xFFFF;
+ if (unlikely (!c->copy<HBUINT16> (finalcode))) return nullptr;
+ }
+
+ return startCode;
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ HBINT16* serialize_idDelta_array (hb_serialize_context_t *c,
+ Iterator it,
+ HBUINT16 *endCode,
+ HBUINT16 *startCode,
+ unsigned segcount)
+ {
+ unsigned i = 0;
+ hb_codepoint_t last_gid = 0, start_gid = 0, last_cp = 0xFFFF;
+ bool use_delta = true;
+
+ HBINT16 *idDelta = c->start_embed<HBINT16> ();
+ if ((char *)idDelta - (char *)startCode != (int) segcount * (int) HBINT16::static_size)
+ return nullptr;
+
+ for (const hb_item_type<Iterator> _ : +it)
+ {
+ if (_.first == startCode[i])
+ {
+ use_delta = true;
+ start_gid = _.second;
+ }
+ else if (_.second != last_gid + 1) use_delta = false;
+
+ if (_.first == endCode[i])
+ {
+ HBINT16 delta;
+ if (use_delta) delta = (int)start_gid - (int)startCode[i];
+ else delta = 0;
+ c->copy<HBINT16> (delta);
+
+ i++;
+ }
+
+ last_gid = _.second;
+ last_cp = _.first;
+ }
+
+ if (it.len () == 0 || last_cp != 0xFFFF)
+ {
+ HBINT16 delta;
+ delta = 1;
+ if (unlikely (!c->copy<HBINT16> (delta))) return nullptr;
+ }
+
+ return idDelta;
+ }
+
+ 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)
+ {
+ 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;
+
+ + hb_range (segcount)
+ | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; })
+ | hb_apply ([&] (const unsigned i)
+ {
+ idRangeOffset[i] = 2 * (c->start_embed<HBUINT16> () - idRangeOffset - i);
+
+ + it
+ | hb_filter ([&] (const hb_item_type<Iterator> _) { return _.first >= startCode[i] && _.first <= endCode[i]; })
+ | hb_apply ([&] (const hb_item_type<Iterator> _)
+ {
+ HBUINT16 glyID;
+ glyID = _.second;
+ c->copy<HBUINT16> (glyID);
+ })
+ ;
+
+
+ })
+ ;
+
+ 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_pair_t<hb_codepoint_t, hb_codepoint_t> _)
+ { return _.first <= 0xFFFF; })
+ ;
+
+ if (format4_iter.len () == 0) return;
+
+ unsigned table_initpos = c->length ();
+ if (unlikely (!c->extend_min (*this))) return;
+ this->format = 4;
+
+ //serialize endCode[]
+ HBUINT16 *endCode = serialize_endcode_array (c, format4_iter);
+ if (unlikely (!endCode)) return;
+
+ unsigned segcount = (c->length () - min_size) / HBUINT16::static_size;
+
+ // 2 bytes of padding.
+ if (unlikely (!c->allocate_size<HBUINT16> (HBUINT16::static_size))) return; // 2 bytes of padding.
+
+ // serialize startCode[]
+ HBUINT16 *startCode = serialize_startcode_array (c, format4_iter);
+ if (unlikely (!startCode)) return;
+
+ //serialize idDelta[]
+ HBINT16 *idDelta = serialize_idDelta_array (c, format4_iter, endCode, startCode, segcount);
+ if (unlikely (!idDelta)) return;
+
+ HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount);
+ if (unlikely (!c->check_success (idRangeOffset))) return;
+
+ if (unlikely (!c->check_assign(this->length, c->length () - table_initpos))) 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;
+ }
+
+ struct accelerator_t
+ {
+ accelerator_t () {}
+ accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); }
+ ~accelerator_t () { fini (); }
+
+ 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;
+ }
+ void fini () {}
+
+ 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]) return -1;
+ return 0;
+ }
+ HBUINT16 last;
+ };
+
+ const HBUINT16 *found = hb_bsearch (codepoint,
+ this->endCount,
+ this->segCount,
+ 2,
+ _hb_cmp_method<hb_codepoint_t, CustomRange, unsigned>,
+ this->segCount + 1);
+ if (!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 (!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];
+ 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;
+ out->add (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))
+ break;
+ hb_codepoint_t gid = this->glyphIdArray[index];
+ if (unlikely (!gid))
+ continue;
+ out->add (codepoint);
+ }
+ }
+ }
+ }
+
+ void collect_mapping (hb_set_t *unicodes, /* OUT */
+ hb_map_t *mapping /* OUT */) const
+ {
+ 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);
+
+ 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 (!gid)
+ return false;
+ *glyph = gid;
+ return true;
+ }
+ 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<HBGlyphID, 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 (!gid)
+ return false;
+ *glyph = gid;
+ return true;
+ }
+
+ 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, end);
+ }
+ }
+
+ void collect_mapping (hb_set_t *unicodes, /* OUT */
+ hb_map_t *mapping, /* OUT */
+ unsigned num_glyphs) const
+ {
+ for (unsigned i = 0; i < this->groups.len; i++)
+ {
+ hb_codepoint_t start = this->groups[i].startCharCode;
+ hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode,
+ (hb_codepoint_t) HB_UNICODE_MAX);
+ 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;
+
+ for (unsigned cp = start; cp <= end; cp++)
+ {
+ unicodes->add (cp);
+ mapping->set (cp, gid);
+ gid++;
+ }
+ }
+ }
+
+ 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. */
+ SortedArrayOf<CmapSubtableLongGroup, HBUINT32>
+ groups; /* Groupings. */
+ public:
+ DEFINE_SIZE_ARRAY (16, groups);
+};
+
+struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
+{
+ 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.len () == 0) return;
+ unsigned table_initpos = c->length ();
+ if (unlikely (!c->extend_min (*this))) return;
+
+ hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF;
+ hb_codepoint_t glyphID = 0;
+
+ for (const hb_item_type<Iterator> _ : +it)
+ {
+ if (startCharCode == 0xFFFF)
+ {
+ 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 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 : SortedArrayOf<UnicodeValueRange, HBUINT32>
+{
+ 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
+ {
+ DefaultUVS *out = c->start_embed<DefaultUVS> ();
+ if (unlikely (!out)) return nullptr;
+ auto snap = c->snapshot ();
+
+ HBUINT32 len;
+ len = 0;
+ if (unlikely (!c->copy<HBUINT32> (len))) return nullptr;
+ unsigned init_len = c->length ();
+
+ hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID;
+ int count = -1;
+
+ for (const UnicodeValueRange& _ : as_array ())
+ {
+ for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1))
+ {
+ unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt;
+ if (!unicodes->has (curEntry)) continue;
+ count += 1;
+ if (lastCode == HB_MAP_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))) 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 */
+ HBGlyphID glyphID; /* Glyph ID of the UVS */
+ public:
+ DEFINE_SIZE_STATIC (5);
+};
+
+struct NonDefaultUVS : SortedArrayOf<UVSMapping, HBUINT32>
+{
+ void collect_unicodes (hb_set_t *out) const
+ {
+ unsigned int count = len;
+ for (unsigned int i = 0; i < count; i++)
+ out->add (arrayZ[i].unicodeValue);
+ }
+
+ void collect_mapping (hb_set_t *unicodes, /* OUT */
+ hb_map_t *mapping /* OUT */) const
+ {
+ unsigned count = len;
+ for (unsigned i = 0; i < count; i++)
+ {
+ hb_codepoint_t unicode = arrayZ[i].unicodeValue;
+ hb_codepoint_t glyphid = arrayZ[i].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
+ {
+ NonDefaultUVS *out = c->start_embed<NonDefaultUVS> ();
+ if (unlikely (!out)) return nullptr;
+
+ 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. */
+ LOffsetTo<DefaultUVS>
+ defaultUVS; /* Offset to Default UVS Table. May be 0. */
+ LOffsetTo<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
+ {
+ unsigned int count = record.len;
+ for (unsigned int i = 0; i < count; i++)
+ out->add (record.arrayZ[i].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);
+ c->check_assign (this->record.len,
+ (c->length () - table_initpos - CmapSubtableFormat14::min_size) /
+ VariationSelectorRecord::static_size);
+
+ /* 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. */
+ SortedArrayOf<VariationSelectorRecord, HBUINT32>
+ 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;
+ }
+ }
+
+ 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);
+ 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. */
+ LOffsetTo<CmapSubtable>
+ subtable; /* Byte offset from beginning of table to the subtable for this encoding. */
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+struct cmap
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap;
+
+ template<typename Iterator, typename EncodingRecIter,
+ hb_requires (hb_is_iterator (EncodingRecIter))>
+ void serialize (hb_serialize_context_t *c,
+ Iterator it,
+ EncodingRecIter encodingrec_iter,
+ const void *base,
+ const hb_subset_plan_t *plan)
+ {
+ if (unlikely (!c->extend_min ((*this)))) return;
+ this->version = 0;
+
+ unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
+
+ for (const EncodingRecord& _ : encodingrec_iter)
+ {
+ unsigned format = (base+_.subtable).u.format;
+ if (!plan->glyphs_requested->is_empty ())
+ {
+ hb_set_t unicodes_set;
+ hb_map_t cp_glyphid_map;
+ (base+_.subtable).collect_mapping (&unicodes_set, &cp_glyphid_map);
+
+ auto table_iter =
+ + hb_zip (unicodes_set.iter(), unicodes_set.iter() | hb_map(cp_glyphid_map))
+ | hb_filter (plan->_glyphset, hb_second)
+ | hb_filter ([plan] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& p)
+ {
+ return plan->unicodes->has (p.first) ||
+ plan->glyphs_requested->has (p.second);
+ })
+ | hb_map ([plan] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t>& p_org)
+ {
+ return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (p_org.first, plan->glyph_map->get(p_org.second));
+ })
+ ;
+
+ if (format == 4) c->copy (_, table_iter, 4u, base, plan, &format4objidx);
+ else if (format == 12) c->copy (_, table_iter, 12u, base, plan, &format12objidx);
+ else if (format == 14) c->copy (_, table_iter, 14u, base, plan, &format14objidx);
+ }
+ /* when --gids option is not used, we iterate input unicodes instead of
+ * all codepoints in each subtable, which is more efficient */
+ else
+ {
+ hb_set_t unicodes_set;
+ (base+_.subtable).collect_unicodes (&unicodes_set);
+
+ if (format == 4) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx);
+ else if (format == 12) 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);
+ }
+
+ 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> ();
+ if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false);
+
+ auto encodingrec_iter =
+ + hb_iter (encodingRecord)
+ | hb_filter ([&] (const EncodingRecord& _)
+ {
+ if ((_.platformID == 0 && _.encodingID == 3) ||
+ (_.platformID == 0 && _.encodingID == 4) ||
+ (_.platformID == 3 && _.encodingID == 1) ||
+ (_.platformID == 3 && _.encodingID == 10) ||
+ (this + _.subtable).u.format == 14)
+ return true;
+
+ return false;
+ })
+ ;
+
+ 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 = hb_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 =
+ + hb_iter (c->plan->unicodes)
+ | hb_map ([&] (hb_codepoint_t _)
+ {
+ hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID;
+ c->plan->new_gid_for_codepoint (_, &new_gid);
+ return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, new_gid);
+ })
+ | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _)
+ { return (_.second != HB_MAP_VALUE_INVALID); })
+ ;
+ cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan);
+ return_trace (true);
+ }
+
+ 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
+ {
+ void init (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))
+ this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable>;
+ 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;
+ }
+ }
+ }
+ }
+
+ void fini () { this->table.destroy (); }
+
+ bool get_nominal_glyph (hb_codepoint_t unicode,
+ hb_codepoint_t *glyph) const
+ {
+ if (unlikely (!this->get_glyph_funcZ)) return false;
+ return this->get_glyph_funcZ (this->get_glyph_data, unicode, 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) const
+ {
+ if (unlikely (!this->get_glyph_funcZ)) return 0;
+
+ hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ;
+ const void *get_glyph_data = this->get_glyph_data;
+
+ unsigned int done;
+ for (done = 0;
+ done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph);
+ 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) 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);
+ }
+
+ 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);
+
+ 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_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 (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 typed_obj->get_glyph (0xF000u + codepoint, 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) &&
+ likely (version == 0) &&
+ encodingRecord.sanitize (c, this));
+ }
+
+ protected:
+ HBUINT16 version; /* Table version number (0). */
+ SortedArrayOf<EncodingRecord>
+ encodingRecord; /* Encoding tables. */
+ public:
+ DEFINE_SIZE_ARRAY (4, encodingRecord);
+};
+
+struct cmap_accelerator_t : cmap::accelerator_t {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_CMAP_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh
new file mode 100644
index 0000000000..aaa1c37c64
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-color-cbdt-table.hh
@@ -0,0 +1,985 @@
+/*
+ * 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 HB_OT_COLOR_CBDT_TABLE_HH
+#define HB_OT_COLOR_CBDT_TABLE_HH
+
+#include "hb-open-type.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;
+ 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) const
+ {
+ extents->x_bearing = font->em_scale_x (bearingX);
+ extents->y_bearing = font->em_scale_y (bearingY);
+ extents->width = font->em_scale_x (width);
+ extents->height = font->em_scale_y (-static_cast<int>(height));
+ }
+
+ 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);
+ 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) 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
+{
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ firstGlyphIndex <= lastGlyphIndex &&
+ offsetToSubtable.sanitize (c, base, lastGlyphIndex - firstGlyphIndex + 1));
+ }
+
+ const IndexSubtable* get_subtable (const void *base) const
+ {
+ return &(base+offsetToSubtable);
+ }
+
+ bool add_new_subtable (hb_subset_context_t* c,
+ cblc_bitmap_size_subset_context_t *bitmap_size_context,
+ IndexSubtableRecord *record,
+ const hb_vector_t<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 (!subtable)) return_trace (false);
+ 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 (!records->resize (records->length + 1)))
+ return_trace (c->serializer->check_success (false));
+
+ (*records)[records->length - 1].firstGlyphIndex = 1;
+ (*records)[records->length - 1].lastGlyphIndex = 0;
+ bitmap_size_context->size += IndexSubtableRecord::min_size;
+
+ c->serializer->push ();
+
+ if (unlikely (!add_new_subtable (c, bitmap_size_context, &((*records)[records->length - 1]), 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) const
+ { return (base+offsetToSubtable).get_extents (extents); }
+
+ 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);
+ }
+
+ HBGlyphID firstGlyphIndex;
+ HBGlyphID lastGlyphIndex;
+ LOffsetTo<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;
+ for (hb_codepoint_t new_gid = 0; new_gid < c->plan->num_output_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);
+
+ auto *dst = c->serializer->start_embed<IndexSubtableArray> ();
+ if (unlikely (!dst)) return_trace (false);
+
+ hb_vector_t<hb_pair_t<hb_codepoint_t, const IndexSubtableRecord*>> lookup;
+ build_lookup (c, bitmap_size_context, &lookup);
+ if (unlikely (lookup.in_error ()))
+ return c->serializer->check_success (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) &&
+ 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:
+ LNNOffsetTo<IndexSubtableArray>
+ indexSubtableArrayOffset;
+ HBUINT32 indexTablesSize;
+ HBUINT32 numberOfIndexSubtables;
+ HBUINT32 colorRef;
+ SBitLineMetrics horizontal;
+ SBitLineMetrics vertical;
+ HBGlyphID startGlyphIndex;
+ HBGlyphID endGlyphIndex;
+ HBUINT8 ppemX;
+ HBUINT8 ppemY;
+ HBUINT8 bitDepth;
+ HBINT8 flags;
+ public:
+ DEFINE_SIZE_STATIC (48);
+};
+
+
+/*
+ * Glyph Bitmap Data Formats.
+ */
+
+struct GlyphBitmapDataFormat17
+{
+ SmallGlyphMetrics glyphMetrics;
+ LArrayOf<HBUINT8> data;
+ public:
+ DEFINE_SIZE_ARRAY (9, data);
+};
+
+struct GlyphBitmapDataFormat18
+{
+ BigGlyphMetrics glyphMetrics;
+ LArrayOf<HBUINT8> data;
+ public:
+ DEFINE_SIZE_ARRAY (12, data);
+};
+
+struct GlyphBitmapDataFormat19
+{
+ LArrayOf<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) &&
+ likely (version.major == 2 || version.major == 3) &&
+ 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,
+ 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;
+ LArrayOf<BitmapSizeTable> sizeTables;
+ public:
+ DEFINE_SIZE_ARRAY (8, sizeTables);
+};
+
+struct CBDT
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_CBDT;
+
+ struct accelerator_t
+ {
+ void init (hb_face_t *face)
+ {
+ cblc = hb_sanitize_context_t ().reference_table<CBLC> (face);
+ cbdt = hb_sanitize_context_t ().reference_table<CBDT> (face);
+
+ upem = hb_face_get_upem (face);
+ }
+
+ void fini ()
+ {
+ this->cblc.destroy ();
+ this->cbdt.destroy ();
+ }
+
+ bool
+ get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) 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))
+ 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);
+ 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);
+ break;
+ }
+ default: return false; /* TODO: Support other image formats. */
+ }
+
+ /* Convert to font units. */
+ 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 (); }
+
+ 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) &&
+ 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);
+
+ auto *cblc_prime = c->serializer->start_embed<CBLC> ();
+
+ // Use a vector as a secondary buffer as the tables need to be built in parallel.
+ hb_vector_t<char> cbdt_prime;
+
+ if (unlikely (!cblc_prime)) return_trace (false);
+ 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 {};
+
+} /* namespace OT */
+
+#endif /* HB_OT_COLOR_CBDT_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh
new file mode 100644
index 0000000000..92a49bb4f4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-color-colr-table.hh
@@ -0,0 +1,278 @@
+/*
+ * 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 HB_OT_COLOR_COLR_TABLE_HH
+#define HB_OT_COLOR_COLR_TABLE_HH
+
+#include "hb-open-type.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 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:
+ HBGlyphID 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 (likely (c->check_struct (this)));
+ }
+
+ public:
+ HBGlyphID 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);
+};
+
+struct COLR
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_COLR;
+
+ bool has_data () const { return numBaseGlyphs; }
+
+ 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 () {}
+ ~accelerator_t () { fini (); }
+
+ void init (hb_face_t *face)
+ { colr = hb_sanitize_context_t ().reference_table<COLR> (face); }
+
+ void fini () { 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); }
+
+ 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);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ (this+baseGlyphsZ).sanitize (c, numBaseGlyphs) &&
+ (this+layersZ).sanitize (c, numLayers)));
+ }
+
+ template<typename BaseIterator, typename LayerIterator,
+ hb_requires (hb_is_iterator (BaseIterator)),
+ hb_requires (hb_is_iterator (LayerIterator))>
+ bool serialize (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);
+
+ if (unlikely (!c->extend_min (this))) return_trace (false);
+ this->version = version;
+ numLayers = 0;
+ numBaseGlyphs = base_it.len ();
+ baseGlyphsZ = COLR::min_size;
+ layersZ = COLR::min_size + numBaseGlyphs * BaseGlyphRecord::min_size;
+
+ 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;
+ }
+
+ for (const hb_item_type<LayerIterator>& _ : + layer_it.iter ())
+ _.as_array ().copy (c);
+
+ return_trace (true);
+ }
+
+ const BaseGlyphRecord* get_base_glyph_record (hb_codepoint_t gid) const
+ {
+ if ((unsigned int) gid == 0) // Ignore notdef.
+ return nullptr;
+ const BaseGlyphRecord* record = &(this+baseGlyphsZ).bsearch (numBaseGlyphs, (unsigned int) 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;
+
+ auto base_it =
+ + hb_range (c->plan->num_output_glyphs ())
+ | 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_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;
+ }
+
+ return hb_pair_t<bool, hb_vector_t<LayerRecord>> (true, out_layers);
+ })
+ | hb_filter (hb_first)
+ | hb_map_retains_sorting (hb_second)
+ ;
+
+ if (unlikely (!base_it || !layer_it || base_it.len () != layer_it.len ()))
+ return_trace (false);
+
+ COLR *colr_prime = c->serializer->start_embed<COLR> ();
+ return_trace (colr_prime->serialize (c->serializer, version, base_it, layer_it));
+ }
+
+ protected:
+ HBUINT16 version; /* Table version number (starts at 0). */
+ HBUINT16 numBaseGlyphs; /* Number of Base Glyph Records. */
+ LNNOffsetTo<SortedUnsizedArrayOf<BaseGlyphRecord>>
+ baseGlyphsZ; /* Offset to Base Glyph records. */
+ LNNOffsetTo<UnsizedArrayOf<LayerRecord>>
+ layersZ; /* Offset to Layer Records. */
+ HBUINT16 numLayers; /* Number of Layer Records. */
+ public:
+ DEFINE_SIZE_STATIC (14);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_COLOR_COLR_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh
new file mode 100644
index 0000000000..fa7d3207be
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-color-cpal-table.hh
@@ -0,0 +1,190 @@
+/*
+ * 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 HB_OT_COLOR_CPAL_TABLE_HH
+#define HB_OT_COLOR_CPAL_TABLE_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:
+ 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:
+ LNNOffsetTo<UnsizedArrayOf<HBUINT32>>
+ paletteFlagsZ; /* Offset from the beginning of CPAL table to
+ * the Palette Type Array. Set to 0 if no array
+ * is provided. */
+ LNNOffsetTo<UnsizedArrayOf<NameID>>
+ paletteLabelsZ; /* Offset from the beginning of CPAL table to
+ * the palette labels array. Set to 0 if no
+ * array is provided. */
+ LNNOffsetTo<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;
+ }
+
+ private:
+ const CPALV1Tail& v1 () const
+ {
+ if (version == 0) return Null (CPALV1Tail);
+ return StructAfter<CPALV1Tail> (*this);
+ }
+
+ public:
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ (this+colorRecordsZ).sanitize (c, numColorRecords) &&
+ colorRecordIndicesZ.sanitize (c, numPalettes) &&
+ (version == 0 || v1 ().sanitize (c, this, numPalettes, numColors)));
+ }
+
+ protected:
+ HBUINT16 version; /* Table version number */
+ /* Version 0 */
+ HBUINT16 numColors; /* Number of colors in each palette. */
+ HBUINT16 numPalettes; /* Number of palettes in the table. */
+ HBUINT16 numColorRecords; /* Total number of color records, combined for
+ * all palettes. */
+ LNNOffsetTo<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 /* HB_OT_COLOR_CPAL_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh
new file mode 100644
index 0000000000..09da11597d
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-color-sbix-table.hh
@@ -0,0 +1,414 @@
+/*
+ * 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 HB_OT_COLOR_SBIX_TABLE_HH
+#define HB_OT_COLOR_SBIX_TABLE_HH
+
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.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 (!new_glyph)) return_trace (nullptr);
+ 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> ();
+ if (unlikely (!out)) return_trace (false);
+ 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<LOffsetTo<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
+ {
+ void init (hb_face_t *face)
+ {
+ table = hb_sanitize_context_t ().reference_table<sbix> (face);
+ num_glyphs = face->get_num_glyphs ();
+ }
+ void fini () { 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) const
+ {
+ /* We only support PNG right now, and following function checks type. */
+ return get_png_extents (font, glyph, extents);
+ }
+
+ 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);
+ }
+
+ 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) 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>();
+
+ 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)
+ {
+ float scale = font->face->get_upem () / (float) strike_ppem;
+ extents->x_bearing = font->em_scalef_x (extents->x_bearing * scale);
+ extents->y_bearing = font->em_scalef_y (extents->y_bearing * scale);
+ extents->width = font->em_scalef_x (extents->width * scale);
+ extents->height = font->em_scalef_y (extents->height * scale);
+ }
+ else
+ {
+ extents->x_bearing = font->em_scale_x (extents->x_bearing);
+ extents->y_bearing = font->em_scale_y (extents->y_bearing);
+ extents->width = font->em_scale_x (extents->width);
+ extents->height = font->em_scale_y (extents->height);
+ }
+
+ 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) &&
+ 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<LOffsetLArrayOf<SBIXStrike>> ();
+ if (unlikely (!out)) return_trace (false);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+
+ hb_vector_t<LOffsetTo<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);
+
+ sbix *sbix_prime = c->serializer->start_embed<sbix> ();
+ if (unlikely (!sbix_prime)) return_trace (false);
+ 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). */
+ LOffsetLArrayOf<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 {};
+
+} /* namespace OT */
+
+#endif /* HB_OT_COLOR_SBIX_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh b/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh
new file mode 100644
index 0000000000..1cc40ae53f
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-color-svg-table.hh
@@ -0,0 +1,124 @@
+/*
+ * 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_COLOR_SVG_TABLE_HH
+#define HB_OT_COLOR_SVG_TABLE_HH
+
+#include "hb-open-type.hh"
+
+/*
+ * SVG -- SVG (Scalable Vector Graphics)
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/svg
+ */
+
+#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ')
+
+
+namespace OT {
+
+
+struct SVGDocumentIndexEntry
+{
+ int cmp (hb_codepoint_t g) const
+ { return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; }
+
+ hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const
+ {
+ return hb_blob_create_sub_blob (svg_blob,
+ index_offset + (unsigned int) svgDoc,
+ svgDocLength);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ svgDoc.sanitize (c, base, svgDocLength));
+ }
+
+ protected:
+ HBUINT16 startGlyphID; /* The first glyph ID in the range described by
+ * this index entry. */
+ HBUINT16 endGlyphID; /* The last glyph ID in the range described by
+ * this index entry. Must be >= startGlyphID. */
+ LNNOffsetTo<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
+ {
+ void init (hb_face_t *face)
+ { table = hb_sanitize_context_t ().reference_table<SVG> (face); }
+ void fini () { 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 (); }
+
+ private:
+ hb_blob_ptr_t<SVG> table;
+ };
+
+ 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). */
+ LOffsetTo<SortedArrayOf<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 {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_COLOR_SVG_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-color.cc b/thirdparty/harfbuzz/src/hb-ot-color.cc
new file mode 100644
index 0000000000..0e7203a88b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-color.cc
@@ -0,0 +1,321 @@
+/*
+ * 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 "hb-ot-color-cbdt-table.hh"
+#include "hb-ot-color-colr-table.hh"
+#include "hb-ot-color-cpal-table.hh"
+#include "hb-ot-color-sbix-table.hh"
+#include "hb-ot-color-svg-table.hh"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+/**
+ * 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 specificed 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.
+ *
+ * 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 any `COLR` color layers.
+ *
+ * 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_data ();
+}
+
+/**
+ * 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.
+ *
+ * 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 UPEM value must be set on the @font
+ * object. If UPEM is unset, the blob returned will be the largest PNG available.
+ *
+ * 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/thirdparty/harfbuzz/src/hb-ot-color.h b/thirdparty/harfbuzz/src/hb-ot-color.h
new file mode 100644
index 0000000000..63ef20a1a0
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-color.h
@@ -0,0 +1,139 @@
+/*
+ * 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
+ */
+
+#ifndef HB_OT_H_IN
+#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.
+ *
+ * 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:
+ *
+ * Pairs of glyph and color index.
+ *
+ * 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. */);
+
+/*
+ * 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/thirdparty/harfbuzz/src/hb-ot-deprecated.h b/thirdparty/harfbuzz/src/hb-ot-deprecated.h
new file mode 100644
index 0000000000..bc72f8a701
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-deprecated.h
@@ -0,0 +1,111 @@
+/*
+ * 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_H_IN
+#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 */
+#define HB_MATH_GLYPH_PART_FLAG_EXTENDER HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER
+
+
+/* Like hb_ot_layout_table_find_script, but takes zero-terminated array of scripts to test */
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_table_select_script) 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_EXTERN HB_DEPRECATED_FOR (hb_ot_layout_script_select_language) 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_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) void
+hb_ot_tags_from_script (hb_script_t script,
+ hb_tag_t *script_tag_1,
+ hb_tag_t *script_tag_2);
+
+HB_EXTERN HB_DEPRECATED_FOR (hb_ot_tags_from_script_and_language) hb_tag_t
+hb_ot_tag_from_language (hb_language_t language);
+
+
+/**
+ * HB_OT_VAR_NO_AXIS_INDEX:
+ *
+ * Since: 1.4.2
+ * Deprecated: 2.2.0
+ */
+#define HB_OT_VAR_NO_AXIS_INDEX 0xFFFFFFFFu
+
+/**
+ * hb_ot_var_axis_t:
+ *
+ * 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_EXTERN HB_DEPRECATED_FOR (hb_ot_var_get_axis_infos) 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_EXTERN HB_DEPRECATED_FOR (hb_ot_var_find_axis_info) 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/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh
new file mode 100644
index 0000000000..367e143fdf
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh
@@ -0,0 +1,138 @@
+/*
+ * 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_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 free. */
+
+/* v--- Add new tables in the right place here. */
+
+
+/* OpenType fundamentals. */
+HB_OT_TABLE (OT, head)
+#if !defined(HB_NO_FACE_COLLECT_UNICODES) || !defined(HB_NO_OT_FONT)
+HB_OT_ACCELERATOR (OT, cmap)
+#endif
+HB_OT_TABLE (OT, hhea)
+HB_OT_ACCELERATOR (OT, hmtx)
+HB_OT_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_TABLE (OT, STAT)
+#endif
+#ifndef HB_NO_META
+HB_OT_ACCELERATOR (OT, meta)
+#endif
+
+/* Vertical layout. */
+HB_OT_TABLE (OT, vhea)
+HB_OT_ACCELERATOR (OT, vmtx)
+
+/* TrueType outlines. */
+HB_OT_ACCELERATOR (OT, glyf)
+
+/* CFF outlines. */
+#ifndef HB_NO_CFF
+HB_OT_ACCELERATOR (OT, cff1)
+HB_OT_ACCELERATOR (OT, cff2)
+HB_OT_TABLE (OT, VORG)
+#endif
+
+/* OpenType variations. */
+#ifndef HB_NO_VAR
+HB_OT_TABLE (OT, fvar)
+HB_OT_TABLE (OT, avar)
+HB_OT_ACCELERATOR (OT, gvar)
+HB_OT_TABLE (OT, MVAR)
+#endif
+
+/* Legacy kern. */
+#ifndef HB_NO_OT_KERN
+HB_OT_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_TABLE (OT, JSTF)
+#endif
+
+/* OpenType baseline. */
+#ifndef HB_NO_BASE
+HB_OT_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_TABLE (OT, COLR)
+HB_OT_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_TABLE (OT, MATH)
+#endif
+
+
+#ifdef _HB_OT_ACCELERATOR_UNDEF
+#undef HB_OT_ACCELERATOR
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-face.cc b/thirdparty/harfbuzz/src/hb-ot-face.cc
new file mode 100644
index 0000000000..5ef8df43ce
--- /dev/null
+++ b/thirdparty/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 "hb-ot-color-cbdt-table.hh"
+#include "hb-ot-color-sbix-table.hh"
+#include "hb-ot-color-svg-table.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/thirdparty/harfbuzz/src/hb-ot-face.hh b/thirdparty/harfbuzz/src/hb-ot-face.hh
new file mode 100644
index 0000000000..e24d380bca
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-face.hh
@@ -0,0 +1,74 @@
+/*
+ * 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_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_TABLE
+};
+
+
+#endif /* HB_OT_FACE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-font.cc b/thirdparty/harfbuzz/src/hb-ot-font.cc
new file mode 100644
index 0000000000..a1dc88603a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-font.cc
@@ -0,0 +1,336 @@
+/*
+ * 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-font.hh"
+#include "hb-machinery.hh"
+#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-os2-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 "hb-ot-color-cbdt-table.hh"
+#include "hb-ot-color-sbix-table.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.
+ **/
+
+
+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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+ return ot_face->cmap->get_nominal_glyph (unicode, glyph);
+}
+
+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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+ return ot_face->cmap->get_nominal_glyphs (count,
+ first_unicode, unicode_stride,
+ first_glyph, glyph_stride);
+}
+
+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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+ return ot_face->cmap->get_variation_glyph (unicode, variation_selector, glyph);
+}
+
+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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+ const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
+
+ for (unsigned int i = 0; i < count; i++)
+ {
+ *first_advance = font->em_scale_x (hmtx.get_advance (*first_glyph, font));
+ first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+ first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+ }
+}
+
+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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+ const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
+
+ for (unsigned int i = 0; i < count; i++)
+ {
+ *first_advance = font->em_scale_y (-(int) vmtx.get_advance (*first_glyph, font));
+ first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
+ first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
+ }
+}
+
+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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+
+ *x = font->get_glyph_h_advance (glyph) / 2;
+
+#ifndef HB_NO_OT_FONT_CFF
+ const OT::VORG &VORG = *ot_face->VORG;
+ if (VORG.has_data ())
+ {
+ *y = font->em_scale_y (VORG.get_y_origin (glyph));
+ return true;
+ }
+#endif
+
+ hb_glyph_extents_t extents = {0};
+ if (ot_face->glyf->get_extents (font, glyph, &extents))
+ {
+ const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
+ hb_position_t tsb = vmtx.get_side_bearing (font, glyph);
+ *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);
+ *y = font_extents.ascender;
+
+ return true;
+}
+
+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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+
+#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
+ if (ot_face->sbix->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->cff1->get_extents (font, glyph, extents)) return true;
+ if (ot_face->cff2->get_extents (font, glyph, extents)) return true;
+#endif
+#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
+ if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
+#endif
+
+ // TODO Hook up side-bearings variations.
+ 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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+ 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_face_t *ot_face = (const hb_ot_face_t *) font_data;
+ 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)
+{
+ return _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);
+}
+
+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);
+}
+
+#if HB_USE_ATEXIT
+static void free_static_ot_funcs ();
+#endif
+
+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_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr);
+ hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr);
+ hb_font_funcs_set_nominal_glyph_func (funcs, hb_ot_get_nominal_glyph, nullptr, nullptr);
+ hb_font_funcs_set_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_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr);
+ hb_font_funcs_set_glyph_extents_func (funcs, hb_ot_get_glyph_extents, nullptr, nullptr);
+ //hb_font_funcs_set_glyph_contour_point_func (funcs, hb_ot_get_glyph_contour_point, nullptr, nullptr);
+#ifndef HB_NO_OT_FONT_GLYPH_NAMES
+ hb_font_funcs_set_glyph_name_func (funcs, hb_ot_get_glyph_name, nullptr, nullptr);
+ hb_font_funcs_set_glyph_from_name_func (funcs, hb_ot_get_glyph_from_name, nullptr, nullptr);
+#endif
+
+ hb_font_funcs_make_immutable (funcs);
+
+#if HB_USE_ATEXIT
+ atexit (free_static_ot_funcs);
+#endif
+
+ return funcs;
+ }
+} static_ot_funcs;
+
+#if HB_USE_ATEXIT
+static
+void free_static_ot_funcs ()
+{
+ static_ot_funcs.free_instance ();
+}
+#endif
+
+static hb_font_funcs_t *
+_hb_ot_get_font_funcs ()
+{
+ return static_ot_funcs.get_unconst ();
+}
+
+
+/**
+ * hb_ot_font_set_funcs:
+ *
+ * Since: 0.9.28
+ **/
+void
+hb_ot_font_set_funcs (hb_font_t *font)
+{
+ hb_font_set_funcs (font,
+ _hb_ot_get_font_funcs (),
+ &font->face->table,
+ nullptr);
+}
+
+#ifndef HB_NO_VAR
+int
+_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical)
+{
+ return font->face->table.glyf->get_side_bearing_var (font, glyph, is_vertical);
+}
+
+unsigned
+_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical)
+{
+ return font->face->table.glyf->get_advance_var (font, glyph, is_vertical);
+}
+#endif
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-font.h b/thirdparty/harfbuzz/src/hb-ot-font.h
new file mode 100644
index 0000000000..80eaa54b1a
--- /dev/null
+++ b/thirdparty/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
+ */
+
+#ifndef HB_OT_H_IN
+#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/thirdparty/harfbuzz/src/hb-ot-gasp-table.hh b/thirdparty/harfbuzz/src/hb-ot-gasp-table.hh
new file mode 100644
index 0000000000..4f291924af
--- /dev/null
+++ b/thirdparty/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) */
+ ArrayOf<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/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh b/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh
new file mode 100644
index 0000000000..5470bd96da
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-glyf-table.hh
@@ -0,0 +1,1261 @@
+/*
+ * 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 "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"
+
+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;
+
+ 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. */
+};
+
+
+/*
+ * 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
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
+
+ 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);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_source_of (Iterator, unsigned int))>
+ static bool
+ _add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets)
+ {
+ unsigned max_offset =
+ + padded_offsets
+ | hb_reduce (hb_add, 0)
+ ;
+ unsigned num_offsets = padded_offsets.len () + 1;
+ bool use_short_loca = max_offset < 0x1FFFF;
+ unsigned entry_size = use_short_loca ? 2 : 4;
+ char *loca_prime_data = (char *) calloc (entry_size, num_offsets);
+
+ if (unlikely (!loca_prime_data)) return false;
+
+ DEBUG_MSG (SUBSET, nullptr, "loca entry_size %d num_offsets %d "
+ "max_offset %d size %d",
+ entry_size, num_offsets, max_offset, entry_size * num_offsets);
+
+ if (use_short_loca)
+ _write_loca (padded_offsets, 1, hb_array ((HBUINT16 *) loca_prime_data, num_offsets));
+ else
+ _write_loca (padded_offsets, 0, hb_array ((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,
+ free);
+
+ bool result = plan->add_table (HB_OT_TAG_loca, loca_blob)
+ && _add_head_and_set_loca_version (plan, use_short_loca);
+
+ hb_blob_destroy (loca_blob);
+ return result;
+ }
+
+ template<typename IteratorIn, typename IteratorOut,
+ hb_requires (hb_is_source_of (IteratorIn, unsigned int)),
+ hb_requires (hb_is_sink_of (IteratorOut, unsigned))>
+ static void
+ _write_loca (IteratorIn it, unsigned right_shift, IteratorOut dest)
+ {
+ unsigned int offset = 0;
+ dest << 0;
+ + it
+ | hb_map ([=, &offset] (unsigned int padded_size)
+ {
+ offset += padded_size;
+ DEBUG_MSG (SUBSET, nullptr, "loca entry offset %d", offset);
+ return offset >> right_shift;
+ })
+ | hb_sink (dest)
+ ;
+ }
+
+ /* requires source of SubsetGlyph complains the identifier isn't declared */
+ template <typename Iterator>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator it,
+ const hb_subset_plan_t *plan)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned init_len = c->length ();
+ for (const auto &_ : it) _.serialize (c, plan);
+
+ /* 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);
+
+ glyf *glyf_prime = c->serializer->start_embed <glyf> ();
+ if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
+
+ hb_vector_t<SubsetGlyph> glyphs;
+ _populate_subset_glyphs (c->plan, &glyphs);
+
+ glyf_prime->serialize (c->serializer, hb_iter (glyphs), c->plan);
+
+ auto padded_offsets =
+ + hb_iter (glyphs)
+ | hb_map (&SubsetGlyph::padded_size)
+ ;
+
+ if (c->serializer->in_error ()) return_trace (false);
+ return_trace (c->serializer->check_success (_add_loca_and_head (c->plan,
+ padded_offsets)));
+ }
+
+ template <typename SubsetGlyph>
+ void
+ _populate_subset_glyphs (const hb_subset_plan_t *plan,
+ hb_vector_t<SubsetGlyph> *glyphs /* OUT */) const
+ {
+ OT::glyf::accelerator_t glyf;
+ glyf.init (plan->source);
+
+ + hb_range (plan->num_output_glyphs ())
+ | hb_map ([&] (hb_codepoint_t new_gid)
+ {
+ SubsetGlyph subset_glyph = {0};
+ subset_glyph.new_gid = new_gid;
+
+ /* should never fail: all old gids should be mapped */
+ if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid))
+ return subset_glyph;
+
+ subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
+ if (plan->drop_hints) subset_glyph.drop_hints_bytes ();
+ else subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
+
+ return subset_glyph;
+ })
+ | hb_sink (glyphs)
+ ;
+
+ glyf.fini ();
+ }
+
+ 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;
+ bool success = plan->add_table (HB_OT_TAG_head, head_prime_blob);
+
+ hb_blob_destroy (head_prime_blob);
+ return success;
+ }
+
+ struct CompositeGlyphChain
+ {
+ 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
+ };
+
+ public:
+ unsigned int get_size () const
+ {
+ unsigned int size = min_size;
+ /* 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 set_glyph_index (hb_codepoint_t new_gid) { glyphIndex = new_gid; }
+ hb_codepoint_t get_glyph_index () const { return glyphIndex; }
+
+ void drop_instructions_flag () { flags = (uint16_t) flags & ~WE_HAVE_INSTRUCTIONS; }
+ 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 HBUINT8 *p = &StructAfter<const HBUINT8> (glyphIndex);
+ 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];
+ }
+ }
+
+ void transform_points (contour_point_vector_t &points) const
+ {
+ float matrix[4];
+ contour_point_t trans;
+ if (get_transformation (matrix, trans))
+ {
+ if (scaled_offsets ())
+ {
+ points.translate (trans);
+ points.transform (matrix);
+ }
+ else
+ {
+ points.transform (matrix);
+ points.translate (trans);
+ }
+ }
+ }
+
+ protected:
+ bool scaled_offsets () const
+ { return (flags & (SCALED_COMPONENT_OFFSET | UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; }
+
+ bool get_transformation (float (&matrix)[4], contour_point_t &trans) const
+ {
+ matrix[0] = matrix[3] = 1.f;
+ matrix[1] = matrix[2] = 0.f;
+
+ int tx, ty;
+ const HBINT8 *p = &StructAfter<const HBINT8> (glyphIndex);
+ 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;
+ }
+
+ protected:
+ HBUINT16 flags;
+ HBGlyphID glyphIndex;
+ public:
+ DEFINE_SIZE_MIN (4);
+ };
+
+ struct composite_iter_t : hb_iter_with_fallback_t<composite_iter_t, const CompositeGlyphChain &>
+ {
+ typedef const CompositeGlyphChain *__item_t__;
+ composite_iter_t (hb_bytes_t glyph_, __item_t__ current_) :
+ glyph (glyph_), current (current_)
+ { if (!check_range (current)) current = nullptr; }
+ composite_iter_t () : glyph (hb_bytes_t ()), current (nullptr) {}
+
+ const CompositeGlyphChain &__item__ () const { return *current; }
+ bool __more__ () const { return current; }
+ void __next__ ()
+ {
+ if (!current->has_more ()) { current = nullptr; return; }
+
+ const CompositeGlyphChain *possible = &StructAfter<CompositeGlyphChain,
+ CompositeGlyphChain> (*current);
+ if (!check_range (possible)) { current = nullptr; return; }
+ current = possible;
+ }
+ bool operator != (const composite_iter_t& o) const
+ { return glyph != o.glyph || current != o.current; }
+
+ bool check_range (const CompositeGlyphChain *composite) const
+ {
+ return glyph.check_range (composite, CompositeGlyphChain::min_size)
+ && glyph.check_range (composite, composite->get_size ());
+ }
+
+ private:
+ hb_bytes_t glyph;
+ __item_t__ current;
+ };
+
+ enum phantom_point_index_t
+ {
+ PHANTOM_LEFT = 0,
+ PHANTOM_RIGHT = 1,
+ PHANTOM_TOP = 2,
+ PHANTOM_BOTTOM = 3,
+ PHANTOM_COUNT = 4
+ };
+
+ struct accelerator_t;
+
+ struct Glyph
+ {
+ 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_RESERVED1 = 0x40,
+ FLAG_RESERVED2 = 0x80
+ };
+
+ private:
+ struct GlyphHeader
+ {
+ bool has_data () const { return numberOfContours; }
+
+ bool get_extents (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); */
+ extents->x_bearing = font->em_scale_x (glyf_accelerator.hmtx->get_side_bearing (gid));
+ extents->y_bearing = font->em_scale_y (hb_max (yMin, yMax));
+ extents->width = font->em_scale_x (hb_max (xMin, xMax) - hb_min (xMin, xMax));
+ extents->height = font->em_scale_y (hb_min (yMin, yMax) - hb_max (yMin, yMax));
+
+ 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);
+ };
+
+ struct SimpleGlyph
+ {
+ 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; }
+
+ 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 Glyph trim_padding () const
+ {
+ /* based on FontTools _g_l_y_f.py::trim */
+ const char *glyph = bytes.arrayZ;
+ const char *glyph_end = glyph + bytes.length;
+ /* simple glyph w/contours, possibly trimmable */
+ glyph += instruction_len_offset ();
+
+ if (unlikely (glyph + 2 >= glyph_end)) return Glyph ();
+ 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 Glyph ();
+ 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 Glyph ();
+ return Glyph (bytes.sub_array (0, bytes.length + coord_bytes - (glyph_end - glyph)));
+ }
+
+ /* zero instruction length */
+ void drop_hints ()
+ {
+ 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);
+ }
+
+ static bool read_points (const HBUINT8 *&p /* IN/OUT */,
+ contour_point_vector_t &points_ /* IN/OUT */,
+ const hb_bytes_t &bytes,
+ void (* setter) (contour_point_t &_, float v),
+ const simple_glyph_flag_t short_flag,
+ const simple_glyph_flag_t same_flag)
+ {
+ float v = 0;
+ for (unsigned i = 0; i < points_.length; i++)
+ {
+ uint8_t flag = points_[i].flag;
+ if (flag & short_flag)
+ {
+ if (unlikely (!bytes.check_range (p))) return false;
+ if (flag & same_flag)
+ v += *p++;
+ else
+ v -= *p++;
+ }
+ else
+ {
+ if (!(flag & same_flag))
+ {
+ if (unlikely (!bytes.check_range ((const HBUINT16 *) p))) return false;
+ v += *(const HBINT16 *) p;
+ p += HBINT16::static_size;
+ }
+ }
+ setter (points_[i], 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;
+ if (unlikely (!bytes.check_range (&endPtsOfContours[num_contours + 1]))) return false;
+ unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
+
+ points_.resize (num_points);
+ for (unsigned int i = 0; i < points_.length; i++) points_[i].init ();
+ 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]);
+
+ /* Read flags */
+ for (unsigned int i = 0; i < num_points; i++)
+ {
+ if (unlikely (!bytes.check_range (p))) return false;
+ uint8_t flag = *p++;
+ points_[i].flag = flag;
+ if (flag & FLAG_REPEAT)
+ {
+ if (unlikely (!bytes.check_range (p))) return false;
+ unsigned int repeat_count = *p++;
+ while ((repeat_count-- > 0) && (++i < num_points))
+ points_[i].flag = flag;
+ }
+ }
+
+ /* Read x & y coordinates */
+ return read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.x = v; },
+ FLAG_X_SHORT, FLAG_X_SAME)
+ && read_points (p, points_, bytes, [] (contour_point_t &p, float v) { p.y = v; },
+ FLAG_Y_SHORT, FLAG_Y_SAME);
+ }
+ };
+
+ struct CompositeGlyph
+ {
+ const GlyphHeader &header;
+ hb_bytes_t bytes;
+ CompositeGlyph (const GlyphHeader &header_, hb_bytes_t bytes_) :
+ header (header_), bytes (bytes_) {}
+
+ composite_iter_t get_iterator () const
+ { return composite_iter_t (bytes, &StructAfter<CompositeGlyphChain, GlyphHeader> (header)); }
+
+ unsigned int instructions_length (hb_bytes_t bytes) const
+ {
+ unsigned int start = bytes.length;
+ unsigned int end = bytes.length;
+ const CompositeGlyphChain *last = nullptr;
+ for (auto &item : get_iterator ())
+ 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 Glyph trim_padding () const { return Glyph (bytes); }
+
+ void drop_hints ()
+ {
+ for (const auto &_ : get_iterator ())
+ const_cast<CompositeGlyphChain &> (_).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)); }
+ };
+
+ enum glyph_type_t { EMPTY, SIMPLE, COMPOSITE };
+
+ public:
+ composite_iter_t get_composite_iterator () const
+ {
+ if (type != COMPOSITE) return composite_iter_t ();
+ return CompositeGlyph (*header, bytes).get_iterator ();
+ }
+
+ const Glyph trim_padding () const
+ {
+ switch (type) {
+ case COMPOSITE: return CompositeGlyph (*header, bytes).trim_padding ();
+ case SIMPLE: return SimpleGlyph (*header, bytes).trim_padding ();
+ default: return bytes;
+ }
+ }
+
+ void drop_hints ()
+ {
+ switch (type) {
+ case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints (); return;
+ case SIMPLE: SimpleGlyph (*header, bytes).drop_hints (); return;
+ default: return;
+ }
+ }
+
+ void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
+ {
+ switch (type) {
+ case COMPOSITE: CompositeGlyph (*header, bytes).drop_hints_bytes (dest_start); return;
+ case SIMPLE: SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
+ default: return;
+ }
+ }
+
+ /* Note: Recursively calls itself.
+ * all_points includes phantom points
+ */
+ bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
+ contour_point_vector_t &all_points /* OUT */,
+ bool phantom_only = false,
+ unsigned int depth = 0) const
+ {
+ if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
+ contour_point_vector_t points;
+
+ switch (type) {
+ case COMPOSITE:
+ {
+ /* pseudo component points for each component in composite glyph */
+ unsigned num_points = hb_len (CompositeGlyph (*header, bytes).get_iterator ());
+ if (unlikely (!points.resize (num_points))) return false;
+ for (unsigned i = 0; i < points.length; i++)
+ points[i].init ();
+ break;
+ }
+ case SIMPLE:
+ if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, phantom_only)))
+ return false;
+ break;
+ }
+
+ /* Init phantom points */
+ if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
+ hb_array_t<contour_point_t> phantoms = points.sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
+ {
+ for (unsigned i = 0; i < PHANTOM_COUNT; ++i) phantoms[i].init ();
+ int h_delta = (int) header->xMin - glyf_accelerator.hmtx->get_side_bearing (gid);
+ int v_orig = (int) header->yMax + glyf_accelerator.vmtx->get_side_bearing (gid);
+ unsigned h_adv = glyf_accelerator.hmtx->get_advance (gid);
+ unsigned v_adv = glyf_accelerator.vmtx->get_advance (gid);
+ phantoms[PHANTOM_LEFT].x = h_delta;
+ phantoms[PHANTOM_RIGHT].x = h_adv + h_delta;
+ phantoms[PHANTOM_TOP].y = v_orig;
+ phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
+ }
+
+#ifndef HB_NO_VAR
+ if (unlikely (!glyf_accelerator.gvar->apply_deltas_to_points (gid, font, points.as_array ())))
+ return false;
+#endif
+
+ switch (type) {
+ case SIMPLE:
+ all_points.extend (points.as_array ());
+ break;
+ case COMPOSITE:
+ {
+ unsigned int comp_index = 0;
+ for (auto &item : get_composite_iterator ())
+ {
+ contour_point_vector_t comp_points;
+ if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_glyph_index ())
+ .get_points (font, glyf_accelerator, comp_points,
+ phantom_only, depth + 1)
+ || comp_points.length < PHANTOM_COUNT))
+ return false;
+
+ /* Copy phantom points from component if USE_MY_METRICS flag set */
+ if (item.is_use_my_metrics ())
+ for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
+ phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
+
+ /* Apply component transformation & translation */
+ item.transform_points (comp_points);
+
+ /* Apply translation from gvar */
+ comp_points.translate (points[comp_index]);
+
+ if (item.is_anchored ())
+ {
+ 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);
+
+ comp_points.translate (delta);
+ }
+ }
+
+ all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT));
+
+ comp_index++;
+ }
+
+ all_points.extend (phantoms);
+ } break;
+ default:
+ all_points.extend (phantoms);
+ }
+
+ if (depth == 0) /* Apply at top level */
+ {
+ /* Undocumented rasterizer behavior:
+ * Shift points horizontally by the updated left side bearing
+ */
+ contour_point_t delta;
+ delta.init (-phantoms[PHANTOM_LEFT].x, 0.f);
+ if (delta.x) all_points.translate (delta);
+ }
+
+ return true;
+ }
+
+ bool get_extents (hb_font_t *font, const accelerator_t &glyf_accelerator,
+ hb_glyph_extents_t *extents) const
+ {
+ if (type == EMPTY) return true; /* Empty glyph; zero extents. */
+ return header->get_extents (font, glyf_accelerator, gid, extents);
+ }
+
+ hb_bytes_t get_bytes () const { return bytes; }
+
+ Glyph (hb_bytes_t bytes_ = hb_bytes_t (),
+ hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_), gid (gid_),
+ header (bytes.as<GlyphHeader> ())
+ {
+ int num_contours = header->numberOfContours;
+ if (unlikely (num_contours == 0)) type = EMPTY;
+ else if (num_contours > 0) type = SIMPLE;
+ else type = COMPOSITE; /* negative numbers */
+ }
+
+ protected:
+ hb_bytes_t bytes;
+ hb_codepoint_t gid;
+ const GlyphHeader *header;
+ unsigned type;
+ };
+
+ struct accelerator_t
+ {
+ void init (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;
+ vmtx = nullptr;
+ face = face_;
+ const OT::head &head = *face->table.head;
+ if (head.indexToLocFormat > 1 || head.glyphDataFormat > 0)
+ /* Unknown format. Leave num_glyphs=0, that takes care of disabling us. */
+ return;
+ short_offset = 0 == head.indexToLocFormat;
+
+ loca_table = hb_sanitize_context_t ().reference_table<loca> (face);
+ glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
+#ifndef HB_NO_VAR
+ gvar = face->table.gvar;
+#endif
+ hmtx = face->table.hmtx;
+ vmtx = face->table.vmtx;
+
+ num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
+ num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
+ }
+
+ void fini ()
+ {
+ loca_table.destroy ();
+ glyf_table.destroy ();
+ }
+
+ 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 alloc free 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, phantom_only)))
+ return false;
+
+ if (consumer.is_consuming_contour_points ())
+ {
+ for (unsigned point_index = 0; point_index + 4 < all_points.length; ++point_index)
+ consumer.consume_point (all_points[point_index]);
+ 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 < PHANTOM_COUNT; ++i)
+ phantoms[i] = all_points[all_points.length - PHANTOM_COUNT + i];
+
+ return true;
+ }
+
+#ifndef HB_NO_VAR
+ struct points_aggregator_t
+ {
+ hb_font_t *font;
+ hb_glyph_extents_t *extents;
+ contour_point_t *phantoms;
+
+ 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)
+ {
+ if (unlikely (empty ()))
+ {
+ extents->width = 0;
+ extents->x_bearing = 0;
+ extents->height = 0;
+ extents->y_bearing = 0;
+ return;
+ }
+ extents->x_bearing = font->em_scalef_x (min_x);
+ extents->width = font->em_scalef_x (max_x - min_x);
+ extents->y_bearing = font->em_scalef_y (max_y);
+ extents->height = font->em_scalef_y (min_y - max_y);
+ }
+
+ 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_)
+ {
+ font = font_;
+ extents = extents_;
+ phantoms = phantoms_;
+ if (extents) bounds = contour_bounds_t ();
+ }
+
+ void consume_point (const contour_point_t &point) { bounds.add (point); }
+ void points_end () { bounds.get_extents (font, extents); }
+
+ bool is_consuming_contour_points () { return extents; }
+ contour_point_t *get_phantoms_sink () { return phantoms; }
+ };
+
+ public:
+ unsigned
+ get_advance_var (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[PHANTOM_COUNT];
+ if (likely (font->num_coords == gvar->get_axis_count ()))
+ success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms));
+
+ if (unlikely (!success))
+ return is_vertical ? vmtx->get_advance (gid) : hmtx->get_advance (gid);
+
+ float result = is_vertical
+ ? phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y
+ : phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x;
+ return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
+ }
+
+ int get_side_bearing_var (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
+ {
+ if (unlikely (gid >= num_glyphs)) return 0;
+
+ hb_glyph_extents_t extents;
+
+ contour_point_t phantoms[PHANTOM_COUNT];
+ if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms))))
+ return is_vertical ? vmtx->get_side_bearing (gid) : hmtx->get_side_bearing (gid);
+
+ return is_vertical
+ ? ceilf (phantoms[PHANTOM_TOP].y) - extents.y_bearing
+ : floorf (phantoms[PHANTOM_LEFT].x);
+ }
+#endif
+
+ 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 && font->num_coords == gvar->get_axis_count ())
+ return get_points (font, gid, points_aggregator_t (font, extents, nullptr));
+#endif
+ return glyph_for_gid (gid).get_extents (font, *this, extents);
+ }
+
+ const Glyph
+ glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
+ {
+ if (unlikely (gid >= num_glyphs)) return 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 Glyph ();
+
+ Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
+ end_offset - start_offset), gid);
+ return needs_padding_removal ? glyph.trim_padding () : glyph;
+ }
+
+ void
+ add_gid_and_children (hb_codepoint_t gid, hb_set_t *gids_to_retain,
+ unsigned int depth = 0) const
+ {
+ if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return;
+ /* Check if is already visited */
+ if (gids_to_retain->has (gid)) return;
+
+ gids_to_retain->add (gid);
+
+ for (auto &item : glyph_for_gid (gid).get_composite_iterator ())
+ add_gid_and_children (item.get_glyph_index (), gids_to_retain, depth);
+ }
+
+#ifdef HB_EXPERIMENTAL_API
+ struct path_builder_t
+ {
+ hb_font_t *font;
+ draw_helper_t *draw_helper;
+
+ struct optional_point_t
+ {
+ optional_point_t () { has_data = false; }
+ optional_point_t (float x_, float y_) { x = x_; y = y_; has_data = true; }
+
+ bool has_data;
+ float x;
+ float y;
+
+ optional_point_t lerp (optional_point_t p, float t)
+ { return optional_point_t (x + t * (p.x - x), y + t * (p.y - y)); }
+ } first_oncurve, first_offcurve, last_offcurve;
+
+ path_builder_t (hb_font_t *font_, draw_helper_t &draw_helper_)
+ {
+ font = font_;
+ draw_helper = &draw_helper_;
+ first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+ }
+
+ /* 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 */
+ void consume_point (const contour_point_t &point)
+ {
+ /* Skip empty contours */
+ if (unlikely (point.is_end_point && !first_oncurve.has_data && !first_offcurve.has_data))
+ return;
+
+ bool is_on_curve = point.flag & Glyph::FLAG_ON_CURVE;
+ optional_point_t p (point.x, point.y);
+ if (!first_oncurve.has_data)
+ {
+ if (is_on_curve)
+ {
+ first_oncurve = p;
+ draw_helper->move_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y));
+ }
+ else
+ {
+ if (first_offcurve.has_data)
+ {
+ optional_point_t mid = first_offcurve.lerp (p, .5f);
+ first_oncurve = mid;
+ last_offcurve = p;
+ draw_helper->move_to (font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
+ }
+ else
+ first_offcurve = p;
+ }
+ }
+ else
+ {
+ if (last_offcurve.has_data)
+ {
+ if (is_on_curve)
+ {
+ draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
+ font->em_scalef_x (p.x), font->em_scalef_y (p.y));
+ last_offcurve = optional_point_t ();
+ }
+ else
+ {
+ optional_point_t mid = last_offcurve.lerp (p, .5f);
+ draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
+ font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
+ last_offcurve = p;
+ }
+ }
+ else
+ {
+ if (is_on_curve)
+ draw_helper->line_to (font->em_scalef_x (p.x), font->em_scalef_y (p.y));
+ else
+ last_offcurve = p;
+ }
+ }
+
+ if (point.is_end_point)
+ {
+ if (first_offcurve.has_data && last_offcurve.has_data)
+ {
+ optional_point_t mid = last_offcurve.lerp (first_offcurve, .5f);
+ draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
+ font->em_scalef_x (mid.x), font->em_scalef_y (mid.y));
+ last_offcurve = optional_point_t ();
+ /* now check the rest */
+ }
+
+ if (first_offcurve.has_data && first_oncurve.has_data)
+ draw_helper->quadratic_to (font->em_scalef_x (first_offcurve.x), font->em_scalef_y (first_offcurve.y),
+ font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
+ else if (last_offcurve.has_data && first_oncurve.has_data)
+ draw_helper->quadratic_to (font->em_scalef_x (last_offcurve.x), font->em_scalef_y (last_offcurve.y),
+ font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
+ else if (first_oncurve.has_data)
+ draw_helper->line_to (font->em_scalef_x (first_oncurve.x), font->em_scalef_y (first_oncurve.y));
+
+ /* Getting ready for the next contour */
+ first_oncurve = first_offcurve = last_offcurve = optional_point_t ();
+ draw_helper->end_path ();
+ }
+ }
+ void points_end () {}
+
+ bool is_consuming_contour_points () { return true; }
+ contour_point_t *get_phantoms_sink () { return nullptr; }
+ };
+
+ bool
+ get_path (hb_font_t *font, hb_codepoint_t gid, draw_helper_t &draw_helper) const
+ { return get_points (font, gid, path_builder_t (font, draw_helper)); }
+#endif
+
+#ifndef HB_NO_VAR
+ const gvar_accelerator_t *gvar;
+#endif
+ const hmtx_accelerator_t *hmtx;
+ const vmtx_accelerator_t *vmtx;
+
+ private:
+ bool short_offset;
+ unsigned int num_glyphs;
+ hb_blob_ptr_t<loca> loca_table;
+ hb_blob_ptr_t<glyf> glyf_table;
+ hb_face_t *face;
+ };
+
+ struct SubsetGlyph
+ {
+ hb_codepoint_t new_gid;
+ 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 serialize (hb_serialize_context_t *c,
+ const hb_subset_plan_t *plan) const
+ {
+ TRACE_SERIALIZE (this);
+
+ hb_bytes_t dest_glyph = dest_start.copy (c);
+ dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy (c).length);
+ unsigned int pad_length = padding ();
+ DEBUG_MSG (SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
+
+ HBUINT8 pad;
+ pad = 0;
+ while (pad_length > 0)
+ {
+ 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_glyph_index (), &new_gid))
+ const_cast<CompositeGlyphChain &> (_).set_glyph_index (new_gid);
+ }
+
+ if (plan->drop_hints) Glyph (dest_glyph).drop_hints ();
+
+ return_trace (true);
+ }
+
+ 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 (); }
+ };
+
+ 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 {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_GLYF_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hdmx-table.hh
new file mode 100644
index 0000000000..c9c391bad5
--- /dev/null
+++ b/thirdparty/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)
+ {
+ TRACE_SERIALIZE (this);
+
+ unsigned length = it.len ();
+
+ if (unlikely (!c->extend (*this, length))) return_trace (false);
+
+ this->pixelSize = pixelSize;
+ this->maxWidth =
+ + it
+ | hb_reduce (hb_max, 0u);
+
+ + it
+ | hb_sink (widthsZ.as_array (length));
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, unsigned sizeDeviceRecord) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ c->check_range (this, sizeDeviceRecord)));
+ }
+
+ HBUINT8 pixelSize; /* Pixel size for following widths (as ppem). */
+ HBUINT8 maxWidth; /* Maximum width. */
+ UnsizedArrayOf<HBUINT8> widthsZ; /* Array of widths (numGlyphs is from the 'maxp' table). */
+ public:
+ DEFINE_SIZE_ARRAY (2, widthsZ);
+};
+
+
+struct hdmx
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_hdmx;
+
+ unsigned int get_size () const
+ { return min_size + numRecords * sizeDeviceRecord; }
+
+ const DeviceRecord& operator [] (unsigned int i) const
+ {
+ /* XXX Null(DeviceRecord) is NOT safe as it's num-glyphs lengthed.
+ * https://github.com/harfbuzz/harfbuzz/issues/1300 */
+ if (unlikely (i >= numRecords)) return Null (DeviceRecord);
+ return StructAtOffset<DeviceRecord> (&this->firstDeviceRecord, i * sizeDeviceRecord);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c, unsigned version, Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+
+ if (unlikely (!c->extend_min ((*this)))) return_trace (false);
+
+ this->version = version;
+ this->numRecords = it.len ();
+ this->sizeDeviceRecord = DeviceRecord::get_size (it ? (*it).second.len () : 0);
+
+ for (const hb_item_type<Iterator>& _ : +it)
+ c->start_embed<DeviceRecord> ()->serialize (c, _.first, _.second);
+
+ return_trace (c->successful);
+ }
+
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+
+ hdmx *hdmx_prime = c->serializer->start_embed <hdmx> ();
+ if (unlikely (!hdmx_prime)) return_trace (false);
+
+ auto it =
+ + hb_range ((unsigned) numRecords)
+ | hb_map ([c, this] (unsigned _)
+ {
+ const DeviceRecord *device_record =
+ &StructAtOffset<DeviceRecord> (&firstDeviceRecord,
+ _ * sizeDeviceRecord);
+ auto row =
+ + hb_range (c->plan->num_output_glyphs ())
+ | hb_map (c->plan->reverse_glyph_map)
+ | hb_map ([this, c, device_record] (hb_codepoint_t _)
+ {
+ if (c->plan->is_empty_glyph (_))
+ return Null (HBUINT8);
+ return device_record->widthsZ.as_array (get_num_glyphs ()) [_];
+ })
+ ;
+ return hb_pair ((unsigned) device_record->pixelSize, +row);
+ })
+ ;
+
+ hdmx_prime->serialize (c->serializer, version, it);
+ return_trace (true);
+ }
+
+ unsigned get_num_glyphs () const
+ {
+ return sizeDeviceRecord - DeviceRecord::min_size;
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ !hb_unsigned_mul_overflows (numRecords, sizeDeviceRecord) &&
+ 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/thirdparty/harfbuzz/src/hb-ot-head-table.hh b/thirdparty/harfbuzz/src/hb-ot-head-table.hh
new file mode 100644
index 0000000000..5613a96dbf
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-head-table.hh
@@ -0,0 +1,179 @@
+/*
+ * 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 OffsetTable;
+
+ 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);
+ return_trace (serialize (c->serializer));
+ }
+
+ enum mac_style_flag_t {
+ BOLD = 1u<<0,
+ ITALIC = 1u<<1,
+ UNDERLINE = 1u<<2,
+ OUTLINE = 1u<<3,
+ SHADOW = 1u<<4,
+ CONDENSED = 1u<<5
+ };
+
+ bool is_bold () const { return macStyle & BOLD; }
+ bool is_italic () const { return macStyle & ITALIC; }
+ bool is_condensed () const { return macStyle & CONDENSED; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ 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. */
+ 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. */
+ 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 */
+ 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. */
+ 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/thirdparty/harfbuzz/src/hb-ot-hhea-table.hh b/thirdparty/harfbuzz/src/hb-ot-hhea-table.hh
new file mode 100644
index 0000000000..d9c9bd3537
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-hhea-table.hh
@@ -0,0 +1,104 @@
+/*
+ * 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) && 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/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
new file mode 100644
index 0000000000..d06c0fa4a4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -0,0 +1,340 @@
+/*
+ * 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-hhea-table.hh"
+#include "hb-ot-var-hvar-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 int
+_glyf_get_side_bearing_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
+
+HB_INTERNAL unsigned
+_glyf_get_advance_var (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
+
+
+namespace OT {
+
+
+struct LongMetric
+{
+ UFWORD advance; /* Advance width/height. */
+ FWORD sb; /* Leading (left/top) side bearing. */
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+
+
+template <typename T, typename H>
+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);
+ }
+
+
+ bool subset_update_header (hb_subset_plan_t *plan,
+ unsigned int num_hmetrics) const
+ {
+ hb_blob_t *src_blob = hb_sanitize_context_t ().reference_table<H> (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);
+ table->numberOfLongMetrics = num_hmetrics;
+
+ bool result = 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,
+ unsigned num_advances)
+ {
+ unsigned idx = 0;
+ for (auto _ : it)
+ {
+ if (idx < num_advances)
+ {
+ LongMetric lm;
+ lm.advance = _.first;
+ lm.sb = _.second;
+ if (unlikely (!c->embed<LongMetric> (&lm))) return;
+ }
+ else
+ {
+ FWORD *sb = c->allocate_size<FWORD> (FWORD::static_size);
+ if (unlikely (!sb)) return;
+ *sb = _.second;
+ }
+ idx++;
+ }
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+
+ T *table_prime = c->serializer->start_embed <T> ();
+ if (unlikely (!table_prime)) return_trace (false);
+
+ accelerator_t _mtx;
+ _mtx.init (c->plan->source);
+ unsigned num_advances = _mtx.num_advances_for_subset (c->plan);
+
+ auto it =
+ + hb_range (c->plan->num_output_glyphs ())
+ | hb_map ([c, &_mtx] (unsigned _)
+ {
+ hb_codepoint_t old_gid;
+ if (!c->plan->old_gid_for_new_gid (_, &old_gid))
+ return hb_pair (0u, 0);
+ return hb_pair (_mtx.get_advance (old_gid), _mtx.get_side_bearing (old_gid));
+ })
+ ;
+
+ table_prime->serialize (c->serializer, it, num_advances);
+
+ _mtx.fini ();
+
+ if (unlikely (c->serializer->ran_out_of_room || c->serializer->in_error ()))
+ return_trace (false);
+
+ // Amend header num hmetrics
+ if (unlikely (!subset_update_header (c->plan, num_advances)))
+ return_trace (false);
+
+ return_trace (true);
+ }
+
+ struct accelerator_t
+ {
+ friend struct hmtxvmtx;
+
+ void init (hb_face_t *face,
+ unsigned int default_advance_ = 0)
+ {
+ default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face);
+
+ num_advances = T::is_horizontal ? face->table.hhea->numberOfLongMetrics : face->table.vhea->numberOfLongMetrics;
+
+ table = hb_sanitize_context_t ().reference_table<hmtxvmtx> (face, T::tableTag);
+
+ /* Cap num_metrics() and num_advances() based on table length. */
+ unsigned int len = table.get_length ();
+ if (unlikely (num_advances * 4 > len))
+ num_advances = len / 4;
+ num_metrics = num_advances + (len - 4 * num_advances) / 2;
+
+ /* We MUST set num_metrics to zero if num_advances is zero.
+ * Our get_advance() depends on that. */
+ if (unlikely (!num_advances))
+ {
+ num_metrics = num_advances = 0;
+ table.destroy ();
+ table = hb_blob_get_empty ();
+ }
+
+ var_table = hb_sanitize_context_t ().reference_table<HVARVVAR> (face, T::variationsTag);
+ }
+
+ void fini ()
+ {
+ table.destroy ();
+ var_table.destroy ();
+ }
+
+ int get_side_bearing (hb_codepoint_t glyph) const
+ {
+ if (glyph < num_advances)
+ return table->longMetricZ[glyph].sb;
+
+ if (unlikely (glyph >= num_metrics))
+ return 0;
+
+ const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_advances];
+ return bearings[glyph - num_advances];
+ }
+
+ int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const
+ {
+ int side_bearing = get_side_bearing (glyph);
+
+#ifndef HB_NO_VAR
+ if (unlikely (glyph >= num_metrics) || !font->num_coords)
+ return side_bearing;
+
+ if (var_table.get_length ())
+ return side_bearing + var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
+
+ return _glyf_get_side_bearing_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
+#else
+ return side_bearing;
+#endif
+ }
+
+ unsigned int get_advance (hb_codepoint_t glyph) const
+ {
+ if (unlikely (glyph >= num_metrics))
+ {
+ /* If num_metrics is zero, it means we don't have the metrics table
+ * for this direction: return default advance. Otherwise, it means that the
+ * glyph index is out of bound: return zero. */
+ if (num_metrics)
+ return 0;
+ else
+ return default_advance;
+ }
+
+ return table->longMetricZ[hb_min (glyph, (uint32_t) num_advances - 1)].advance;
+ }
+
+ unsigned int get_advance (hb_codepoint_t glyph,
+ hb_font_t *font) const
+ {
+ unsigned int advance = get_advance (glyph);
+
+#ifndef HB_NO_VAR
+ if (unlikely (glyph >= num_metrics) || !font->num_coords)
+ return advance;
+
+ if (var_table.get_length ())
+ return advance + roundf (var_table->get_advance_var (glyph, font)); // TODO Optimize?!
+
+ return _glyf_get_advance_var (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
+#else
+ return advance;
+#endif
+ }
+
+ unsigned int num_advances_for_subset (const hb_subset_plan_t *plan) const
+ {
+ unsigned int num_advances = plan->num_output_glyphs ();
+ unsigned int last_advance = _advance_for_new_gid (plan,
+ num_advances - 1);
+ while (num_advances > 1 &&
+ last_advance == _advance_for_new_gid (plan,
+ num_advances - 2))
+ {
+ num_advances--;
+ }
+
+ return num_advances;
+ }
+
+ private:
+ unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan,
+ hb_codepoint_t new_gid) const
+ {
+ hb_codepoint_t old_gid;
+ if (!plan->old_gid_for_new_gid (new_gid, &old_gid))
+ return 0;
+
+ return get_advance (old_gid);
+ }
+
+ protected:
+ unsigned int num_metrics;
+ unsigned int num_advances;
+ unsigned int default_advance;
+
+ private:
+ hb_blob_ptr_t<hmtxvmtx> table;
+ hb_blob_ptr_t<HVARVVAR> var_table;
+ };
+
+ 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. */
+ public:
+ DEFINE_SIZE_ARRAY (0, longMetricZ);
+};
+
+struct hmtx : hmtxvmtx<hmtx, hhea> {
+ 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> {
+ 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 {};
+struct vmtx_accelerator_t : vmtx::accelerator_t {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_HMTX_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-kern-table.hh b/thirdparty/harfbuzz/src/hb-ot-kern-table.hh
new file mode 100644
index 0000000000..3563cab8bd
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-kern-table.hh
@@ -0,0 +1,359 @@
+/*
+ * 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) &&
+ 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, hb_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, hb_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) ||
+ 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, hb_forward<Ts> (ds)...));
+#ifndef HB_NO_AAT_SHAPE
+ case 1: return_trace (c->dispatch (u.aat, hb_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);
+ 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/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh
new file mode 100644
index 0000000000..02fe14fa06
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-base-table.hh
@@ -0,0 +1,509 @@
+/*
+ * 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 () const { return coordinate; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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 () const
+ {
+ /* TODO */
+ return 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 */
+ HBGlyphID 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 coordinate + (HB_DIRECTION_IS_VERTICAL (direction) ?
+ device.get_y_delta (font, var_store) :
+ 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 */
+ OffsetTo<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 ();
+ case 2: return u.format2.get_coord ();
+ case 3: return u.format3.get_coord (font, var_store, direction);
+ default:return 0;
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!u.format.sanitize (c))) return_trace (false);
+ switch (u.format) {
+ case 1: return_trace (u.format1.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+ case 3: return_trace (u.format3.sanitize (c));
+ default:return_trace (false);
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format;
+ BaseCoordFormat1 format1;
+ BaseCoordFormat2 format2;
+ BaseCoordFormat3 format3;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format);
+};
+
+struct FeatMinMaxRecord
+{
+ int cmp (hb_tag_t key) const { return tag.cmp (key); }
+
+ bool has_data () const { return tag; }
+
+ void get_min_max (const BaseCoord **min, const BaseCoord **max) const
+ {
+ if (likely (min)) *min = &(this+minCoord);
+ if (likely (max)) *max = &(this+maxCoord);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ minCoord.sanitize (c, this) &&
+ maxCoord.sanitize (c, this)));
+ }
+
+ protected:
+ Tag tag; /* 4-byte feature identification tag--must
+ * match feature tag in FeatureList */
+ OffsetTo<BaseCoord>
+ minCoord; /* Offset to BaseCoord table that defines
+ * the minimum extent value, from beginning
+ * of MinMax table (may be NULL) */
+ OffsetTo<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:
+ OffsetTo<BaseCoord>
+ minCoord; /* Offset to BaseCoord table that defines
+ * minimum extent value, from the beginning
+ * of MinMax table (may be NULL) */
+ OffsetTo<BaseCoord>
+ maxCoord; /* Offset to BaseCoord table that defines
+ * maximum extent value, from the beginning
+ * of MinMax table (may be NULL) */
+ SortedArrayOf<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 */
+ OffsetArrayOf<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, this)));
+ }
+
+ protected:
+ Tag baseLangSysTag; /* 4-byte language system identification tag */
+ OffsetTo<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_data () const { return baseValues; }
+
+ 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:
+ OffsetTo<BaseValues>
+ baseValues; /* Offset to BaseValues table, from beginning
+ * of BaseScript table (may be NULL) */
+ OffsetTo<MinMax>
+ defaultMinMax; /* Offset to MinMax table, from beginning of
+ * BaseScript table (may be NULL) */
+ SortedArrayOf<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 */
+ OffsetTo<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:
+ SortedArrayOf<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_data ()) return false;
+
+ if (likely (coord))
+ {
+ unsigned int tag_index = 0;
+ (this+baseTagList).bfind (baseline_tag, &tag_index);
+ *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_data ()) 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) &&
+ (this+baseTagList).sanitize (c) &&
+ (this+baseScriptList).sanitize (c)));
+ }
+
+ protected:
+ OffsetTo<SortedArrayOf<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 */
+ OffsetTo<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;
+ }
+
+ /* TODO: Expose this separately sometime? */
+ 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 BaseCoord *min_coord, *max_coord;
+ if (!get_axis (direction).get_min_max (script_tag, language_tag, feature_tag,
+ &min_coord, &max_coord))
+ return false;
+
+ const VariationStore &var_store = get_var_store ();
+ if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
+ if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
+ return true;
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ likely (version.major == 1) &&
+ hAxis.sanitize (c, this) &&
+ vAxis.sanitize (c, this) &&
+ (version.to_int () < 0x00010001u || varStore.sanitize (c, this))));
+ }
+
+ protected:
+ FixedVersion<>version; /* Version of the BASE table */
+ OffsetTo<Axis>hAxis; /* Offset to horizontal Axis table, from beginning
+ * of BASE table (may be NULL) */
+ OffsetTo<Axis>vAxis; /* Offset to vertical Axis table, from beginning
+ * of BASE table (may be NULL) */
+ LOffsetTo<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/thirdparty/harfbuzz/src/hb-ot-layout-common.hh b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
new file mode 100644
index 0000000000..3140dd6328
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh
@@ -0,0 +1,3178 @@
+/*
+ * 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"
+
+
+#ifndef HB_MAX_NESTING_LEVEL
+#define HB_MAX_NESTING_LEVEL 6
+#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_pause() is
+ * called in a collect_features call of any shaper.
+ */
+#define HB_CLOSURE_MAX_STAGES 32
+#endif
+
+#ifndef HB_MAX_SCRIPTS
+#define HB_MAX_SCRIPTS 500
+#endif
+
+#ifndef HB_MAX_LANGSYS
+#define HB_MAX_LANGSYS 2000
+#endif
+
+#ifndef HB_MAX_FEATURES
+#define HB_MAX_FEATURES 750
+#endif
+
+#ifndef HB_MAX_FEATURE_INDICES
+#define HB_MAX_FEATURE_INDICES 1500
+#endif
+
+#ifndef HB_MAX_LOOKUP_INDICES
+#define HB_MAX_LOOKUP_INDICES 20000
+#endif
+
+
+namespace OT {
+
+
+#define NOT_COVERED ((unsigned int) -1)
+
+
+template<typename Iterator>
+static inline void Coverage_serialize (hb_serialize_context_t *c,
+ Iterator it);
+
+template<typename Iterator>
+static inline void ClassDef_serialize (hb_serialize_context_t *c,
+ Iterator it);
+
+static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
+ const hb_set_t &glyphset,
+ const hb_map_t &gid_klass_map,
+ hb_sorted_vector_t<HBGlyphID> &glyphs,
+ const hb_set_t &klasses,
+ hb_map_t *klass_map /*INOUT*/);
+
+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_INDICES;
+ }
+
+ hb_subset_context_t *subset_context;
+ const hb_tag_t table_tag;
+ const hb_map_t *lookup_index_map;
+ const hb_map_t *feature_index_map;
+
+ hb_subset_layout_context_t (hb_subset_context_t *c_,
+ hb_tag_t tag_,
+ hb_map_t *lookup_map_,
+ hb_map_t *feature_map_) :
+ subset_context (c_),
+ table_tag (tag_),
+ lookup_index_map (lookup_map_),
+ feature_index_map (feature_map_),
+ script_count (0),
+ langsys_count (0),
+ feature_index_count (0),
+ lookup_index_count (0)
+ {}
+
+ private:
+ unsigned script_count;
+ unsigned langsys_count;
+ unsigned feature_index_count;
+ unsigned lookup_index_count;
+};
+
+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 *o = out.serialize_append (subset_context->serializer);
+ if (unlikely (!o)) return false;
+ auto snap = subset_context->serializer->snapshot ();
+ 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 *o = out.serialize_append (subset_context->serializer);
+ if (unlikely (!o)) return false;
+ auto snap = subset_context->serializer->snapshot ();
+ 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;
+};
+
+/*
+ * 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); }
+}
+HB_FUNCOBJ (subset_record_array);
+
+/*
+ *
+ * OpenType Layout Common Table Formats
+ *
+ */
+
+
+/*
+ * Script, ScriptList, LangSys, Feature, FeatureList, Lookup, LookupList
+ */
+
+struct Record_sanitize_closure_t {
+ hb_tag_t tag;
+ const void *list_base;
+};
+
+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
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->subset_context->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+ bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag);
+ 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 */
+ OffsetTo<Type>
+ offset; /* Offset from beginning of object holding
+ * the Record */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+template <typename Type>
+struct RecordArrayOf : SortedArrayOf<Record<Type>>
+{
+ const OffsetTo<Type>& get_offset (unsigned int i) const
+ { return (*this)[i].offset; }
+ OffsetTo<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->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_BFIND_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 Feature;
+
+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 (!out || !c->serializer->extend_min (out))) return_trace (false);
+
+ unsigned count = this->len;
+ + hb_zip (*this, hb_range (count))
+ | hb_filter (l->feature_index_map, hb_second)
+ | hb_map (hb_first)
+ | hb_apply (subset_record_array (l, out, this))
+ ;
+ return_trace (true);
+ }
+};
+
+struct RangeRecord
+{
+ 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));
+ }
+
+ 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); }
+
+ HBGlyphID first; /* First GlyphID in the range */
+ HBGlyphID last; /* Last GlyphID in the range */
+ HBUINT16 value; /* Value */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+DECLARE_NULL_NAMESPACE_BYTES (OT, RangeRecord);
+
+
+struct IndexArray : ArrayOf<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->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 (arrayZ, len);
+ }
+};
+
+
+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 operator == (const LangSys& o) const
+ {
+ if (featureIndex.len != o.featureIndex.len ||
+ reqFeatureIndex != o.reqFeatureIndex)
+ return false;
+
+ for (const auto _ : + hb_zip (featureIndex, o.featureIndex))
+ if (_.first != _.second) return false;
+
+ return true;
+ }
+
+ 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 (!out || !c->serializer->extend_min (out))) return_trace (false);
+
+ out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex) ? l->feature_index_map->get (reqFeatureIndex) : 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; }
+
+ 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);
+
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!out || !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;
+ }
+ }
+
+ + langSys.iter ()
+ | hb_filter ([=] (const Record<LangSys>& record) {return l->visitLangSys (); })
+ | hb_filter ([&] (const Record<LangSys>& record)
+ {
+ const LangSys& d = this+defaultLangSys;
+ const LangSys& l = this+record.offset;
+ return !(l == d);
+ })
+ | 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:
+ OffsetTo<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);
+};
+
+typedef RecordListOf<Script> ScriptList;
+
+
+/* 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);
+
+ /* This subtable has some "history", if you will. Some earlier versions of
+ * Adobe tools calculated the offset of the FeatureParams sutable 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);
+ }
+
+ 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));
+ }
+
+ 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.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; }
+
+ 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.) */
+ ArrayOf<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);
+ }
+
+ 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 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); }
+
+ 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 (!out || !c->serializer->extend_min (out))) return_trace (false);
+
+ bool subset_featureParams = 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);
+ return_trace (bool (it) || subset_featureParams
+ || (tag && *tag == HB_TAG ('p', 'r', 'e', 'f')));
+ }
+
+ 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);
+
+ /* 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);
+
+ 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));
+
+ OffsetTo<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);
+ }
+
+ OffsetTo<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);
+};
+
+typedef RecordListOf<Feature> FeatureList;
+
+
+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 OffsetArrayOf<TSubTable>& get_subtables () const
+ { return reinterpret_cast<const OffsetArrayOf<TSubTable> &> (subTable); }
+ template <typename TSubTable>
+ OffsetArrayOf<TSubTable>& get_subtables ()
+ { return reinterpret_cast<OffsetArrayOf<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, hb_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 (!out || !c->serializer->extend_min (out))) return_trace (false);
+ out->lookupType = lookupType;
+ out->lookupFlag = lookupFlag;
+
+ const hb_set_t *glyphset = c->plan->glyphset ();
+ unsigned int lookup_type = get_type ();
+ + hb_iter (get_subtables <TSubTable> ())
+ | hb_filter ([this, glyphset, lookup_type] (const OffsetTo<TSubTable> &_) { return (this+_).intersects (glyphset, lookup_type); })
+ | hb_apply (subset_offset_array (c, out->get_subtables<TSubTable> (), this, lookup_type))
+ ;
+
+ 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);
+
+ 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 ()))
+ {
+ /* 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);
+ }
+
+ private:
+ HBUINT16 lookupType; /* Different enumerations for GSUB and GPOS */
+ HBUINT16 lookupFlag; /* Lookup qualifiers */
+ ArrayOf<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);
+};
+
+typedef OffsetListOf<Lookup> LookupList;
+
+template <typename TLookup>
+struct LookupOffsetList : OffsetListOf<TLookup>
+{
+ 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);
+
+ unsigned count = this->len;
+ + hb_zip (*this, hb_range (count))
+ | hb_filter (l->lookup_index_map, hb_second)
+ | hb_map (hb_first)
+ | hb_apply (subset_offset_array (c, *out, this))
+ ;
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (OffsetListOf<TLookup>::sanitize (c, this));
+ }
+};
+
+
+/*
+ * Coverage Table
+ */
+
+struct CoverageFormat1
+{
+ friend struct Coverage;
+
+ private:
+ unsigned int get_coverage (hb_codepoint_t glyph_id) const
+ {
+ unsigned int i;
+ glyphArray.bfind (glyph_id, &i, HB_BFIND_NOT_FOUND_STORE, NOT_COVERED);
+ return i;
+ }
+
+ 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 sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (glyphArray.sanitize (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ /* TODO Speed up, using hb_set_next() and bsearch()? */
+ unsigned int count = glyphArray.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (glyphs->has (glyphArray[i]))
+ return true;
+ return false;
+ }
+ bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+ { return glyphs->has (glyphArray[index]); }
+
+ template <typename set_t>
+ bool collect_coverage (set_t *glyphs) const
+ { return glyphs->add_sorted_array (glyphArray.arrayZ, glyphArray.len); }
+
+ public:
+ /* Older compilers need this to be public. */
+ struct iter_t
+ {
+ void init (const struct CoverageFormat1 &c_) { c = &c_; i = 0; }
+ void fini () {}
+ 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 || c != o.c; }
+
+ private:
+ const struct CoverageFormat1 *c;
+ unsigned int i;
+ };
+ private:
+
+ protected:
+ HBUINT16 coverageFormat; /* Format identifier--format = 1 */
+ SortedArrayOf<HBGlyphID>
+ glyphArray; /* Array of GlyphIDs--in numerical order */
+ public:
+ DEFINE_SIZE_ARRAY (4, glyphArray);
+};
+
+struct CoverageFormat2
+{
+ friend struct Coverage;
+
+ private:
+ unsigned int get_coverage (hb_codepoint_t glyph_id) const
+ {
+ const RangeRecord &range = rangeRecord.bsearch (glyph_id);
+ return likely (range.first <= range.last)
+ ? (unsigned int) range.value + (glyph_id - range.first)
+ : NOT_COVERED;
+ }
+
+ 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);
+
+ if (unlikely (!glyphs))
+ {
+ rangeRecord.len = 0;
+ return_trace (true);
+ }
+
+ /* TODO(iter) Write more efficiently? */
+
+ 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);
+
+ unsigned count = 0;
+ unsigned range = (unsigned) -1;
+ last = (hb_codepoint_t) -2;
+ for (auto g: glyphs)
+ {
+ if (last + 1 != g)
+ {
+ range++;
+ rangeRecord[range].first = g;
+ rangeRecord[range].value = count;
+ }
+ rangeRecord[range].last = g;
+ last = g;
+ count++;
+ }
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (rangeRecord.sanitize (c));
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ /* TODO Speed up, using hb_set_next() and bsearch()? */
+ unsigned int count = rangeRecord.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (rangeRecord[i].intersects (glyphs))
+ return true;
+ return false;
+ }
+ bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const
+ {
+ unsigned int i;
+ unsigned int count = rangeRecord.len;
+ for (i = 0; i < count; i++) {
+ const RangeRecord &range = rangeRecord[i];
+ if (range.value <= index &&
+ index < (unsigned int) range.value + (range.last - range.first) &&
+ range.intersects (glyphs))
+ return true;
+ else if (index < range.value)
+ return false;
+ }
+ return false;
+ }
+
+ template <typename set_t>
+ bool collect_coverage (set_t *glyphs) const
+ {
+ unsigned int count = rangeRecord.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!rangeRecord[i].collect_coverage (glyphs)))
+ return false;
+ return true;
+ }
+
+ public:
+ /* Older compilers need this to be public. */
+ struct iter_t
+ {
+ void init (const CoverageFormat2 &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;
+ }
+ }
+ void fini () {}
+ 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[i].first;
+ coverage = c->rangeRecord[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;
+ return;
+ }
+ }
+ 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 || c != o.c; }
+
+ private:
+ const struct CoverageFormat2 *c;
+ unsigned int i, coverage;
+ hb_codepoint_t j;
+ };
+ private:
+
+ protected:
+ HBUINT16 coverageFormat; /* Format identifier--format = 2 */
+ SortedArrayOf<RangeRecord>
+ rangeRecord; /* Array of glyph ranges--ordered by
+ * Start GlyphID. rangeCount entries
+ * long */
+ public:
+ DEFINE_SIZE_ARRAY (4, rangeRecord);
+};
+
+struct Coverage
+{
+ /* Has interface. */
+ static constexpr unsigned SENTINEL = NOT_COVERED;
+ typedef unsigned int value_t;
+ value_t operator [] (hb_codepoint_t k) const { return get (k); }
+ bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; }
+ /* 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);
+ 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 = 0;
+ unsigned num_ranges = 0;
+ hb_codepoint_t last = (hb_codepoint_t) -2;
+ for (auto g: glyphs)
+ {
+ if (last + 1 != g)
+ num_ranges++;
+ last = g;
+ count++;
+ }
+ u.format = count <= num_ranges * 3 ? 1 : 2;
+
+ switch (u.format)
+ {
+ case 1: return_trace (u.format1.serialize (c, glyphs));
+ case 2: return_trace (u.format2.serialize (c, glyphs));
+ default:return_trace (false);
+ }
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto it =
+ + iter ()
+ | hb_filter (glyphset)
+ | hb_map_retains_sorting (glyph_map)
+ ;
+
+ bool ret = bool (it);
+ Coverage_serialize (c->serializer, it);
+ return_trace (ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.format.sanitize (c)) return_trace (false);
+ switch (u.format)
+ {
+ case 1: return_trace (u.format1.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+ default:return_trace (true);
+ }
+ }
+
+ 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);
+ 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);
+ 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);
+ default:return false;
+ }
+ }
+
+ 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))
+ {
+ 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;
+ default: return;
+ }
+ }
+ bool __more__ () const
+ {
+ switch (format)
+ {
+ case 1: return u.format1.more ();
+ case 2: return u.format2.more ();
+ default:return false;
+ }
+ }
+ void __next__ ()
+ {
+ switch (format)
+ {
+ case 1: u.format1.next (); break;
+ case 2: u.format2.next (); break;
+ 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 ();
+ default:return 0;
+ }
+ }
+ bool operator != (const iter_t& o) const
+ {
+ if (format != o.format) return true;
+ switch (format)
+ {
+ case 1: return u.format1 != o.u.format1;
+ case 2: return u.format2 != o.u.format2;
+ default:return false;
+ }
+ }
+
+ private:
+ unsigned int format;
+ union {
+ CoverageFormat2::iter_t format2; /* Put this one first since it's larger; helps shut up compiler. */
+ CoverageFormat1::iter_t format1;
+ } u;
+ };
+ iter_t iter () const { return iter_t (*this); }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ CoverageFormat1 format1;
+ CoverageFormat2 format2;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format);
+};
+
+template<typename Iterator>
+static inline void
+Coverage_serialize (hb_serialize_context_t *c,
+ Iterator it)
+{ c->start_embed<Coverage> ()->serialize (c, it); }
+
+static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
+ const hb_set_t &glyphset,
+ const hb_map_t &gid_klass_map,
+ hb_sorted_vector_t<HBGlyphID> &glyphs,
+ const hb_set_t &klasses,
+ hb_map_t *klass_map /*INOUT*/)
+{
+ if (!klass_map)
+ {
+ ClassDef_serialize (c, hb_zip (glyphs.iter (), + glyphs.iter ()
+ | hb_map (gid_klass_map)));
+ return;
+ }
+
+ /* 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 (glyphset.get_population () > gid_klass_map.get_population ())
+ klass_map->set (0, 0);
+
+ unsigned idx = klass_map->has (0) ? 1 : 0;
+ for (const unsigned k: klasses.iter ())
+ {
+ if (klass_map->has (k)) continue;
+ klass_map->set (k, idx);
+ idx++;
+ }
+
+ auto it =
+ + glyphs.iter ()
+ | hb_map_retains_sorting ([&] (const HBGlyphID& gid) -> hb_pair_t<hb_codepoint_t, unsigned>
+ {
+ unsigned new_klass = klass_map->get (gid_klass_map[gid]);
+ return hb_pair ((hb_codepoint_t)gid, new_klass);
+ })
+ ;
+
+ c->propagate_error (glyphs, klasses);
+ ClassDef_serialize (c, it);
+}
+
+/*
+ * Class Definition Table
+ */
+
+struct ClassDefFormat1
+{
+ friend struct ClassDef;
+
+ private:
+ unsigned int get_class (hb_codepoint_t glyph_id) const
+ {
+ return classValue[(unsigned int) (glyph_id - startGlyph)];
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+
+ if (unlikely (!it))
+ {
+ 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, unsigned> 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*/) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->_glyphset_gsub;
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ hb_sorted_vector_t<HBGlyphID> glyphs;
+ hb_set_t orig_klasses;
+ hb_map_t gid_org_klass_map;
+
+ hb_codepoint_t start = startGlyph;
+ hb_codepoint_t end = start + classValue.len;
+ for (const hb_codepoint_t gid : + hb_range (start, end)
+ | hb_filter (glyphset))
+ {
+ unsigned klass = classValue[gid - start];
+ if (!klass) continue;
+
+ glyphs.push (glyph_map[gid]);
+ gid_org_klass_map.set (glyph_map[gid], klass);
+ orig_klasses.add (klass);
+ }
+
+ ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map,
+ glyphs, orig_klasses, klass_map);
+ return_trace ((bool) glyphs);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && classValue.sanitize (c));
+ }
+
+ 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 int 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
+ {
+ /* TODO Speed up, using hb_set_next()? */
+ hb_codepoint_t start = startGlyph;
+ hb_codepoint_t end = startGlyph + classValue.len;
+ for (hb_codepoint_t iter = startGlyph - 1;
+ hb_set_next (glyphs, &iter) && iter < end;)
+ if (classValue[iter - start]) return true;
+ return false;
+ }
+ bool intersects_class (const hb_set_t *glyphs, unsigned int 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 (!hb_set_next (glyphs, &g)) return false;
+ if (g < startGlyph) return true;
+ g = startGlyph + count - 1;
+ if (hb_set_next (glyphs, &g)) return true;
+ /* Fall through. */
+ }
+ for (unsigned int i = 0; i < count; i++)
+ if (classValue[i] == klass && glyphs->has (startGlyph + i))
+ return true;
+ return false;
+ }
+
+ protected:
+ HBUINT16 classFormat; /* Format identifier--format = 1 */
+ HBGlyphID startGlyph; /* First GlyphID of the classValueArray */
+ ArrayOf<HBUINT16>
+ classValue; /* Array of Class Values--one per GlyphID */
+ public:
+ DEFINE_SIZE_ARRAY (6, classValue);
+};
+
+struct ClassDefFormat2
+{
+ friend struct ClassDef;
+
+ private:
+ unsigned int get_class (hb_codepoint_t glyph_id) const
+ {
+ return rangeRecord.bsearch (glyph_id).value;
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+
+ if (unlikely (!it))
+ {
+ rangeRecord.len = 0;
+ return_trace (true);
+ }
+
+ unsigned num_ranges = 1;
+ hb_codepoint_t prev_gid = (*it).first;
+ unsigned prev_klass = (*it).second;
+
+ RangeRecord range_rec;
+ range_rec.first = prev_gid;
+ range_rec.last = prev_gid;
+ range_rec.value = prev_klass;
+
+ RangeRecord *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 (!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 (likely (record)) record->last = prev_gid;
+ rangeRecord.len = num_ranges;
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c,
+ hb_map_t *klass_map = nullptr /*OUT*/) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->_glyphset_gsub;
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ hb_sorted_vector_t<HBGlyphID> glyphs;
+ hb_set_t orig_klasses;
+ hb_map_t gid_org_klass_map;
+
+ unsigned count = rangeRecord.len;
+ for (unsigned i = 0; i < count; i++)
+ {
+ unsigned klass = rangeRecord[i].value;
+ if (!klass) continue;
+ hb_codepoint_t start = rangeRecord[i].first;
+ hb_codepoint_t end = rangeRecord[i].last + 1;
+ for (hb_codepoint_t g = start; g < end; g++)
+ {
+ if (!glyphset.has (g)) continue;
+ glyphs.push (glyph_map[g]);
+ gid_org_klass_map.set (glyph_map[g], klass);
+ orig_klasses.add (klass);
+ }
+ }
+
+ ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map,
+ glyphs, orig_klasses, klass_map);
+ return_trace ((bool) glyphs);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (rangeRecord.sanitize (c));
+ }
+
+ template <typename set_t>
+ bool collect_coverage (set_t *glyphs) const
+ {
+ unsigned int count = rangeRecord.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (rangeRecord[i].value)
+ if (unlikely (!rangeRecord[i].collect_coverage (glyphs)))
+ return false;
+ return true;
+ }
+
+ template <typename set_t>
+ bool collect_class (set_t *glyphs, unsigned int klass) const
+ {
+ unsigned int count = rangeRecord.len;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ if (rangeRecord[i].value == klass)
+ if (unlikely (!rangeRecord[i].collect_coverage (glyphs)))
+ return false;
+ }
+ return true;
+ }
+
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ /* TODO Speed up, using hb_set_next() and bsearch()? */
+ unsigned int count = rangeRecord.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (rangeRecord[i].intersects (glyphs))
+ return true;
+ return false;
+ }
+ bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const
+ {
+ unsigned int count = rangeRecord.len;
+ if (klass == 0)
+ {
+ /* Match if there's any glyph that is not listed! */
+ hb_codepoint_t g = HB_SET_VALUE_INVALID;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ if (!hb_set_next (glyphs, &g))
+ break;
+ if (g < rangeRecord[i].first)
+ return true;
+ g = rangeRecord[i].last;
+ }
+ if (g != HB_SET_VALUE_INVALID && hb_set_next (glyphs, &g))
+ return true;
+ /* Fall through. */
+ }
+ for (unsigned int i = 0; i < count; i++)
+ if (rangeRecord[i].value == klass && rangeRecord[i].intersects (glyphs))
+ return true;
+ return false;
+ }
+
+ protected:
+ HBUINT16 classFormat; /* Format identifier--format = 2 */
+ SortedArrayOf<RangeRecord>
+ rangeRecord; /* Array of glyph ranges--ordered by
+ * Start GlyphID */
+ public:
+ DEFINE_SIZE_ARRAY (4, rangeRecord);
+};
+
+struct ClassDef
+{
+ /* Has interface. */
+ static constexpr unsigned SENTINEL = 0;
+ typedef unsigned int value_t;
+ value_t operator [] (hb_codepoint_t k) const { return get (k); }
+ bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; }
+ /* 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);
+ default:return 0;
+ }
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ bool serialize (hb_serialize_context_t *c, Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+
+ unsigned format = 2;
+ if (likely (it))
+ {
+ hb_codepoint_t glyph_min = (*it).first;
+ hb_codepoint_t glyph_max = + it
+ | hb_map (hb_first)
+ | hb_reduce (hb_max, 0u);
+
+ 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;
+ if (cur_gid == glyph_min || !cur_klass) continue;
+ if (cur_gid != prev_gid + 1 ||
+ cur_klass != prev_klass)
+ num_ranges++;
+
+ prev_gid = cur_gid;
+ prev_klass = cur_klass;
+ }
+
+ if (1 + (glyph_max - glyph_min + 1) <= num_ranges * 3)
+ format = 1;
+ }
+ u.format = format;
+
+ switch (u.format)
+ {
+ case 1: return_trace (u.format1.serialize (c, it));
+ case 2: return_trace (u.format2.serialize (c, it));
+ default:return_trace (false);
+ }
+ }
+
+ bool subset (hb_subset_context_t *c,
+ hb_map_t *klass_map = nullptr /*OUT*/) const
+ {
+ TRACE_SUBSET (this);
+ switch (u.format) {
+ case 1: return_trace (u.format1.subset (c, klass_map));
+ case 2: return_trace (u.format2.subset (c, klass_map));
+ default:return_trace (false);
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.format.sanitize (c)) return_trace (false);
+ switch (u.format) {
+ case 1: return_trace (u.format1.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+ default:return_trace (true);
+ }
+ }
+
+ /* 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);
+ 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);
+ 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);
+ 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);
+ default:return false;
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ ClassDefFormat1 format1;
+ ClassDefFormat2 format2;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format);
+};
+
+template<typename Iterator>
+static inline void ClassDef_serialize (hb_serialize_context_t *c,
+ Iterator it)
+{ c->start_embed<ClassDef> ()->serialize (c, it); }
+
+
+/*
+ * Item Variation Store
+ */
+
+struct VarRegionAxis
+{
+ float evaluate (int coord) const
+ {
+ int start = startCoord, peak = peakCoord, end = endCoord;
+
+ /* TODO Move these to sanitize(). */
+ 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 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. */
+ }
+
+ public:
+ F2DOT14 startCoord;
+ F2DOT14 peakCoord;
+ F2DOT14 endCoord;
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+struct VarRegionList
+{
+ float evaluate (unsigned int region_index,
+ const int *coords, unsigned int coord_len) const
+ {
+ if (unlikely (region_index >= regionCount))
+ return 0.;
+
+ 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)
+ return 0.;
+ v *= factor;
+ }
+ return v;
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount));
+ }
+
+ bool serialize (hb_serialize_context_t *c, const VarRegionList *src, const hb_bimap_t &region_map)
+ {
+ TRACE_SERIALIZE (this);
+ VarRegionList *out = c->allocate_min<VarRegionList> ();
+ if (unlikely (!out)) return_trace (false);
+ axisCount = src->axisCount;
+ regionCount = region_map.get_population ();
+ if (unlikely (!c->allocate_size<VarRegionList> (get_size () - min_size))) return_trace (false);
+ unsigned int region_count = src->get_region_count ();
+ for (unsigned int r = 0; r < regionCount; r++)
+ {
+ unsigned int backward = region_map.backward (r);
+ if (backward >= region_count) return_trace (false);
+ memcpy (&axesZ[axisCount * r], &src->axesZ[axisCount * backward], VarRegionAxis::static_size * axisCount);
+ }
+
+ return_trace (true);
+ }
+
+ unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; }
+ unsigned int get_region_count () const { return regionCount; }
+
+ protected:
+ HBUINT16 axisCount;
+ HBUINT16 regionCount;
+ UnsizedArrayOf<VarRegionAxis>
+ axesZ;
+ public:
+ DEFINE_SIZE_ARRAY (4, axesZ);
+};
+
+struct VarData
+{
+ unsigned int get_region_index_count () const
+ { return regionIndices.len; }
+
+ unsigned int get_row_size () const
+ { return shortCount + regionIndices.len; }
+
+ unsigned int get_size () const
+ { return itemCount * get_row_size (); }
+
+ float get_delta (unsigned int inner,
+ const int *coords, unsigned int coord_count,
+ const VarRegionList &regions) const
+ {
+ if (unlikely (inner >= itemCount))
+ return 0.;
+
+ unsigned int count = regionIndices.len;
+ unsigned int scount = shortCount;
+
+ const HBUINT8 *bytes = get_delta_bytes ();
+ const HBUINT8 *row = bytes + inner * (scount + count);
+
+ float delta = 0.;
+ unsigned int i = 0;
+
+ const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (row);
+ for (; i < scount; i++)
+ {
+ float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count);
+ 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);
+ delta += scalar * *bcursor++;
+ }
+
+ return delta;
+ }
+
+ void get_scalars (const int *coords, unsigned int coord_count,
+ const VarRegionList &regions,
+ 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) &&
+ shortCount <= regionIndices.len &&
+ c->check_range (get_delta_bytes (),
+ itemCount,
+ get_row_size ()));
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ const VarData *src,
+ const hb_inc_bimap_t &inner_map,
+ const hb_bimap_t &region_map)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ itemCount = inner_map.get_next_value ();
+
+ /* Optimize short count */
+ unsigned short ri_count = src->regionIndices.len;
+ enum delta_size_t { kZero=0, kByte, kShort };
+ hb_vector_t<delta_size_t> delta_sz;
+ hb_vector_t<unsigned int> ri_map; /* maps old index to new index */
+ delta_sz.resize (ri_count);
+ ri_map.resize (ri_count);
+ unsigned int new_short_count = 0;
+ unsigned int r;
+ for (r = 0; r < ri_count; r++)
+ {
+ delta_sz[r] = kZero;
+ for (unsigned int i = 0; i < inner_map.get_next_value (); i++)
+ {
+ unsigned int old = inner_map.backward (i);
+ int16_t delta = src->get_item_delta (old, r);
+ if (delta < -128 || 127 < delta)
+ {
+ delta_sz[r] = kShort;
+ new_short_count++;
+ break;
+ }
+ else if (delta != 0)
+ delta_sz[r] = kByte;
+ }
+ }
+ unsigned int short_index = 0;
+ unsigned int byte_index = new_short_count;
+ unsigned int new_ri_count = 0;
+ for (r = 0; r < ri_count; r++)
+ if (delta_sz[r])
+ {
+ ri_map[r] = (delta_sz[r] == kShort)? short_index++ : byte_index++;
+ new_ri_count++;
+ }
+
+ shortCount = new_short_count;
+ regionIndices.len = new_ri_count;
+
+ unsigned int size = regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (get_row_size () * itemCount);
+ if (unlikely (!c->allocate_size<HBUINT8> (size)))
+ return_trace (false);
+
+ for (r = 0; r < ri_count; r++)
+ if (delta_sz[r]) regionIndices[ri_map[r]] = region_map[src->regionIndices[r]];
+
+ for (unsigned int i = 0; i < itemCount; i++)
+ {
+ unsigned int old = inner_map.backward (i);
+ for (unsigned int r = 0; r < ri_count; r++)
+ if (delta_sz[r]) set_item_delta (i, ri_map[r], src->get_item_delta (old, r));
+ }
+
+ return_trace (true);
+ }
+
+ void collect_region_refs (hb_inc_bimap_t &region_map, const hb_inc_bimap_t &inner_map) const
+ {
+ for (unsigned int r = 0; r < regionIndices.len; r++)
+ {
+ unsigned int region = regionIndices[r];
+ if (region_map.has (region)) continue;
+ for (unsigned int i = 0; i < inner_map.get_next_value (); i++)
+ if (get_item_delta (inner_map.backward (i), r) != 0)
+ {
+ region_map.add (region);
+ break;
+ }
+ }
+ }
+
+ protected:
+ const HBUINT8 *get_delta_bytes () const
+ { return &StructAfter<HBUINT8> (regionIndices); }
+
+ HBUINT8 *get_delta_bytes ()
+ { return &StructAfter<HBUINT8> (regionIndices); }
+
+ int16_t get_item_delta (unsigned int item, unsigned int region) const
+ {
+ if ( item >= itemCount || unlikely (region >= regionIndices.len)) return 0;
+ const HBINT8 *p = (const HBINT8 *)get_delta_bytes () + item * get_row_size ();
+ if (region < shortCount)
+ return ((const HBINT16 *)p)[region];
+ else
+ return (p + HBINT16::static_size * shortCount)[region - shortCount];
+ }
+
+ void set_item_delta (unsigned int item, unsigned int region, int16_t delta)
+ {
+ HBINT8 *p = (HBINT8 *)get_delta_bytes () + item * get_row_size ();
+ if (region < shortCount)
+ ((HBINT16 *)p)[region] = delta;
+ else
+ (p + HBINT16::static_size * shortCount)[region - shortCount] = delta;
+ }
+
+ protected:
+ HBUINT16 itemCount;
+ HBUINT16 shortCount;
+ ArrayOf<HBUINT16> regionIndices;
+/*UnsizedArrayOf<HBUINT8>bytesX;*/
+ public:
+ DEFINE_SIZE_ARRAY (6, regionIndices);
+};
+
+struct VariationStore
+{
+ float get_delta (unsigned int outer, unsigned int inner,
+ const int *coords, unsigned int coord_count) 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);
+ }
+
+ float get_delta (unsigned int index,
+ const int *coords, unsigned int coord_count) const
+ {
+ unsigned int outer = index >> 16;
+ unsigned int inner = index & 0xFFFF;
+ return get_delta (outer, inner, coords, coord_count);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+#ifdef HB_NO_VAR
+ return true;
+#endif
+
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ format == 1 &&
+ regions.sanitize (c, this) &&
+ dataSets.sanitize (c, this));
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ const VariationStore *src,
+ const hb_array_t <hb_inc_bimap_t> &inner_maps)
+ {
+ TRACE_SERIALIZE (this);
+ unsigned int set_count = 0;
+ for (unsigned int i = 0; i < inner_maps.length; i++)
+ if (inner_maps[i].get_population () > 0) set_count++;
+
+ unsigned int size = min_size + HBUINT32::static_size * set_count;
+ if (unlikely (!c->allocate_size<HBUINT32> (size))) return_trace (false);
+ format = 1;
+
+ hb_inc_bimap_t region_map;
+ for (unsigned int i = 0; i < inner_maps.length; i++)
+ (src+src->dataSets[i]).collect_region_refs (region_map, inner_maps[i]);
+ region_map.sort ();
+
+ if (unlikely (!regions.serialize (c, this)
+ .serialize (c, &(src+src->regions), region_map))) return_trace (false);
+
+ /* TODO: The following code could be simplified when
+ * OffsetListOf::subset () can take a custom param to be passed to VarData::serialize ()
+ */
+ dataSets.len = set_count;
+ unsigned int set_index = 0;
+ for (unsigned int i = 0; i < inner_maps.length; i++)
+ {
+ if (inner_maps[i].get_population () == 0) continue;
+ if (unlikely (!dataSets[set_index++].serialize (c, this)
+ .serialize (c, &(src+src->dataSets[i]), inner_maps[i], region_map)))
+ return_trace (false);
+ }
+
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+
+ VariationStore *varstore_prime = c->serializer->start_embed<VariationStore> ();
+ if (unlikely (!varstore_prime)) return_trace (false);
+
+ const hb_set_t *variation_indices = c->plan->layout_variation_indices;
+ if (variation_indices->is_empty ()) return_trace (false);
+
+ hb_vector_t<hb_inc_bimap_t> inner_maps;
+ inner_maps.resize ((unsigned) dataSets.len);
+ for (unsigned i = 0; i < inner_maps.length; i++)
+ inner_maps[i].init ();
+
+ for (unsigned idx : c->plan->layout_variation_indices->iter ())
+ {
+ uint16_t major = idx >> 16;
+ uint16_t minor = idx & 0xFFFF;
+
+ if (major >= inner_maps.length)
+ {
+ for (unsigned i = 0; i < inner_maps.length; i++)
+ inner_maps[i].fini ();
+ return_trace (false);
+ }
+ inner_maps[major].add (minor);
+ }
+ varstore_prime->serialize (c->serializer, this, inner_maps.as_array ());
+
+ for (unsigned i = 0; i < inner_maps.length; i++)
+ inner_maps[i].fini ();
+ return_trace (bool (varstore_prime->dataSets));
+ }
+
+ unsigned int get_region_index_count (unsigned int ivs) const
+ { return (this+dataSets[ivs]).get_region_index_count (); }
+
+ void get_scalars (unsigned int ivs,
+ 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[ivs]).get_scalars (coords, coord_count, this+regions,
+ &scalars[0], num_scalars);
+ }
+
+ unsigned int get_sub_table_count () const { return dataSets.len; }
+
+ protected:
+ HBUINT16 format;
+ LOffsetTo<VarRegionList> regions;
+ LOffsetArrayOf<VarData> dataSets;
+ public:
+ DEFINE_SIZE_ARRAY (8, dataSets);
+};
+
+/*
+ * Feature Variations
+ */
+
+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);
+ return_trace (true);
+ }
+
+ private:
+ bool evaluate (const int *coords, unsigned int coord_len) const
+ {
+ int coord = axisIndex < coord_len ? coords[axisIndex] : 0;
+ return filterRangeMinValue <= coord && coord <= filterRangeMaxValue;
+ }
+
+ 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;
+ }
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_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);
+ 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;
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->start_embed (this);
+ if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+
+ + conditions.iter ()
+ | hb_apply (subset_offset_array (c, out->conditions, this))
+ ;
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (conditions.sanitize (c, this));
+ }
+
+ protected:
+ LOffsetArrayOf<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);
+ }
+
+ bool subset (hb_subset_layout_context_t *c, const void *base) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->subset_context->serializer->embed (this);
+ if (unlikely (!out)) return_trace (false);
+
+ out->featureIndex = c->feature_index_map->get (featureIndex);
+ bool ret = out->feature.serialize_subset (c->subset_context, feature, base, c);
+ return_trace (ret);
+ }
+
+ 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;
+ LOffsetTo<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 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;
+
+ + substitutions.iter ()
+ | hb_apply (subset_record_array (l, &(out->substitutions), this))
+ ;
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (version.sanitize (c) &&
+ likely (version.major == 1) &&
+ substitutions.sanitize (c, this));
+ }
+
+ protected:
+ FixedVersion<> version; /* Version--0x00010000u */
+ ArrayOf<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 subset (hb_subset_layout_context_t *c, const void *base) 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);
+ out->substitutions.serialize_subset (c->subset_context, substitutions, base, c);
+
+ 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:
+ LOffsetTo<ConditionSet>
+ conditions;
+ LOffsetTo<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);
+ }
+
+ 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,
+ hb_set_t *lookup_indexes /* OUT */) const
+ {
+ for (const FeatureVariationRecord& r : varRecords)
+ r.collect_lookups (this, feature_indexes, lookup_indexes);
+ }
+
+ void closure_features (const hb_map_t *lookup_indexes,
+ hb_set_t *feature_indexes /* OUT */) const
+ {
+ for (const FeatureVariationRecord& record : varRecords)
+ record.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;
+
+ + varRecords.iter ()
+ | hb_apply (subset_record_array (l, &(out->varRecords), this))
+ ;
+ return_trace (bool (out->varRecords));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (version.sanitize (c) &&
+ likely (version.major == 1) &&
+ varRecords.sanitize (c, this));
+ }
+
+ protected:
+ FixedVersion<> version; /* Version--0x00010000u */
+ LArrayOf<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) const
+ { return font->em_scalef_x (get_delta (font, store)); }
+
+ hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store) const
+ { return font->em_scalef_y (get_delta (font, store)); }
+
+ VariationDevice* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const
+ {
+ TRACE_SERIALIZE (this);
+ auto snap = c->snapshot ();
+ auto *out = c->embed (this);
+ if (unlikely (!out)) return_trace (nullptr);
+ if (!layout_variation_idx_map || layout_variation_idx_map->is_empty ()) return_trace (out);
+
+ unsigned org_idx = (outerIndex << 16) + innerIndex;
+ if (!layout_variation_idx_map->has (org_idx))
+ {
+ c->revert (snap);
+ return_trace (nullptr);
+ }
+ unsigned new_idx = layout_variation_idx_map->get (org_idx);
+ out->outerIndex = new_idx >> 16;
+ out->innerIndex = new_idx & 0xFFFF;
+ return_trace (out);
+ }
+
+ void record_variation_index (hb_set_t *layout_variation_indices) const
+ {
+ unsigned var_idx = (outerIndex << 16) + innerIndex;
+ layout_variation_indices->add (var_idx);
+ }
+
+ 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) const
+ {
+ return store.get_delta (outerIndex, innerIndex, font->coords, font->num_coords);
+ }
+
+ protected:
+ HBUINT16 outerIndex;
+ HBUINT16 innerIndex;
+ 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)) 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);
+#endif
+ default:
+ return 0;
+ }
+ }
+ hb_position_t get_y_delta (hb_font_t *font, const VariationStore &store=Null (VariationStore)) 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);
+#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_map_t *layout_variation_idx_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_map)));
+#endif
+ default:
+ return_trace (nullptr);
+ }
+ }
+
+ void collect_variation_indices (hb_set_t *layout_variation_indices) 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.record_variation_index (layout_variation_indices);
+ return;
+#endif
+ default:
+ return;
+ }
+ }
+
+ 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/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh
new file mode 100644
index 0000000000..437e760f64
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -0,0 +1,725 @@
+/*
+ * 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 "hb-ot-layout-common.hh"
+
+#include "hb-font.hh"
+
+
+namespace OT {
+
+
+/*
+ * Attachment List Table
+ */
+
+/* Array of contour point indices--in increasing numerical order */
+struct AttachPoint : ArrayOf<HBUINT16>
+{
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ 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.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 ();
+ 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 (c->serializer, out)
+ .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:
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table -- from
+ * beginning of AttachList table */
+ OffsetArrayOf<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->embed (this);
+ if (unlikely (!out)) 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_map));
+ }
+
+ void collect_variation_indices (hb_set_t *layout_variation_indices) const
+ { (this+deviceTable).collect_variation_indices (layout_variation_indices); }
+
+ 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 */
+ OffsetTo<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
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+ case 3: return_trace (c->dispatch (u.format3, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ void collect_variation_indices (hb_set_t *layout_variation_indices) const
+ {
+ switch (u.format) {
+ case 1:
+ case 2:
+ return;
+ case 3:
+ u.format3.collect_variation_indices (layout_variation_indices);
+ return;
+ default: return;
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.format.sanitize (c)) return_trace (false);
+ switch (u.format) {
+ case 1: return_trace (u.format1.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+ case 3: return_trace (u.format3.sanitize (c));
+ default:return_trace (true);
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ CaretValueFormat1 format1;
+ CaretValueFormat2 format2;
+ CaretValueFormat3 format3;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format);
+};
+
+struct LigGlyph
+{
+ unsigned get_lig_carets (hb_font_t *font,
+ hb_direction_t direction,
+ hb_codepoint_t glyph_id,
+ const VariationStore &var_store,
+ unsigned start_offset,
+ unsigned *caret_count /* IN/OUT */,
+ hb_position_t *caret_array /* OUT */) const
+ {
+ if (caret_count)
+ {
+ + carets.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 OffsetTo<CaretValue>& offset : carets.iter ())
+ (this+offset).collect_variation_indices (c->layout_variation_indices);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (carets.sanitize (c, this));
+ }
+
+ protected:
+ OffsetArrayOf<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 ();
+ 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 (c->serializer, out)
+ .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:
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of LigCaretList table */
+ OffsetArrayOf<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; }
+
+ 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 LOffsetTo<Coverage>& offset : coverage.iter ())
+ {
+ auto *o = out->coverage.serialize_append (c->serializer);
+ if (unlikely (!o))
+ {
+ ret = false;
+ break;
+ }
+
+ //not using o->serialize_subset (c, offset, this, out) here because
+ //OTS doesn't allow null offset.
+ //See issue: https://github.com/khaledhosny/ots/issues/172
+ c->serializer->push ();
+ c->dispatch (this+offset);
+ c->serializer->add_link (*o, c->serializer->pop_pack ());
+ }
+
+ return_trace (ret && out->coverage.len);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ ArrayOf<LOffsetTo<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;
+ }
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ switch (u.format) {
+ case 1: return_trace (u.format1.subset (c));
+ default:return_trace (false);
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.format.sanitize (c)) return_trace (false);
+ switch (u.format) {
+ case 1: return_trace (u.format1.sanitize (c));
+ default:return_trace (true);
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ MarkGlyphSetsFormat1 format1;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format);
+};
+
+
+/*
+ * GDEF -- Glyph Definition
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gdef
+ */
+
+
+struct GDEF
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_GDEF;
+
+ enum GlyphClasses {
+ UnclassifiedGlyph = 0,
+ BaseGlyph = 1,
+ LigatureGlyph = 2,
+ MarkGlyph = 3,
+ ComponentGlyph = 4
+ };
+
+ bool has_data () const { return version.to_int (); }
+ bool has_glyph_classes () const { return glyphClassDef != 0; }
+ unsigned int get_glyph_class (hb_codepoint_t glyph) const
+ { return (this+glyphClassDef).get_class (glyph); }
+ void get_glyphs_in_class (unsigned int klass, hb_set_t *glyphs) const
+ { (this+glyphClassDef).collect_class (glyphs, klass); }
+
+ bool has_mark_attachment_types () const { return markAttachClassDef != 0; }
+ unsigned int get_mark_attachment_type (hb_codepoint_t glyph) const
+ { return (this+markAttachClassDef).get_class (glyph); }
+
+ bool has_attach_points () const { return attachList != 0; }
+ 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 (this+attachList).get_attach_points (glyph_id, start_offset, point_count, point_array); }
+
+ bool has_lig_carets () const { return ligCaretList != 0; }
+ 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 (this+ligCaretList).get_lig_carets (font,
+ direction, glyph_id, get_var_store(),
+ start_offset, caret_count, caret_array); }
+
+ bool has_mark_sets () const { return version.to_int () >= 0x00010002u && markGlyphSetsDef != 0; }
+ bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
+ { return version.to_int () >= 0x00010002u && (this+markGlyphSetsDef).covers (set_index, glyph_id); }
+
+ bool has_var_store () const { return version.to_int () >= 0x00010003u && varStore != 0; }
+ const VariationStore &get_var_store () const
+ { return version.to_int () >= 0x00010003u ? this+varStore : Null (VariationStore); }
+
+ /* 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 0;
+ 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
+ {
+ void init (hb_face_t *face)
+ {
+ this->table = hb_sanitize_context_t ().reference_table<GDEF> (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 ();
+ }
+ }
+
+ void fini () { this->table.destroy (); }
+
+ hb_blob_ptr_t<GDEF> table;
+ };
+
+ unsigned int get_size () const
+ {
+ return min_size +
+ (version.to_int () >= 0x00010002u ? markGlyphSetsDef.static_size : 0) +
+ (version.to_int () >= 0x00010003u ? varStore.static_size : 0);
+ }
+
+ void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+ { (this+ligCaretList).collect_variation_indices (c); }
+
+ void remap_layout_variation_indices (const hb_set_t *layout_variation_indices,
+ hb_map_t *layout_variation_idx_map /* OUT */) const
+ {
+ if (version.to_int () < 0x00010003u || !varStore) return;
+ if (layout_variation_indices->is_empty ()) return;
+
+ unsigned new_major = 0, new_minor = 0;
+ unsigned last_major = (layout_variation_indices->get_min ()) >> 16;
+ for (unsigned idx : layout_variation_indices->iter ())
+ {
+ uint16_t major = idx >> 16;
+ if (major >= (this+varStore).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_map->set (idx, new_idx);
+ ++new_minor;
+ last_major = major;
+ }
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->serializer->embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this);
+ bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
+ bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
+ bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this);
+
+ bool subset_markglyphsetsdef = true;
+ if (version.to_int () >= 0x00010002u)
+ {
+ subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
+ if (!subset_markglyphsetsdef &&
+ version.to_int () == 0x00010002u)
+ out->version.minor = 0;
+ }
+
+ bool subset_varstore = true;
+ if (version.to_int () >= 0x00010003u)
+ {
+ subset_varstore = out->varStore.serialize_subset (c, varStore, this);
+ if (!subset_varstore && version.to_int () == 0x00010003u)
+ out->version.minor = 2;
+ }
+
+ return_trace (subset_glyphclassdef || subset_attachlist ||
+ subset_ligcaretlist || subset_markattachclassdef ||
+ (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) ||
+ (out->version.to_int () >= 0x00010003u && subset_varstore));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (version.sanitize (c) &&
+ likely (version.major == 1) &&
+ glyphClassDef.sanitize (c, this) &&
+ attachList.sanitize (c, this) &&
+ ligCaretList.sanitize (c, this) &&
+ markAttachClassDef.sanitize (c, this) &&
+ (version.to_int () < 0x00010002u || markGlyphSetsDef.sanitize (c, this)) &&
+ (version.to_int () < 0x00010003u || varStore.sanitize (c, this)));
+ }
+
+ protected:
+ FixedVersion<>version; /* Version of the GDEF table--currently
+ * 0x00010003u */
+ OffsetTo<ClassDef>
+ glyphClassDef; /* Offset to class definition table
+ * for glyph type--from beginning of
+ * GDEF header (may be Null) */
+ OffsetTo<AttachList>
+ attachList; /* Offset to list of glyphs with
+ * attachment points--from beginning
+ * of GDEF header (may be Null) */
+ OffsetTo<LigCaretList>
+ ligCaretList; /* Offset to list of positioning points
+ * for ligature carets--from beginning
+ * of GDEF header (may be Null) */
+ OffsetTo<ClassDef>
+ markAttachClassDef; /* Offset to class definition table for
+ * mark attachment type--from beginning
+ * of GDEF header (may be Null) */
+ OffsetTo<MarkGlyphSets>
+ markGlyphSetsDef; /* Offset to the table of mark set
+ * definitions--from beginning of GDEF
+ * header (may be NULL). Introduced
+ * in version 0x00010002. */
+ LOffsetTo<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 (12);
+};
+
+struct GDEF_accelerator_t : GDEF::accelerator_t {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh
new file mode 100644
index 0000000000..2217d298fb
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -0,0 +1,2740 @@
+/*
+ * 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 "hb-ot-layout-gsubgpos.hh"
+
+
+namespace OT {
+
+struct MarkArray;
+static 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 */);
+
+/* 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. */
+
+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,
+};
+
+
+/* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
+
+typedef HBUINT16 Value;
+
+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) */
+ OffsetTo<Device> xPlaDevice; /* Offset to Device table for
+ * horizontal placement--measured from
+ * beginning of PosTable (may be NULL) */
+ OffsetTo<Device> yPlaDevice; /* Offset to Device table for vertical
+ * placement--measured from beginning
+ * of PosTable (may be NULL) */
+ OffsetTo<Device> xAdvDevice; /* Offset to Device table for
+ * horizontal advance--measured from
+ * beginning of PosTable (may be NULL) */
+ OffsetTo<Device> yAdvDevice; /* Offset to Device table for vertical
+ * advance--measured from beginning of
+ * PosTable (may be NULL) */
+#endif
+
+ unsigned int get_len () const { return hb_popcount ((unsigned int) *this); }
+ unsigned int get_size () const { return get_len () * Value::static_size; }
+
+ bool apply_value (hb_ot_apply_context_t *c,
+ const void *base,
+ const Value *values,
+ hb_glyph_position_t &glyph_pos) const
+ {
+ bool ret = false;
+ unsigned int format = *this;
+ if (!format) return ret;
+
+ hb_font_t *font = c->font;
+ bool horizontal = HB_DIRECTION_IS_HORIZONTAL (c->direction);
+
+ 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;
+
+ /* pixel -> fractional pixel */
+ if (format & xPlaDevice) {
+ if (use_x_device) glyph_pos.x_offset += (base + get_device (values, &ret)).get_x_delta (font, store);
+ values++;
+ }
+ if (format & yPlaDevice) {
+ if (use_y_device) glyph_pos.y_offset += (base + get_device (values, &ret)).get_y_delta (font, store);
+ values++;
+ }
+ if (format & xAdvDevice) {
+ if (horizontal && use_x_device) glyph_pos.x_advance += (base + get_device (values, &ret)).get_x_delta (font, store);
+ 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 -= (base + get_device (values, &ret)).get_y_delta (font, store);
+ values++;
+ }
+ return ret;
+ }
+
+ void serialize_copy (hb_serialize_context_t *c, const void *base,
+ const Value *values, const hb_map_t *layout_variation_idx_map) const
+ {
+ unsigned int format = *this;
+ if (!format) return;
+
+ if (format & xPlacement) c->copy (*values++);
+ if (format & yPlacement) c->copy (*values++);
+ if (format & xAdvance) c->copy (*values++);
+ if (format & yAdvance) c->copy (*values++);
+
+ if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
+ if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
+ if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
+ if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
+ }
+
+ void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+ const void *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->layout_variation_indices);
+ i++;
+ }
+
+ if (format & ValueFormat::yPlaDevice)
+ {
+ (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+ i++;
+ }
+
+ if (format & ValueFormat::xAdvDevice)
+ {
+
+ (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+ i++;
+ }
+
+ if (format & ValueFormat::yAdvDevice)
+ {
+
+ (base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
+ i++;
+ }
+ }
+
+ private:
+ bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
+ {
+ unsigned int format = *this;
+
+ if (format & xPlacement) values++;
+ if (format & yPlacement) values++;
+ if (format & xAdvance) values++;
+ if (format & yAdvance) values++;
+
+ if ((format & xPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
+ if ((format & yPlaDevice) && !get_device (values++).sanitize (c, base)) return false;
+ if ((format & xAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
+ if ((format & yAdvDevice) && !get_device (values++).sanitize (c, base)) return false;
+
+ return true;
+ }
+
+ static inline OffsetTo<Device>& get_device (Value* value)
+ {
+ return *static_cast<OffsetTo<Device> *> (value);
+ }
+ static inline const OffsetTo<Device>& get_device (const Value* value, bool *worked=nullptr)
+ {
+ if (worked) *worked |= bool (*value);
+ return *static_cast<const OffsetTo<Device> *> (value);
+ }
+
+ bool copy_device (hb_serialize_context_t *c, const void *base,
+ const Value *src_value, const hb_map_t *layout_variation_idx_map) const
+ {
+ 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_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 void *base, const Value *values) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
+ }
+
+ bool sanitize_values (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count) const
+ {
+ TRACE_SANITIZE (this);
+ unsigned int len = get_len ();
+
+ if (!c->check_range (values, count, get_size ())) return_trace (false);
+
+ 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 += len;
+ }
+
+ return_trace (true);
+ }
+
+ /* Just sanitize referenced Device tables. Doesn't check the values themselves. */
+ bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, const void *base, const Value *values, unsigned int count, unsigned int stride) const
+ {
+ TRACE_SANITIZE (this);
+
+ if (!has_device ()) return_trace (true);
+
+ for (unsigned int i = 0; i < count; i++) {
+ if (!sanitize_value_devices (c, base, values))
+ return_trace (false);
+ values += stride;
+ }
+
+ return_trace (true);
+ }
+};
+
+template<typename Iterator>
+static void SinglePos_serialize (hb_serialize_context_t *c,
+ const void *src,
+ Iterator it,
+ ValueFormat valFormat,
+ const hb_map_t *layout_variation_idx_map);
+
+
+struct AnchorFormat1
+{
+ 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);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ AnchorFormat1* copy (hb_serialize_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+ return_trace (c->embed<AnchorFormat1> (this));
+ }
+
+ 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);
+};
+
+struct AnchorFormat2
+{
+ 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);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ AnchorFormat2* copy (hb_serialize_context_t *c) const
+ {
+ TRACE_SERIALIZE (this);
+ return_trace (c->embed<AnchorFormat2> (this));
+ }
+
+ 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);
+};
+
+struct AnchorFormat3
+{
+ 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)
+ *x += (this+xDeviceTable).get_x_delta (font, c->var_store);
+ if (font->y_ppem || font->num_coords)
+ *y += (this+yDeviceTable).get_y_delta (font, c->var_store);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
+ }
+
+ AnchorFormat3* copy (hb_serialize_context_t *c,
+ const hb_map_t *layout_variation_idx_map) const
+ {
+ TRACE_SERIALIZE (this);
+ if (!layout_variation_idx_map) return_trace (nullptr);
+
+ auto *out = c->embed<AnchorFormat3> (this);
+ if (unlikely (!out)) return_trace (nullptr);
+
+ out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
+ out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
+ return_trace (out);
+ }
+
+ void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
+ {
+ (this+xDeviceTable).collect_variation_indices (c->layout_variation_indices);
+ (this+yDeviceTable).collect_variation_indices (c->layout_variation_indices);
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 3 */
+ FWORD xCoordinate; /* Horizontal value--in design units */
+ FWORD yCoordinate; /* Vertical value--in design units */
+ OffsetTo<Device>
+ xDeviceTable; /* Offset to Device table for X
+ * coordinate-- from beginning of
+ * Anchor table (may be NULL) */
+ OffsetTo<Device>
+ yDeviceTable; /* Offset to Device table for Y
+ * coordinate-- from beginning of
+ * Anchor table (may be NULL) */
+ public:
+ DEFINE_SIZE_STATIC (10);
+};
+
+struct Anchor
+{
+ 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 sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!u.format.sanitize (c)) return_trace (false);
+ switch (u.format) {
+ case 1: return_trace (u.format1.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+ case 3: return_trace (u.format3.sanitize (c));
+ default:return_trace (true);
+ }
+ }
+
+ Anchor* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const
+ {
+ TRACE_SERIALIZE (this);
+ switch (u.format) {
+ case 1: return_trace (reinterpret_cast<Anchor *> (u.format1.copy (c)));
+ case 2: return_trace (reinterpret_cast<Anchor *> (u.format2.copy (c)));
+ case 3: return_trace (reinterpret_cast<Anchor *> (u.format3.copy (c, layout_variation_idx_map)));
+ default:return_trace (nullptr);
+ }
+ }
+
+ 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;
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ AnchorFormat1 format1;
+ AnchorFormat2 format2;
+ AnchorFormat3 format3;
+ } u;
+ public:
+ DEFINE_SIZE_UNION (2, format);
+};
+
+
+struct AnchorMatrix
+{
+ const Anchor& get_anchor (unsigned int row, unsigned int col,
+ unsigned int cols, bool *found) const
+ {
+ *found = false;
+ if (unlikely (row >= rows || col >= cols)) return Null (Anchor);
+ *found = !matrixZ[row * cols + col].is_null ();
+ return this+matrixZ[row * cols + col];
+ }
+
+ 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 serialize (hb_serialize_context_t *c,
+ unsigned num_rows,
+ AnchorMatrix const *offset_matrix,
+ const hb_map_t *layout_variation_idx_map,
+ Iterator index_iter)
+ {
+ TRACE_SERIALIZE (this);
+ if (!index_iter) return_trace (false);
+ if (unlikely (!c->extend_min ((*this)))) return_trace (false);
+
+ this->rows = num_rows;
+ for (const unsigned i : index_iter)
+ {
+ auto *offset = c->embed (offset_matrix->matrixZ[i]);
+ if (!offset) return_trace (false);
+ offset->serialize_copy (c, offset_matrix->matrixZ[i],
+ offset_matrix, c->to_bias (this),
+ hb_serialize_context_t::Head,
+ layout_variation_idx_map);
+ }
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, unsigned int cols) const
+ {
+ TRACE_SANITIZE (this);
+ if (!c->check_struct (this)) return_trace (false);
+ if (unlikely (hb_unsigned_mul_overflows (rows, cols))) return_trace (false);
+ unsigned int count = rows * cols;
+ if (!c->check_array (matrixZ.arrayZ, count)) return_trace (false);
+ for (unsigned int i = 0; i < count; i++)
+ if (!matrixZ[i].sanitize (c, this)) return_trace (false);
+ return_trace (true);
+ }
+
+ HBUINT16 rows; /* Number of rows */
+ UnsizedArrayOf<OffsetTo<Anchor>>
+ matrixZ; /* Matrix of offsets to Anchor tables--
+ * from beginning of AnchorMatrix table */
+ public:
+ DEFINE_SIZE_ARRAY (2, matrixZ);
+};
+
+
+struct MarkRecord
+{
+ friend struct MarkArray;
+
+ 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));
+ }
+
+ MarkRecord *copy (hb_serialize_context_t *c,
+ const void *src_base,
+ unsigned dst_bias,
+ const hb_map_t *klass_mapping,
+ const hb_map_t *layout_variation_idx_map) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->embed (this);
+ if (unlikely (!out)) return_trace (nullptr);
+
+ out->klass = klass_mapping->get (klass);
+ out->markAnchor.serialize_copy (c, markAnchor, src_base, dst_bias, hb_serialize_context_t::Head, layout_variation_idx_map);
+ return_trace (out);
+ }
+
+ void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+ const void *src_base) const
+ {
+ (src_base+markAnchor).collect_variation_indices (c);
+ }
+
+ protected:
+ HBUINT16 klass; /* Class defined for this mark */
+ OffsetTo<Anchor>
+ markAnchor; /* Offset to Anchor table--from
+ * beginning of MarkArray table */
+ public:
+ DEFINE_SIZE_STATIC (4);
+};
+
+struct MarkArray : ArrayOf<MarkRecord> /* Array of MarkRecords--in Coverage order */
+{
+ 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 = ArrayOf<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 (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);
+ 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);
+
+ 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;
+
+ buffer->idx++;
+ return_trace (true);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_source_of (Iterator, MarkRecord))>
+ bool serialize (hb_serialize_context_t *c,
+ const hb_map_t *klass_mapping,
+ const hb_map_t *layout_variation_idx_map,
+ const void *base,
+ Iterator it)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ if (unlikely (!c->check_assign (len, it.len ()))) return_trace (false);
+ c->copy_all (it, base, c->to_bias (this), klass_mapping, layout_variation_idx_map);
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (ArrayOf<MarkRecord>::sanitize (c, this));
+ }
+};
+
+
+/* Lookups */
+
+struct SinglePosFormat1
+{
+ 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_iter (this+coverage)
+ | hb_filter (c->glyph_set)
+ ;
+
+ if (!it) 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; }
+
+ 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);
+
+ valueFormat.apply_value (c, this, values, buffer->cur_pos());
+
+ buffer->idx++;
+ return_trace (true);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ void serialize (hb_serialize_context_t *c,
+ const void *src,
+ Iterator it,
+ ValueFormat valFormat,
+ const hb_map_t *layout_variation_idx_map)
+ {
+ auto out = c->extend_min (*this);
+ if (unlikely (!out)) return;
+ if (unlikely (!c->check_assign (valueFormat, valFormat))) return;
+
+ + it
+ | hb_map (hb_second)
+ | hb_apply ([&] (hb_array_t<const Value> _)
+ { valFormat.serialize_copy (c, src, &_, layout_variation_idx_map); })
+ ;
+
+ auto glyphs =
+ + it
+ | hb_map_retains_sorting (hb_first)
+ ;
+
+ coverage.serialize (c, this).serialize (c, glyphs);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ 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)
+ | hb_zip (hb_repeat (values.as_array (valueFormat.get_len ())))
+ ;
+
+ bool ret = bool (it);
+ SinglePos_serialize (c->serializer, this, it, valueFormat, c->plan->layout_variation_idx_map);
+ return_trace (ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ coverage.sanitize (c, this) &&
+ valueFormat.sanitize_value (c, this, values));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<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);
+};
+
+struct SinglePosFormat2
+{
+ 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; }
+
+ 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 (likely (index >= valueCount)) return_trace (false);
+
+ valueFormat.apply_value (c, this,
+ &values[index * valueFormat.get_len ()],
+ buffer->cur_pos());
+
+ buffer->idx++;
+ return_trace (true);
+ }
+
+ template<typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ void serialize (hb_serialize_context_t *c,
+ const void *src,
+ Iterator it,
+ ValueFormat valFormat,
+ const hb_map_t *layout_variation_idx_map)
+ {
+ auto out = c->extend_min (*this);
+ if (unlikely (!out)) return;
+ if (unlikely (!c->check_assign (valueFormat, valFormat))) return;
+ if (unlikely (!c->check_assign (valueCount, it.len ()))) return;
+
+ + it
+ | hb_map (hb_second)
+ | hb_apply ([&] (hb_array_t<const Value> _)
+ { valFormat.serialize_copy (c, src, &_, layout_variation_idx_map); })
+ ;
+
+ auto glyphs =
+ + it
+ | hb_map_retains_sorting (hb_first)
+ ;
+
+ coverage.serialize (c, this).serialize (c, glyphs);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ 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));
+ })
+ ;
+
+ bool ret = bool (it);
+ SinglePos_serialize (c->serializer, this, it, valueFormat, c->plan->layout_variation_idx_map);
+ return_trace (ret);
+ }
+
+ 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));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 2 */
+ OffsetTo<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);
+};
+
+struct SinglePos
+{
+ 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,
+ hb_requires (hb_is_iterator (Iterator))>
+ void serialize (hb_serialize_context_t *c,
+ const void *src,
+ Iterator glyph_val_iter_pairs,
+ ValueFormat valFormat,
+ const hb_map_t *layout_variation_idx_map)
+ {
+ if (unlikely (!c->extend_min (u.format))) return;
+ unsigned format = 2;
+
+ 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, valFormat, layout_variation_idx_map);
+ return;
+ case 2: u.format2.serialize (c, src, glyph_val_iter_pairs, valFormat, layout_variation_idx_map);
+ return;
+ default:return;
+ }
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ SinglePosFormat1 format1;
+ SinglePosFormat2 format2;
+ } u;
+};
+
+template<typename Iterator>
+static void
+SinglePos_serialize (hb_serialize_context_t *c,
+ const void *src,
+ Iterator it,
+ ValueFormat valFormat,
+ const hb_map_t *layout_variation_idx_map)
+{ c->start_embed<SinglePos> ()->serialize (c, src, it, valFormat, layout_variation_idx_map); }
+
+
+struct PairValueRecord
+{
+ friend struct PairSet;
+
+ int cmp (hb_codepoint_t k) const
+ { return secondGlyph.cmp (k); }
+
+ struct serialize_closure_t
+ {
+ const void *base;
+ const ValueFormat *valueFormats;
+ unsigned len1; /* valueFormats[0].get_len() */
+ const hb_map_t *glyph_map;
+ const hb_map_t *layout_variation_idx_map;
+ };
+
+ bool serialize (hb_serialize_context_t *c,
+ serialize_closure_t *closure) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->start_embed (*this);
+ if (unlikely (!c->extend_min (out))) return_trace (false);
+
+ out->secondGlyph = (*closure->glyph_map)[secondGlyph];
+
+ closure->valueFormats[0].serialize_copy (c, closure->base, &values[0], closure->layout_variation_idx_map);
+ closure->valueFormats[1].serialize_copy (c, closure->base, &values[closure->len1], closure->layout_variation_idx_map);
+
+ return_trace (true);
+ }
+
+ void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+ const ValueFormat *valueFormats,
+ const void *base) const
+ {
+ unsigned record1_len = valueFormats[0].get_len ();
+ unsigned record2_len = valueFormats[1].get_len ();
+ const hb_array_t<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));
+ }
+
+ protected:
+ 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 (2, values);
+};
+
+struct PairSet
+{
+ friend struct PairPosFormat1;
+
+ bool intersects (const hb_set_t *glyphs,
+ const ValueFormat *valueFormats) const
+ {
+ unsigned int len1 = valueFormats[0].get_len ();
+ unsigned int len2 = valueFormats[1].get_len ();
+ unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+ 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 int len1 = valueFormats[0].get_len ();
+ unsigned int len2 = valueFormats[1].get_len ();
+ unsigned int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+ 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 len1 = valueFormats[0].get_len ();
+ unsigned len2 = valueFormats[1].get_len ();
+ unsigned record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+ 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 int record_size = HBUINT16::static_size * (1 + len1 + len2);
+
+ const PairValueRecord *record = hb_bsearch (buffer->info[pos].codepoint,
+ &firstPairValueRecord,
+ len,
+ record_size);
+ if (record)
+ {
+ /* Note the intentional use of "|" instead of short-circuit "||". */
+ if (valueFormats[0].apply_value (c, this, &record->values[0], buffer->cur_pos()) |
+ valueFormats[1].apply_value (c, this, &record->values[len1], buffer->pos[pos]))
+ buffer->unsafe_to_break (buffer->idx, pos + 1);
+ if (len2)
+ pos++;
+ buffer->idx = pos;
+ return_trace (true);
+ }
+ return_trace (false);
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const ValueFormat valueFormats[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 ();
+ 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 = HBUINT16::static_size + Value::static_size * (len1 + len2);
+
+ PairValueRecord::serialize_closure_t closure =
+ {
+ this,
+ valueFormats,
+ len1,
+ &glyph_map,
+ c->plan->layout_variation_idx_map
+ };
+
+ const PairValueRecord *record = &firstPairValueRecord;
+ unsigned count = len, num = 0;
+ for (unsigned i = 0; i < count; i++)
+ {
+ if (glyphset.has (record->secondGlyph)
+ && record->serialize (c->serializer, &closure)) num++;
+ record = &StructAtOffset<const PairValueRecord> (record, record_size);
+ }
+
+ out->len = num;
+ if (!num) c->serializer->revert (snap);
+ return_trace (num);
+ }
+
+ struct sanitize_closure_t
+ {
+ const ValueFormat *valueFormats;
+ unsigned int len1; /* valueFormats[0].get_len() */
+ unsigned int stride; /* 1 + len1 + len2 */
+ };
+
+ bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) const
+ {
+ TRACE_SANITIZE (this);
+ if (!(c->check_struct (this)
+ && c->check_range (&firstPairValueRecord,
+ len,
+ HBUINT16::static_size,
+ closure->stride))) return_trace (false);
+
+ unsigned int count = len;
+ const PairValueRecord *record = &firstPairValueRecord;
+ return_trace (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));
+ }
+
+ protected:
+ HBUINT16 len; /* Number of PairValueRecords */
+ PairValueRecord firstPairValueRecord;
+ /* Array of PairValueRecords--ordered
+ * by GlyphID of the second glyph */
+ public:
+ DEFINE_SIZE_MIN (2);
+};
+
+struct PairPosFormat1
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ return
+ + hb_zip (this+coverage, pairSet)
+ | hb_filter (*glyphs, hb_first)
+ | hb_map (hb_second)
+ | hb_map ([glyphs, this] (const 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 (buffer->idx, 1);
+ if (!skippy_iter.next ()) 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 ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ out->format = format;
+ out->valueFormat[0] = valueFormat[0];
+ out->valueFormat[1] = valueFormat[1];
+
+ 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 OffsetTo<PairSet>& _)
+ {
+ auto *o = out->pairSet.serialize_append (c->serializer);
+ if (unlikely (!o)) return false;
+ auto snap = c->serializer->snapshot ();
+ bool ret = o->serialize_subset (c, _, this, 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 (c->serializer, out)
+ .serialize (c->serializer, new_coverage.iter ());
+
+ return_trace (bool (new_coverage));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+
+ if (!c->check_struct (this)) return_trace (false);
+
+ unsigned int len1 = valueFormat[0].get_len ();
+ unsigned int len2 = valueFormat[1].get_len ();
+ PairSet::sanitize_closure_t closure =
+ {
+ valueFormat,
+ len1,
+ 1 + len1 + len2
+ };
+
+ return_trace (coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ 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) */
+ OffsetArrayOf<PairSet>
+ pairSet; /* Array of PairSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (10, pairSet);
+};
+
+struct PairPosFormat2
+{
+ 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 ((!valueFormat1.has_device ()) && (!valueFormat2.has_device ())) return;
+
+ hb_set_t class1_set, class2_set;
+ for (const unsigned cp : c->glyph_set->iter ())
+ {
+ unsigned klass1 = (this+classDef1).get (cp);
+ unsigned klass2 = (this+classDef2).get (cp);
+ class1_set.add (klass1);
+ class2_set.add (klass2);
+ }
+
+ if (class1_set.is_empty () || class2_set.is_empty ()) 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 (buffer->idx, 1);
+ if (!skippy_iter.next ()) return_trace (false);
+
+ unsigned int len1 = valueFormat1.get_len ();
+ unsigned int len2 = valueFormat2.get_len ();
+ unsigned int record_len = len1 + len2;
+
+ unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
+ unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
+ if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return_trace (false);
+
+ const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
+ /* Note the intentional use of "|" instead of short-circuit "||". */
+ if (valueFormat1.apply_value (c, this, v, buffer->cur_pos()) |
+ valueFormat2.apply_value (c, this, v + len1, buffer->pos[skippy_iter.idx]))
+ buffer->unsafe_to_break (buffer->idx, skippy_iter.idx + 1);
+
+ buffer->idx = skippy_iter.idx;
+ if (len2)
+ buffer->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;
+ out->valueFormat1 = valueFormat1;
+ out->valueFormat2 = valueFormat2;
+
+ hb_map_t klass1_map;
+ out->classDef1.serialize_subset (c, classDef1, this, &klass1_map);
+ out->class1Count = klass1_map.get_population ();
+
+ hb_map_t klass2_map;
+ out->classDef2.serialize_subset (c, classDef2, this, &klass2_map);
+ out->class2Count = klass2_map.get_population ();
+
+ unsigned len1 = valueFormat1.get_len ();
+ unsigned len2 = valueFormat2.get_len ();
+
+ + hb_range ((unsigned) class1Count)
+ | hb_filter (klass1_map)
+ | hb_apply ([&] (const unsigned class1_idx)
+ {
+ + hb_range ((unsigned) class2Count)
+ | hb_filter (klass2_map)
+ | hb_apply ([&] (const unsigned class2_idx)
+ {
+ unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
+ valueFormat1.serialize_copy (c->serializer, this, &values[idx], c->plan->layout_variation_idx_map);
+ valueFormat2.serialize_copy (c->serializer, this, &values[idx + len1], c->plan->layout_variation_idx_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 (c->serializer, out).serialize (c->serializer, it);
+ return_trace (out->class1Count && out->class2Count && bool (it));
+ }
+
+ 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 = len1 + len2;
+ unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
+ unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
+ return_trace (c->check_range ((const void *) values,
+ count,
+ record_size) &&
+ valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
+ valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 2 */
+ 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) */
+ OffsetTo<ClassDef>
+ classDef1; /* Offset to ClassDef table--from
+ * beginning of PairPos subtable--for
+ * the first glyph of the pair */
+ 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 (16, values);
+};
+
+struct PairPos
+{
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ PairPosFormat1 format1;
+ PairPosFormat2 format2;
+ } u;
+};
+
+
+struct EntryExitRecord
+{
+ friend struct CursivePosFormat1;
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
+ }
+
+ void collect_variation_indices (hb_collect_variation_indices_context_t *c,
+ const void *src_base) const
+ {
+ (src_base+entryAnchor).collect_variation_indices (c);
+ (src_base+exitAnchor).collect_variation_indices (c);
+ }
+
+ EntryExitRecord* copy (hb_serialize_context_t *c,
+ const void *src_base,
+ const void *dst_base,
+ const hb_map_t *layout_variation_idx_map) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->embed (this);
+ if (unlikely (!out)) return_trace (nullptr);
+
+ out->entryAnchor.serialize_copy (c, entryAnchor, src_base, c->to_bias (dst_base), hb_serialize_context_t::Head, layout_variation_idx_map);
+ out->exitAnchor.serialize_copy (c, exitAnchor, src_base, c->to_bias (dst_base), hb_serialize_context_t::Head, layout_variation_idx_map);
+ return_trace (out);
+ }
+
+ protected:
+ OffsetTo<Anchor>
+ entryAnchor; /* Offset to EntryAnchor table--from
+ * beginning of CursivePos
+ * subtable--may be NULL */
+ OffsetTo<Anchor>
+ 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);
+
+struct CursivePosFormat1
+{
+ 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) return_trace (false);
+
+ hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
+ skippy_iter.reset (buffer->idx, 1);
+ if (!skippy_iter.prev ()) return_trace (false);
+
+ const EntryExitRecord &prev_record = entryExitRecord[(this+coverage).get_coverage (buffer->info[skippy_iter.idx].codepoint)];
+ if (!prev_record.exitAnchor) return_trace (false);
+
+ unsigned int i = skippy_iter.idx;
+ unsigned int j = buffer->idx;
+
+ buffer->unsafe_to_break (i, j);
+ 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 = entry_x - exit_x;
+ hb_position_t y_offset = 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, break them free.
+ * https://github.com/harfbuzz/harfbuzz/issues/2469
+ */
+ if (unlikely (pos[parent].attach_chain() == -pos[child].attach_chain()))
+ pos[parent].attach_chain() = 0;
+
+ buffer->idx++;
+ return_trace (true);
+ }
+
+ template <typename Iterator,
+ hb_requires (hb_is_iterator (Iterator))>
+ void serialize (hb_serialize_context_t *c,
+ Iterator it,
+ const void *src_base,
+ const hb_map_t *layout_variation_idx_map)
+ {
+ if (unlikely (!c->extend_min ((*this)))) return;
+ this->format = 1;
+ this->entryExitRecord.len = it.len ();
+
+ for (const EntryExitRecord& entry_record : + it
+ | hb_map (hb_second))
+ c->copy (entry_record, src_base, this, layout_variation_idx_map);
+
+ auto glyphs =
+ + it
+ | hb_map_retains_sorting (hb_first)
+ ;
+
+ coverage.serialize (c, this).serialize (c, glyphs);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ auto *out = c->serializer->start_embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ 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->serializer, it, this, c->plan->layout_variation_idx_map);
+ return_trace (ret);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of subtable */
+ ArrayOf<EntryExitRecord>
+ entryExitRecord; /* Array of EntryExit records--in
+ * Coverage Index order */
+ public:
+ DEFINE_SIZE_ARRAY (6, entryExitRecord);
+};
+
+struct CursivePos
+{
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ CursivePosFormat1 format1;
+ } u;
+};
+
+
+typedef AnchorMatrix BaseArray; /* base-major--
+ * in order of BaseCoverage Index--,
+ * mark-minor--
+ * ordered by class--zero-based. */
+
+static 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++;
+ }
+}
+
+struct MarkBasePosFormat1
+{
+ 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; }
+
+ 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.reset (buffer->idx, 1);
+ skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
+ do {
+ if (!skippy_iter.prev ()) return_trace (false);
+ /* 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 */
+ if (!_hb_glyph_info_multiplied (&buffer->info[skippy_iter.idx]) ||
+ 0 == _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) ||
+ (skippy_iter.idx == 0 ||
+ _hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx - 1]) ||
+ _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx]) !=
+ _hb_glyph_info_get_lig_id (&buffer->info[skippy_iter.idx - 1]) ||
+ _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx]) !=
+ _hb_glyph_info_get_lig_comp (&buffer->info[skippy_iter.idx - 1]) + 1
+ ))
+ break;
+ skippy_iter.reject ();
+ } while (true);
+
+ /* Checking that matched glyph is actually a base glyph by GDEF is too strong; disabled */
+ //if (!_hb_glyph_info_is_base_glyph (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+
+ unsigned int base_index = (this+baseCoverage).get_coverage (buffer->info[skippy_iter.idx].codepoint);
+ if (base_index == NOT_COVERED) return_trace (false);
+
+ return_trace ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ 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 (c->serializer, out)
+ .serialize (c->serializer, new_coverage.iter ()))
+ return_trace (false);
+
+ out->markArray.serialize (c->serializer, out)
+ .serialize (c->serializer, &klass_mapping, c->plan->layout_variation_idx_map, &(this+markArray), + mark_iter
+ | hb_map (hb_second));
+
+ 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 (c->serializer, out)
+ .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)
+ ;
+ }
+ out->baseArray.serialize (c->serializer, out)
+ .serialize (c->serializer, base_iter.len (), &(this+baseArray), c->plan->layout_variation_idx_map, base_indexes.iter ());
+
+ return_trace (true);
+ }
+
+ 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));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ markCoverage; /* Offset to MarkCoverage table--from
+ * beginning of MarkBasePos subtable */
+ OffsetTo<Coverage>
+ baseCoverage; /* Offset to BaseCoverage table--from
+ * beginning of MarkBasePos subtable */
+ HBUINT16 classCount; /* Number of classes defined for marks */
+ OffsetTo<MarkArray>
+ markArray; /* Offset to MarkArray table--from
+ * beginning of MarkBasePos subtable */
+ OffsetTo<BaseArray>
+ baseArray; /* Offset to BaseArray table--from
+ * beginning of MarkBasePos subtable */
+ public:
+ DEFINE_SIZE_STATIC (12);
+};
+
+struct MarkBasePos
+{
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ MarkBasePosFormat1 format1;
+ } u;
+};
+
+
+typedef AnchorMatrix LigatureAttach; /* component-major--
+ * in order of writing direction--,
+ * mark-minor--
+ * ordered by class--zero-based. */
+
+typedef OffsetListOf<LigatureAttach> LigatureArray;
+ /* Array of LigatureAttach
+ * tables ordered by
+ * LigatureCoverage Index */
+
+struct MarkLigPosFormat1
+{
+ 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.reset (buffer->idx, 1);
+ skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
+ if (!skippy_iter.prev ()) return_trace (false);
+
+ /* Checking that matched glyph is actually a ligature by GDEF is too strong; disabled */
+ //if (!_hb_glyph_info_is_ligature (&buffer->info[skippy_iter.idx])) { return_trace (false); }
+
+ unsigned int j = skippy_iter.idx;
+ unsigned int lig_index = (this+ligatureCoverage).get_coverage (buffer->info[j].codepoint);
+ if (lig_index == NOT_COVERED) 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)) 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[j]);
+ 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, j));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ // TODO(subset)
+ return_trace (false);
+ }
+
+ 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));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ markCoverage; /* Offset to Mark Coverage table--from
+ * beginning of MarkLigPos subtable */
+ OffsetTo<Coverage>
+ ligatureCoverage; /* Offset to Ligature Coverage
+ * table--from beginning of MarkLigPos
+ * subtable */
+ HBUINT16 classCount; /* Number of defined mark classes */
+ OffsetTo<MarkArray>
+ markArray; /* Offset to MarkArray table--from
+ * beginning of MarkLigPos subtable */
+ OffsetTo<LigatureArray>
+ ligatureArray; /* Offset to LigatureArray table--from
+ * beginning of MarkLigPos subtable */
+ public:
+ DEFINE_SIZE_STATIC (12);
+};
+
+struct MarkLigPos
+{
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ MarkLigPosFormat1 format1;
+ } u;
+};
+
+
+typedef AnchorMatrix Mark2Array; /* mark2-major--
+ * in order of Mark2Coverage Index--,
+ * mark1-minor--
+ * ordered by class--zero-based. */
+
+struct MarkMarkPosFormat1
+{
+ 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 (buffer->idx, 1);
+ skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
+ if (!skippy_iter.prev ()) return_trace (false);
+
+ if (!_hb_glyph_info_is_mark (&buffer->info[skippy_iter.idx])) { 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. */
+ return_trace (false);
+
+ good:
+ unsigned int mark2_index = (this+mark2Coverage).get_coverage (buffer->info[j].codepoint);
+ if (mark2_index == NOT_COVERED) 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 ();
+ 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 (c->serializer, out)
+ .serialize (c->serializer, new_coverage.iter ()))
+ return_trace (false);
+
+ out->mark1Array.serialize (c->serializer, out)
+ .serialize (c->serializer, &klass_mapping, c->plan->layout_variation_idx_map, &(this+mark1Array), + mark1_iter
+ | hb_map (hb_second));
+
+ 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 (c->serializer, out)
+ .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)
+ ;
+ }
+ out->mark2Array.serialize (c->serializer, out)
+ .serialize (c->serializer, mark2_iter.len (), &(this+mark2Array), c->plan->layout_variation_idx_map, mark2_indexes.iter ());
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ mark1Coverage.sanitize (c, this) &&
+ mark2Coverage.sanitize (c, this) &&
+ mark1Array.sanitize (c, this) &&
+ mark2Array.sanitize (c, this, (unsigned int) classCount));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ mark1Coverage; /* Offset to Combining Mark1 Coverage
+ * table--from beginning of MarkMarkPos
+ * subtable */
+ OffsetTo<Coverage>
+ mark2Coverage; /* Offset to Combining Mark2 Coverage
+ * table--from beginning of MarkMarkPos
+ * subtable */
+ HBUINT16 classCount; /* Number of defined mark classes */
+ OffsetTo<MarkArray>
+ mark1Array; /* Offset to Mark1Array table--from
+ * beginning of MarkMarkPos subtable */
+ OffsetTo<Mark2Array>
+ mark2Array; /* Offset to Mark2Array table--from
+ * beginning of MarkMarkPos subtable */
+ public:
+ DEFINE_SIZE_STATIC (12);
+};
+
+struct MarkMarkPos
+{
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ MarkMarkPosFormat1 format1;
+ } u;
+};
+
+
+struct ContextPos : Context {};
+
+struct ChainContextPos : ChainContext {};
+
+struct ExtensionPos : Extension<ExtensionPos>
+{
+ typedef struct PosLookupSubTable SubTable;
+};
+
+
+
+/*
+ * PosLookup
+ */
+
+
+struct PosLookupSubTable
+{
+ friend struct 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, hb_forward<Ts> (ds)...));
+ case Pair: return_trace (u.pair.dispatch (c, hb_forward<Ts> (ds)...));
+ case Cursive: return_trace (u.cursive.dispatch (c, hb_forward<Ts> (ds)...));
+ case MarkBase: return_trace (u.markBase.dispatch (c, hb_forward<Ts> (ds)...));
+ case MarkLig: return_trace (u.markLig.dispatch (c, hb_forward<Ts> (ds)...));
+ case MarkMark: return_trace (u.markMark.dispatch (c, hb_forward<Ts> (ds)...));
+ case Context: return_trace (u.context.dispatch (c, hb_forward<Ts> (ds)...));
+ case ChainContext: return_trace (u.chainContext.dispatch (c, hb_forward<Ts> (ds)...));
+ case Extension: return_trace (u.extension.dispatch (c, hb_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);
+};
+
+
+struct PosLookup : Lookup
+{
+ typedef struct PosLookupSubTable SubTable;
+
+ 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 ();
+ }
+ c->set_recurse_func (dispatch_closure_lookups_recurse_func);
+
+ 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);
+ }
+
+ static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
+
+ template <typename context_t>
+ static typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+
+ HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned this_index);
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ { return Lookup::dispatch<SubTable> (c, hb_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); }
+};
+
+/*
+ * 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;
+
+ 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, c->plan->gpos_lookups, c->plan->gpos_features);
+ return GSUBGPOS::subset<PosLookup> (&l);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ { return 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
+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;
+}
+static void
+propagate_attachment_offsets (hb_glyph_position_t *pos,
+ unsigned int len,
+ unsigned int i,
+ hb_direction_t direction)
+{
+ /* 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;
+
+ propagate_attachment_offsets (pos, len, j, direction);
+
+ assert (!!(type & ATTACH_TYPE_MARK) ^ !!(type & ATTACH_TYPE_CURSIVE));
+
+ if (type & 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 & 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_UNUSED, 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 int i = 0; i < len; i++)
+ propagate_attachment_offsets (pos, len, i, direction);
+}
+
+
+struct GPOS_accelerator_t : GPOS::accelerator_t {};
+
+
+/* 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);
+}
+
+/*static*/ inline hb_closure_lookups_context_t::return_t PosLookup::dispatch_closure_lookups_recurse_func (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);
+}
+
+/*static*/ bool PosLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
+{
+ const PosLookup &l = c->face->table.GPOS.get_relaxed ()->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 = l.dispatch (c);
+ c->set_lookup_index (saved_lookup_index);
+ c->set_lookup_props (saved_lookup_props);
+ return ret;
+}
+#endif
+
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh
new file mode 100644
index 0000000000..2f41d67819
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -0,0 +1,1627 @@
+/*
+ * 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 "hb-ot-layout-gsubgpos.hh"
+
+
+namespace OT {
+
+typedef hb_pair_t<hb_codepoint_t, hb_codepoint_t> hb_codepoint_pair_t;
+
+template<typename Iterator>
+static void SingleSubst_serialize (hb_serialize_context_t *c,
+ Iterator it);
+
+
+struct SingleSubstFormat1
+{
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ unsigned d = deltaGlyphID;
+ + hb_iter (this+coverage)
+ | hb_filter (*c->glyphs)
+ | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
+ | 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 d = deltaGlyphID;
+ + hb_iter (this+coverage)
+ | hb_map ([d] (hb_codepoint_t g) { return (g + d) & 0xFFFFu; })
+ | 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; }
+
+ 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);
+
+ /* According to the Adobe Annotated OpenType Suite, result is always
+ * limited to 16bit. */
+ glyph_id = (glyph_id + deltaGlyphID) & 0xFFFFu;
+ c->replace_glyph (glyph_id);
+
+ 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 (c, this).serialize (c, glyphs))) return_trace (false);
+ c->check_assign (deltaGlyphID, delta);
+ 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 delta = deltaGlyphID;
+
+ auto it =
+ + hb_iter (this+coverage)
+ | hb_filter (glyphset)
+ | hb_map_retains_sorting ([&] (hb_codepoint_t g) {
+ return hb_codepoint_pair_t (g,
+ (g + delta) & 0xFFFF); })
+ | 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);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ HBUINT16 deltaGlyphID; /* Add to original GlyphID to get
+ * substitute GlyphID, modulo 0x10000 */
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+
+struct SingleSubstFormat2
+{
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_zip (this+coverage, substitute)
+ | hb_filter (*c->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;
+ + 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; }
+
+ 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);
+
+ c->replace_glyph (substitute[index]);
+
+ 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 (c, this).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 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);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (coverage.sanitize (c, this) && substitute.sanitize (c));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 2 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ ArrayOf<HBGlyphID>
+ substitute; /* Array of substitute
+ * GlyphIDs--ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, substitute);
+};
+
+struct SingleSubst
+{
+
+ 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;
+ auto get_delta = [=] (hb_codepoint_pair_t _)
+ { return (unsigned) (_.second - _.first) & 0xFFFF; };
+ delta = get_delta (*glyphs);
+ if (!hb_all (++(+glyphs), delta, get_delta)) format = 2;
+ }
+ 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));
+ default:return_trace (false);
+ }
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ SingleSubstFormat1 format1;
+ SingleSubstFormat2 format2;
+ } u;
+};
+
+template<typename Iterator>
+static void
+SingleSubst_serialize (hb_serialize_context_t *c,
+ Iterator it)
+{ c->start_embed<SingleSubst> ()->serialize (c, it); }
+
+struct Sequence
+{
+ 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))
+ {
+ c->replace_glyph (substitute.arrayZ[0]);
+ return_trace (true);
+ }
+ /* Spec disallows this, but Uniscribe allows it.
+ * https://github.com/harfbuzz/harfbuzz/issues/253 */
+ else if (unlikely (count == 0))
+ {
+ c->buffer->delete_glyph ();
+ return_trace (true);
+ }
+
+ unsigned int klass = _hb_glyph_info_is_ligature (&c->buffer->cur()) ?
+ HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
+
+ for (unsigned int i = 0; i < count; i++) {
+ _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 ();
+
+ 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 ();
+ 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));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (substitute.sanitize (c));
+ }
+
+ protected:
+ ArrayOf<HBGlyphID>
+ substitute; /* String of GlyphIDs to substitute */
+ public:
+ DEFINE_SIZE_ARRAY (2, substitute);
+};
+
+struct MultipleSubstFormat1
+{
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_zip (this+coverage, sequence)
+ | hb_filter (*c->glyphs, hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Sequence &_) { _.closure (c); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+ + hb_zip (this+coverage, sequence)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Sequence &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ return_trace ((this+sequence[index]).apply (c));
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID> glyphs,
+ hb_array_t<const unsigned int> substitute_len_list,
+ hb_array_t<const HBGlyphID> substitute_glyphs_list)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!c->extend_min (*this))) return_trace (false);
+ if (unlikely (!sequence.serialize (c, glyphs.length))) return_trace (false);
+ for (unsigned int i = 0; i < glyphs.length; i++)
+ {
+ unsigned int substitute_len = substitute_len_list[i];
+ if (unlikely (!sequence[i].serialize (c, this)
+ .serialize (c, substitute_glyphs_list.sub_array (0, substitute_len))))
+ return_trace (false);
+ substitute_glyphs_list += substitute_len;
+ }
+ return_trace (coverage.serialize (c, this).serialize (c, glyphs));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ 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 (c->serializer, out)
+ .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) && sequence.sanitize (c, this));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ OffsetArrayOf<Sequence>
+ sequence; /* Array of Sequence tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, sequence);
+};
+
+struct MultipleSubst
+{
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID> glyphs,
+ hb_array_t<const unsigned int> substitute_len_list,
+ hb_array_t<const HBGlyphID> substitute_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, substitute_len_list, substitute_glyphs_list));
+ default:return_trace (false);
+ }
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ MultipleSubstFormat1 format1;
+ } u;
+};
+
+struct AlternateSet
+{
+ 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)
+ alt_index = c->random_number () % count + 1;
+
+ if (unlikely (alt_index > count || alt_index == 0)) return_trace (false);
+
+ c->replace_glyph (alternates[alt_index - 1]);
+
+ 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.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 ();
+ 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);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (alternates.sanitize (c));
+ }
+
+ protected:
+ ArrayOf<HBGlyphID>
+ alternates; /* Array of alternate GlyphIDs--in
+ * arbitrary order */
+ public:
+ DEFINE_SIZE_ARRAY (2, alternates);
+};
+
+struct AlternateSubstFormat1
+{
+ bool intersects (const hb_set_t *glyphs) const
+ { return (this+coverage).intersects (glyphs); }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_zip (this+coverage, alternateSet)
+ | hb_filter (c->glyphs, hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const AlternateSet &_) { _.closure (c); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+ + hb_zip (this+coverage, alternateSet)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const AlternateSet &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ unsigned
+ get_glyph_alternates (hb_codepoint_t gid,
+ unsigned start_offset,
+ unsigned *alternate_count /* IN/OUT. May be NULL. */,
+ hb_codepoint_t *alternate_glyphs /* OUT. May be NULL. */) const
+ { return (this+alternateSet[(this+coverage).get_coverage (gid)])
+ .get_alternates (start_offset, alternate_count, alternate_glyphs); }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ return_trace ((this+alternateSet[index]).apply (c));
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID> glyphs,
+ hb_array_t<const unsigned int> alternate_len_list,
+ hb_array_t<const HBGlyphID> 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 (c, this)
+ .serialize (c, alternate_glyphs_list.sub_array (0, alternate_len))))
+ return_trace (false);
+ alternate_glyphs_list += alternate_len;
+ }
+ return_trace (coverage.serialize (c, this).serialize (c, glyphs));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ 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 (c->serializer, out)
+ .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) && alternateSet.sanitize (c, this));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ OffsetArrayOf<AlternateSet>
+ alternateSet; /* Array of AlternateSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, alternateSet);
+};
+
+struct AlternateSubst
+{
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID> glyphs,
+ hb_array_t<const unsigned int> alternate_len_list,
+ hb_array_t<const HBGlyphID> 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);
+ }
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ AlternateSubstFormat1 format1;
+ } u;
+};
+
+
+struct Ligature
+{
+ bool intersects (const hb_set_t *glyphs) const
+ { return hb_all (component, glyphs); }
+
+ 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))
+ {
+ c->replace_glyph (ligGlyph);
+ return_trace (true);
+ }
+
+ unsigned int total_component_count = 0;
+
+ unsigned int match_length = 0;
+ unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
+
+ if (likely (!match_input (c, count,
+ &component[1],
+ match_glyph,
+ nullptr,
+ &match_length,
+ match_positions,
+ &total_component_count)))
+ return_trace (false);
+
+ ligate_input (c,
+ count,
+ match_positions,
+ match_length,
+ ligGlyph,
+ total_component_count);
+
+ 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) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ const hb_map_t &glyph_map = *c->plan->glyph_map;
+
+ if (!intersects (&glyphset) || !glyphset.has (ligGlyph)) return_trace (false);
+
+ 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));
+ }
+
+ public:
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (ligGlyph.sanitize (c) && component.sanitize (c));
+ }
+
+ protected:
+ HBGlyphID ligGlyph; /* GlyphID of ligature to substitute */
+ HeadlessArrayOf<HBGlyphID>
+ component; /* Array of component GlyphIDs--start
+ * with the second component--ordered
+ * in writing direction */
+ public:
+ DEFINE_SIZE_ARRAY (4, component);
+};
+
+struct LigatureSet
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ return
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_map ([glyphs] (const Ligature &_) { return _.intersects (glyphs); })
+ | hb_any
+ ;
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Ligature &_) { _.closure (c); })
+ ;
+ }
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const Ligature &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ {
+ return
+ + hb_iter (ligature)
+ | hb_map (hb_add (this))
+ | hb_map ([c] (const Ligature &_) { return _.would_apply (c); })
+ | hb_any
+ ;
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ unsigned int num_ligs = ligature.len;
+ for (unsigned int i = 0; i < num_ligs; i++)
+ {
+ const Ligature &lig = this+ligature[i];
+ if (lig.apply (c)) return_trace (true);
+ }
+
+ return_trace (false);
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ hb_array_t<const HBGlyphID> ligatures,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID> &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 (c, this)
+ .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) 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))
+ | hb_drain
+ ;
+ return_trace (bool (out->ligature));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (ligature.sanitize (c, this));
+ }
+
+ protected:
+ OffsetArrayOf<Ligature>
+ ligature; /* Array LigatureSet tables
+ * ordered by preference */
+ public:
+ DEFINE_SIZE_ARRAY (2, ligature);
+};
+
+struct LigatureSubstFormat1
+{
+ 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 OffsetTo<LigatureSet> &_)
+ { return (this+_).intersects (glyphs); })
+ | hb_any
+ ;
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ + hb_zip (this+coverage, ligatureSet)
+ | hb_filter (*c->glyphs, hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const LigatureSet &_) { _.closure (c); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const {}
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c) const
+ {
+ if (unlikely (!(this+coverage).collect_coverage (c->input))) return;
+
+ + hb_zip (this+coverage, ligatureSet)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([c] (const LigatureSet &_) { _.collect_glyphs (c); })
+ ;
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ {
+ unsigned int index = (this+coverage).get_coverage (c->glyphs[0]);
+ if (likely (index == NOT_COVERED)) return false;
+
+ const LigatureSet &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 LigatureSet &lig_set = this+ligatureSet[index];
+ return_trace (lig_set.apply (c));
+ }
+
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID> first_glyphs,
+ hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+ hb_array_t<const HBGlyphID> ligatures_list,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID> 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 (c, this)
+ .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 (c, this).serialize (c, first_glyphs));
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ 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, ligatureSet)
+ | hb_filter (glyphset, hb_first)
+ | hb_filter (subset_offset_array (c, out->ligatureSet, this), hb_second)
+ | hb_map (hb_first)
+ | hb_map (glyph_map)
+ | hb_sink (new_coverage)
+ ;
+ out->coverage.serialize (c->serializer, out)
+ .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) && ligatureSet.sanitize (c, this));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of Substitution table */
+ OffsetArrayOf<LigatureSet>
+ ligatureSet; /* Array LigatureSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, ligatureSet);
+};
+
+struct LigatureSubst
+{
+ bool serialize (hb_serialize_context_t *c,
+ hb_sorted_array_t<const HBGlyphID> first_glyphs,
+ hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+ hb_array_t<const HBGlyphID> ligatures_list,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID> 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);
+ }
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ LigatureSubstFormat1 format1;
+ } u;
+};
+
+
+struct ContextSubst : Context {};
+
+struct ChainContextSubst : ChainContext {};
+
+struct ExtensionSubst : Extension<ExtensionSubst>
+{
+ typedef struct SubstLookupSubTable SubTable;
+ bool is_reverse () const;
+};
+
+
+struct ReverseChainSingleSubstFormat1
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ if (!(this+coverage).intersects (glyphs))
+ return false;
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (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;
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ if (!intersects (c->glyphs)) return;
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
+
+ + hb_zip (this+coverage, substitute)
+ | hb_filter (*c->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 OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ count = lookahead.len;
+ for (unsigned int i = 0; i < count; i++)
+ if (unlikely (!(this+lookahead[i]).collect_coverage (c->after))) return;
+
+ const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
+ count = substitute.len;
+ c->output->add_array (substitute.arrayZ, substitute.len);
+ }
+
+ const Coverage &get_coverage () const { return this+coverage; }
+
+ bool would_apply (hb_would_apply_context_t *c) const
+ { return c->len == 1 && (this+coverage).get_coverage (c->glyphs[0]) != NOT_COVERED; }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
+ return_trace (false); /* No chaining to this type */
+
+ unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (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,
+ 1, &end_index))
+ {
+ c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index);
+ c->replace_glyph_inplace (substitute[index]);
+ /* 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);
+ }
+
+ return_trace (false);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ // TODO(subset)
+ return_trace (false);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
+ return_trace (false);
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ if (!lookahead.sanitize (c, this))
+ return_trace (false);
+ const ArrayOf<HBGlyphID> &substitute = StructAfter<ArrayOf<HBGlyphID>> (lookahead);
+ return_trace (substitute.sanitize (c));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 1 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetArrayOf<Coverage>
+ backtrack; /* Array of coverage tables
+ * in backtracking sequence, in glyph
+ * sequence order */
+ OffsetArrayOf<Coverage>
+ lookaheadX; /* Array of coverage tables
+ * in lookahead sequence, in glyph
+ * sequence order */
+ ArrayOf<HBGlyphID>
+ substituteX; /* Array of substitute
+ * GlyphIDs--ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_MIN (10);
+};
+
+struct ReverseChainSingleSubst
+{
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ ReverseChainSingleSubstFormat1 format1;
+ } u;
+};
+
+
+
+/*
+ * SubstLookup
+ */
+
+struct SubstLookupSubTable
+{
+ friend struct Lookup;
+ friend struct SubstLookup;
+
+ 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, hb_forward<Ts> (ds)...));
+ case Multiple: return_trace (u.multiple.dispatch (c, hb_forward<Ts> (ds)...));
+ case Alternate: return_trace (u.alternate.dispatch (c, hb_forward<Ts> (ds)...));
+ case Ligature: return_trace (u.ligature.dispatch (c, hb_forward<Ts> (ds)...));
+ case Context: return_trace (u.context.dispatch (c, hb_forward<Ts> (ds)...));
+ case ChainContext: return_trace (u.chainContext.dispatch (c, hb_forward<Ts> (ds)...));
+ case Extension: return_trace (u.extension.dispatch (c, hb_forward<Ts> (ds)...));
+ case ReverseChainSingle: return_trace (u.reverseChainContextSingle.dispatch (c, hb_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 {
+ SingleSubst single;
+ MultipleSubst multiple;
+ AlternateSubst alternate;
+ LigatureSubst ligature;
+ ContextSubst context;
+ ChainContextSubst chainContext;
+ ExtensionSubst extension;
+ ReverseChainSingleSubst reverseChainContextSingle;
+ } u;
+ public:
+ DEFINE_SIZE_MIN (0);
+};
+
+
+struct SubstLookup : Lookup
+{
+ typedef SubstLookupSubTable SubTable;
+
+ 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 reinterpret_cast<const ExtensionSubst &> (get_subtable (0)).is_reverse ();
+ return lookup_type_is_reverse (type);
+ }
+
+ 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 ();
+ }
+
+ c->set_recurse_func (dispatch_closure_lookups_recurse_func);
+
+ 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);
+ }
+
+ static inline bool apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index);
+
+ SubTable& serialize_subtable (hb_serialize_context_t *c,
+ unsigned int i)
+ { return get_subtables<SubTable> ()[i].serialize (c, this); }
+
+ bool serialize_single (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ hb_sorted_array_t<const HBGlyphID> glyphs,
+ hb_array_t<const HBGlyphID> substitutes)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Single, lookup_props, 1))) return_trace (false);
+ return_trace (serialize_subtable (c, 0).u.single.
+ serialize (c, hb_zip (glyphs, substitutes)));
+ }
+
+ bool serialize_multiple (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ hb_sorted_array_t<const HBGlyphID> glyphs,
+ hb_array_t<const unsigned int> substitute_len_list,
+ hb_array_t<const HBGlyphID> substitute_glyphs_list)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Multiple, lookup_props, 1))) return_trace (false);
+ return_trace (serialize_subtable (c, 0).u.multiple.
+ serialize (c,
+ glyphs,
+ substitute_len_list,
+ substitute_glyphs_list));
+ }
+
+ bool serialize_alternate (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ hb_sorted_array_t<const HBGlyphID> glyphs,
+ hb_array_t<const unsigned int> alternate_len_list,
+ hb_array_t<const HBGlyphID> alternate_glyphs_list)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Alternate, lookup_props, 1))) return_trace (false);
+ return_trace (serialize_subtable (c, 0).u.alternate.
+ serialize (c,
+ glyphs,
+ alternate_len_list,
+ alternate_glyphs_list));
+ }
+
+ bool serialize_ligature (hb_serialize_context_t *c,
+ uint32_t lookup_props,
+ hb_sorted_array_t<const HBGlyphID> first_glyphs,
+ hb_array_t<const unsigned int> ligature_per_first_glyph_count_list,
+ hb_array_t<const HBGlyphID> ligatures_list,
+ hb_array_t<const unsigned int> component_count_list,
+ hb_array_t<const HBGlyphID> component_list /* Starting from second for each ligature */)
+ {
+ TRACE_SERIALIZE (this);
+ if (unlikely (!Lookup::serialize (c, SubTable::Ligature, lookup_props, 1))) return_trace (false);
+ return_trace (serialize_subtable (c, 0).u.ligature.
+ serialize (c,
+ first_glyphs,
+ ligature_per_first_glyph_count_list,
+ ligatures_list,
+ component_count_list,
+ component_list));
+ }
+
+ template <typename context_t>
+ static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+
+ static inline hb_closure_context_t::return_t dispatch_closure_recurse_func (hb_closure_context_t *c, unsigned int lookup_index)
+ {
+ if (!c->should_visit_lookup (lookup_index))
+ return hb_empty_t ();
+
+ hb_closure_context_t::return_t ret = dispatch_recurse_func (c, lookup_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;
+ }
+
+ HB_INTERNAL static hb_closure_lookups_context_t::return_t dispatch_closure_lookups_recurse_func (hb_closure_lookups_context_t *c, unsigned 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, hb_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); }
+};
+
+/*
+ * GSUB -- Glyph Substitution
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/gsub
+ */
+
+struct GSUB : GSUBGPOS
+{
+ 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, c->plan->gsub_lookups, c->plan->gsub_features);
+ return GSUBGPOS::subset<SubstLookup> (&l);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ { return 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 : GSUB::accelerator_t {};
+
+
+/* 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*/ inline hb_closure_lookups_context_t::return_t SubstLookup::dispatch_closure_lookups_recurse_func (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);
+}
+
+/*static*/ bool SubstLookup::apply_recurse_func (hb_ot_apply_context_t *c, unsigned int lookup_index)
+{
+ const SubstLookup &l = c->face->table.GSUB.get_relaxed ()->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 = l.dispatch (c);
+ c->set_lookup_index (saved_lookup_index);
+ c->set_lookup_props (saved_lookup_props);
+ return ret;
+}
+#endif
+
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
new file mode 100644
index 0000000000..03a578cec0
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh
@@ -0,0 +1,3422 @@
+/*
+ * 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_closure_context_t :
+ hb_dispatch_context_t<hb_closure_context_t>
+{
+ typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_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 int lookup_index)
+ {
+ if (unlikely (nesting_level_left == 0 || !recurse_func))
+ return;
+
+ nesting_level_left--;
+ recurse_func (this, lookup_index);
+ nesting_level_left++;
+ }
+
+ bool lookup_limit_exceeded ()
+ { return lookup_count > HB_MAX_LOOKUP_INDICES; }
+
+ bool should_visit_lookup (unsigned int lookup_index)
+ {
+ if (lookup_count++ > HB_MAX_LOOKUP_INDICES)
+ return false;
+
+ if (is_lookup_done (lookup_index))
+ return false;
+
+ done_lookups->set (lookup_index, glyphs->get_population ());
+ return true;
+ }
+
+ bool is_lookup_done (unsigned int lookup_index)
+ {
+ if (done_lookups->in_error ())
+ return true;
+
+ /* Have we visited this lookup with the current set of glyphs? */
+ return done_lookups->get (lookup_index) == glyphs->get_population ();
+ }
+
+ hb_face_t *face;
+ hb_set_t *glyphs;
+ hb_set_t output[1];
+ recurse_func_t recurse_func;
+ unsigned int nesting_level_left;
+
+ hb_closure_context_t (hb_face_t *face_,
+ hb_set_t *glyphs_,
+ hb_map_t *done_lookups_,
+ unsigned int nesting_level_left_ = HB_MAX_NESTING_LEVEL) :
+ face (face_),
+ glyphs (glyphs_),
+ recurse_func (nullptr),
+ nesting_level_left (nesting_level_left_),
+ done_lookups (done_lookups_),
+ lookup_count (0)
+ {}
+
+ ~hb_closure_context_t () { flush (); }
+
+ void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+
+ void flush ()
+ {
+ hb_set_del_range (output, face->get_num_glyphs (), hb_set_get_max (output)); /* Remove invalid glyphs. */
+ hb_set_union (glyphs, output);
+ hb_set_clear (output);
+ }
+
+ private:
+ hb_map_t *done_lookups;
+ unsigned int lookup_count;
+};
+
+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 (is_lookup_visited (lookup_index))
+ return;
+
+ set_lookup_visited (lookup_index);
+ 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 ()
+ { return lookup_count > HB_MAX_LOOKUP_INDICES; }
+
+ bool is_lookup_visited (unsigned lookup_index)
+ {
+ if (lookup_count++ > HB_MAX_LOOKUP_INDICES)
+ return true;
+
+ if (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
+ {
+ matcher_t () :
+ lookup_props (0),
+ ignore_zwnj (false),
+ ignore_zwj (false),
+ mask (-1),
+#define arg1(arg) (arg) /* Remove the macro to see why it's needed! */
+ syllable arg1(0),
+#undef arg1
+ match_func (nullptr),
+ match_data (nullptr) {}
+
+ typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &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_syllable (uint8_t syllable_) { syllable = syllable_; }
+ 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
+ };
+
+ may_match_t may_match (const hb_glyph_info_t &info,
+ const HBUINT16 *glyph_data) const
+ {
+ if (!(info.mask & mask) ||
+ (syllable && syllable != info.syllable ()))
+ return MATCH_NO;
+
+ if (match_func)
+ return match_func (info.codepoint, *glyph_data, match_data) ? MATCH_YES : MATCH_NO;
+
+ return MATCH_MAYBE;
+ }
+
+ enum may_skip_t {
+ SKIP_NO,
+ SKIP_YES,
+ SKIP_MAYBE
+ };
+
+ 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;
+ bool ignore_zwnj;
+ bool ignore_zwj;
+ hb_mask_t mask;
+ uint8_t syllable;
+ match_func_t match_func;
+ const void *match_data;
+ };
+
+ struct skipping_iterator_t
+ {
+ void init (hb_ot_apply_context_t *c_, bool context_match = false)
+ {
+ c = c_;
+ match_glyph_data = nullptr;
+ 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);
+ }
+ 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_,
+ const HBUINT16 glyph_data[])
+ {
+ matcher.set_match_func (match_func_, match_data_);
+ match_glyph_data = glyph_data;
+ }
+
+ void reset (unsigned int start_index_,
+ unsigned int num_items_)
+ {
+ idx = start_index_;
+ num_items = num_items_;
+ end = c->buffer->len;
+ matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
+ }
+
+ void reject ()
+ {
+ num_items++;
+ if (match_glyph_data) match_glyph_data--;
+ }
+
+ matcher_t::may_skip_t
+ may_skip (const hb_glyph_info_t &info) const
+ { return matcher.may_skip (c, info); }
+
+ bool next ()
+ {
+ assert (num_items > 0);
+ while (idx + num_items < end)
+ {
+ idx++;
+ const hb_glyph_info_t &info = c->buffer->info[idx];
+
+ matcher_t::may_skip_t skip = matcher.may_skip (c, info);
+ if (unlikely (skip == matcher_t::SKIP_YES))
+ continue;
+
+ matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
+ if (match == matcher_t::MATCH_YES ||
+ (match == matcher_t::MATCH_MAYBE &&
+ skip == matcher_t::SKIP_NO))
+ {
+ num_items--;
+ if (match_glyph_data) match_glyph_data++;
+ return true;
+ }
+
+ if (skip == matcher_t::SKIP_NO)
+ return false;
+ }
+ return false;
+ }
+ bool prev ()
+ {
+ assert (num_items > 0);
+ while (idx > num_items - 1)
+ {
+ idx--;
+ const hb_glyph_info_t &info = c->buffer->out_info[idx];
+
+ matcher_t::may_skip_t skip = matcher.may_skip (c, info);
+ if (unlikely (skip == matcher_t::SKIP_YES))
+ continue;
+
+ matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
+ if (match == matcher_t::MATCH_YES ||
+ (match == matcher_t::MATCH_MAYBE &&
+ skip == matcher_t::SKIP_NO))
+ {
+ num_items--;
+ if (match_glyph_data) match_glyph_data++;
+ return true;
+ }
+
+ if (skip == matcher_t::SKIP_NO)
+ return false;
+ }
+ return false;
+ }
+
+ unsigned int idx;
+ protected:
+ hb_ot_apply_context_t *c;
+ matcher_t matcher;
+ const HBUINT16 *match_glyph_data;
+
+ unsigned int num_items;
+ 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))
+ 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;
+
+ hb_font_t *font;
+ hb_face_t *face;
+ hb_buffer_t *buffer;
+ recurse_func_t recurse_func;
+ const GDEF &gdef;
+ const VariationStore &var_store;
+
+ hb_direction_t direction;
+ hb_mask_t lookup_mask;
+ unsigned int table_index; /* GSUB/GPOS */
+ unsigned int lookup_index;
+ unsigned int lookup_props;
+ unsigned int nesting_level_left;
+
+ bool has_glyph_classes;
+ bool auto_zwnj;
+ bool auto_zwj;
+ bool random;
+
+ uint32_t random_state;
+
+
+ hb_ot_apply_context_t (unsigned int table_index_,
+ hb_font_t *font_,
+ hb_buffer_t *buffer_) :
+ iter_input (), iter_context (),
+ font (font_), face (font->face), buffer (buffer_),
+ recurse_func (nullptr),
+ gdef (
+#ifndef HB_NO_OT_LAYOUT
+ *face->table.GDEF->table
+#else
+ Null (GDEF)
+#endif
+ ),
+ var_store (gdef.get_var_store ()),
+ direction (buffer_->props.direction),
+ lookup_mask (1),
+ table_index (table_index_),
+ lookup_index ((unsigned int) -1),
+ lookup_props (0),
+ nesting_level_left (HB_MAX_NESTING_LEVEL),
+ has_glyph_classes (gdef.has_glyph_classes ()),
+ auto_zwnj (true),
+ auto_zwj (true),
+ random (false),
+ random_state (1) { init_iters (); }
+
+ void init_iters ()
+ {
+ iter_input.init (this, false);
+ iter_context.init (this, true);
+ }
+
+ void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; init_iters (); }
+ void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; init_iters (); }
+ void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; 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.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;
+ }
+
+ bool check_glyph_property (const hb_glyph_info_t *info,
+ unsigned int match_props) const
+ {
+ hb_codepoint_t glyph = info->codepoint;
+ 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 (glyph, glyph_props, match_props);
+
+ return true;
+ }
+
+ void _set_glyph_props (hb_codepoint_t glyph_index,
+ unsigned int class_guess = 0,
+ bool ligature = false,
+ bool component = false) const
+ {
+ unsigned int add_in = _hb_glyph_info_get_glyph_props (&buffer->cur()) &
+ HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE;
+ add_in |= HB_OT_LAYOUT_GLYPH_PROPS_SUBSTITUTED;
+ if (ligature)
+ {
+ add_in |= 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.
+ */
+ add_in &= ~HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
+ }
+ if (component)
+ add_in |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED;
+ if (likely (has_glyph_classes))
+ _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | gdef.get_glyph_props (glyph_index));
+ else if (class_guess)
+ _hb_glyph_info_set_glyph_props (&buffer->cur(), add_in | class_guess);
+ }
+
+ void replace_glyph (hb_codepoint_t glyph_index) const
+ {
+ _set_glyph_props (glyph_index);
+ buffer->replace_glyph (glyph_index);
+ }
+ void replace_glyph_inplace (hb_codepoint_t glyph_index) const
+ {
+ _set_glyph_props (glyph_index);
+ buffer->cur().codepoint = glyph_index;
+ }
+ void replace_glyph_with_ligature (hb_codepoint_t glyph_index,
+ unsigned int class_guess) const
+ {
+ _set_glyph_props (glyph_index, class_guess, true);
+ buffer->replace_glyph (glyph_index);
+ }
+ void output_glyph_for_component (hb_codepoint_t glyph_index,
+ unsigned int class_guess) const
+ {
+ _set_glyph_props (glyph_index, class_guess, false, true);
+ buffer->output_glyph (glyph_index);
+ }
+};
+
+
+struct hb_get_subtables_context_t :
+ hb_dispatch_context_t<hb_get_subtables_context_t>
+{
+ template <typename Type>
+ static inline bool apply_to (const void *obj, OT::hb_ot_apply_context_t *c)
+ {
+ const Type *typed_obj = (const Type *) obj;
+ return typed_obj->apply (c);
+ }
+
+ typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_ot_apply_context_t *c);
+
+ struct hb_applicable_t
+ {
+ template <typename T>
+ void init (const T &obj_, hb_apply_func_t apply_func_)
+ {
+ obj = &obj_;
+ apply_func = apply_func_;
+ digest.init ();
+ obj_.get_coverage ().collect_coverage (&digest);
+ }
+
+ bool apply (OT::hb_ot_apply_context_t *c) const
+ {
+ return digest.may_have (c->buffer->cur().codepoint) && apply_func (obj, c);
+ }
+
+ private:
+ const void *obj;
+ hb_apply_func_t apply_func;
+ hb_set_digest_t digest;
+ };
+
+ typedef hb_vector_t<hb_applicable_t> array_t;
+
+ /* Dispatch interface. */
+ template <typename T>
+ return_t dispatch (const T &obj)
+ {
+ hb_applicable_t *entry = array.push();
+ entry->init (obj, apply_to<T>);
+ return hb_empty_t ();
+ }
+ static return_t default_return_value () { return hb_empty_t (); }
+
+ hb_get_subtables_context_t (array_t &array_) :
+ array (array_) {}
+
+ array_t &array;
+};
+
+
+
+
+typedef bool (*intersects_func_t) (const hb_set_t *glyphs, const HBUINT16 &value, const void *data);
+typedef void (*collect_glyphs_func_t) (hb_set_t *glyphs, const HBUINT16 &value, const void *data);
+typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data);
+
+struct ContextClosureFuncs
+{
+ intersects_func_t intersects;
+};
+struct ContextCollectGlyphsFuncs
+{
+ collect_glyphs_func_t collect;
+};
+struct ContextApplyFuncs
+{
+ match_func_t match;
+};
+
+
+static inline bool intersects_glyph (const hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
+{
+ return glyphs->has (value);
+}
+static inline bool intersects_class (const hb_set_t *glyphs, const HBUINT16 &value, const void *data)
+{
+ const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+ return class_def.intersects_class (glyphs, value);
+}
+static inline bool intersects_coverage (const hb_set_t *glyphs, const HBUINT16 &value, const void *data)
+{
+ const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+ return (data+coverage).intersects (glyphs);
+}
+
+static inline bool array_is_subset_of (const hb_set_t *glyphs,
+ unsigned int count,
+ const HBUINT16 values[],
+ intersects_func_t intersects_func,
+ const void *intersects_data)
+{
+ for (const HBUINT16 &_ : + hb_iter (values, count))
+ if (!intersects_func (glyphs, _, intersects_data)) return false;
+ return true;
+}
+
+
+static inline void collect_glyph (hb_set_t *glyphs, const HBUINT16 &value, const void *data HB_UNUSED)
+{
+ glyphs->add (value);
+}
+static inline void collect_class (hb_set_t *glyphs, const HBUINT16 &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, const HBUINT16 &value, const void *data)
+{
+ const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+ (data+coverage).collect_coverage (glyphs);
+}
+static inline void collect_array (hb_collect_glyphs_context_t *c HB_UNUSED,
+ hb_set_t *glyphs,
+ unsigned int count,
+ const HBUINT16 values[],
+ collect_glyphs_func_t collect_func,
+ const void *collect_data)
+{
+ return
+ + hb_iter (values, count)
+ | hb_apply ([&] (const HBUINT16 &_) { collect_func (glyphs, _, collect_data); })
+ ;
+}
+
+
+static inline bool match_glyph (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data HB_UNUSED)
+{
+ return glyph_id == value;
+}
+static inline bool match_class (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
+{
+ const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
+ return class_def.get_class (glyph_id) == value;
+}
+static inline bool match_coverage (hb_codepoint_t glyph_id, const HBUINT16 &value, const void *data)
+{
+ const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
+ return (data+coverage).get_coverage (glyph_id) != NOT_COVERED;
+}
+
+static inline bool would_match_input (hb_would_apply_context_t *c,
+ unsigned int count, /* Including the first glyph (not matched) */
+ const HBUINT16 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++)
+ if (likely (!match_func (c->glyphs[i], input[i - 1], match_data)))
+ return false;
+
+ return true;
+}
+static inline bool match_input (hb_ot_apply_context_t *c,
+ unsigned int count, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int *end_offset,
+ 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, count - 1);
+ skippy_iter.set_match_func (match_func, match_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;
+ total_component_count += _hb_glyph_info_get_lig_num_comps (&buffer->cur());
+
+ 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;
+
+ match_positions[0] = buffer->idx;
+ for (unsigned int i = 1; i < count; i++)
+ {
+ if (!skippy_iter.next ()) 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_offset = skippy_iter.idx - buffer->idx + 1;
+
+ if (p_total_component_count)
+ *p_total_component_count = total_component_count;
+
+ 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_length,
+ hb_codepoint_t lig_glyph,
+ unsigned int total_component_count)
+{
+ TRACE_APPLY (nullptr);
+
+ hb_buffer_t *buffer = c->buffer;
+
+ buffer->merge_clusters (buffer->idx, buffer->idx + match_length);
+
+ /* - 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);
+ }
+ 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);
+}
+
+static inline bool match_backtrack (hb_ot_apply_context_t *c,
+ unsigned int count,
+ const HBUINT16 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 (), count);
+ skippy_iter.set_match_func (match_func, match_data, backtrack);
+
+ for (unsigned int i = 0; i < count; i++)
+ if (!skippy_iter.prev ())
+ return_trace (false);
+
+ *match_start = skippy_iter.idx;
+
+ return_trace (true);
+}
+
+static inline bool match_lookahead (hb_ot_apply_context_t *c,
+ unsigned int count,
+ const HBUINT16 lookahead[],
+ match_func_t match_func,
+ const void *match_data,
+ unsigned int offset,
+ unsigned int *end_index)
+{
+ TRACE_APPLY (nullptr);
+
+ hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_context;
+ skippy_iter.reset (c->buffer->idx + offset - 1, count);
+ skippy_iter.set_match_func (match_func, match_data, lookahead);
+
+ for (unsigned int i = 0; i < count; i++)
+ if (!skippy_iter.next ())
+ return_trace (false);
+
+ *end_index = skippy_iter.idx + 1;
+
+ return_trace (true);
+}
+
+
+
+struct LookupRecord
+{
+ LookupRecord* copy (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 (nullptr);
+
+ out->lookupListIndex = hb_map_get (lookup_map, lookupListIndex);
+ return_trace (out);
+ }
+
+ 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);
+};
+
+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 bool 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_length)
+{
+ TRACE_APPLY (nullptr);
+
+ 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_length;
+
+ 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;
+
+ /* Don't recurse to ourself at same position.
+ * Note that this test is too naive, it doesn't catch longer loops. */
+ if (idx == 0 && lookupRecord[i].lookupListIndex == c->lookup_index)
+ continue;
+
+ if (unlikely (!buffer->move_to (match_positions[idx])))
+ break;
+
+ if (unlikely (buffer->max_ops <= 0))
+ break;
+
+ unsigned int orig_len = buffer->backtrack_len () + buffer->lookahead_len ();
+ if (!c->recurse (lookupRecord[i].lookupListIndex))
+ continue;
+
+ 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 might
+ * have been marks and default-ignorables in the sequence. We
+ * should instead drop match positions between current-position
+ * and current-position + n instead.
+ *
+ * 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, more than we have had matched.
+ * Just never rewind end back and get out of here.
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=659496 */
+ end = match_positions[idx];
+ /* There can't be any further changes. */
+ break;
+ }
+
+ 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 negative. */
+ 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;
+ }
+
+ buffer->move_to (end);
+
+ return_trace (true);
+}
+
+
+
+/* Contextual lookups */
+
+struct ContextClosureLookupContext
+{
+ ContextClosureFuncs funcs;
+ const void *intersects_data;
+};
+
+struct ContextCollectGlyphsLookupContext
+{
+ ContextCollectGlyphsFuncs funcs;
+ const void *collect_data;
+};
+
+struct ContextApplyLookupContext
+{
+ ContextApplyFuncs funcs;
+ const void *match_data;
+};
+
+static inline bool context_intersects (const hb_set_t *glyphs,
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 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);
+}
+
+static inline void context_closure_lookup (hb_closure_context_t *c,
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookupCount,
+ const LookupRecord lookupRecord[],
+ ContextClosureLookupContext &lookup_context)
+{
+ if (context_intersects (c->glyphs,
+ inputCount, input,
+ lookup_context))
+ recurse_lookups (c,
+ lookupCount, lookupRecord);
+}
+
+static inline void context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c,
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 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);
+}
+
+static inline bool context_would_apply_lookup (hb_would_apply_context_t *c,
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookupCount HB_UNUSED,
+ const LookupRecord lookupRecord[] HB_UNUSED,
+ ContextApplyLookupContext &lookup_context)
+{
+ return would_match_input (c,
+ inputCount, input,
+ lookup_context.funcs.match, lookup_context.match_data);
+}
+static inline bool context_apply_lookup (hb_ot_apply_context_t *c,
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookupCount,
+ const LookupRecord lookupRecord[],
+ ContextApplyLookupContext &lookup_context)
+{
+ unsigned int match_length = 0;
+ unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
+ return match_input (c,
+ inputCount, input,
+ lookup_context.funcs.match, lookup_context.match_data,
+ &match_length, match_positions)
+ && (c->buffer->unsafe_to_break (c->buffer->idx, c->buffer->idx + match_length),
+ apply_lookup (c,
+ inputCount, match_positions,
+ lookupCount, lookupRecord,
+ match_length));
+}
+
+struct Rule
+{
+ 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, ContextClosureLookupContext &lookup_context) const
+ {
+ if (unlikely (c->lookup_limit_exceeded ())) return;
+
+ const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
+ (inputZ.as_array ((inputCount ? inputCount - 1 : 0)));
+ context_closure_lookup (c,
+ inputCount, inputZ.arrayZ,
+ lookupCount, lookupRecord.arrayZ,
+ lookup_context);
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ if (unlikely (c->lookup_limit_exceeded ())) return;
+
+ const UnsizedArrayOf<LookupRecord> &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 UnsizedArrayOf<LookupRecord> &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,
+ ContextApplyLookupContext &lookup_context) const
+ {
+ const UnsizedArrayOf<LookupRecord> &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,
+ ContextApplyLookupContext &lookup_context) const
+ {
+ TRACE_APPLY (this);
+ const UnsizedArrayOf<LookupRecord> &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;
+ out->lookupCount = lookupCount;
+
+ const hb_array_t<const HBUINT16> input = inputZ.as_array (inputCount - 1);
+ for (const auto org : input)
+ {
+ HBUINT16 d;
+ d = input_mapping->get (org);
+ c->copy (d);
+ }
+
+ const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
+ (inputZ.as_array ((inputCount ? inputCount - 1 : 0)));
+ for (unsigned i = 0; i < (unsigned) lookupCount; i++)
+ c->copy (lookupRecord[i], lookup_map);
+
+ return_trace (true);
+ }
+
+ bool subset (hb_subset_context_t *c,
+ const hb_map_t *lookup_map,
+ const hb_map_t *klass_map = nullptr) const
+ {
+ TRACE_SUBSET (this);
+
+ const hb_array_t<const HBUINT16> input = inputZ.as_array ((inputCount ? inputCount - 1 : 0));
+ if (!input.length) return_trace (false);
+
+ 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 (inputCount.sanitize (c) &&
+ lookupCount.sanitize (c) &&
+ 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<HBUINT16>
+ inputZ; /* Array of match inputs--start with
+ * second glyph */
+/*UnsizedArrayOf<LookupRecord>
+ lookupRecordX;*/ /* Array of LookupRecords--in
+ * design order */
+ public:
+ DEFINE_SIZE_ARRAY (4, inputZ);
+};
+
+struct RuleSet
+{
+ 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,
+ 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, lookup_context); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ if (unlikely (c->lookup_limit_exceeded ())) return;
+
+ return
+ + hb_iter (rule)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const Rule &_) { _.closure_lookups (c); })
+ ;
+ }
+
+ 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,
+ 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,
+ ContextApplyLookupContext &lookup_context) const
+ {
+ TRACE_APPLY (this);
+ return_trace (
+ + hb_iter (rule)
+ | hb_map (hb_add (this))
+ | hb_map ([&] (const Rule &_) { return _.apply (c, lookup_context); })
+ | hb_any
+ )
+ ;
+ }
+
+ 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 OffsetTo<Rule>& _ : rule)
+ {
+ if (!_) continue;
+ auto *o = out->rule.serialize_append (c->serializer);
+ if (unlikely (!o)) continue;
+
+ auto o_snap = c->serializer->snapshot ();
+ 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:
+ OffsetArrayOf<Rule>
+ rule; /* Array of Rule tables
+ * ordered by preference */
+ public:
+ DEFINE_SIZE_ARRAY (2, rule);
+};
+
+
+struct ContextFormat1
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ struct ContextClosureLookupContext lookup_context = {
+ {intersects_glyph},
+ 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
+ ;
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ struct ContextClosureLookupContext lookup_context = {
+ {intersects_glyph},
+ 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 (c, lookup_context); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ + hb_iter (ruleSet)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); })
+ ;
+ }
+
+ 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 ();
+ 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 (c->serializer, out)
+ .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 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetArrayOf<RuleSet>
+ ruleSet; /* Array of RuleSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, ruleSet);
+};
+
+
+struct ContextFormat2
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ if (!(this+coverage).intersects (glyphs))
+ return false;
+
+ const ClassDef &class_def = this+classDef;
+
+ struct ContextClosureLookupContext lookup_context = {
+ {intersects_class},
+ &class_def
+ };
+
+ 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) &&
+ p.second.intersects (glyphs, lookup_context); })
+ | hb_any
+ ;
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ if (!(this+coverage).intersects (c->glyphs))
+ return;
+
+ const ClassDef &class_def = this+classDef;
+
+ struct ContextClosureLookupContext lookup_context = {
+ {intersects_class},
+ &class_def
+ };
+
+ return
+ + hb_enumerate (ruleSet)
+ | hb_filter ([&] (unsigned _)
+ { return class_def.intersects_class (c->glyphs, _); },
+ hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const RuleSet &_) { _.closure (c, lookup_context); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ + hb_iter (ruleSet)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const RuleSet &_) { _.closure_lookups (c); })
+ ;
+ }
+
+ 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; }
+
+ 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 ClassDef &class_def = this+classDef;
+ index = class_def.get_class (c->buffer->cur().codepoint);
+ const RuleSet &rule_set = this+ruleSet[index];
+ struct ContextApplyLookupContext lookup_context = {
+ {match_class},
+ &class_def
+ };
+ 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_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups;
+ bool ret = true;
+ unsigned non_zero_index = 0, index = 0;
+ for (const hb_pair_t<unsigned, const OffsetTo<RuleSet>&> _ : + hb_enumerate (ruleSet)
+ | hb_filter (klass_map, hb_first))
+ {
+ auto *o = out->ruleSet.serialize_append (c->serializer);
+ if (unlikely (!o))
+ {
+ ret = false;
+ break;
+ }
+
+ if (o->serialize_subset (c, _.second, this, lookup_map, &klass_map))
+ non_zero_index = index;
+
+ index++;
+ }
+
+ if (!ret) return_trace (ret);
+
+ //prune empty trailing ruleSets
+ --index;
+ while (index > non_zero_index)
+ {
+ out->ruleSet.pop ();
+ index--;
+ }
+
+ 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 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetTo<ClassDef>
+ classDef; /* Offset to glyph ClassDef table--from
+ * beginning of table */
+ OffsetArrayOf<RuleSet>
+ ruleSet; /* Array of RuleSet tables
+ * ordered by class */
+ public:
+ DEFINE_SIZE_ARRAY (8, ruleSet);
+};
+
+
+struct ContextFormat3
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ if (!(this+coverageZ[0]).intersects (glyphs))
+ return false;
+
+ struct ContextClosureLookupContext lookup_context = {
+ {intersects_coverage},
+ this
+ };
+ return context_intersects (glyphs,
+ glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
+ lookup_context);
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ if (!(this+coverageZ[0]).intersects (c->glyphs))
+ return;
+
+ const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
+ struct ContextClosureLookupContext lookup_context = {
+ {intersects_coverage},
+ this
+ };
+ context_closure_lookup (c,
+ glyphCount, (const HBUINT16 *) (coverageZ.arrayZ + 1),
+ lookupCount, lookupRecord,
+ lookup_context);
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ 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;
+ out->lookupCount = lookupCount;
+
+ auto coverages = coverageZ.as_array (glyphCount);
+
+ for (const OffsetTo<Coverage>& offset : coverages)
+ {
+ auto *o = c->serializer->allocate_size<OffsetTo<Coverage>> (OffsetTo<Coverage>::static_size);
+ if (unlikely (!o)) return_trace (false);
+ if (!o->serialize_subset (c, offset, this)) return_trace (false);
+ }
+
+ const LookupRecord *lookupRecord = &StructAfter<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;
+ for (unsigned i = 0; i < (unsigned) lookupCount; i++)
+ c->serializer->copy (lookupRecord[i], lookup_map);
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!c->check_struct (this)) return_trace (false);
+ unsigned int count = glyphCount;
+ if (!count) return_trace (false); /* We want to access coverageZ[0] freely. */
+ if (!c->check_array (coverageZ.arrayZ, count)) return_trace (false);
+ for (unsigned int i = 0; i < count; i++)
+ if (!coverageZ[i].sanitize (c, this)) return_trace (false);
+ const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
+ return_trace (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<OffsetTo<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
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+ case 3: return_trace (c->dispatch (u.format3, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ ContextFormat1 format1;
+ ContextFormat2 format2;
+ ContextFormat3 format3;
+ } u;
+};
+
+
+/* Chaining Contextual lookups */
+
+struct ChainContextClosureLookupContext
+{
+ ContextClosureFuncs funcs;
+ const void *intersects_data[3];
+};
+
+struct ChainContextCollectGlyphsLookupContext
+{
+ ContextCollectGlyphsFuncs funcs;
+ const void *collect_data[3];
+};
+
+struct ChainContextApplyLookupContext
+{
+ ContextApplyFuncs funcs;
+ const void *match_data[3];
+};
+
+static inline bool chain_context_intersects (const hb_set_t *glyphs,
+ unsigned int backtrackCount,
+ const HBUINT16 backtrack[],
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookaheadCount,
+ const HBUINT16 lookahead[],
+ ChainContextClosureLookupContext &lookup_context)
+{
+ return array_is_subset_of (glyphs,
+ backtrackCount, backtrack,
+ lookup_context.funcs.intersects, lookup_context.intersects_data[0])
+ && array_is_subset_of (glyphs,
+ inputCount ? inputCount - 1 : 0, input,
+ lookup_context.funcs.intersects, lookup_context.intersects_data[1])
+ && array_is_subset_of (glyphs,
+ lookaheadCount, lookahead,
+ lookup_context.funcs.intersects, lookup_context.intersects_data[2]);
+}
+
+static inline void chain_context_closure_lookup (hb_closure_context_t *c,
+ unsigned int backtrackCount,
+ const HBUINT16 backtrack[],
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookaheadCount,
+ const HBUINT16 lookahead[],
+ unsigned int lookupCount,
+ const LookupRecord lookupRecord[],
+ ChainContextClosureLookupContext &lookup_context)
+{
+ if (chain_context_intersects (c->glyphs,
+ backtrackCount, backtrack,
+ inputCount, input,
+ lookaheadCount, lookahead,
+ lookup_context))
+ recurse_lookups (c,
+ lookupCount, lookupRecord);
+}
+
+static inline void chain_context_collect_glyphs_lookup (hb_collect_glyphs_context_t *c,
+ unsigned int backtrackCount,
+ const HBUINT16 backtrack[],
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookaheadCount,
+ const HBUINT16 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);
+}
+
+static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c,
+ unsigned int backtrackCount,
+ const HBUINT16 backtrack[] HB_UNUSED,
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookaheadCount,
+ const HBUINT16 lookahead[] HB_UNUSED,
+ unsigned int lookupCount HB_UNUSED,
+ const LookupRecord lookupRecord[] HB_UNUSED,
+ ChainContextApplyLookupContext &lookup_context)
+{
+ return (c->zero_context ? !backtrackCount && !lookaheadCount : true)
+ && would_match_input (c,
+ inputCount, input,
+ lookup_context.funcs.match, lookup_context.match_data[1]);
+}
+
+static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
+ unsigned int backtrackCount,
+ const HBUINT16 backtrack[],
+ unsigned int inputCount, /* Including the first glyph (not matched) */
+ const HBUINT16 input[], /* Array of input values--start with second glyph */
+ unsigned int lookaheadCount,
+ const HBUINT16 lookahead[],
+ unsigned int lookupCount,
+ const LookupRecord lookupRecord[],
+ ChainContextApplyLookupContext &lookup_context)
+{
+ unsigned int start_index = 0, match_length = 0, end_index = 0;
+ unsigned int match_positions[HB_MAX_CONTEXT_LENGTH];
+ return match_input (c,
+ inputCount, input,
+ lookup_context.funcs.match, lookup_context.match_data[1],
+ &match_length, match_positions)
+ && match_backtrack (c,
+ backtrackCount, backtrack,
+ lookup_context.funcs.match, lookup_context.match_data[0],
+ &start_index)
+ && match_lookahead (c,
+ lookaheadCount, lookahead,
+ lookup_context.funcs.match, lookup_context.match_data[2],
+ match_length, &end_index)
+ && (c->buffer->unsafe_to_break_from_outbuffer (start_index, end_index),
+ apply_lookup (c,
+ inputCount, match_positions,
+ lookupCount, lookupRecord,
+ match_length));
+}
+
+struct ChainRule
+{
+ bool intersects (const hb_set_t *glyphs, ChainContextClosureLookupContext &lookup_context) const
+ {
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (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,
+ ChainContextClosureLookupContext &lookup_context) const
+ {
+ if (unlikely (c->lookup_limit_exceeded ())) return;
+
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ chain_context_closure_lookup (c,
+ backtrack.len, backtrack.arrayZ,
+ input.lenP1, input.arrayZ,
+ lookahead.len, lookahead.arrayZ,
+ lookup.len, lookup.arrayZ,
+ lookup_context);
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ if (unlikely (c->lookup_limit_exceeded ())) return;
+
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ recurse_lookups (c, lookup.len, lookup.arrayZ);
+ }
+
+ void collect_glyphs (hb_collect_glyphs_context_t *c,
+ ChainContextCollectGlyphsLookupContext &lookup_context) const
+ {
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (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,
+ ChainContextApplyLookupContext &lookup_context) const
+ {
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (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, ChainContextApplyLookupContext &lookup_context) const
+ {
+ TRACE_APPLY (this);
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (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)
+ {
+ HBUINT16 gid;
+ gid = g;
+ c->copy (gid);
+ }
+ }
+
+ ChainRule* copy (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);
+ auto *out = c->start_embed (this);
+ if (unlikely (!out)) return_trace (nullptr);
+
+ const hb_map_t *mapping = backtrack_map;
+ serialize_array (c, backtrack.len, + backtrack.iter ()
+ | hb_map (mapping));
+
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ if (input_map) mapping = input_map;
+ serialize_array (c, input.lenP1, + input.iter ()
+ | hb_map (mapping));
+
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+ if (lookahead_map) mapping = lookahead_map;
+ serialize_array (c, lookahead.len, + lookahead.iter ()
+ | hb_map (mapping));
+
+ const ArrayOf<LookupRecord> &lookupRecord = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ HBUINT16 lookupCount;
+ lookupCount = lookupRecord.len;
+ if (!c->copy (lookupCount)) return_trace (nullptr);
+
+ for (unsigned i = 0; i < (unsigned) lookupCount; i++)
+ if (!c->copy (lookupRecord[i], lookup_map)) return_trace (nullptr);
+
+ return_trace (out);
+ }
+
+ 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 HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+
+ if (!backtrack_map)
+ {
+ const hb_set_t &glyphset = *c->plan->glyphset ();
+ if (!hb_all (backtrack, glyphset) ||
+ !hb_all (input, glyphset) ||
+ !hb_all (lookahead, glyphset))
+ return_trace (false);
+
+ copy (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);
+
+ copy (c->serializer, lookup_map, backtrack_map, input_map, lookahead_map);
+ }
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!backtrack.sanitize (c)) return_trace (false);
+ const HeadlessArrayOf<HBUINT16> &input = StructAfter<HeadlessArrayOf<HBUINT16>> (backtrack);
+ if (!input.sanitize (c)) return_trace (false);
+ const ArrayOf<HBUINT16> &lookahead = StructAfter<ArrayOf<HBUINT16>> (input);
+ if (!lookahead.sanitize (c)) return_trace (false);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ return_trace (lookup.sanitize (c));
+ }
+
+ protected:
+ ArrayOf<HBUINT16>
+ backtrack; /* Array of backtracking values
+ * (to be matched before the input
+ * sequence) */
+ HeadlessArrayOf<HBUINT16>
+ inputX; /* Array of input values (start with
+ * second glyph) */
+ ArrayOf<HBUINT16>
+ lookaheadX; /* Array of lookahead values's (to be
+ * matched after the input sequence) */
+ ArrayOf<LookupRecord>
+ lookupX; /* Array of LookupRecords--in
+ * design order) */
+ public:
+ DEFINE_SIZE_MIN (8);
+};
+
+struct ChainRuleSet
+{
+ 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, 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, lookup_context); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ if (unlikely (c->lookup_limit_exceeded ())) return;
+
+ return
+ + hb_iter (rule)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const ChainRule &_) { _.closure_lookups (c); })
+ ;
+ }
+
+ 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, 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, ChainContextApplyLookupContext &lookup_context) const
+ {
+ TRACE_APPLY (this);
+ return_trace (
+ + hb_iter (rule)
+ | hb_map (hb_add (this))
+ | hb_map ([&] (const ChainRule &_) { return _.apply (c, lookup_context); })
+ | hb_any
+ )
+ ;
+ }
+
+ 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 OffsetTo<ChainRule>& _ : rule)
+ {
+ if (!_) continue;
+ auto *o = out->rule.serialize_append (c->serializer);
+ if (unlikely (!o)) continue;
+
+ auto o_snap = c->serializer->snapshot ();
+ 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:
+ OffsetArrayOf<ChainRule>
+ rule; /* Array of ChainRule tables
+ * ordered by preference */
+ public:
+ DEFINE_SIZE_ARRAY (2, rule);
+};
+
+struct ChainContextFormat1
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ struct ChainContextClosureLookupContext lookup_context = {
+ {intersects_glyph},
+ {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
+ ;
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ struct ChainContextClosureLookupContext lookup_context = {
+ {intersects_glyph},
+ {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 (c, lookup_context); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ + hb_iter (ruleSet)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); })
+ ;
+ }
+
+ 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},
+ {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},
+ {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 ();
+ 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 (c->serializer, out)
+ .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 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetArrayOf<ChainRuleSet>
+ ruleSet; /* Array of ChainRuleSet tables
+ * ordered by Coverage Index */
+ public:
+ DEFINE_SIZE_ARRAY (6, ruleSet);
+};
+
+struct ChainContextFormat2
+{
+ 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;
+
+ struct ChainContextClosureLookupContext lookup_context = {
+ {intersects_class},
+ {&backtrack_class_def,
+ &input_class_def,
+ &lookahead_class_def}
+ };
+
+ 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) &&
+ p.second.intersects (glyphs, lookup_context); })
+ | hb_any
+ ;
+ }
+ void closure (hb_closure_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;
+
+ struct ChainContextClosureLookupContext lookup_context = {
+ {intersects_class},
+ {&backtrack_class_def,
+ &input_class_def,
+ &lookahead_class_def}
+ };
+
+ return
+ + hb_enumerate (ruleSet)
+ | hb_filter ([&] (unsigned _)
+ { return input_class_def.intersects_class (c->glyphs, _); },
+ hb_first)
+ | hb_map (hb_second)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const ChainRuleSet &_) { _.closure (c, lookup_context); })
+ ;
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ + hb_iter (ruleSet)
+ | hb_map (hb_add (this))
+ | hb_apply ([&] (const ChainRuleSet &_) { _.closure_lookups (c); })
+ ;
+ }
+
+ 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},
+ {&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; }
+
+ 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 ClassDef &backtrack_class_def = this+backtrackClassDef;
+ const ClassDef &input_class_def = this+inputClassDef;
+ const ClassDef &lookahead_class_def = this+lookaheadClassDef;
+
+ index = input_class_def.get_class (c->buffer->cur().codepoint);
+ const ChainRuleSet &rule_set = this+ruleSet[index];
+ struct ChainContextApplyLookupContext lookup_context = {
+ {match_class},
+ {&backtrack_class_def,
+ &input_class_def,
+ &lookahead_class_def}
+ };
+ 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;
+ out->backtrackClassDef.serialize_subset (c, backtrackClassDef, this, &backtrack_klass_map);
+ if (unlikely (!c->serializer->check_success (!backtrack_klass_map.in_error ())))
+ return_trace (false);
+
+ // subset inputClassDef based on glyphs survived in Coverage subsetting
+ hb_map_t input_klass_map;
+ out->inputClassDef.serialize_subset (c, inputClassDef, this, &input_klass_map);
+ if (unlikely (!c->serializer->check_success (!input_klass_map.in_error ())))
+ return_trace (false);
+
+ hb_map_t lookahead_klass_map;
+ out->lookaheadClassDef.serialize_subset (c, lookaheadClassDef, this, &lookahead_klass_map);
+ if (unlikely (!c->serializer->check_success (!lookahead_klass_map.in_error ())))
+ return_trace (false);
+
+ unsigned non_zero_index = 0, 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;
+ for (const OffsetTo<ChainRuleSet>& _ : + hb_enumerate (ruleSet)
+ | hb_filter (input_klass_map, hb_first)
+ | hb_map (hb_second))
+ {
+ auto *o = out->ruleSet.serialize_append (c->serializer);
+ if (unlikely (!o))
+ {
+ ret = false;
+ break;
+ }
+ if (o->serialize_subset (c, _, this,
+ lookup_map,
+ &backtrack_klass_map,
+ &input_klass_map,
+ &lookahead_klass_map))
+ non_zero_index = index;
+
+ index++;
+ }
+
+ if (!ret) return_trace (ret);
+
+ //prune empty trailing ruleSets
+ --index;
+ while (index > non_zero_index)
+ {
+ out->ruleSet.pop ();
+ index--;
+ }
+
+ 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 */
+ OffsetTo<Coverage>
+ coverage; /* Offset to Coverage table--from
+ * beginning of table */
+ OffsetTo<ClassDef>
+ backtrackClassDef; /* Offset to glyph ClassDef table
+ * containing backtrack sequence
+ * data--from beginning of table */
+ OffsetTo<ClassDef>
+ inputClassDef; /* Offset to glyph ClassDef
+ * table containing input sequence
+ * data--from beginning of table */
+ OffsetTo<ClassDef>
+ lookaheadClassDef; /* Offset to glyph ClassDef table
+ * containing lookahead sequence
+ * data--from beginning of table */
+ OffsetArrayOf<ChainRuleSet>
+ ruleSet; /* Array of ChainRuleSet tables
+ * ordered by class */
+ public:
+ DEFINE_SIZE_ARRAY (12, ruleSet);
+};
+
+struct ChainContextFormat3
+{
+ bool intersects (const hb_set_t *glyphs) const
+ {
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+
+ if (!(this+input[0]).intersects (glyphs))
+ return false;
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ struct ChainContextClosureLookupContext lookup_context = {
+ {intersects_coverage},
+ {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);
+ }
+
+ void closure (hb_closure_context_t *c) const
+ {
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+
+ if (!(this+input[0]).intersects (c->glyphs))
+ return;
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ struct ChainContextClosureLookupContext lookup_context = {
+ {intersects_coverage},
+ {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,
+ lookup_context);
+ }
+
+ void closure_lookups (hb_closure_lookups_context_t *c) const
+ {
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (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 OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+
+ (this+input[0]).collect_coverage (c->input);
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (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 OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ struct ChainContextApplyLookupContext lookup_context = {
+ {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 OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ return this+input[0];
+ }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ TRACE_APPLY (this);
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+
+ unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint);
+ if (likely (index == NOT_COVERED)) return_trace (false);
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ struct ChainContextApplyLookupContext lookup_context = {
+ {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<OffsetArrayOf<Coverage>> ();
+
+ if (unlikely (!c->serializer->allocate_size<HBUINT16> (HBUINT16::static_size))) return_trace (false);
+
+ + it
+ | hb_apply (subset_offset_array (c, *out, base))
+ ;
+
+ return_trace (out->len);
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+
+ auto *out = c->serializer->start_embed (this);
+ if (unlikely (!out)) return_trace (false);
+ if (unlikely (!c->serializer->embed (this->format))) return_trace (false);
+
+ if (!serialize_coverage_offsets (c, backtrack.iter (), this))
+ return_trace (false);
+
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ if (!serialize_coverage_offsets (c, input.iter (), this))
+ return_trace (false);
+
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ if (!serialize_coverage_offsets (c, lookahead.iter (), this))
+ return_trace (false);
+
+ const ArrayOf<LookupRecord> &lookupRecord = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ HBUINT16 lookupCount;
+ lookupCount = lookupRecord.len;
+ if (!c->serializer->copy (lookupCount)) return_trace (false);
+
+ const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups;
+ for (unsigned i = 0; i < (unsigned) lookupCount; i++)
+ if (!c->serializer->copy (lookupRecord[i], lookup_map)) return_trace (false);
+
+ return_trace (true);
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (!backtrack.sanitize (c, this)) return_trace (false);
+ const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage>> (backtrack);
+ if (!input.sanitize (c, this)) return_trace (false);
+ if (!input.len) return_trace (false); /* To be consistent with Context. */
+ const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage>> (input);
+ if (!lookahead.sanitize (c, this)) return_trace (false);
+ const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord>> (lookahead);
+ return_trace (lookup.sanitize (c));
+ }
+
+ protected:
+ HBUINT16 format; /* Format identifier--format = 3 */
+ OffsetArrayOf<Coverage>
+ backtrack; /* Array of coverage tables
+ * in backtracking sequence, in glyph
+ * sequence order */
+ OffsetArrayOf<Coverage>
+ inputX ; /* Array of coverage
+ * tables in input sequence, in glyph
+ * sequence order */
+ OffsetArrayOf<Coverage>
+ lookaheadX; /* Array of coverage tables
+ * in lookahead sequence, in glyph
+ * sequence order */
+ ArrayOf<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
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
+ case 2: return_trace (c->dispatch (u.format2, hb_forward<Ts> (ds)...));
+ case 3: return_trace (c->dispatch (u.format3, hb_forward<Ts> (ds)...));
+ default:return_trace (c->default_return_value ());
+ }
+ }
+
+ protected:
+ union {
+ HBUINT16 format; /* Format identifier */
+ ChainContextFormat1 format1;
+ ChainContextFormat2 format2;
+ ChainContextFormat3 format3;
+ } 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 LOffsetTo<typename T::SubTable> &> (extensionOffset); }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, format);
+ if (unlikely (!c->may_dispatch (this, this))) return_trace (c->no_dispatch_return_value ());
+ return_trace (get_subtable<typename T::SubTable> ().dispatch (c, get_type (), hb_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) &&
+ extensionLookupType != T::SubTable::Extension);
+ }
+
+ 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);
+ }
+ }
+
+ template <typename context_t, typename ...Ts>
+ typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
+ {
+ TRACE_DISPATCH (this, u.format);
+ if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
+ switch (u.format) {
+ case 1: return_trace (u.format1.dispatch (c, hb_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>
+ void init (const TLookup &lookup)
+ {
+ digest.init ();
+ lookup.collect_coverage (&digest);
+
+ subtables.init ();
+ OT::hb_get_subtables_context_t c_get_subtables (subtables);
+ lookup.dispatch (&c_get_subtables);
+ }
+ void fini () { subtables.fini (); }
+
+ bool may_have (hb_codepoint_t g) const
+ { return digest.may_have (g); }
+
+ bool apply (hb_ot_apply_context_t *c) const
+ {
+ for (unsigned int i = 0; i < subtables.length; i++)
+ if (subtables[i].apply (c))
+ return true;
+ return false;
+ }
+
+ private:
+ hb_set_digest_t digest;
+ hb_get_subtables_context_t::array_t subtables;
+};
+
+struct GSUBGPOS
+{
+ bool has_data () const { return version.to_int (); }
+ unsigned int get_script_count () const
+ { return (this+scriptList).len; }
+ const Tag& get_script_tag (unsigned int i) const
+ { return (this+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 (this+scriptList).get_tags (start_offset, script_count, script_tags); }
+ const Script& get_script (unsigned int i) const
+ { return (this+scriptList)[i]; }
+ bool find_script_index (hb_tag_t tag, unsigned int *index) const
+ { return (this+scriptList).find_index (tag, index); }
+
+ unsigned int get_feature_count () const
+ { return (this+featureList).len; }
+ hb_tag_t get_feature_tag (unsigned int i) const
+ { return i == Index::NOT_FOUND_INDEX ? HB_TAG_NONE : (this+featureList).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 (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
+ const Feature& get_feature (unsigned int i) const
+ { return (this+featureList)[i]; }
+ bool find_feature_index (hb_tag_t tag, unsigned int *index) const
+ { return (this+featureList).find_index (tag, index); }
+
+ unsigned int get_lookup_count () const
+ { return (this+lookupList).len; }
+ const Lookup& get_lookup (unsigned int i) const
+ { return (this+lookupList)[i]; }
+
+ 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 (version.to_int () >= 0x00010001u ? this+featureVars : Null (FeatureVariations))
+ .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 &&
+ version.to_int () >= 0x00010001u)
+ {
+ const Feature *feature = (this+featureVars).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,
+ hb_set_t *lookup_indexes /* OUT */) const
+ {
+#ifndef HB_NO_VAR
+ if (version.to_int () >= 0x00010001u)
+ (this+featureVars).collect_lookups (feature_indexes, lookup_indexes);
+#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;
+ OT::hb_closure_lookups_context_t c (face, glyphs, &visited_lookups, &inactive_lookups);
+
+ for (unsigned lookup_index : + hb_iter (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);
+ }
+
+ template <typename TLookup>
+ bool subset (hb_subset_layout_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ auto *out = c->subset_context->serializer->embed (*this);
+ if (unlikely (!out)) return_trace (false);
+
+ typedef LookupOffsetList<TLookup> TLookupList;
+ reinterpret_cast<OffsetTo<TLookupList> &> (out->lookupList)
+ .serialize_subset (c->subset_context,
+ reinterpret_cast<const OffsetTo<TLookupList> &> (lookupList),
+ this,
+ c);
+
+ reinterpret_cast<OffsetTo<RecordListOfFeature> &> (out->featureList)
+ .serialize_subset (c->subset_context,
+ reinterpret_cast<const OffsetTo<RecordListOfFeature> &> (featureList),
+ this,
+ c);
+
+ out->scriptList.serialize_subset (c->subset_context,
+ scriptList,
+ this,
+ c);
+
+#ifndef HB_NO_VAR
+ if (version.to_int () >= 0x00010001u)
+ {
+ bool ret = out->featureVars.serialize_subset (c->subset_context, featureVars, this, c);
+ if (!ret)
+ {
+ out->version.major = 1;
+ out->version.minor = 0;
+ }
+ }
+#endif
+
+ return_trace (true);
+ }
+
+ void closure_features (const hb_map_t *lookup_indexes, /* IN */
+ hb_set_t *feature_indexes /* OUT */) const
+ {
+ unsigned int feature_count = hb_min (get_feature_count (), (unsigned) HB_MAX_FEATURES);
+ for (unsigned i = 0; i < feature_count; i++)
+ {
+ const Feature& f = get_feature (i);
+ if ((!f.featureParams.is_null ()) || f.intersects_lookup_indexes (lookup_indexes))
+ feature_indexes->add (i);
+ }
+#ifndef HB_NO_VAR
+ if (version.to_int () >= 0x00010001u)
+ (this+featureVars).closure_features (lookup_indexes, feature_indexes);
+#endif
+ }
+
+ unsigned int get_size () const
+ {
+ return min_size +
+ (version.to_int () >= 0x00010001u ? featureVars.static_size : 0);
+ }
+
+ template <typename TLookup>
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ typedef OffsetListOf<TLookup> TLookupList;
+ if (unlikely (!(version.sanitize (c) &&
+ likely (version.major == 1) &&
+ scriptList.sanitize (c, this) &&
+ featureList.sanitize (c, this) &&
+ reinterpret_cast<const 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 T>
+ struct accelerator_t
+ {
+ void init (hb_face_t *face)
+ {
+ this->table = hb_sanitize_context_t ().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_ot_layout_lookup_accelerator_t *) calloc (this->lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t));
+ if (unlikely (!this->accels))
+ this->lookup_count = 0;
+
+ for (unsigned int i = 0; i < this->lookup_count; i++)
+ this->accels[i].init (table->get_lookup (i));
+ }
+
+ void fini ()
+ {
+ for (unsigned int i = 0; i < this->lookup_count; i++)
+ this->accels[i].fini ();
+ free (this->accels);
+ this->table.destroy ();
+ }
+
+ hb_blob_ptr_t<T> table;
+ unsigned int lookup_count;
+ hb_ot_layout_lookup_accelerator_t *accels;
+ };
+
+ protected:
+ FixedVersion<>version; /* Version of the GSUB/GPOS table--initially set
+ * to 0x00010000u */
+ OffsetTo<ScriptList>
+ scriptList; /* ScriptList table */
+ OffsetTo<FeatureList>
+ featureList; /* FeatureList table */
+ OffsetTo<LookupList>
+ lookupList; /* LookupList table */
+ LOffsetTo<FeatureVariations>
+ featureVars; /* Offset to Feature Variations
+ table--from beginning of table
+ * (may be NULL). Introduced
+ * in version 0x00010001. */
+ public:
+ DEFINE_SIZE_MIN (10);
+};
+
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_LAYOUT_GSUBGPOS_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-jstf-table.hh b/thirdparty/harfbuzz/src/hb-ot-layout-jstf-table.hh
new file mode 100644
index 0000000000..ffd2bf4574
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout-jstf-table.hh
@@ -0,0 +1,235 @@
+/*
+ * 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 OffsetListOf<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:
+ OffsetTo<JstfModList>
+ shrinkageEnableGSUB; /* Offset to Shrinkage Enable GSUB
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<JstfModList>
+ shrinkageDisableGSUB; /* Offset to Shrinkage Disable GSUB
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<JstfModList>
+ shrinkageEnableGPOS; /* Offset to Shrinkage Enable GPOS
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<JstfModList>
+ shrinkageDisableGPOS; /* Offset to Shrinkage Disable GPOS
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<JstfMax>
+ shrinkageJstfMax; /* Offset to Shrinkage JstfMax table--
+ * from beginning of JstfPriority table
+ * --may be NULL */
+ OffsetTo<JstfModList>
+ extensionEnableGSUB; /* Offset to Extension Enable GSUB
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<JstfModList>
+ extensionDisableGSUB; /* Offset to Extension Disable GSUB
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<JstfModList>
+ extensionEnableGPOS; /* Offset to Extension Enable GPOS
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<JstfModList>
+ extensionDisableGPOS; /* Offset to Extension Disable GPOS
+ * JstfModList table--from beginning of
+ * JstfPriority table--may be NULL */
+ OffsetTo<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 : OffsetListOf<JstfPriority>
+{
+ bool sanitize (hb_sanitize_context_t *c,
+ const Record_sanitize_closure_t * = nullptr) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (OffsetListOf<JstfPriority>::sanitize (c));
+ }
+};
+
+
+/*
+ * ExtenderGlyphs -- Extender Glyph Table
+ */
+
+typedef SortedArrayOf<HBGlyphID> 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:
+ OffsetTo<ExtenderGlyphs>
+ extenderGlyphs; /* Offset to ExtenderGlyph table--from beginning
+ * of JstfScript table-may be NULL */
+ OffsetTo<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) &&
+ 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/thirdparty/harfbuzz/src/hb-ot-layout.cc b/thirdparty/harfbuzz/src/hb-ot-layout.cc
new file mode 100644
index 0000000000..46408bb9d3
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.cc
@@ -0,0 +1,1993 @@
+/*
+ * 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" // Just so we compile it; unused otherwise.
+#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.
+
+/**
+ * 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.
+ **/
+
+
+/*
+ * 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);
+
+ kern.apply (&c);
+}
+#endif
+
+
+/*
+ * GDEF
+ */
+
+bool
+OT::GDEF::is_blocklisted (hb_blob_t *blob,
+ hb_face_t *face) const
+{
+#ifdef HB_NO_OT_LAYOUT_BLACKLIST
+ 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 OT::GDEF &gdef = *font->face->table.GDEF->table;
+ unsigned int count = buffer->len;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
+ _hb_glyph_info_clear_lig_props (&buffer->info[i]);
+ buffer->info[i].syllable() = 0;
+ }
+}
+
+/* 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) (allow-none): 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.
+ *
+ **/
+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) (allow-none): 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.
+ *
+ **/
+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
+OT::GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED,
+ hb_face_t *face) const
+{
+#ifdef HB_NO_OT_LAYOUT_BLACKLIST
+ return false;
+#endif
+ return false;
+}
+
+bool
+OT::GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED,
+ hb_face_t *face HB_UNUSED) const
+{
+#ifdef HB_NO_OT_LAYOUT_BLACKLIST
+ 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) (allow-none): 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.
+ *
+ **/
+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 requested script tag
+ * @chosen_script: (out): #hb_tag_t of the script tag requested
+ *
+ * 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): The index of the requested script
+ * @chosen_script: (out): #hb_tag_t of the requested script
+ *
+ * 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_OT_LAYOUT_NO_SCRIPT_INDEX;
+ 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) (allow-none): 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.
+ *
+ **/
+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 og 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
+ **/
+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) (allow-none): 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.
+ *
+ **/
+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: ??
+ * Deprecated: ??
+ **/
+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_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 a given language tag in the specified face's GSUB table
+ * or GPOS table, underneath the specified script index.
+ *
+ * Return value: true if the language tag 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 */)
+{
+ 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))
+ return true;
+ }
+
+ /* try finding 'dflt' */
+ if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
+ return false;
+
+ if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
+ return false;
+}
+
+
+/**
+ * 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
+ *
+ **/
+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) (allow-none): 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.
+ **/
+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) (allow-none): 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.
+ *
+ **/
+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
+ *
+ **/
+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) (allow-none): 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.
+ *
+ * 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.
+ *
+ * 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_indexes_)
+ : g (get_gsubgpos_table (face, table_tag)),
+ feature_indexes (feature_indexes_),
+ script_count (0),langsys_count (0), feature_index_count (0) {}
+
+ 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_indexes;
+
+ 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,
+ const hb_tag_t *features)
+{
+ if (c->visited (l)) return;
+
+ if (!features)
+ {
+ /* All features. */
+ if (l.has_required_feature () && !c->visited_feature_indices (1))
+ c->feature_indexes->add (l.get_required_feature_index ());
+
+ if (!c->visited_feature_indices (l.featureIndex.len))
+ l.add_feature_indexes_to (c->feature_indexes);
+ }
+ else
+ {
+ /* Ugh. Any faster way? */
+ for (; *features; features++)
+ {
+ hb_tag_t feature_tag = *features;
+ 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 (feature_tag == c->g.get_feature_tag (feature_index))
+ {
+ c->feature_indexes->add (feature_index);
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+script_collect_features (hb_collect_features_context_t *c,
+ const OT::Script &s,
+ const hb_tag_t *languages,
+ const hb_tag_t *features)
+{
+ if (c->visited (s)) return;
+
+ if (!languages)
+ {
+ /* All languages. */
+ if (s.has_default_lang_sys ())
+ langsys_collect_features (c,
+ s.get_default_lang_sys (),
+ features);
+
+ 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),
+ features);
+ }
+ 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),
+ features);
+ }
+ }
+}
+
+
+/**
+ * hb_ot_layout_collect_features:
+ * @face: #hb_face_t to work upon
+ * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @scripts: The array of scripts to collect features for
+ * @languages: The array of languages to collect features for
+ * @features: The array of features to collect
+ * @feature_indexes: (out): The array 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);
+ 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,
+ features);
+ }
+ 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,
+ features);
+ }
+ }
+}
+
+
+/**
+ * hb_ot_layout_collect_lookups:
+ * @face: #hb_face_t to work upon
+ * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
+ * @scripts: The array of scripts to collect lookups for
+ * @languages: The array of languages to collect lookups for
+ * @features: The array of features to collect lookups for
+ * @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 (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID;
+ hb_set_next (&feature_indexes, &feature_index);)
+ g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
+
+ g.feature_variation_collect_lookups (&feature_indexes, 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 substitued 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 coorinates
+ * @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.
+ *
+ **/
+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) (allow-none): 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.
+ *
+ **/
+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
+ *
+ **/
+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 substitutions should be context-free
+ *
+ * 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)
+{
+ if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false;
+ OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
+
+ const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
+ return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]);
+}
+
+
+/**
+ * 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);
+}
+
+void
+hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer,
+ 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 = 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 (filter (&info[i]))
+ {
+ /* Merge clusters.
+ * Same logic as buffer->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--)
+ buffer->set_cluster (info[k - 1], cluster, mask);
+ }
+ continue;
+ }
+
+ if (i + 1 < count)
+ buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
+
+ continue;
+ }
+
+ if (j != i)
+ {
+ info[j] = info[i];
+ pos[j] = pos[i];
+ }
+ j++;
+ }
+ buffer->len = j;
+}
+
+/**
+ * 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;
+ OT::hb_closure_context_t c (face, glyphs, &done_lookups);
+
+ 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;
+ OT::hb_closure_context_t c (face, glyphs, &done_lookups);
+ const OT::GSUB& gsub = *face->table.GSUB->table;
+
+ unsigned int iteration_count = 0;
+ unsigned int glyphs_length;
+ do
+ {
+ glyphs_length = glyphs->get_population ();
+ if (lookups)
+ {
+ for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);)
+ 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 ());
+}
+
+/*
+ * OT::GPOS
+ */
+
+
+/**
+ * hb_ot_layout_has_positioning:
+ * @face: #hb_face_t to work upon
+ *
+ * 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)
+{
+ OT::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)
+{
+ OT::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)
+{
+ OT::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 OT::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 &params = 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) (allow-none): The ‘name’ table name ID that specifies a string
+ * for a user-interface label for this feature. (May be NULL.)
+ * @tooltip_id: (out) (allow-none): 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) (allow-none): The ‘name’ table name ID that specifies sample text
+ * that illustrates the effect of this feature. (May be NULL.)
+ * @num_named_parameters: (out) (allow-none): Number of named parameters. (May be zero.)
+ * @first_param_id: (out) (allow-none): 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) (allow-none): 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 inplace = false;
+ typedef OT::SubstLookup Lookup;
+
+ GSUBProxy (hb_face_t *face) :
+ table (*face->table.GSUB->table),
+ accels (face->table.GSUB->accels) {}
+
+ const OT::GSUB &table;
+ const OT::hb_ot_layout_lookup_accelerator_t *accels;
+};
+
+struct GPOSProxy
+{
+ static constexpr unsigned table_index = 1u;
+ static constexpr bool inplace = true;
+ typedef OT::PosLookup Lookup;
+
+ GPOSProxy (hb_face_t *face) :
+ table (*face->table.GPOS->table),
+ accels (face->table.GPOS->accels) {}
+
+ const OT::GPOS &table;
+ const OT::hb_ot_layout_lookup_accelerator_t *accels;
+};
+
+
+static inline bool
+apply_forward (OT::hb_ot_apply_context_t *c,
+ const OT::hb_ot_layout_lookup_accelerator_t &accel)
+{
+ bool ret = false;
+ hb_buffer_t *buffer = c->buffer;
+ while (buffer->idx < buffer->len && buffer->successful)
+ {
+ bool applied = false;
+ if (accel.may_have (buffer->cur().codepoint) &&
+ (buffer->cur().mask & c->lookup_mask) &&
+ c->check_glyph_property (&buffer->cur(), c->lookup_props))
+ {
+ applied = accel.apply (c);
+ }
+
+ if (applied)
+ ret = true;
+ else
+ buffer->next_glyph ();
+ }
+ return ret;
+}
+
+static inline bool
+apply_backward (OT::hb_ot_apply_context_t *c,
+ const OT::hb_ot_layout_lookup_accelerator_t &accel)
+{
+ bool ret = false;
+ hb_buffer_t *buffer = c->buffer;
+ do
+ {
+ if (accel.may_have (buffer->cur().codepoint) &&
+ (buffer->cur().mask & c->lookup_mask) &&
+ c->check_glyph_property (&buffer->cur(), c->lookup_props))
+ ret |= accel.apply (c);
+
+ /* The reverse lookup doesn't "advance" cursor (for good reason). */
+ buffer->idx--;
+
+ }
+ while ((int) buffer->idx >= 0);
+ return ret;
+}
+
+template <typename Proxy>
+static inline void
+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;
+
+ if (unlikely (!buffer->len || !c->lookup_mask))
+ return;
+
+ c->set_lookup_props (lookup.get_props ());
+
+ if (likely (!lookup.is_reverse ()))
+ {
+ /* in/out forward substitution/positioning */
+ if (Proxy::table_index == 0u)
+ buffer->clear_output ();
+ buffer->idx = 0;
+
+ bool ret;
+ ret = apply_forward (c, accel);
+ if (ret)
+ {
+ if (!Proxy::inplace)
+ buffer->swap_buffers ();
+ else
+ assert (!buffer->has_separate_output ());
+ }
+ }
+ else
+ {
+ /* in-place backward substitution/positioning */
+ if (Proxy::table_index == 0u)
+ buffer->remove_output ();
+ buffer->idx = buffer->len - 1;
+
+ apply_backward (c, accel);
+ }
+}
+
+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);
+ c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
+
+ 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++)
+ {
+ unsigned int lookup_index = lookups[table_index][i].index;
+ if (!buffer->message (font, "start lookup %d", lookup_index)) continue;
+ c.set_lookup_index (lookup_index);
+ c.set_lookup_mask (lookups[table_index][i].mask);
+ c.set_auto_zwj (lookups[table_index][i].auto_zwj);
+ c.set_auto_zwnj (lookups[table_index][i].auto_zwnj);
+ if (lookups[table_index][i].random)
+ {
+ c.set_random (true);
+ buffer->unsafe_to_break_all ();
+ }
+ apply_string<Proxy> (&c,
+ proxy.table.get_lookup (lookup_index),
+ proxy.accels[lookup_index]);
+ (void) buffer->message (font, "end lookup %d", lookup_index);
+ }
+
+ if (stage->pause_func)
+ {
+ buffer->clear_output ();
+ stage->pause_func (plan, font, buffer);
+ }
+ }
+}
+
+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->message (font, "start table GSUB")) return;
+ apply (proxy, plan, font, buffer);
+ (void)buffer->message (font, "end table GSUB");
+}
+
+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->message (font, "start table GPOS")) return;
+ apply (proxy, plan, font, buffer);
+ (void)buffer->message (font, "end table GPOS");
+}
+
+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
+/**
+ * hb_ot_layout_get_baseline:
+ * @font: a font
+ * @baseline_tag: a baseline tag
+ * @direction: text direction.
+ * @script_tag: script tag.
+ * @language_tag: language tag.
+ * @coord: (out): baseline value if found.
+ *
+ * Fetches a baseline value from the face.
+ *
+ * Return value: 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. */)
+{
+ bool result = font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord);
+
+ if (result && coord)
+ *coord = HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (*coord) : font->em_scale_x (*coord);
+
+ return result;
+}
+#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; }
+
+ hb_face_t *face;
+
+ hb_get_glyph_alternates_dispatch_t (hb_face_t *face) :
+ face (face) {}
+
+ private:
+ template <typename T, typename ...Ts> auto
+ _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
+ ( obj.get_glyph_alternates (hb_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, hb_forward<Ts> (ds)...) )
+};
+
+/**
+ * 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) (allow-none): 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 (face);
+ 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;
+}
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.h b/thirdparty/harfbuzz/src/hb-ot-layout.h
new file mode 100644
index 0000000000..545d5f7fc4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.h
@@ -0,0 +1,462 @@
+/*
+ * 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
+ */
+
+#ifndef HB_OT_H_IN
+#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
+
+
+#define HB_OT_TAG_BASE HB_TAG('B','A','S','E')
+#define HB_OT_TAG_GDEF HB_TAG('G','D','E','F')
+#define HB_OT_TAG_GSUB HB_TAG('G','S','U','B')
+#define HB_OT_TAG_GPOS HB_TAG('G','P','O','S')
+#define HB_OT_TAG_JSTF HB_TAG('J','S','T','F')
+
+
+/*
+ * Script & Language tags.
+ */
+
+#define HB_OT_TAG_DEFAULT_SCRIPT HB_TAG ('D', 'F', 'L', 'T')
+#define HB_OT_TAG_DEFAULT_LANGUAGE HB_TAG ('d', 'f', 'l', 't')
+
+/**
+ * HB_OT_MAX_TAGS_PER_SCRIPT:
+ *
+ * Since: 2.0.0
+ **/
+#define HB_OT_MAX_TAGS_PER_SCRIPT 3u
+/**
+ * HB_OT_MAX_TAGS_PER_LANGUAGE:
+ *
+ * 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
+ */
+
+#define HB_OT_LAYOUT_NO_SCRIPT_INDEX 0xFFFFu
+#define HB_OT_LAYOUT_NO_FEATURE_INDEX 0xFFFFu
+#define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX 0xFFFFu
+#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_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_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 */);
+
+#ifdef HB_NOT_IMPLEMENTED
+typedef struct
+{
+ const hb_codepoint_t *before,
+ unsigned int before_length,
+ const hb_codepoint_t *input,
+ unsigned int input_length,
+ const hb_codepoint_t *after,
+ unsigned int after_length,
+} hb_ot_layout_glyph_sequence_t;
+
+typedef hb_bool_t
+(*hb_ot_layout_glyph_sequence_func_t) (hb_font_t *font,
+ hb_tag_t table_tag,
+ unsigned int lookup_index,
+ const hb_ot_layout_glyph_sequence_t *sequence,
+ void *user_data);
+
+HB_EXTERN void
+Xhb_ot_layout_lookup_enumerate_sequences (hb_face_t *face,
+ hb_tag_t table_tag,
+ unsigned int lookup_index,
+ hb_ot_layout_glyph_sequence_func_t callback,
+ void *user_data);
+#endif
+
+/* 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);
+
+
+#ifdef HB_NOT_IMPLEMENTED
+/* Note: You better have GDEF when using this API, or marks won't do much. */
+HB_EXTERN hb_bool_t
+Xhb_ot_layout_lookup_substitute (hb_font_t *font,
+ unsigned int lookup_index,
+ const hb_ot_layout_glyph_sequence_t *sequence,
+ unsigned int out_size,
+ hb_codepoint_t *glyphs_out, /* OUT */
+ unsigned int *clusters_out, /* OUT */
+ unsigned int *out_length /* OUT */);
+#endif
+
+
+/*
+ * GPOS
+ */
+
+HB_EXTERN hb_bool_t
+hb_ot_layout_has_positioning (hb_face_t *face);
+
+#ifdef HB_NOT_IMPLEMENTED
+/* Note: You better have GDEF when using this API, or marks won't do much. */
+HB_EXTERN hb_bool_t
+Xhb_ot_layout_lookup_position (hb_font_t *font,
+ unsigned int lookup_index,
+ const hb_ot_layout_glyph_sequence_t *sequence,
+ hb_glyph_position_t *positions /* IN / OUT */);
+#endif
+
+/* 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_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_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_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,
+ * 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 https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags
+ *
+ * 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_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_MATH = HB_TAG ('m','a','t','h'),
+
+ _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
+} hb_ot_layout_baseline_tag_t;
+
+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_END_DECLS
+
+#endif /* HB_OT_LAYOUT_H */
diff --git a/thirdparty/harfbuzz/src/hb-ot-layout.hh b/thirdparty/harfbuzz/src/hb-ot-layout.hh
new file mode 100644
index 0000000000..f3bb15581a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-layout.hh
@@ -0,0 +1,627 @@
+/*
+ * 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);
+
+HB_INTERNAL void
+hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer,
+ bool (*filter) (const hb_glyph_info_t *info));
+
+namespace OT {
+ struct hb_ot_apply_context_t;
+ struct SubstLookup;
+ struct hb_ot_layout_lookup_accelerator_t;
+}
+
+HB_INTERNAL 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);
+
+
+/* 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;
+}
+
+static inline void
+_hb_clear_syllables (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++)
+ info[i].syllable() = 0;
+}
+
+
+/* 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 three 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..3, 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_range<hb_codepoint_t> (u, 0x180Bu, 0x180Du))) 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_ligated (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_ligated (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_ligated (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;
+}
+/* Loop over grapheme. Based on foreach_cluster(). */
+#define foreach_grapheme(buffer, start, end) \
+ for (unsigned int \
+ _count = buffer->len, \
+ start = 0, end = _count ? _hb_next_grapheme (buffer, 0) : 0; \
+ start < _count; \
+ start = end, end = _hb_next_grapheme (buffer, start))
+
+static inline unsigned int
+_hb_next_grapheme (hb_buffer_t *buffer, unsigned int start)
+{
+ hb_glyph_info_t *info = buffer->info;
+ unsigned int count = buffer->len;
+
+ while (++start < count && _hb_glyph_info_is_continuation (&info[start]))
+ ;
+
+ return start;
+}
+
+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 void
+_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]);
+}
+
+
+/* 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);
+ HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
+}
+
+static inline void
+_hb_buffer_deallocate_gsubgpos_vars (hb_buffer_t *buffer)
+{
+ HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
+ 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);
+ HB_BUFFER_ASSERT_VAR (buffer, syllable);
+}
+
+/* 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/thirdparty/harfbuzz/src/hb-ot-map.cc b/thirdparty/harfbuzz/src/hb-ot-map.cc
new file mode 100644
index 0000000000..e4bb4b6366
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-map.cc
@@ -0,0 +1,342 @@
+/*
+ * 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_)
+{
+ 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];
+}
+
+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)
+{
+ 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;
+ }
+
+ 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)
+{
+ static_assert ((!(HB_GLYPH_FLAG_DEFINED & (HB_GLYPH_FLAG_DEFINED + 1))), "");
+ unsigned int global_bit_mask = HB_GLYPH_FLAG_DEFINED + 1;
+ unsigned int global_bit_shift = hb_popcount (HB_GLYPH_FLAG_DEFINED);
+
+ 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)
+ {
+ feature_infos.qsort ();
+ unsigned int j = 0;
+ for (unsigned int i = 1; i < feature_infos.length; i++)
+ if (feature_infos[i].tag != feature_infos[j].tag)
+ feature_infos[++j] = feature_infos[i];
+ else {
+ if (feature_infos[i].flags & F_GLOBAL) {
+ feature_infos[j].flags |= F_GLOBAL;
+ feature_infos[j].max_value = feature_infos[i].max_value;
+ feature_infos[j].default_value = feature_infos[i].default_value;
+ } else {
+ if (feature_infos[j].flags & F_GLOBAL)
+ feature_infos[j].flags ^= F_GLOBAL;
+ feature_infos[j].max_value = hb_max (feature_infos[j].max_value, feature_infos[i].max_value);
+ /* Inherit default_value from j */
+ }
+ feature_infos[j].flags |= (feature_infos[i].flags & F_HAS_FALLBACK);
+ feature_infos[j].stage[0] = hb_min (feature_infos[j].stage[0], feature_infos[i].stage[0]);
+ feature_infos[j].stage[1] = hb_min (feature_infos[j].stage[1], feature_infos[i].stage[1]);
+ }
+ feature_infos.shrink (j + 1);
+ }
+
+
+ /* Allocate bits now */
+ unsigned int next_bit = global_bit_shift + 1;
+
+ for (unsigned int i = 0; i < feature_infos.length; 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 > 8 * sizeof (hb_mask_t))
+ 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];
+
+ found |= (bool) hb_ot_layout_language_find_feature (face,
+ table_tags[table_index],
+ script_index[table_index],
+ language_index[table_index],
+ info->tag,
+ &feature_index[table_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);
+ 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 */
+
+
+ add_gsub_pause (nullptr);
+ add_gpos_pause (nullptr);
+
+ for (unsigned int table_index = 0; table_index < 2; table_index++)
+ {
+ /* Collect lookup indices for features */
+
+ 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 (unsigned i = 0; i < m.features.length; i++)
+ if (m.features[i].stage[table_index] == stage)
+ add_lookups (m, table_index,
+ m.features[i].index[table_index],
+ key.variations_index[table_index],
+ m.features[i].mask,
+ m.features[i].auto_zwnj,
+ m.features[i].auto_zwj,
+ m.features[i].random);
+
+ /* Sort lookups and merge duplicates */
+ if (last_num_lookups < m.lookups[table_index].length)
+ {
+ m.lookups[table_index].qsort (last_num_lookups, m.lookups[table_index].length);
+
+ unsigned int j = last_num_lookups;
+ for (unsigned int i = j + 1; i < m.lookups[table_index].length; i++)
+ if (m.lookups[table_index][i].index != m.lookups[table_index][j].index)
+ m.lookups[table_index][++j] = m.lookups[table_index][i];
+ else
+ {
+ m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask;
+ m.lookups[table_index][j].auto_zwnj &= m.lookups[table_index][i].auto_zwnj;
+ m.lookups[table_index][j].auto_zwj &= m.lookups[table_index][i].auto_zwj;
+ }
+ m.lookups[table_index].shrink (j + 1);
+ }
+
+ last_num_lookups = m.lookups[table_index].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/thirdparty/harfbuzz/src/hb-ot-map.hh b/thirdparty/harfbuzz/src/hb-ot-map.hh
new file mode 100644
index 0000000000..7629a869bd
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-map.hh
@@ -0,0 +1,284 @@
+/*
+ * 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;
+
+ int cmp (const hb_tag_t tag_) const
+ { return tag_ < tag ? -1 : tag_ > tag ? 1 : 0; }
+ };
+
+ struct lookup_map_t {
+ unsigned short index;
+ unsigned short auto_zwnj : 1;
+ unsigned short auto_zwj : 1;
+ unsigned short random : 1;
+ hb_mask_t mask;
+
+ 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;
+ }
+ };
+
+ typedef void (*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 ()
+ {
+ memset (this, 0, sizeof (*this));
+
+ features.init ();
+ for (unsigned int table_index = 0; table_index < 2; table_index++)
+ {
+ lookups[table_index].init ();
+ stages[table_index].init ();
+ }
+ }
+ 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;
+ }
+
+ void get_stage_lookups (unsigned int table_index, unsigned int stage,
+ const struct lookup_map_t **plookups, unsigned int *lookup_count) const
+ {
+ if (unlikely (stage == UINT_MAX)) {
+ *plookups = nullptr;
+ *lookup_count = 0;
+ return;
+ }
+ assert (stage <= stages[table_index].length);
+ 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;
+ *plookups = end == start ? nullptr : &lookups[table_index][start];
+ *lookup_count = 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;
+
+ 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. */
+};
+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);
+
+ 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);
+
+ 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;
+
+ 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/thirdparty/harfbuzz/src/hb-ot-math-table.hh b/thirdparty/harfbuzz/src/hb-ot-math-table.hh
new file mode 100644
index 0000000000..26aa080603
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-math-table.hh
@@ -0,0 +1,728 @@
+/*
+ * 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); }
+
+ 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 */
+ OffsetTo<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
+{
+ 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 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:
+ OffsetTo<Coverage> coverage; /* Offset to Coverage table -
+ * from the beginning of
+ * MathItalicsCorrectionInfo
+ * table. */
+ ArrayOf<MathValueRecord> italicsCorrection; /* Array of MathValueRecords
+ * defining italics correction
+ * values for each
+ * covered glyph. */
+
+ public:
+ DEFINE_SIZE_ARRAY (4, italicsCorrection);
+};
+
+struct MathTopAccentAttachment
+{
+ 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:
+ OffsetTo<Coverage> topAccentCoverage; /* Offset to Coverage table -
+ * from the beginning of
+ * MathTopAccentAttachment
+ * table. */
+ ArrayOf<MathValueRecord> topAccentAttachment; /* Array of MathValueRecords
+ * defining top accent
+ * attachment points for each
+ * covered glyph. */
+
+ public:
+ DEFINE_SIZE_ARRAY (2 + 2, topAccentAttachment);
+};
+
+struct MathKern
+{
+ 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) &&
+ 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);
+ }
+
+ 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
+{
+ 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);
+ }
+
+ protected:
+ /* Offset to MathKern table for each corner -
+ * from the beginning of MathKernInfo table. May be NULL. */
+ OffsetTo<MathKern> mathKern[4];
+
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+struct MathKernInfo
+{
+ 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);
+ }
+
+ protected:
+ OffsetTo<Coverage>
+ mathKernCoverage;
+ /* Offset to Coverage table -
+ * from the beginning of the
+ * MathKernInfo table. */
+ ArrayOf<MathKernInfoRecord>
+ mathKernInfoRecords;
+ /* Array of MathKernInfoRecords,
+ * per-glyph information for
+ * mathematical positioning
+ * of subscripts and
+ * superscripts. */
+
+ public:
+ DEFINE_SIZE_ARRAY (4, mathKernInfoRecords);
+};
+
+struct MathGlyphInfo
+{
+ 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); }
+
+ protected:
+ /* Offset to MathItalicsCorrectionInfo table -
+ * from the beginning of MathGlyphInfo table. */
+ OffsetTo<MathItalicsCorrectionInfo> mathItalicsCorrectionInfo;
+
+ /* Offset to MathTopAccentAttachment table -
+ * from the beginning of MathGlyphInfo table. */
+ OffsetTo<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.. */
+ OffsetTo<Coverage> extendedShapeCoverage;
+
+ /* Offset to MathKernInfo table -
+ * from the beginning of MathGlyphInfo table. */
+ OffsetTo<MathKernInfo> mathKernInfo;
+
+ public:
+ DEFINE_SIZE_STATIC (8);
+};
+
+struct MathGlyphVariantRecord
+{
+ friend struct MathGlyphConstruction;
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ protected:
+ HBGlyphID 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 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);
+ }
+
+ protected:
+ HBGlyphID 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 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.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;
+ }
+
+ protected:
+ MathValueRecord
+ italicsCorrection;
+ /* Italics correction of this
+ * MathGlyphAssembly. Should not
+ * depend on the assembly size. */
+ ArrayOf<MathGlyphPartRecord>
+ partRecords; /* Array of part records, from
+ * left to right and bottom to
+ * top. */
+
+ public:
+ DEFINE_SIZE_ARRAY (6, partRecords);
+};
+
+struct MathGlyphConstruction
+{
+ 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.sub_array (start_offset, variants_count),
+ hb_array (variants, *variants_count)))
+ _.second = {_.first.variantGlyph, font->em_mult (_.first.advanceMeasurement, mult)};
+ }
+ return mathGlyphVariantRecord.len;
+ }
+
+ protected:
+ /* Offset to MathGlyphAssembly table for this shape - from the beginning of
+ MathGlyphConstruction table. May be NULL. */
+ OffsetTo<MathGlyphAssembly> glyphAssembly;
+
+ /* MathGlyphVariantRecords for alternative variants of the glyphs. */
+ ArrayOf<MathGlyphVariantRecord> mathGlyphVariantRecord;
+
+ public:
+ DEFINE_SIZE_ARRAY (4, mathGlyphVariantRecord);
+};
+
+struct MathVariants
+{
+ 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) &&
+ 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 OffsetTo<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. */
+ OffsetTo<Coverage> vertGlyphCoverage;
+ /* Offset to Coverage table -
+ * from the beginning of MathVariants
+ * table. */
+ OffsetTo<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<OffsetTo<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 (); }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (version.sanitize (c) &&
+ likely (version.major == 1) &&
+ 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 */
+ OffsetTo<MathConstants>
+ mathConstants; /* MathConstants table */
+ OffsetTo<MathGlyphInfo>
+ mathGlyphInfo; /* MathGlyphInfo table */
+ OffsetTo<MathVariants>
+ mathVariants; /* MathVariants table */
+
+ public:
+ DEFINE_SIZE_STATIC (10);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_MATH_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-math.cc b/thirdparty/harfbuzz/src/hb-ot-math.cc
new file mode 100644
index 0000000000..9d8c6e735a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-math.cc
@@ -0,0 +1,293 @@
+/*
+ * 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_SCRIPT_PERCENT_SCALE_DOWN, 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_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/thirdparty/harfbuzz/src/hb-ot-math.h b/thirdparty/harfbuzz/src/hb-ot-math.h
new file mode 100644
index 0000000000..ad864a762d
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-math.h
@@ -0,0 +1,230 @@
+/*
+ * 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_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_MATH_H
+#define HB_OT_MATH_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * MATH
+ */
+
+#define HB_OT_TAG_MATH HB_TAG('M','A','T','H')
+
+/* Use with hb_buffer_set_script() for math shaping. */
+#define HB_OT_MATH_SCRIPT HB_TAG('m','a','t','h')
+
+/* Types */
+
+/**
+ * hb_ot_math_constant_t:
+ *
+ * The 'MATH' table constants specified at
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/math
+ *
+ * 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:
+ *
+ * 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_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:
+ *
+ * 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_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/thirdparty/harfbuzz/src/hb-ot-maxp-table.hh b/thirdparty/harfbuzz/src/hb-ot-maxp-table.hh
new file mode 100644
index 0000000000..929956d12b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-maxp-table.hh
@@ -0,0 +1,142 @@
+/*
+ * 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);
+
+ 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 = c->plan->num_output_glyphs ();
+ if (maxp_prime->version.major == 1)
+ {
+ 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->drop_hints)
+ drop_hint_fields (dest_v1);
+ }
+
+ return_trace (true);
+ }
+
+ 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/thirdparty/harfbuzz/src/hb-ot-meta-table.hh b/thirdparty/harfbuzz/src/hb-ot-meta-table.hh
new file mode 100644
index 0000000000..1225e26ce1
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-meta-table.hh
@@ -0,0 +1,127 @@
+/*
+ * 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) &&
+ dataZ.sanitize (c, base, dataLength)));
+ }
+
+ protected:
+ Tag tag; /* A tag indicating the type of metadata. */
+ LNNOffsetTo<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
+ {
+ void init (hb_face_t *face)
+ { table = hb_sanitize_context_t ().reference_table<meta> (face); }
+ void fini () { 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.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) &&
+ 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. */
+ LArrayOf<DataMap>
+ dataMaps;/* Array of data map records. */
+ public:
+ DEFINE_SIZE_ARRAY (16, dataMaps);
+};
+
+struct meta_accelerator_t : meta::accelerator_t {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_META_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-meta.cc b/thirdparty/harfbuzz/src/hb-ot-meta.cc
new file mode 100644
index 0000000000..54a0e10f9b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-meta.cc
@@ -0,0 +1,77 @@
+/*
+ * 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) (allow-none): buffer size as input, filled size as output
+ * @entries: (out caller-allocates) (array length=entries_count): entries tags buffer
+ *
+ * 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/thirdparty/harfbuzz/src/hb-ot-meta.h b/thirdparty/harfbuzz/src/hb-ot-meta.h
new file mode 100644
index 0000000000..0278d84148
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-meta.h
@@ -0,0 +1,71 @@
+/*
+ * 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_H_IN
+#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'),
+
+ _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/thirdparty/harfbuzz/src/hb-ot-metrics.cc b/thirdparty/harfbuzz/src/hb-ot-metrics.cc
new file mode 100644
index 0000000000..181ac4d57e
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-metrics.cc
@@ -0,0 +1,231 @@
+/*
+ * 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"
+
+
+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 () && \
+ (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 () && \
+ (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);
+ 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);
+#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: a #hb_font_t object.
+ * @metrics_tag: tag of metrics value you like to fetch.
+ * @position: (out) (optional): result of metrics value from the font.
+ *
+ * It fetches metrics value corresponding to a given tag from a 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 () && \
+ (position && (*position = font->em_scalef_x (face->table.TABLE->ATTR + GET_VAR)), true))
+#define GET_METRIC_Y(TABLE, ATTR) \
+ (face->table.TABLE->has_data () && \
+ (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: return GET_METRIC_Y (hhea, caretSlopeRise);
+ case HB_OT_METRICS_TAG_HORIZONTAL_CARET_RUN: return GET_METRIC_X (hhea, caretSlopeRun);
+ case HB_OT_METRICS_TAG_HORIZONTAL_CARET_OFFSET: return GET_METRIC_X (hhea, caretOffset);
+ 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);
+ 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;
+ }
+}
+
+#ifndef HB_NO_VAR
+/**
+ * hb_ot_metrics_get_variation:
+ * @font:
+ * @metrics_tag:
+ *
+ * Returns:
+ *
+ * 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:
+ * @metrics_tag:
+ *
+ * Returns:
+ *
+ * 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:
+ * @metrics_tag:
+ *
+ * Returns:
+ *
+ * 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/thirdparty/harfbuzz/src/hb-ot-metrics.h b/thirdparty/harfbuzz/src/hb-ot-metrics.h
new file mode 100644
index 0000000000..42c7363c03
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-metrics.h
@@ -0,0 +1,122 @@
+/*
+ * 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_H_IN
+#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.
+ *
+ * From 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'),
+
+ _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 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/thirdparty/harfbuzz/src/hb-ot-metrics.hh b/thirdparty/harfbuzz/src/hb-ot-metrics.hh
new file mode 100644
index 0000000000..19a5e9ed41
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-ot-name-language-static.hh b/thirdparty/harfbuzz/src/hb-ot-name-language-static.hh
new file mode 100644
index 0000000000..c496dc2981
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-ot-name-language.hh b/thirdparty/harfbuzz/src/hb-ot-name-language.hh
new file mode 100644
index 0000000000..903076c0d5
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-ot-name-table.hh b/thirdparty/harfbuzz/src/hb-ot-name-table.hh
new file mode 100644
index 0000000000..ece3c28466
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-name-table.hh
@@ -0,0 +1,376 @@
+/*
+ * 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 "hb-open-type.hh"
+#include "hb-ot-name-language.hh"
+#include "hb-aat-layout.hh"
+
+
+namespace OT {
+
+
+#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) const
+ {
+ TRACE_SERIALIZE (this);
+ auto *out = c->embed (this);
+ if (unlikely (!out)) return_trace (nullptr);
+ out->offset.serialize_copy (c, offset, base, 0, hb_serialize_context_t::Tail, length);
+ return_trace (out);
+ }
+
+ bool isUnicode () const
+ {
+ unsigned int p = platformID;
+ unsigned int e = encodingID;
+
+ return (p == 0 ||
+ (p == 3 && (e == 0 || e == 1 || e == 10)));
+ }
+
+ static int cmp (const void *pa, const void *pb)
+ {
+ const NameRecord *a = (const NameRecord *)pa;
+ const NameRecord *b = (const NameRecord *)pb;
+
+ if (a->platformID != b->platformID)
+ return a->platformID - b->platformID;
+
+ if (a->encodingID != b->encodingID)
+ return a->encodingID - b->encodingID;
+
+ if (a->languageID != b->languageID)
+ return a->languageID - b->languageID;
+
+ if (a->nameID != b->nameID)
+ return a->nameID - b->nameID;
+
+ if (a->length != b->length)
+ return a->length - b->length;
+
+ return 0;
+ }
+
+ bool sanitize (hb_sanitize_context_t *c, const void *base) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) && offset.sanitize (c, base, length));
+ }
+
+ HBUINT16 platformID; /* Platform ID. */
+ HBUINT16 encodingID; /* Platform-specific encoding ID. */
+ HBUINT16 languageID; /* Language ID. */
+ HBUINT16 nameID; /* Name ID. */
+ HBUINT16 length; /* String length (in bytes). */
+ NNOffsetTo<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)
+{
+ 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;
+ return strcmp (hb_language_to_string (a->language),
+ hb_language_to_string (b->language));
+}
+
+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);
+ 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)
+ {
+ TRACE_SERIALIZE (this);
+
+ if (unlikely (!c->extend_min ((*this)))) return_trace (false);
+
+ this->format = 0;
+ this->count = it.len ();
+
+ NameRecord *name_records = (NameRecord *) calloc (it.len (), NameRecord::static_size);
+ if (unlikely (!name_records)) return_trace (false);
+
+ hb_array_t<NameRecord> records (name_records, it.len ());
+
+ for (const NameRecord& record : it)
+ {
+ memcpy (name_records, &record, NameRecord::static_size);
+ name_records++;
+ }
+
+ records.qsort ();
+
+ c->copy_all (records, src_string_pool);
+ 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
+ {
+ TRACE_SUBSET (this);
+
+ name *name_prime = c->serializer->start_embed<name> ();
+ if (unlikely (!name_prime)) return_trace (false);
+
+ 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->name_legacy || namerecord.isUnicode (); })
+ ;
+
+ name_prime->serialize (c->serializer, it, hb_addressof (this + stringOffset));
+ return_trace (name_prime->count);
+ }
+
+ bool sanitize_records (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ const void *string_pool = (this+stringOffset).arrayZ;
+ return_trace (nameRecordZ.sanitize (c, count, string_pool));
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ likely (format == 0 || format == 1) &&
+ c->check_array (nameRecordZ.arrayZ, count) &&
+ c->check_range (this, stringOffset) &&
+ sanitize_records (c));
+ }
+
+ struct accelerator_t
+ {
+ void init (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.init ();
+ this->names.alloc (all_names.length);
+
+ 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);
+ }
+
+ void fini ()
+ {
+ this->names.fini ();
+ 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);
+ 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;
+ };
+
+ /* We only implement format 0 for now. */
+ HBUINT16 format; /* Format selector (=0/1). */
+ HBUINT16 count; /* Number of name records. */
+ NNOffsetTo<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 {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_NAME_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-name.cc b/thirdparty/harfbuzz/src/hb-ot-name.cc
new file mode 100644
index 0000000000..10122b8c2e
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-name.cc
@@ -0,0 +1,228 @@
+/*
+ * 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) (allow-none): 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: (out) (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 in_utf_t, typename out_utf_t>
+static 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)--; /* Same 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;
+}
+
+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 hb_ot_name_convert_utf<hb_utf16_be_t, utf_t> (bytes, text_size, text);
+
+ if (width == 1) /* ASCII */
+ return 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) (allow-none): 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.
+ *
+ * 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) (allow-none): 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.
+ *
+ * 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) (allow-none): 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.
+ *
+ * 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/thirdparty/harfbuzz/src/hb-ot-name.h b/thirdparty/harfbuzz/src/hb-ot-name.h
new file mode 100644
index 0000000000..3b4ad581c7
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-name.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.
+ */
+
+#ifndef HB_OT_H_IN
+#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_t:
+ * @HB_OT_NAME_ID_INVALID: Value to represent a nonexistent name ID.
+ *
+ * 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
+ **/
+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
+};
+
+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/thirdparty/harfbuzz/src/hb-ot-os2-table.hh b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh
new file mode 100644
index 0000000000..7d31b712c4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-os2-table.hh
@@ -0,0 +1,316 @@
+/*
+ * 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-cmap-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; }
+
+ 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;
+ }
+ }
+
+ 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);
+
+ hb_set_t unicodes;
+ if (!c->plan->glyphs_requested->is_empty ())
+ {
+ hb_map_t unicode_glyphid_map;
+
+ OT::cmap::accelerator_t cmap;
+ cmap.init (c->plan->source);
+ cmap.collect_mapping (&unicodes, &unicode_glyphid_map);
+ cmap.fini ();
+
+ if (c->plan->unicodes->is_empty ()) unicodes.clear ();
+ else hb_set_set (&unicodes, c->plan->unicodes);
+
+ + unicode_glyphid_map.iter ()
+ | hb_filter (c->plan->glyphs_requested, hb_second)
+ | hb_map (hb_first)
+ | hb_sink (unicodes)
+ ;
+ }
+ /* when --gids option is not used, no need to do collect_mapping that is
+ * iterating all codepoints in each subtable, which is not efficient */
+ uint16_t min_cp, max_cp;
+ find_min_and_max_codepoint (unicodes.is_empty () ? c->plan->unicodes : &unicodes, &min_cp, &max_cp);
+ os2_prime->usFirstCharIndex = min_cp;
+ os2_prime->usLastCharIndex = max_cp;
+
+ _update_unicode_ranges (unicodes.is_empty () ? c->plan->unicodes : &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;
+
+ hb_codepoint_t cp = HB_SET_VALUE_INVALID;
+ while (codepoints->next (&cp)) {
+ 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
+ }
+
+ static void find_min_and_max_codepoint (const hb_set_t *codepoints,
+ uint16_t *min_cp, /* OUT */
+ uint16_t *max_cp /* OUT */)
+ {
+ *min_cp = hb_min (0xFFFFu, codepoints->get_min ());
+ *max_cp = hb_min (0xFFFFu, codepoints->get_max ());
+ }
+
+ /* https://github.com/Microsoft/Font-Validator/blob/520aaae/OTFontFileVal/val_OS2.cs#L644-L681 */
+ enum font_page_t
+ {
+ 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);
+ 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/thirdparty/harfbuzz/src/hb-ot-os2-unicode-ranges.hh b/thirdparty/harfbuzz/src/hb-ot-os2-unicode-ranges.hh
new file mode 100644
index 0000000000..9613d2d186
--- /dev/null
+++ b/thirdparty/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 < start) ? -1 : key <= end ? 0 : +1; }
+
+ hb_codepoint_t start;
+ hb_codepoint_t end;
+ 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 : -1;
+}
+
+} /* namespace OT */
+
+#endif /* HB_OT_OS2_UNICODE_RANGES_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-post-macroman.hh b/thirdparty/harfbuzz/src/hb-ot-post-macroman.hh
new file mode 100644
index 0000000000..b4df8aaeea
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-ot-post-table.hh b/thirdparty/harfbuzz/src/hb-ot-post-table.hh
new file mode 100644
index 0000000000..8586331cd4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-post-table.hh
@@ -0,0 +1,298 @@
+/*
+ * 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"
+
+#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));
+ }
+
+ protected:
+ ArrayOf<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;
+
+ void serialize (hb_serialize_context_t *c) const
+ {
+ post *post_prime = c->allocate_min<post> ();
+ if (unlikely (!post_prime)) return;
+
+ memcpy (post_prime, this, post::min_size);
+ post_prime->version.major = 3; // Version 3 does not have any glyph names.
+ }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+ post *post_prime = c->serializer->start_embed<post> ();
+ if (unlikely (!post_prime)) return_trace (false);
+
+ serialize (c->serializer);
+ if (c->serializer->in_error () || c->serializer->ran_out_of_room) return_trace (false);
+
+ return_trace (true);
+ }
+
+ struct accelerator_t
+ {
+ void init (hb_face_t *face)
+ {
+ index_to_offset.init ();
+
+ 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;
+
+ 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;
+ 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);
+ }
+ void fini ()
+ {
+ index_to_offset.fini ();
+ free (gids_sorted_by_name.get ());
+ 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 ();
+
+ if (unlikely (!gids))
+ {
+ gids = (uint16_t *) 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)))
+ {
+ 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)
+ return format1_names_length;
+
+ if (version == 0x00020000)
+ 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)
+ {
+ if (glyph >= format1_names_length)
+ return hb_bytes_t ();
+
+ return format1_names (glyph);
+ }
+
+ if (version != 0x00020000 || 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 ArrayOf<HBUINT16> *glyphNameIndex;
+ hb_vector_t<uint32_t> index_to_offset;
+ const uint8_t *pool;
+ 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 (likely (c->check_struct (this) &&
+ (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 */
+ HBFixed 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 {};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_POST_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh
new file mode 100644
index 0000000000..244e967b12
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -0,0 +1,348 @@
+/*
+ * 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_COMPLEX_ARABIC_FALLBACK_HH
+#define HB_OT_SHAPE_COMPLEX_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. */
+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'),
+};
+
+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::HBGlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
+ OT::HBGlyphID 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::HBGlyphID::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;
+}
+
+static OT::SubstLookup *
+arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font)
+{
+ OT::HBGlyphID 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 are 2-component */
+ OT::HBGlyphID 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::HBGlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */];
+ unsigned int num_ligatures = 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::HBGlyphID::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 second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++)
+ {
+ hb_codepoint_t second_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second;
+ hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature;
+ hb_codepoint_t second_glyph, ligature_glyph;
+ if (!second_u ||
+ !hb_font_get_glyph (font, second_u, 0, &second_glyph) ||
+ !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph))
+ continue;
+
+ ligature_per_first_glyph_count_list[i]++;
+
+ ligature_list[num_ligatures] = ligature_glyph;
+ component_count_list[num_ligatures] = 2;
+ component_list[num_ligatures] = second_glyph;
+ 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,
+ OT::LookupFlag::IgnoreMarks,
+ 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_ligatures));
+ c.end_serialize ();
+ /* TODO sanitize the results? */
+
+ 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
+ return arabic_fallback_synthesize_lookup_ligature (plan, font);
+}
+
+#define ARABIC_FALLBACK_MAX_LOOKUPS 5
+
+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-shape-complex-arabic-win1256.hh"
+#endif
+
+struct ManifestLookup
+{
+ public:
+ OT::Tag tag;
+ OT::OffsetTo<OT::SubstLookup> lookupOffset;
+ public:
+ DEFINE_SIZE_STATIC (6);
+};
+typedef OT::ArrayOf<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), "");
+ /* TODO sanitize the table? */
+
+ 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].init (*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].init (*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 *) 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);
+ 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])
+ {
+ fallback_plan->accel_array[i].fini ();
+ if (fallback_plan->free_lookups)
+ free (fallback_plan->lookup_array[i]);
+ }
+
+ 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);
+ 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]);
+ hb_ot_layout_substitute_lookup (&c,
+ *fallback_plan->lookup_array[i],
+ fallback_plan->accel_array[i]);
+ }
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-joining-list.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-joining-list.hh
new file mode 100644
index 0000000000..c022d4bb06
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-joining-list.hh
@@ -0,0 +1,46 @@
+/* == 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-13.0.0.txt
+ * # Date: 2020-01-31, 23:55:00 GMT [KW, RP]
+ * # Scripts-13.0.0.txt
+ * # Date: 2020-01-22, 00:07:43 GMT
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH
+#define HB_OT_SHAPE_COMPLEX_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_PHAGS_PA:
+ case HB_SCRIPT_PSALTER_PAHLAVI:
+ case HB_SCRIPT_SOGDIAN:
+ case HB_SCRIPT_SYRIAC:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_JOINING_LIST_HH */
+
+/* == End of generated function == */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
new file mode 100644
index 0000000000..70ffe623c0
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
@@ -0,0 +1,433 @@
+/* == 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-13.0.0.txt
+ * # Date: 2020-01-31, 23:55:00 GMT [KW, RP]
+ * # Blocks-13.0.0.txt
+ * # Date: 2019-07-10, 19:06:00 GMT [KW]
+ * UnicodeData.txt does not have a header.
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
+#define HB_OT_SHAPE_COMPLEX_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,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,
+ /* 0880 */ 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,
+
+ /* 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,X,D,D,D,R,D,D,D,D,D,D,
+ /* 08C0 */ 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,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,
+
+#define joining_offset_0x10fb0u 1219
+
+ /* 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 1247
+
+ /* Kaithi */
+
+ /* 110A0 */ U,X,X,
+ /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U,
+
+#define joining_offset_0x1e900u 1264
+
+ /* 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: 1340; 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, 0x10F54u)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x10FB0u, 0x10FCBu)) return joining_table[u - 0x10FB0u + joining_offset_0x10fb0u];
+ 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 second;
+ uint16_t ligature;
+ } ligatures[4];
+} ligature_table[] =
+{
+ { 0xFEDFu, {
+ { 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, {
+ { 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 */
+ }},
+};
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */
+
+/* == End of generated table == */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
new file mode 100644
index 0000000000..b15e145f2f
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic-win1256.hh
@@ -0,0 +1,323 @@
+/*
+ * 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_SHAPE_COMPLEX_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
+
+#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)) \
+ /* ASSERT_STATIC_EXPR_ZERO (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)) \
+ /* ASSERT_STATIC_EXPR_ZERO (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)
+
+/* Shorthand. */
+#define G OT_GLYPHID
+
+/*
+ * 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 OT_TABLE_START
+#undef OT_TABLE_END
+#undef OT_LABEL_START
+#undef OT_LABEL_END
+#undef OT_UINT8
+#undef OT_UINT16
+#undef OT_DISTANCE
+#undef OT_COUNT
+
+/*
+ * 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-shape-complex-arabic-win1256.hh"
+#endif
+
+#define HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc
new file mode 100644
index 0000000000..1e93f0efd5
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -0,0 +1,716 @@
+/*
+ * 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-shape-complex-arabic.hh"
+#include "hb-ot-shape.hh"
+
+
+/* buffer var allocations */
+#define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
+
+#define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0
+
+/* 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-shape-complex-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 void
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+
+static void
+record_stch (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+
+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.
+ *
+ * A pause after calt is required to make KFGQPC Uthmanic Script HAFS
+ * work correctly. See https://github.com/harfbuzz/harfbuzz/issues/505
+ */
+
+
+ map->enable_feature (HB_TAG('s','t','c','h'));
+ map->add_gsub_pause (record_stch);
+
+ map->enable_feature (HB_TAG('c','c','m','p'));
+ map->enable_feature (HB_TAG('l','o','c','l'));
+
+ 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], has_fallback ? F_HAS_FALLBACK : F_NONE);
+ map->add_gsub_pause (nullptr);
+ }
+
+ /* 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);
+
+ /* No pause after rclt. See 98460779bae19e4d64d29461ff154b3527bf8420. */
+ map->enable_feature (HB_TAG('r','c','l','t'), F_MANUAL_ZWJ);
+ map->enable_feature (HB_TAG('c','a','l','t'), F_MANUAL_ZWJ);
+ map->add_gsub_pause (nullptr);
+
+ /* 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'));
+ map->enable_feature (HB_TAG('m','s','e','t'));
+}
+
+#include "hb-ot-shape-complex-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 *) 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);
+
+ 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->unsafe_to_break (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;
+ 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_range<hb_codepoint_t> (info[i].codepoint, 0x180Bu, 0x180Du)))
+ 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 void
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+#ifdef HB_NO_OT_SHAPE_COMPLEX_ARABIC_FALLBACK
+ return;
+#endif
+
+ const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
+
+ if (!arabic_plan->do_fallback)
+ return;
+
+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);
+}
+
+/*
+ * 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 void
+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;
+
+ /* '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;
+ }
+}
+
+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;
+
+ /* The Arabic shaper currently always processes in RTL mode, so we should
+ * stretch / position the stretched pieces to the left / preceding glyphs. */
+
+ /* 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 (%d,%d,%d)",
+ step == MEASURE ? "measuring" : "cutting", context, start, end);
+ DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%d 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);
+ }
+
+ 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 = 0;
+ 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 %d copies of glyph %d; j=%d",
+ repeat, info[k - 1].codepoint, j);
+ for (unsigned int n = 0; n < repeat; n++)
+ {
+ 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 (step == MEASURE)
+ {
+ if (unlikely (!buffer->ensure (count + extra_glyphs_needed)))
+ break;
+ }
+ else
+ {
+ assert (j == 0);
+ buffer->len = new_len;
+ }
+ }
+}
+
+
+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);
+
+ HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
+}
+
+/* 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 */
+ 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 %d to %d", start, end);
+
+ unsigned int i = start;
+ for (unsigned int cc = 220; cc <= 230; cc += 10)
+ {
+ DEBUG_MSG (ARABIC, buffer, "Looking for %d's starting at %d", cc, i);
+ while (i < end && info_cc(info[i]) < cc)
+ i++;
+ DEBUG_MSG (ARABIC, buffer, "Looking for %d's stopped at %d", 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 %d's from %d to %d", cc, i, j);
+
+ /* Shift it! */
+ DEBUG_MSG (ARABIC, buffer, "Shifting %d's: %d %d", cc, i, j);
+ hb_glyph_info_t temp[HB_OT_SHAPE_COMPLEX_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_complex_shaper_t _hb_ot_complex_shaper_arabic =
+{
+ collect_features_arabic,
+ nullptr, /* override_features */
+ data_create_arabic,
+ data_destroy_arabic,
+ nullptr, /* preprocess_text */
+ postprocess_glyphs_arabic,
+ HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+ nullptr, /* decompose */
+ nullptr, /* compose */
+ setup_masks_arabic,
+ HB_TAG_NONE, /* gpos_tag */
+ reorder_marks_arabic,
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
+ true, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-arabic.hh
new file mode 100644
index 0000000000..5bf6ff6338
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-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_SHAPE_COMPLEX_ARABIC_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape-complex.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_SHAPE_COMPLEX_ARABIC_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-default.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-default.cc
new file mode 100644
index 0000000000..a755aea098
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-default.cc
@@ -0,0 +1,73 @@
+/*
+ * 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-shape-complex.hh"
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
+{
+ nullptr, /* collect_features */
+ nullptr, /* override_features */
+ nullptr, /* data_create */
+ nullptr, /* data_destroy */
+ nullptr, /* preprocess_text */
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+ nullptr, /* decompose */
+ nullptr, /* compose */
+ nullptr, /* setup_masks */
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
+ true, /* fallback_position */
+};
+
+/* Same as default but no mark advance zeroing / fallback positioning.
+ * Dumbest shaper ever, basically. */
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_dumber =
+{
+ nullptr, /* collect_features */
+ nullptr, /* override_features */
+ nullptr, /* data_create */
+ nullptr, /* data_destroy */
+ nullptr, /* preprocess_text */
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+ nullptr, /* decompose */
+ nullptr, /* compose */
+ nullptr, /* setup_masks */
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+ false, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc
new file mode 100644
index 0000000000..f5915f43ae
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -0,0 +1,439 @@
+/*
+ * 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-shape-complex.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 *) 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)
+{
+ 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() complex_var_u8_0() /* 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, partically 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);
+ buffer->next_glyph ();
+ 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;
+ }
+ buffer->replace_glyphs (1, 2, chars);
+ }
+ else
+ {
+ /* No dotted circle available in the font; just leave tone mark untouched. */
+ 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))
+ {
+ buffer->replace_glyphs (t ? 3 : 2, 1, &s);
+ if (unlikely (!buffer->successful))
+ return;
+ 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;
+ buffer->next_glyph ();
+ buffer->cur().hangul_shaping_feature() = VJMO;
+ buffer->next_glyph ();
+ if (t)
+ {
+ buffer->cur().hangul_shaping_feature() = TJMO;
+ buffer->next_glyph ();
+ end = start + 3;
+ }
+ else
+ end = start + 2;
+ 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))
+ {
+ buffer->replace_glyphs (2, 1, &new_s);
+ if (unlikely (!buffer->successful))
+ return;
+ 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;
+ 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)
+ {
+ buffer->next_glyph ();
+ s_len++;
+ }
+
+ if (unlikely (!buffer->successful))
+ return;
+
+ /* 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. */
+ end = start + 1;
+ buffer->next_glyph ();
+ continue;
+ }
+ }
+
+ /* Didn't find a recognizable syllable, so we leave end <= start;
+ * this will prevent tone-mark reordering happening.
+ */
+ buffer->next_glyph ();
+ }
+ buffer->swap_buffers ();
+}
+
+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_complex_shaper_t _hb_ot_complex_shaper_hangul =
+{
+ collect_features_hangul,
+ override_features_hangul,
+ data_create_hangul,
+ data_destroy_hangul,
+ preprocess_text_hangul,
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
+ nullptr, /* decompose */
+ nullptr, /* compose */
+ setup_masks_hangul,
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+ false, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-hebrew.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-hebrew.cc
new file mode 100644
index 0000000000..334d3ded82
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-hebrew.cc
@@ -0,0 +1,185 @@
+/*
+ * 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-shape-complex.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_SHAPE_COMPLEX_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;
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
+{
+ nullptr, /* collect_features */
+ nullptr, /* override_features */
+ nullptr, /* data_create */
+ nullptr, /* data_destroy */
+ nullptr, /* preprocess_text */
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+ nullptr, /* decompose */
+ compose_hebrew,
+ nullptr, /* setup_masks */
+ HB_TAG ('h','e','b','r'), /* gpos_tag. https://github.com/harfbuzz/harfbuzz/issues/347#issuecomment-267838368 */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
+ true, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
new file mode 100644
index 0000000000..670b6bf486
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
@@ -0,0 +1,574 @@
+
+#line 1 "hb-ot-shape-complex-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_SHAPE_COMPLEX_INDIC_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
+
+#include "hb.hh"
+
+
+#line 36 "hb-ot-shape-complex-indic-machine.hh"
+static const unsigned char _indic_syllable_machine_trans_keys[] = {
+ 8u, 8u, 4u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u,
+ 4u, 13u, 4u, 8u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u, 6u, 6u, 16u, 16u,
+ 4u, 8u, 4u, 13u, 4u, 13u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u, 4u, 8u,
+ 6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 4u, 13u, 8u, 8u, 5u, 7u, 5u, 8u,
+ 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 4u, 8u, 5u, 8u, 8u, 8u, 1u, 19u,
+ 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u,
+ 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u,
+ 3u, 10u, 5u, 10u, 3u, 17u, 3u, 17u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u,
+ 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u,
+ 1u, 16u, 3u, 10u, 4u, 10u, 5u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u,
+ 5u, 10u, 3u, 17u, 3u, 17u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u,
+ 3u, 17u, 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u,
+ 4u, 10u, 5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u,
+ 3u, 17u, 4u, 13u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u,
+ 1u, 16u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 1u, 16u, 3u, 10u, 4u, 10u,
+ 5u, 10u, 3u, 17u, 4u, 10u, 5u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 1u, 17u,
+ 3u, 17u, 1u, 17u, 4u, 13u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 3u, 10u,
+ 5u, 10u, 5u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 0
+};
+
+static const char _indic_syllable_machine_key_spans[] = {
+ 1, 5, 3, 4, 5, 1, 1, 5,
+ 10, 5, 1, 3, 4, 5, 1, 1,
+ 5, 10, 10, 10, 1, 3, 4, 5,
+ 1, 1, 5, 5, 10, 1, 3, 4,
+ 5, 1, 1, 5, 5, 4, 1, 19,
+ 15, 15, 14, 16, 6, 6, 1, 6,
+ 16, 16, 16, 8, 7, 6, 7, 6,
+ 8, 6, 15, 15, 15, 15, 14, 16,
+ 15, 15, 14, 16, 6, 1, 6, 16,
+ 16, 8, 7, 6, 7, 6, 6, 8,
+ 6, 15, 15, 5, 15, 15, 14, 16,
+ 15, 16, 6, 1, 6, 16, 16, 8,
+ 7, 6, 15, 7, 6, 6, 8, 6,
+ 15, 10, 5, 15, 15, 14, 16, 15,
+ 16, 6, 1, 6, 16, 16, 8, 7,
+ 6, 15, 7, 6, 6, 8, 6, 17,
+ 15, 17, 10, 6, 1, 6, 16, 8,
+ 6, 6, 1, 6, 16
+};
+
+static const short _indic_syllable_machine_index_offsets[] = {
+ 0, 2, 8, 12, 17, 23, 25, 27,
+ 33, 44, 50, 52, 56, 61, 67, 69,
+ 71, 77, 88, 99, 110, 112, 116, 121,
+ 127, 129, 131, 137, 143, 154, 156, 160,
+ 165, 171, 173, 175, 181, 187, 192, 194,
+ 214, 230, 246, 261, 278, 285, 292, 294,
+ 301, 318, 335, 352, 361, 369, 376, 384,
+ 391, 400, 407, 423, 439, 455, 471, 486,
+ 503, 519, 535, 550, 567, 574, 576, 583,
+ 600, 617, 626, 634, 641, 649, 656, 663,
+ 672, 679, 695, 711, 717, 733, 749, 764,
+ 781, 797, 814, 821, 823, 830, 847, 864,
+ 873, 881, 888, 904, 912, 919, 926, 935,
+ 942, 958, 969, 975, 991, 1007, 1022, 1039,
+ 1055, 1072, 1079, 1081, 1088, 1105, 1122, 1131,
+ 1139, 1146, 1162, 1170, 1177, 1184, 1193, 1200,
+ 1218, 1234, 1252, 1263, 1270, 1272, 1279, 1296,
+ 1305, 1312, 1319, 1321, 1328
+};
+
+static const unsigned char _indic_syllable_machine_indicies[] = {
+ 1, 0, 2, 3, 3, 4, 1, 0,
+ 3, 3, 4, 0, 3, 3, 4, 1,
+ 0, 5, 3, 3, 4, 1, 0, 6,
+ 0, 7, 0, 8, 3, 3, 4, 1,
+ 0, 2, 3, 3, 4, 1, 0, 0,
+ 0, 0, 9, 0, 11, 12, 12, 13,
+ 14, 10, 14, 10, 12, 12, 13, 10,
+ 12, 12, 13, 14, 10, 15, 12, 12,
+ 13, 14, 10, 16, 10, 17, 10, 18,
+ 12, 12, 13, 14, 10, 11, 12, 12,
+ 13, 14, 10, 10, 10, 10, 19, 10,
+ 11, 12, 12, 13, 14, 10, 10, 10,
+ 10, 20, 10, 22, 23, 23, 24, 25,
+ 21, 21, 21, 21, 26, 21, 25, 21,
+ 23, 23, 24, 27, 23, 23, 24, 25,
+ 21, 28, 23, 23, 24, 25, 21, 29,
+ 21, 30, 21, 22, 23, 23, 24, 25,
+ 21, 31, 23, 23, 24, 25, 21, 33,
+ 34, 34, 35, 36, 32, 32, 32, 32,
+ 37, 32, 36, 32, 34, 34, 35, 32,
+ 34, 34, 35, 36, 32, 38, 34, 34,
+ 35, 36, 32, 39, 32, 40, 32, 33,
+ 34, 34, 35, 36, 32, 41, 34, 34,
+ 35, 36, 32, 23, 23, 24, 1, 0,
+ 43, 42, 45, 46, 47, 48, 49, 50,
+ 24, 25, 44, 51, 52, 52, 26, 44,
+ 53, 54, 55, 56, 57, 44, 59, 60,
+ 61, 62, 4, 1, 58, 63, 58, 58,
+ 9, 58, 58, 58, 64, 58, 65, 60,
+ 66, 66, 4, 1, 58, 63, 58, 58,
+ 58, 58, 58, 58, 64, 58, 60, 66,
+ 66, 4, 1, 58, 63, 58, 58, 58,
+ 58, 58, 58, 64, 58, 45, 58, 58,
+ 58, 67, 68, 58, 1, 58, 63, 58,
+ 58, 58, 58, 58, 45, 58, 69, 69,
+ 58, 1, 58, 63, 58, 63, 58, 58,
+ 70, 58, 63, 58, 63, 58, 63, 58,
+ 58, 58, 58, 63, 58, 45, 58, 71,
+ 58, 69, 69, 58, 1, 58, 63, 58,
+ 58, 58, 58, 58, 45, 58, 45, 58,
+ 58, 58, 69, 69, 58, 1, 58, 63,
+ 58, 58, 58, 58, 58, 45, 58, 45,
+ 58, 58, 58, 69, 68, 58, 1, 58,
+ 63, 58, 58, 58, 58, 58, 45, 58,
+ 72, 7, 73, 74, 4, 1, 58, 63,
+ 58, 7, 73, 74, 4, 1, 58, 63,
+ 58, 73, 73, 4, 1, 58, 63, 58,
+ 75, 76, 76, 4, 1, 58, 63, 58,
+ 67, 77, 58, 1, 58, 63, 58, 67,
+ 58, 69, 69, 58, 1, 58, 63, 58,
+ 69, 77, 58, 1, 58, 63, 58, 59,
+ 60, 66, 66, 4, 1, 58, 63, 58,
+ 58, 58, 58, 58, 58, 64, 58, 59,
+ 60, 61, 66, 4, 1, 58, 63, 58,
+ 58, 9, 58, 58, 58, 64, 58, 79,
+ 80, 81, 82, 13, 14, 78, 83, 78,
+ 78, 20, 78, 78, 78, 84, 78, 85,
+ 80, 86, 82, 13, 14, 78, 83, 78,
+ 78, 78, 78, 78, 78, 84, 78, 80,
+ 86, 82, 13, 14, 78, 83, 78, 78,
+ 78, 78, 78, 78, 84, 78, 87, 78,
+ 78, 78, 88, 89, 78, 14, 78, 83,
+ 78, 78, 78, 78, 78, 87, 78, 90,
+ 80, 91, 92, 13, 14, 78, 83, 78,
+ 78, 19, 78, 78, 78, 84, 78, 93,
+ 80, 86, 86, 13, 14, 78, 83, 78,
+ 78, 78, 78, 78, 78, 84, 78, 80,
+ 86, 86, 13, 14, 78, 83, 78, 78,
+ 78, 78, 78, 78, 84, 78, 87, 78,
+ 78, 78, 94, 89, 78, 14, 78, 83,
+ 78, 78, 78, 78, 78, 87, 78, 83,
+ 78, 78, 95, 78, 83, 78, 83, 78,
+ 83, 78, 78, 78, 78, 83, 78, 87,
+ 78, 96, 78, 94, 94, 78, 14, 78,
+ 83, 78, 78, 78, 78, 78, 87, 78,
+ 87, 78, 78, 78, 94, 94, 78, 14,
+ 78, 83, 78, 78, 78, 78, 78, 87,
+ 78, 97, 17, 98, 99, 13, 14, 78,
+ 83, 78, 17, 98, 99, 13, 14, 78,
+ 83, 78, 98, 98, 13, 14, 78, 83,
+ 78, 100, 101, 101, 13, 14, 78, 83,
+ 78, 88, 102, 78, 14, 78, 83, 78,
+ 94, 94, 78, 14, 78, 83, 78, 88,
+ 78, 94, 94, 78, 14, 78, 83, 78,
+ 94, 102, 78, 14, 78, 83, 78, 90,
+ 80, 86, 86, 13, 14, 78, 83, 78,
+ 78, 78, 78, 78, 78, 84, 78, 90,
+ 80, 91, 86, 13, 14, 78, 83, 78,
+ 78, 19, 78, 78, 78, 84, 78, 11,
+ 12, 12, 13, 14, 78, 79, 80, 86,
+ 82, 13, 14, 78, 83, 78, 78, 78,
+ 78, 78, 78, 84, 78, 104, 48, 105,
+ 105, 24, 25, 103, 51, 103, 103, 103,
+ 103, 103, 103, 55, 103, 48, 105, 105,
+ 24, 25, 103, 51, 103, 103, 103, 103,
+ 103, 103, 55, 103, 106, 103, 103, 103,
+ 107, 108, 103, 25, 103, 51, 103, 103,
+ 103, 103, 103, 106, 103, 47, 48, 109,
+ 110, 24, 25, 103, 51, 103, 103, 26,
+ 103, 103, 103, 55, 103, 106, 103, 103,
+ 103, 111, 108, 103, 25, 103, 51, 103,
+ 103, 103, 103, 103, 106, 103, 51, 103,
+ 103, 112, 103, 51, 103, 51, 103, 51,
+ 103, 103, 103, 103, 51, 103, 106, 103,
+ 113, 103, 111, 111, 103, 25, 103, 51,
+ 103, 103, 103, 103, 103, 106, 103, 106,
+ 103, 103, 103, 111, 111, 103, 25, 103,
+ 51, 103, 103, 103, 103, 103, 106, 103,
+ 114, 30, 115, 116, 24, 25, 103, 51,
+ 103, 30, 115, 116, 24, 25, 103, 51,
+ 103, 115, 115, 24, 25, 103, 51, 103,
+ 47, 48, 105, 105, 24, 25, 103, 51,
+ 103, 103, 103, 103, 103, 103, 55, 103,
+ 117, 118, 118, 24, 25, 103, 51, 103,
+ 107, 119, 103, 25, 103, 51, 103, 111,
+ 111, 103, 25, 103, 51, 103, 107, 103,
+ 111, 111, 103, 25, 103, 51, 103, 111,
+ 119, 103, 25, 103, 51, 103, 47, 48,
+ 109, 105, 24, 25, 103, 51, 103, 103,
+ 26, 103, 103, 103, 55, 103, 22, 23,
+ 23, 24, 25, 120, 120, 120, 120, 26,
+ 120, 22, 23, 23, 24, 25, 120, 122,
+ 123, 124, 125, 35, 36, 121, 126, 121,
+ 121, 37, 121, 121, 121, 127, 121, 128,
+ 123, 125, 125, 35, 36, 121, 126, 121,
+ 121, 121, 121, 121, 121, 127, 121, 123,
+ 125, 125, 35, 36, 121, 126, 121, 121,
+ 121, 121, 121, 121, 127, 121, 129, 121,
+ 121, 121, 130, 131, 121, 36, 121, 126,
+ 121, 121, 121, 121, 121, 129, 121, 122,
+ 123, 124, 52, 35, 36, 121, 126, 121,
+ 121, 37, 121, 121, 121, 127, 121, 129,
+ 121, 121, 121, 132, 131, 121, 36, 121,
+ 126, 121, 121, 121, 121, 121, 129, 121,
+ 126, 121, 121, 133, 121, 126, 121, 126,
+ 121, 126, 121, 121, 121, 121, 126, 121,
+ 129, 121, 134, 121, 132, 132, 121, 36,
+ 121, 126, 121, 121, 121, 121, 121, 129,
+ 121, 129, 121, 121, 121, 132, 132, 121,
+ 36, 121, 126, 121, 121, 121, 121, 121,
+ 129, 121, 135, 40, 136, 137, 35, 36,
+ 121, 126, 121, 40, 136, 137, 35, 36,
+ 121, 126, 121, 136, 136, 35, 36, 121,
+ 126, 121, 122, 123, 125, 125, 35, 36,
+ 121, 126, 121, 121, 121, 121, 121, 121,
+ 127, 121, 138, 139, 139, 35, 36, 121,
+ 126, 121, 130, 140, 121, 36, 121, 126,
+ 121, 132, 132, 121, 36, 121, 126, 121,
+ 130, 121, 132, 132, 121, 36, 121, 126,
+ 121, 132, 140, 121, 36, 121, 126, 121,
+ 45, 46, 47, 48, 109, 105, 24, 25,
+ 103, 51, 52, 52, 26, 103, 103, 45,
+ 55, 103, 59, 141, 61, 62, 4, 1,
+ 58, 63, 58, 58, 9, 58, 58, 58,
+ 64, 58, 45, 46, 47, 48, 142, 143,
+ 24, 144, 58, 145, 58, 52, 26, 58,
+ 58, 45, 55, 58, 22, 146, 146, 24,
+ 144, 58, 63, 58, 58, 26, 58, 145,
+ 58, 58, 147, 58, 145, 58, 145, 58,
+ 145, 58, 58, 58, 58, 145, 58, 45,
+ 58, 71, 22, 146, 146, 24, 144, 58,
+ 63, 58, 58, 58, 58, 58, 45, 58,
+ 149, 148, 150, 150, 148, 43, 148, 151,
+ 148, 150, 150, 148, 43, 148, 151, 148,
+ 151, 148, 148, 152, 148, 151, 148, 151,
+ 148, 151, 148, 148, 148, 148, 151, 148,
+ 45, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 52, 120, 120, 120, 120, 45,
+ 120, 0
+};
+
+static const unsigned char _indic_syllable_machine_trans_targs[] = {
+ 39, 45, 50, 2, 51, 5, 6, 53,
+ 57, 58, 39, 67, 11, 73, 68, 14,
+ 15, 75, 80, 81, 84, 39, 89, 21,
+ 95, 90, 98, 39, 24, 25, 97, 103,
+ 39, 112, 30, 118, 113, 121, 33, 34,
+ 120, 126, 39, 137, 39, 40, 60, 85,
+ 87, 105, 106, 91, 107, 127, 128, 99,
+ 135, 140, 39, 41, 43, 8, 59, 46,
+ 54, 42, 1, 44, 48, 0, 47, 49,
+ 52, 3, 4, 55, 7, 56, 39, 61,
+ 63, 18, 83, 69, 76, 62, 9, 64,
+ 78, 71, 65, 17, 82, 66, 10, 70,
+ 72, 74, 12, 13, 77, 16, 79, 39,
+ 86, 26, 88, 101, 93, 19, 104, 20,
+ 92, 94, 96, 22, 23, 100, 27, 102,
+ 39, 39, 108, 110, 28, 35, 114, 122,
+ 109, 111, 124, 116, 29, 115, 117, 119,
+ 31, 32, 123, 36, 125, 129, 130, 134,
+ 131, 132, 37, 133, 39, 136, 38, 138,
+ 139
+};
+
+static const char _indic_syllable_machine_trans_actions[] = {
+ 1, 0, 2, 0, 2, 0, 0, 2,
+ 2, 2, 3, 2, 0, 2, 0, 0,
+ 0, 2, 2, 2, 2, 4, 2, 0,
+ 5, 0, 5, 6, 0, 0, 5, 2,
+ 7, 2, 0, 2, 0, 2, 0, 0,
+ 2, 2, 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, 0, 0, 0, 0, 2, 14, 2,
+ 0, 0, 2, 0, 2, 2, 0, 2,
+ 2, 2, 2, 0, 2, 2, 0, 0,
+ 2, 2, 0, 0, 0, 0, 2, 15,
+ 5, 0, 5, 2, 2, 0, 5, 0,
+ 0, 2, 5, 0, 0, 0, 0, 2,
+ 16, 17, 2, 0, 0, 0, 0, 2,
+ 2, 2, 2, 2, 0, 0, 2, 2,
+ 0, 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, 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
+};
+
+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, 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
+};
+
+static const short _indic_syllable_machine_eof_trans[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 22, 22, 28, 22, 22,
+ 22, 22, 22, 22, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 1, 43, 0,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 79, 79, 79, 79,
+ 79, 79, 79, 79, 79, 79, 79, 79,
+ 79, 79, 79, 79, 79, 79, 79, 79,
+ 79, 79, 79, 79, 79, 104, 104, 104,
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ 104, 104, 104, 104, 104, 104, 104, 104,
+ 104, 121, 121, 122, 122, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 122, 122,
+ 122, 122, 122, 122, 122, 122, 122, 104,
+ 59, 59, 59, 59, 59, 59, 59, 149,
+ 149, 149, 149, 149, 121
+};
+
+static const int indic_syllable_machine_start = 39;
+static const int indic_syllable_machine_first_final = 39;
+static const int indic_syllable_machine_error = -1;
+
+static const int indic_syllable_machine_en_main = 39;
+
+
+#line 36 "hb-ot-shape-complex-indic-machine.rl"
+
+
+
+#line 93 "hb-ot-shape-complex-indic-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+ HB_STMT_START { \
+ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+ for (unsigned int i = ts; i < te; i++) \
+ info[i].syllable() = (syllable_serial << 4) | indic_##syllable_type; \
+ syllable_serial++; \
+ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+ } HB_STMT_END
+
+static 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 411 "hb-ot-shape-complex-indic-machine.hh"
+ {
+ cs = indic_syllable_machine_start;
+ ts = 0;
+ te = 0;
+ act = 0;
+ }
+
+#line 113 "hb-ot-shape-complex-indic-machine.rl"
+
+
+ p = 0;
+ pe = eof = buffer->len;
+
+ unsigned int syllable_serial = 1;
+
+#line 427 "hb-ot-shape-complex-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 441 "hb-ot-shape-complex-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 89 "hb-ot-shape-complex-indic-machine.rl"
+ {te = p+1;{ found_syllable (non_indic_cluster); }}
+ break;
+ case 13:
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
+ {te = p;p--;{ found_syllable (consonant_syllable); }}
+ break;
+ case 14:
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
+ {te = p;p--;{ found_syllable (vowel_syllable); }}
+ break;
+ case 17:
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
+ {te = p;p--;{ found_syllable (standalone_cluster); }}
+ break;
+ case 19:
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
+ {te = p;p--;{ found_syllable (symbol_cluster); }}
+ break;
+ case 15:
+#line 88 "hb-ot-shape-complex-indic-machine.rl"
+ {te = p;p--;{ found_syllable (broken_cluster); }}
+ break;
+ case 16:
+#line 89 "hb-ot-shape-complex-indic-machine.rl"
+ {te = p;p--;{ found_syllable (non_indic_cluster); }}
+ break;
+ case 1:
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
+ break;
+ case 3:
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (vowel_syllable); }}
+ break;
+ case 7:
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (standalone_cluster); }}
+ break;
+ case 8:
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (symbol_cluster); }}
+ break;
+ case 4:
+#line 88 "hb-ot-shape-complex-indic-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+ break;
+ case 6:
+#line 1 "NONE"
+ { switch( act ) {
+ case 1:
+ {{p = ((te))-1;} found_syllable (consonant_syllable); }
+ break;
+ case 5:
+ {{p = ((te))-1;} found_syllable (broken_cluster); }
+ break;
+ case 6:
+ {{p = ((te))-1;} found_syllable (non_indic_cluster); }
+ break;
+ }
+ }
+ break;
+ case 18:
+#line 1 "NONE"
+ {te = p+1;}
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
+ {act = 1;}
+ break;
+ case 5:
+#line 1 "NONE"
+ {te = p+1;}
+#line 88 "hb-ot-shape-complex-indic-machine.rl"
+ {act = 5;}
+ break;
+ case 12:
+#line 1 "NONE"
+ {te = p+1;}
+#line 89 "hb-ot-shape-complex-indic-machine.rl"
+ {act = 6;}
+ break;
+#line 544 "hb-ot-shape-complex-indic-machine.hh"
+ }
+
+_again:
+ switch ( _indic_syllable_machine_to_state_actions[cs] ) {
+ case 9:
+#line 1 "NONE"
+ {ts = 0;}
+ break;
+#line 553 "hb-ot-shape-complex-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 121 "hb-ot-shape-complex-indic-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-table.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-table.cc
new file mode 100644
index 0000000000..a150fd2486
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic-table.cc
@@ -0,0 +1,501 @@
+/* == 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-13.0.0.txt
+ * # Date: 2019-07-22, 19:55:00 GMT [KW, RP]
+ * # IndicPositionalCategory-13.0.0.txt
+ * # Date: 2019-07-23, 00:01:00 GMT [KW, RP]
+ * # Blocks-13.0.0.txt
+ * # Date: 2019-07-10, 19:06:00 GMT [KW]
+ */
+
+#include "hb.hh"
+
+#ifndef HB_NO_OT_SHAPE
+
+#include "hb-ot-shape-complex-indic.hh"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-macros"
+
+#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 17 chars; Avagraha */
+#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 91 chars; Bindu */
+#define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */
+#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 59 chars; Cantillation_Mark */
+#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 2195 chars; Consonant */
+#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 12 chars; Consonant_Dead */
+#define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 67 chars; Consonant_Final */
+#define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */
+#define ISC_CIP INDIC_SYLLABIC_CATEGORY_CONSONANT_INITIAL_POSTFIXED /* 1 chars; Consonant_Initial_Postfixed */
+#define ISC_CK INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER /* 2 chars; Consonant_Killer */
+#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 31 chars; Consonant_Medial */
+#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 22 chars; Consonant_Placeholder */
+#define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 3 chars; Consonant_Preceding_Repha */
+#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 10 chars; Consonant_Prefixed */
+#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 94 chars; Consonant_Subjoined */
+#define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */
+#define ISC_CWS INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER /* 8 chars; Consonant_With_Stacker */
+#define ISC_GM INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK /* 3 chars; Gemination_Mark */
+#define ISC_IS INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER /* 12 chars; Invisible_Stacker */
+#define ISC_ZWJ INDIC_SYLLABIC_CATEGORY_JOINER /* 1 chars; Joiner */
+#define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */
+#define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */
+#define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 31 chars; Nukta */
+#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 491 chars; Number */
+#define ISC_NJ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER /* 1 chars; Number_Joiner */
+#define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */
+#define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 23 chars; Pure_Killer */
+#define ISC_RS INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER /* 2 chars; Register_Shifter */
+#define ISC_SM INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER /* 25 chars; Syllable_Modifier */
+#define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */
+#define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 42 chars; Tone_Mark */
+#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 27 chars; Virama */
+#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 35 chars; Visarga */
+#define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */
+#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 683 chars; Vowel_Dependent */
+#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 484 chars; Vowel_Independent */
+
+#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 351 chars; Bottom */
+#define IMC_BL INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT /* 1 chars; Bottom_And_Left */
+#define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 4 chars; Bottom_And_Right */
+#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 64 chars; Left */
+#define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 22 chars; Left_And_Right */
+#define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */
+#define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 10 chars; Overstruck */
+#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 288 chars; Right */
+#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 415 chars; Top */
+#define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 10 chars; Top_And_Bottom */
+#define IMC_TBL INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT /* 2 chars; Top_And_Bottom_And_Left */
+#define IMC_TBR INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT /* 1 chars; Top_And_Bottom_And_Right */
+#define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT /* 6 chars; Top_And_Left */
+#define IMC_TLR INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT /* 4 chars; Top_And_Left_And_Right */
+#define IMC_TR INDIC_MATRA_CATEGORY_TOP_AND_RIGHT /* 13 chars; Top_And_Right */
+#define IMC_VOL INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT /* 19 chars; Visual_Order_Left */
+
+#pragma GCC diagnostic pop
+
+#define _(S,M) INDIC_COMBINE_CATEGORIES (ISC_##S, IMC_##M)
+
+
+static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = {
+
+
+#define indic_offset_0x0028u 0
+
+
+ /* Basic Latin */
+
+ /* 0028 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(CP,x), _(x,x), _(x,x),
+ /* 0030 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0038 */ _(Nd,x), _(Nd,x), _(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,x), _(SM,x), _(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), _(CP,x),
+
+#define indic_offset_0x0900u 64
+
+
+ /* Devanagari */
+
+ /* 0900 */ _(Bi,T), _(Bi,T), _(Bi,T), _(Vs,R), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0908 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0910 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0918 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0920 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0928 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0930 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0938 */ _(C,x), _(C,x), _(M,T), _(M,R), _(N,B), _(A,x), _(M,R), _(M,L),
+ /* 0940 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(M,T), _(M,T), _(M,T),
+ /* 0948 */ _(M,T), _(M,R), _(M,R), _(M,R), _(M,R), _(V,B), _(M,L), _(M,R),
+ /* 0950 */ _(x,x), _(Ca,T), _(Ca,B), _(x,T), _(x,T), _(M,T), _(M,B), _(M,B),
+ /* 0958 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0960 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0968 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0970 */ _(x,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0978 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+
+ /* Bengali */
+
+ /* 0980 */ _(CP,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0988 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(VI,x),
+ /* 0990 */ _(VI,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0998 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 09A0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 09A8 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 09B0 */ _(C,x), _(x,x), _(C,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x),
+ /* 09B8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,L),
+ /* 09C0 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(x,x), _(x,x), _(M,L),
+ /* 09C8 */ _(M,L), _(x,x), _(x,x), _(M,LR), _(M,LR), _(V,B), _(CD,x), _(x,x),
+ /* 09D0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R),
+ /* 09D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x),
+ /* 09E0 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 09E8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 09F0 */ _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x),
+ /* 09F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(Bi,x), _(x,x), _(SM,T), _(x,x),
+
+ /* Gurmukhi */
+
+ /* 0A00 */ _(x,x), _(Bi,T), _(Bi,T), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0A08 */ _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(x,x), _(x,x), _(VI,x),
+ /* 0A10 */ _(VI,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0A18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0A20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0A28 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0A30 */ _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(x,x),
+ /* 0A38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(x,x), _(M,R), _(M,L),
+ /* 0A40 */ _(M,R), _(M,B), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T),
+ /* 0A48 */ _(M,T), _(x,x), _(x,x), _(M,T), _(M,T), _(V,B), _(x,x), _(x,x),
+ /* 0A50 */ _(x,x), _(Ca,B), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x),
+ /* 0A58 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x),
+ /* 0A60 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0A68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0A70 */ _(Bi,T), _(GM,T), _(CP,x), _(CP,x), _(x,x), _(CM,B), _(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), _(Bi,T), _(Bi,T), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0A88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x),
+ /* 0A90 */ _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0A98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0AA0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0AA8 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0AB0 */ _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x),
+ /* 0AB8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,L),
+ /* 0AC0 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(M,T), _(x,x), _(M,T),
+ /* 0AC8 */ _(M,T), _(M,TR), _(x,x), _(M,R), _(M,R), _(V,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 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0AE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0AF0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x),
+ /* 0AF8 */ _(x,x), _(C,x), _(Ca,T), _(Ca,T), _(Ca,T), _(N,T), _(N,T), _(N,T),
+
+ /* Oriya */
+
+ /* 0B00 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0B08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(VI,x),
+ /* 0B10 */ _(VI,x), _(x,x), _(x,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0B18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0B20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0B28 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0B30 */ _(C,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x),
+ /* 0B38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,T),
+ /* 0B40 */ _(M,R), _(M,B), _(M,B), _(M,B), _(M,B), _(x,x), _(x,x), _(M,L),
+ /* 0B48 */ _(M,TL), _(x,x), _(x,x), _(M,LR),_(M,TLR), _(V,B), _(x,x), _(x,x),
+ /* 0B50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,T), _(M,TR),
+ /* 0B58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(C,x),
+ /* 0B60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0B68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0B70 */ _(x,x), _(C,x), _(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), _(Bi,T), _(ML,x), _(x,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0B88 */ _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(x,x), _(VI,x), _(VI,x),
+ /* 0B90 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(x,x), _(x,x),
+ /* 0B98 */ _(x,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), _(C,x), _(C,x),
+ /* 0BA0 */ _(x,x), _(x,x), _(x,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x),
+ /* 0BA8 */ _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(C,x), _(C,x),
+ /* 0BB0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0BB8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), _(M,R),
+ /* 0BC0 */ _(M,T), _(M,R), _(M,R), _(x,x), _(x,x), _(x,x), _(M,L), _(M,L),
+ /* 0BC8 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T), _(x,x), _(x,x),
+ /* 0BD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R),
+ /* 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), _(Nd,x), _(Nd,x),
+ /* 0BE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 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 */ _(Bi,T), _(Bi,R), _(Bi,R), _(Vs,R), _(Bi,T), _(VI,x), _(VI,x), _(VI,x),
+ /* 0C08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x),
+ /* 0C10 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0C18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0C20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0C28 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0C30 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0C38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(A,x), _(M,T), _(M,T),
+ /* 0C40 */ _(M,T), _(M,R), _(M,R), _(M,R), _(M,R), _(x,x), _(M,T), _(M,T),
+ /* 0C48 */ _(M,TB), _(x,x), _(M,T), _(M,T), _(M,T), _(V,T), _(x,x), _(x,x),
+ /* 0C50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), _(M,B), _(x,x),
+ /* 0C58 */ _(C,x), _(C,x), _(C,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x),
+ /* 0C60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0C68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 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 */ _(Bi,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0C88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x),
+ /* 0C90 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0C98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0CA0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0CA8 */ _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0CB0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x),
+ /* 0CB8 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(A,x), _(M,R), _(M,T),
+ /* 0CC0 */ _(M,TR), _(M,R), _(M,R), _(M,R), _(M,R), _(x,x), _(M,T), _(M,TR),
+ /* 0CC8 */ _(M,TR), _(x,x), _(M,TR), _(M,TR), _(M,T), _(V,T), _(x,x), _(x,x),
+ /* 0CD0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R), _(M,R), _(x,x),
+ /* 0CD8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(C,x), _(x,x),
+ /* 0CE0 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0CE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0CF0 */ _(x,x),_(CWS,x),_(CWS,x), _(x,x), _(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 */ _(Bi,T), _(Bi,T), _(Bi,R), _(Vs,R), _(Bi,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0D08 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x),
+ /* 0D10 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x),
+ /* 0D18 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0D20 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0D28 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0D30 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0D38 */ _(C,x), _(C,x), _(C,x), _(PK,T), _(PK,T), _(A,x), _(M,R), _(M,R),
+ /* 0D40 */ _(M,R), _(M,R), _(M,R), _(M,B), _(M,B), _(x,x), _(M,L), _(M,L),
+ /* 0D48 */ _(M,L), _(x,x), _(M,LR), _(M,LR), _(M,LR), _(V,T),_(CPR,T), _(x,x),
+ /* 0D50 */ _(x,x), _(x,x), _(x,x), _(x,x), _(CD,x), _(CD,x), _(CD,x), _(M,R),
+ /* 0D58 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(VI,x),
+ /* 0D60 */ _(VI,x), _(VI,x), _(M,B), _(M,B), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0D68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0D70 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x),
+ /* 0D78 */ _(x,x), _(x,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x), _(CD,x),
+
+ /* Sinhala */
+
+ /* 0D80 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0D88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 0D90 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x),
+ /* 0D98 */ _(x,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0DA0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0DA8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0DB0 */ _(C,x), _(C,x), _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 0DB8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), _(x,x),
+ /* 0DC0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x),
+ /* 0DC8 */ _(x,x), _(x,x), _(V,T), _(x,x), _(x,x), _(x,x), _(x,x), _(M,R),
+ /* 0DD0 */ _(M,R), _(M,R), _(M,T), _(M,T), _(M,B), _(x,x), _(M,B), _(x,x),
+ /* 0DD8 */ _(M,R), _(M,L), _(M,TL), _(M,L), _(M,LR),_(M,TLR), _(M,LR), _(M,R),
+ /* 0DE0 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x),
+ /* 0DE8 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 0DF0 */ _(x,x), _(x,x), _(M,R), _(M,R), _(x,x), _(x,x), _(x,x), _(x,x),
+
+#define indic_offset_0x1000u 1336
+
+
+ /* Myanmar */
+
+ /* 1000 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1008 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1010 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1018 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1020 */ _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 1028 */ _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R), _(M,T), _(M,T), _(M,B),
+ /* 1030 */ _(M,B), _(M,L), _(M,T), _(M,T), _(M,T), _(M,T), _(Bi,T), _(TM,B),
+ /* 1038 */ _(Vs,R), _(IS,x), _(PK,T), _(CM,R),_(CM,TBL), _(CM,B), _(CM,B), _(C,x),
+ /* 1040 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 1048 */ _(Nd,x), _(Nd,x), _(x,x), _(CP,x), _(x,x), _(x,x), _(CP,x), _(x,x),
+ /* 1050 */ _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(M,R), _(M,R),
+ /* 1058 */ _(M,B), _(M,B), _(C,x), _(C,x), _(C,x), _(C,x), _(CM,B), _(CM,B),
+ /* 1060 */ _(CM,B), _(C,x), _(M,R), _(TM,R), _(TM,R), _(C,x), _(C,x), _(M,R),
+ /* 1068 */ _(M,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(C,x), _(C,x),
+ /* 1070 */ _(C,x), _(M,T), _(M,T), _(M,T), _(M,T), _(C,x), _(C,x), _(C,x),
+ /* 1078 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1080 */ _(C,x), _(C,x), _(CM,B), _(M,R), _(M,L), _(M,T), _(M,T), _(TM,R),
+ /* 1088 */ _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,R), _(TM,B), _(C,x), _(TM,R),
+ /* 1090 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 1098 */ _(Nd,x), _(Nd,x), _(TM,R), _(TM,R), _(M,R), _(M,T), _(x,x), _(x,x),
+
+#define indic_offset_0x1780u 1496
+
+
+ /* Khmer */
+
+ /* 1780 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1788 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1790 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 1798 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* 17A0 */ _(C,x), _(C,x), _(C,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 17A8 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x),
+ /* 17B0 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(x,x), _(M,R), _(M,T),
+ /* 17B8 */ _(M,T), _(M,T), _(M,T), _(M,B), _(M,B), _(M,B), _(M,TL),_(M,TLR),
+ /* 17C0 */ _(M,LR), _(M,L), _(M,L), _(M,L), _(M,LR), _(M,LR), _(Bi,T), _(Vs,R),
+ /* 17C8 */ _(M,R), _(RS,T), _(RS,T), _(SM,T),_(CSR,T), _(CK,T), _(SM,T), _(SM,T),
+ /* 17D0 */ _(SM,T), _(PK,T), _(IS,x), _(SM,T), _(x,x), _(x,x), _(x,x), _(x,x),
+ /* 17D8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(A,x), _(SM,T), _(x,x), _(x,x),
+ /* 17E0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* 17E8 */ _(Nd,x), _(Nd,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x),
+
+#define indic_offset_0x1cd0u 1608
+
+
+ /* Vedic Extensions */
+
+ /* 1CD0 */ _(Ca,T), _(Ca,T), _(Ca,T), _(x,x), _(Ca,O), _(Ca,B), _(Ca,B), _(Ca,B),
+ /* 1CD8 */ _(Ca,B), _(Ca,B), _(Ca,T), _(Ca,T), _(Ca,B), _(Ca,B), _(Ca,B), _(Ca,B),
+ /* 1CE0 */ _(Ca,T), _(Ca,R), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O),
+ /* 1CE8 */ _(x,O), _(x,x), _(x,x), _(x,x), _(x,x), _(x,B), _(x,x), _(x,x),
+ /* 1CF0 */ _(x,x), _(x,x), _(CD,x), _(CD,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R),
+ /* 1CF8 */ _(Ca,x), _(Ca,x), _(CP,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x),
+
+#define indic_offset_0x2008u 1656
+
+
+ /* General Punctuation */
+
+ /* 2008 */ _(x,x), _(x,x), _(x,x), _(x,x),_(ZWNJ,x),_(ZWJ,x), _(x,x), _(x,x),
+ /* 2010 */ _(CP,x), _(CP,x), _(CP,x), _(CP,x), _(CP,x), _(x,x), _(x,x), _(x,x),
+
+#define indic_offset_0x2070u 1672
+
+
+ /* Superscripts and Subscripts */
+
+ /* 2070 */ _(x,x), _(x,x), _(x,x), _(x,x), _(SM,x), _(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,x), _(SM,x), _(SM,x), _(x,x), _(x,x), _(x,x),
+
+#define indic_offset_0xa8e0u 1696
+
+
+ /* Devanagari Extended */
+
+ /* A8E0 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T),
+ /* A8E8 */ _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T), _(Ca,T),
+ /* A8F0 */ _(Ca,T), _(Ca,T), _(Bi,x), _(Bi,x), _(x,x), _(x,x), _(x,x), _(x,x),
+ /* A8F8 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(VI,x), _(M,T),
+
+#define indic_offset_0xa9e0u 1728
+
+
+ /* Myanmar Extended-B */
+
+ /* A9E0 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(M,T), _(x,x), _(C,x),
+ /* A9E8 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* A9F0 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x),
+ /* A9F8 */ _(Nd,x), _(Nd,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x),
+
+#define indic_offset_0xaa60u 1760
+
+
+ /* Myanmar Extended-A */
+
+ /* AA60 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* AA68 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x),
+ /* AA70 */ _(x,x), _(C,x), _(C,x), _(C,x), _(CP,x), _(CP,x), _(CP,x), _(x,x),
+ /* AA78 */ _(x,x), _(x,x), _(C,x), _(TM,R), _(TM,T), _(TM,R), _(C,x), _(C,x),
+
+}; /* Table items: 1792; occupancy: 70% */
+
+INDIC_TABLE_ELEMENT_TYPE
+hb_indic_get_categories (hb_codepoint_t u)
+{
+ switch (u >> 12)
+ {
+ case 0x0u:
+ if (unlikely (u == 0x00A0u)) return _(CP,x);
+ 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, 0x0DF7u)) 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 _(CP,x);
+ if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) 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];
+ 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;
+
+ default:
+ break;
+ }
+ return _(x,x);
+}
+
+#undef _
+
+#undef ISC_A
+#undef ISC_Bi
+#undef ISC_BJN
+#undef ISC_Ca
+#undef ISC_C
+#undef ISC_CD
+#undef ISC_CF
+#undef ISC_CHL
+#undef ISC_CIP
+#undef ISC_CK
+#undef ISC_CM
+#undef ISC_CP
+#undef ISC_CPR
+#undef ISC_CPrf
+#undef ISC_CS
+#undef ISC_CSR
+#undef ISC_CWS
+#undef ISC_GM
+#undef ISC_IS
+#undef ISC_ZWJ
+#undef ISC_ML
+#undef ISC_ZWNJ
+#undef ISC_N
+#undef ISC_Nd
+#undef ISC_NJ
+#undef ISC_x
+#undef ISC_PK
+#undef ISC_RS
+#undef ISC_SM
+#undef ISC_TL
+#undef ISC_TM
+#undef ISC_V
+#undef ISC_Vs
+#undef ISC_Vo
+#undef ISC_M
+#undef ISC_VI
+
+#undef IMC_B
+#undef IMC_BL
+#undef IMC_BR
+#undef IMC_L
+#undef IMC_LR
+#undef IMC_x
+#undef IMC_O
+#undef IMC_R
+#undef IMC_T
+#undef IMC_TB
+#undef IMC_TBL
+#undef IMC_TBR
+#undef IMC_TL
+#undef IMC_TLR
+#undef IMC_TR
+#undef IMC_VOL
+
+#endif
+
+/* == End of generated table == */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.cc
new file mode 100644
index 0000000000..34972f81e2
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -0,0 +1,1615 @@
+/*
+ * 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-complex-indic.hh"
+#include "hb-ot-shape-complex-vowel-constraints.hh"
+#include "hb-ot-layout.hh"
+
+
+/*
+ * Indic shaper.
+ */
+
+
+/*
+ * 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 base_position_t {
+ BASE_POS_LAST_SINHALA,
+ BASE_POS_LAST
+};
+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;
+ base_position_t base_pos;
+ 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,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_DEVANAGARI,true, 0x094Du,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_BENGALI, true, 0x09CDu,BASE_POS_LAST, REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_GURMUKHI, true, 0x0A4Du,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_GUJARATI, true, 0x0ACDu,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_ORIYA, true, 0x0B4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_TAMIL, true, 0x0BCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_TELUGU, true, 0x0C4Du,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY},
+ {HB_SCRIPT_KANNADA, true, 0x0CCDu,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY},
+ {HB_SCRIPT_MALAYALAM, true, 0x0D4Du,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST},
+ {HB_SCRIPT_SINHALA, false,0x0DCAu,BASE_POS_LAST_SINHALA,
+ REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_PRE_AND_POST},
+};
+
+
+
+/*
+ * Indic shaper.
+ */
+
+static const hb_ot_map_feature_t
+indic_features[] =
+{
+ /*
+ * Basic features.
+ * These features are applied in order, one at a time, after initial_reordering.
+ */
+ {HB_TAG('n','u','k','t'), F_GLOBAL_MANUAL_JOINERS},
+ {HB_TAG('a','k','h','n'), F_GLOBAL_MANUAL_JOINERS},
+ {HB_TAG('r','p','h','f'), F_MANUAL_JOINERS},
+ {HB_TAG('r','k','r','f'), F_GLOBAL_MANUAL_JOINERS},
+ {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS},
+ {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS},
+ {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS},
+ {HB_TAG('h','a','l','f'), F_MANUAL_JOINERS},
+ {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS},
+ {HB_TAG('v','a','t','u'), F_GLOBAL_MANUAL_JOINERS},
+ {HB_TAG('c','j','c','t'), F_GLOBAL_MANUAL_JOINERS},
+ /*
+ * Other features.
+ * These features are applied all at once, after final_reordering
+ * but before clearing syllables.
+ * 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},
+ {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},
+ {HB_TAG('h','a','l','n'), F_GLOBAL_MANUAL_JOINERS},
+};
+
+/*
+ * 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 void
+setup_syllables_indic (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
+initial_reordering_indic (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
+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'));
+ /* 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'));
+
+
+ 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]);
+
+ map->enable_feature (HB_TAG('c','a','l','t'));
+ map->enable_feature (HB_TAG('c','l','i','g'));
+
+ map->add_gsub_pause (_hb_clear_syllables);
+}
+
+static void
+override_features_indic (hb_ot_shape_planner_t *plan)
+{
+ plan->map.disable_feature (HB_TAG('l','i','g','a'));
+}
+
+
+struct indic_shape_plan_t
+{
+ bool load_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
+ {
+ hb_codepoint_t glyph = virama_glyph.get_relaxed ();
+ 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.set_relaxed ((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 *) 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.set_relaxed (-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)
+{
+ 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;
+}
+
+
+enum indic_syllable_type_t {
+ indic_consonant_syllable,
+ indic_vowel_syllable,
+ indic_standalone_cluster,
+ indic_symbol_cluster,
+ indic_broken_cluster,
+ indic_non_indic_cluster,
+};
+
+#include "hb-ot-shape-complex-indic-machine.hh"
+
+
+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 void
+setup_syllables_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font HB_UNUSED,
+ hb_buffer_t *buffer)
+{
+ find_syllables_indic (buffer);
+ foreach_syllable (buffer, start, end)
+ buffer->unsafe_to_break (start, end);
+}
+
+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 a < b ? -1 : a == b ? 0 : +1;
+}
+
+
+
+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;
+
+ if (indic_plan->config->base_pos != BASE_POS_LAST)
+ return;
+
+ 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 (OT_Ra)) &&
+ is_one_of (info[start+1], FLAG (OT_H)) &&
+ is_one_of (info[start+2], FLAG (OT_ZWJ)))
+ {
+ buffer->merge_clusters (start+1, start+3);
+ hb_glyph_info_t tmp = info[start+1];
+ info[start+1] = info[start+2];
+ info[start+2] = tmp;
+ }
+
+ /* 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() == OT_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() == OT_Repha)
+ {
+ limit += 1;
+ while (limit < end && is_joiner (info[limit]))
+ limit++;
+ base = start;
+ has_reph = true;
+ }
+
+ switch (indic_plan->config->base_pos)
+ {
+ case BASE_POS_LAST:
+ {
+ /* -> 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() == OT_ZWJ &&
+ info[i - 1].indic_category() == OT_H)
+ break;
+ }
+ } while (i > limit);
+ }
+ break;
+
+ case BASE_POS_LAST_SINHALA:
+ {
+ /* Sinhala base positioning is slightly different from main Indic, in that:
+ * 1. Its ZWJ behavior is different,
+ * 2. We don't need to look into the font for consonant positions.
+ */
+
+ if (!has_reph)
+ base = limit;
+
+ /* Find the last base consonant that is not blocked by ZWJ. If there is
+ * a ZWJ right before a base consonant, that would request a subjoined form. */
+ for (unsigned int i = limit; i < end; i++)
+ if (is_consonant (info[i]))
+ {
+ if (limit < i && info[i - 1].indic_category() == OT_ZWJ)
+ break;
+ else
+ base = i;
+ }
+
+ /* Mark all subsequent consonants as below. */
+ for (unsigned int i = base + 1; i < end; i++)
+ if (is_consonant (info[i]))
+ info[i].indic_position() = POS_BELOW_C;
+ }
+ break;
+ }
+
+ /* -> 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;
+
+ /* Mark final consonants. A final consonant is one appearing after a matra.
+ * Happens in Sinhala. */
+ for (unsigned int i = base + 1; i < end; i++)
+ if (info[i].indic_category() == OT_M) {
+ for (unsigned int j = i + 1; j < end; j++)
+ if (is_consonant (info[j])) {
+ info[j].indic_position() = POS_FINAL_C;
+ break;
+ }
+ break;
+ }
+
+ /* 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() == OT_H)
+ {
+ unsigned int j;
+ for (j = end - 1; j > i; j--)
+ if (is_consonant (info[j]) ||
+ (disallow_double_halants && info[j].indic_category() == OT_H))
+ break;
+ if (info[j].indic_category() != OT_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 (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_H))))
+ {
+ info[i].indic_position() = last_pos;
+ if (unlikely (info[i].indic_category() == OT_H &&
+ info[i].indic_position() == POS_PRE_M))
+ {
+ /*
+ * Uniscribe doesn't move the Halant with Left Matra.
+ * TEST: U+092B,U+093F,U+094DE
+ * We follow. This is important for the Sinhala
+ * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
+ * where U+0DD9 is a left matra and U+0DCA is the virama.
+ * We don't want to move the virama with the left matra.
+ * TEST: U+0D9A,U+0DDA
+ */
+ 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) {
+ 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 (info[i].indic_category() == OT_M)
+ 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 */
+ base = end;
+ for (unsigned int i = start; i < end; i++)
+ if (info[i].indic_position() == POS_BASE_C)
+ {
+ base = i;
+ break;
+ }
+ /* 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.
+ */
+ 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 max = i;
+ unsigned int j = start + info[i].syllable();
+ while (j != i)
+ {
+ 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;
+ }
+ if (i != max)
+ buffer->merge_clusters (i, 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() == OT_Ra &&
+ info[i+1].indic_category() == OT_H &&
+ (i + 2 == base ||
+ info[i+2].indic_category() != OT_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() == OT_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() == OT_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 inline void
+insert_dotted_circles_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
+ return;
+
+ /* Note: This loop is extra overhead, but should not be measurable.
+ * TODO Use a buffer scratch flag to remove the loop. */
+ bool has_broken_syllables = false;
+ unsigned int count = buffer->len;
+ hb_glyph_info_t *info = buffer->info;
+ for (unsigned int i = 0; i < count; i++)
+ if ((info[i].syllable() & 0x0F) == indic_broken_cluster)
+ {
+ has_broken_syllables = true;
+ break;
+ }
+ if (likely (!has_broken_syllables))
+ return;
+
+
+ hb_codepoint_t dottedcircle_glyph;
+ if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
+ return;
+
+ hb_glyph_info_t dottedcircle = {0};
+ dottedcircle.codepoint = 0x25CCu;
+ set_indic_properties (dottedcircle);
+ 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();
+ indic_syllable_type_t syllable_type = (indic_syllable_type_t) (syllable & 0x0F);
+ if (unlikely (last_syllable != syllable && syllable_type == indic_broken_cluster))
+ {
+ 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. */
+ while (buffer->idx < buffer->len && buffer->successful &&
+ last_syllable == buffer->cur().syllable() &&
+ buffer->cur().indic_category() == OT_Repha)
+ buffer->next_glyph ();
+
+ buffer->output_info (ginfo);
+ }
+ else
+ buffer->next_glyph ();
+ }
+ buffer->swap_buffers ();
+}
+
+static void
+initial_reordering_indic (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ update_consonant_positions_indic (plan, font, buffer);
+ insert_dotted_circles_indic (plan, font, buffer);
+
+ foreach_syllable (buffer, start, end)
+ initial_reordering_syllable_indic (plan, font->face, buffer, start, end);
+}
+
+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 OT_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.get_relaxed ();
+ 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() = OT_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++;
+ info[base].indic_position() = POS_BASE_C;
+
+ try_pref = false;
+ }
+ 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 (OT_ZWJ)))
+ base--;
+ if (base < end)
+ while (start < base &&
+ is_one_of (info[base], (FLAG (OT_N) | FLAG (OT_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 (OT_M) | FLAG (OT_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() == OT_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() == OT_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 (info[i].indic_category() == OT_M) {
+ /* 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(OT_M) | FLAG (OT_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:
+ case HB_SCRIPT_SINHALA:
+ break;
+
+ default:
+ /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil & Sinhala.
+ * 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 void
+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;
+
+ foreach_syllable (buffer, start, end)
+ final_reordering_syllable_indic (plan, buffer, start, end);
+
+ HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
+ HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
+}
+
+
+static void
+preprocess_text_indic (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
+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
+ }
+
+ if ((ab == 0x0DDAu || hb_in_range<hb_codepoint_t> (ab, 0x0DDCu, 0x0DDEu)))
+ {
+ /*
+ * Sinhala split matras... Let the fun begin.
+ *
+ * These four characters have Unicode decompositions. However, Uniscribe
+ * decomposes them "Khmer-style", that is, it uses the character itself to
+ * get the second half. The first half of all four decompositions is always
+ * U+0DD9.
+ *
+ * Now, there are buggy fonts, namely, the widely used lklug.ttf, that are
+ * broken with Uniscribe. But we need to support them. As such, we only
+ * do the Uniscribe-style decomposition if the character is transformed into
+ * its "sec.half" form by the 'pstf' feature. Otherwise, we fall back to
+ * Unicode decomposition.
+ *
+ * Note that we can't unconditionally use Unicode decomposition. That would
+ * break some other fonts, that are designed to work with Uniscribe, and
+ * don't have positioning features for the Unicode-style decomposition.
+ *
+ * Argh...
+ *
+ * The Uniscribe behavior is now documented in the newly published Sinhala
+ * spec in 2012:
+ *
+ * https://docs.microsoft.com/en-us/typography/script-development/sinhala#shaping
+ */
+
+
+ const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) c->plan->data;
+ hb_codepoint_t glyph;
+ if (indic_plan->uniscribe_bug_compatible ||
+ (c->font->get_nominal_glyph (ab, &glyph) &&
+ indic_plan->pstf.would_substitute (&glyph, 1, c->font->face)))
+ {
+ /* Ok, safe to use Uniscribe-style decomposition. */
+ *a = 0x0DD9u;
+ *b = ab;
+ return true;
+ }
+ }
+
+ 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_complex_shaper_t _hb_ot_complex_shaper_indic =
+{
+ collect_features_indic,
+ override_features_indic,
+ data_create_indic,
+ data_destroy_indic,
+ preprocess_text_indic,
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+ decompose_indic,
+ compose_indic,
+ setup_masks_indic,
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+ false, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.hh
new file mode 100644
index 0000000000..41bd8bd6cc
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-indic.hh
@@ -0,0 +1,436 @@
+/*
+ * 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_COMPLEX_INDIC_HH
+#define HB_OT_SHAPE_COMPLEX_INDIC_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape-complex.hh"
+
+
+/* buffer var allocations */
+#define indic_category() complex_var_u8_0() /* indic_category_t */
+#define indic_position() complex_var_u8_1() /* indic_position_t */
+
+
+#define INDIC_TABLE_ELEMENT_TYPE uint16_t
+
+/* Cateories used in the OpenType spec:
+ * https://docs.microsoft.com/en-us/typography/script-development/devanagari
+ */
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum indic_category_t {
+ OT_X = 0,
+ OT_C = 1,
+ OT_V = 2,
+ OT_N = 3,
+ OT_H = 4,
+ OT_ZWNJ = 5,
+ OT_ZWJ = 6,
+ OT_M = 7,
+ OT_SM = 8,
+ /* OT_VD = 9, UNUSED; we use OT_A instead. */
+ OT_A = 10,
+ OT_PLACEHOLDER = 11,
+ OT_DOTTEDCIRCLE = 12,
+ OT_RS = 13, /* Register Shifter, used in Khmer OT spec. */
+ OT_Coeng = 14, /* Khmer-style Virama. */
+ OT_Repha = 15, /* Atomically-encoded logical or visual repha. */
+ OT_Ra = 16,
+ OT_CM = 17, /* Consonant-Medial. */
+ OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */
+ OT_CS = 19,
+
+ /* The following are used by Khmer & Myanmar shapers. Defined
+ * here for them to share. */
+ OT_VAbv = 26,
+ OT_VBlw = 27,
+ OT_VPre = 28,
+ OT_VPst = 29,
+};
+
+#define MEDIAL_FLAGS (FLAG (OT_CM))
+
+/* 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! */
+#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE))
+#define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ))
+
+
+/* Visual positions in a syllable from left to right. */
+enum indic_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_FINAL_C = 13,
+ POS_SMVD = 14,
+
+ POS_END = 15
+};
+
+/* Categories used in IndicSyllabicCategory.txt from UCD. */
+enum indic_syllabic_category_t {
+ INDIC_SYLLABIC_CATEGORY_OTHER = OT_X,
+
+ INDIC_SYLLABIC_CATEGORY_AVAGRAHA = OT_Symbol,
+ INDIC_SYLLABIC_CATEGORY_BINDU = OT_SM,
+ INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER = OT_PLACEHOLDER, /* Don't care. */
+ INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK = OT_A,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT = OT_C,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD = OT_C,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL = OT_CM,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER = OT_C,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER = OT_M, /* U+17CD only. */
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL = OT_CM,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER = OT_PLACEHOLDER,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA = OT_Repha,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED = OT_X, /* Don't care. */
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED = OT_CM,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA = OT_CM,
+ INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER = OT_CS,
+ INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK = OT_SM, /* https://github.com/harfbuzz/harfbuzz/issues/552 */
+ INDIC_SYLLABIC_CATEGORY_INVISIBLE_STACKER = OT_Coeng,
+ INDIC_SYLLABIC_CATEGORY_JOINER = OT_ZWJ,
+ INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER = OT_X,
+ INDIC_SYLLABIC_CATEGORY_NON_JOINER = OT_ZWNJ,
+ INDIC_SYLLABIC_CATEGORY_NUKTA = OT_N,
+ INDIC_SYLLABIC_CATEGORY_NUMBER = OT_PLACEHOLDER,
+ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER = OT_PLACEHOLDER, /* Don't care. */
+ INDIC_SYLLABIC_CATEGORY_PURE_KILLER = OT_M, /* Is like a vowel matra. */
+ INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER = OT_RS,
+ INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER = OT_SM,
+ INDIC_SYLLABIC_CATEGORY_TONE_LETTER = OT_X,
+ INDIC_SYLLABIC_CATEGORY_TONE_MARK = OT_N,
+ INDIC_SYLLABIC_CATEGORY_VIRAMA = OT_H,
+ INDIC_SYLLABIC_CATEGORY_VISARGA = OT_SM,
+ INDIC_SYLLABIC_CATEGORY_VOWEL = OT_V,
+ INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT = OT_M,
+ INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT = OT_V
+};
+
+/* Categories used in IndicSMatraCategory.txt from UCD */
+enum indic_matra_category_t {
+ INDIC_MATRA_CATEGORY_NOT_APPLICABLE = POS_END,
+
+ INDIC_MATRA_CATEGORY_LEFT = POS_PRE_C,
+ INDIC_MATRA_CATEGORY_TOP = POS_ABOVE_C,
+ INDIC_MATRA_CATEGORY_BOTTOM = POS_BELOW_C,
+ INDIC_MATRA_CATEGORY_RIGHT = POS_POST_C,
+
+ /* These should resolve to the position of the last part of the split sequence. */
+ INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT,
+ INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT,
+ INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM = INDIC_MATRA_CATEGORY_BOTTOM,
+ INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_LEFT = INDIC_MATRA_CATEGORY_BOTTOM,
+ INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT,
+ INDIC_MATRA_CATEGORY_TOP_AND_LEFT = INDIC_MATRA_CATEGORY_TOP,
+ INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT,
+ INDIC_MATRA_CATEGORY_TOP_AND_RIGHT = INDIC_MATRA_CATEGORY_RIGHT,
+
+ INDIC_MATRA_CATEGORY_OVERSTRUCK = POS_AFTER_MAIN,
+ INDIC_MATRA_CATEGORY_VISUAL_ORDER_LEFT = POS_PRE_M
+};
+
+#define INDIC_COMBINE_CATEGORIES(S,M) \
+ ( \
+ ASSERT_STATIC_EXPR_ZERO (S < 255 && M < 255) + \
+ ( S | \
+ ( \
+ ( \
+ S == INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL || \
+ S == INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK || \
+ S == INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER || \
+ S == INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA || \
+ S == INDIC_SYLLABIC_CATEGORY_VIRAMA || \
+ S == INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT || \
+ false \
+ ? M : INDIC_MATRA_CATEGORY_NOT_APPLICABLE \
+ ) << 8 \
+ ) \
+ ) \
+ )
+
+HB_INTERNAL INDIC_TABLE_ELEMENT_TYPE
+hb_indic_get_categories (hb_codepoint_t u);
+
+
+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);
+}
+
+static inline bool
+is_joiner (const hb_glyph_info_t &info)
+{
+ return is_one_of (info, JOINER_FLAGS);
+}
+
+static inline bool
+is_consonant (const hb_glyph_info_t &info)
+{
+ return is_one_of (info, CONSONANT_FLAGS);
+}
+
+static inline bool
+is_halant (const hb_glyph_info_t &info)
+{
+ return is_one_of (info, FLAG (OT_H));
+}
+
+#define IN_HALF_BLOCK(u, Base) (((u) & ~0x7Fu) == (Base))
+
+#define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900u))
+#define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980u))
+#define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00u))
+#define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80u))
+#define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00u))
+#define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80u))
+#define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00u))
+#define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80u))
+#define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00u))
+#define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80u))
+
+
+#define MATRA_POS_LEFT(u) POS_PRE_M
+#define MATRA_POS_RIGHT(u) ( \
+ IS_DEVA(u) ? POS_AFTER_SUB : \
+ IS_BENG(u) ? POS_AFTER_POST : \
+ IS_GURU(u) ? POS_AFTER_POST : \
+ IS_GUJR(u) ? POS_AFTER_POST : \
+ IS_ORYA(u) ? POS_AFTER_POST : \
+ IS_TAML(u) ? POS_AFTER_POST : \
+ IS_TELU(u) ? (u <= 0x0C42u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
+ IS_KNDA(u) ? (u < 0x0CC3u || u > 0xCD6u ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
+ IS_MLYM(u) ? POS_AFTER_POST : \
+ IS_SINH(u) ? POS_AFTER_SUB : \
+ /*default*/ POS_AFTER_SUB \
+ )
+#define MATRA_POS_TOP(u) ( /* BENG and MLYM don't have top matras. */ \
+ IS_DEVA(u) ? POS_AFTER_SUB : \
+ IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \
+ IS_GUJR(u) ? POS_AFTER_SUB : \
+ IS_ORYA(u) ? POS_AFTER_MAIN : \
+ IS_TAML(u) ? POS_AFTER_SUB : \
+ IS_TELU(u) ? POS_BEFORE_SUB : \
+ IS_KNDA(u) ? POS_BEFORE_SUB : \
+ IS_SINH(u) ? POS_AFTER_SUB : \
+ /*default*/ POS_AFTER_SUB \
+ )
+#define MATRA_POS_BOTTOM(u) ( \
+ IS_DEVA(u) ? POS_AFTER_SUB : \
+ IS_BENG(u) ? POS_AFTER_SUB : \
+ IS_GURU(u) ? POS_AFTER_POST : \
+ IS_GUJR(u) ? POS_AFTER_POST : \
+ IS_ORYA(u) ? POS_AFTER_SUB : \
+ IS_TAML(u) ? POS_AFTER_POST : \
+ IS_TELU(u) ? POS_BEFORE_SUB : \
+ IS_KNDA(u) ? POS_BEFORE_SUB : \
+ IS_MLYM(u) ? POS_AFTER_POST : \
+ IS_SINH(u) ? POS_AFTER_SUB : \
+ /*default*/ POS_AFTER_SUB \
+ )
+
+static inline indic_position_t
+matra_position_indic (hb_codepoint_t u, indic_position_t side)
+{
+ switch ((int) side)
+ {
+ case POS_PRE_C: return MATRA_POS_LEFT (u);
+ case POS_POST_C: return MATRA_POS_RIGHT (u);
+ case POS_ABOVE_C: return MATRA_POS_TOP (u);
+ case POS_BELOW_C: return MATRA_POS_BOTTOM (u);
+ }
+ return side;
+}
+
+/* XXX
+ * This is a hack for now. We should move this data into the main Indic table.
+ * Or completely remove it and just check in the tables.
+ */
+static const hb_codepoint_t ra_chars[] = {
+ 0x0930u, /* Devanagari */
+ 0x09B0u, /* Bengali */
+ 0x09F0u, /* Bengali */
+ 0x0A30u, /* Gurmukhi */ /* No Reph */
+ 0x0AB0u, /* Gujarati */
+ 0x0B30u, /* Oriya */
+ 0x0BB0u, /* Tamil */ /* No Reph */
+ 0x0C30u, /* Telugu */ /* Reph formed only with ZWJ */
+ 0x0CB0u, /* Kannada */
+ 0x0D30u, /* Malayalam */ /* No Reph, Logical Repha */
+
+ 0x0DBBu, /* Sinhala */ /* Reph formed only with ZWJ */
+
+ 0x179Au, /* Khmer */
+};
+
+static inline bool
+is_ra (hb_codepoint_t u)
+{
+ for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
+ if (u == ra_chars[i])
+ return true;
+ return false;
+}
+
+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);
+ indic_category_t cat = (indic_category_t) (type & 0x7Fu);
+ indic_position_t pos = (indic_position_t) (type >> 8);
+
+
+ /*
+ * Re-assign category
+ */
+
+ /* The following act more like the Bindus. */
+ if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0953u, 0x0954u)))
+ cat = OT_SM;
+ /* The following act like consonants. */
+ else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x0A72u, 0x0A73u,
+ 0x1CF5u, 0x1CF6u)))
+ cat = OT_C;
+ /* TODO: The following should only be allowed after a Visarga.
+ * For now, just treat them like regular tone marks. */
+ else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1CE2u, 0x1CE8u)))
+ cat = OT_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. */
+ else if (unlikely (u == 0x1CEDu))
+ cat = OT_A;
+ /* The following take marks in standalone clusters, similar to Avagraha. */
+ else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0xA8F2u, 0xA8F7u,
+ 0x1CE9u, 0x1CECu,
+ 0x1CEEu, 0x1CF1u)))
+ {
+ cat = OT_Symbol;
+ static_assert (((int) INDIC_SYLLABIC_CATEGORY_AVAGRAHA == OT_Symbol), "");
+ }
+ else if (unlikely (u == 0x0A51u))
+ {
+ /* https://github.com/harfbuzz/harfbuzz/issues/524 */
+ cat = OT_M;
+ pos = POS_BELOW_C;
+ }
+
+ /* According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil,
+ * so the Indic shaper needs to know their categories. */
+ else if (unlikely (u == 0x11301u || u == 0x11303u)) cat = OT_SM;
+ else if (unlikely (u == 0x1133Bu || u == 0x1133Cu)) cat = OT_N;
+
+ else if (unlikely (u == 0x0AFBu)) cat = OT_N; /* https://github.com/harfbuzz/harfbuzz/issues/552 */
+
+ else if (unlikely (u == 0x0980u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/issues/538 */
+ else if (unlikely (u == 0x09FCu)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/1613 */
+ else if (unlikely (u == 0x0C80u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/623 */
+ else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x2010u, 0x2011u)))
+ cat = OT_PLACEHOLDER;
+ else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE;
+
+
+ /*
+ * Re-assign position.
+ */
+
+ if ((FLAG_UNSAFE (cat) & CONSONANT_FLAGS))
+ {
+ pos = POS_BASE_C;
+ if (is_ra (u))
+ cat = OT_Ra;
+ }
+ else if (cat == OT_M)
+ {
+ pos = matra_position_indic (u, pos);
+ }
+ else if ((FLAG_UNSAFE (cat) & (FLAG (OT_SM) /* | FLAG (OT_VD) */ | FLAG (OT_A) | FLAG (OT_Symbol))))
+ {
+ pos = POS_SMVD;
+ }
+
+ if (unlikely (u == 0x0B01u)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
+
+
+
+ info.indic_category() = cat;
+ info.indic_position() = pos;
+}
+
+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_;
+ map->get_stage_lookups (0/*GSUB*/,
+ map->get_feature_stage (0/*GSUB*/, feature_tag),
+ &lookups, &count);
+ }
+
+ bool would_substitute (const hb_codepoint_t *glyphs,
+ unsigned int glyphs_count,
+ hb_face_t *face) const
+ {
+ for (unsigned int i = 0; i < count; i++)
+ if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context))
+ return true;
+ return false;
+ }
+
+ private:
+ const hb_ot_map_t::lookup_map_t *lookups;
+ unsigned int count;
+ bool zero_context;
+};
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_INDIC_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.hh
new file mode 100644
index 0000000000..a040318d34
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.hh
@@ -0,0 +1,372 @@
+
+#line 1 "hb-ot-shape-complex-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_SHAPE_COMPLEX_KHMER_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
+
+#include "hb.hh"
+
+
+#line 36 "hb-ot-shape-complex-khmer-machine.hh"
+static const unsigned char _khmer_syllable_machine_trans_keys[] = {
+ 5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u,
+ 5u, 26u, 5u, 21u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u,
+ 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 29u, 5u, 29u, 5u, 29u, 5u, 29u,
+ 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 26u, 5u, 29u,
+ 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, 5u, 29u,
+ 0
+};
+
+static const char _khmer_syllable_machine_key_spans[] = {
+ 22, 17, 22, 17, 16, 17, 22, 17,
+ 22, 17, 17, 22, 17, 16, 17, 22,
+ 17, 22, 17, 22, 29, 25, 25, 25,
+ 1, 18, 25, 25, 25, 16, 22, 25,
+ 25, 1, 18, 25, 25, 16, 25, 25
+};
+
+static const short _khmer_syllable_machine_index_offsets[] = {
+ 0, 23, 41, 64, 82, 99, 117, 140,
+ 158, 181, 199, 217, 240, 258, 275, 293,
+ 316, 334, 357, 375, 398, 428, 454, 480,
+ 506, 508, 527, 553, 579, 605, 622, 645,
+ 671, 697, 699, 718, 744, 770, 787, 813
+};
+
+static const char _khmer_syllable_machine_indicies[] = {
+ 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 3, 0, 0, 0, 0, 4, 0, 1,
+ 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0, 4, 0,
+ 5, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 4, 0, 6, 6, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 6, 0, 7, 7, 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, 0, 10, 0, 0,
+ 0, 0, 4, 0, 9, 9, 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, 0, 12, 0,
+ 0, 0, 0, 4, 0, 11, 11, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 12, 0, 14,
+ 14, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 15,
+ 13, 14, 14, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 15, 16, 16, 16, 16, 17, 16,
+ 18, 18, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 17, 16, 19, 19, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 19, 16, 20, 20, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 21, 16, 22, 22, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 23, 16, 16,
+ 16, 16, 17, 16, 22, 22, 16, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 23, 16, 24, 24,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 25, 16,
+ 16, 16, 16, 17, 16, 24, 24, 16,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 25, 16, 14,
+ 14, 16, 16, 16, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 26, 15,
+ 16, 16, 16, 16, 17, 16, 28, 28,
+ 27, 27, 29, 29, 27, 27, 27, 27,
+ 2, 2, 27, 30, 27, 28, 27, 27,
+ 27, 27, 15, 19, 27, 27, 27, 17,
+ 23, 25, 21, 27, 32, 32, 31, 31,
+ 31, 31, 31, 31, 31, 33, 31, 31,
+ 31, 31, 31, 2, 3, 6, 31, 31,
+ 31, 4, 10, 12, 8, 31, 34, 34,
+ 31, 31, 31, 31, 31, 31, 31, 35,
+ 31, 31, 31, 31, 31, 31, 3, 6,
+ 31, 31, 31, 4, 10, 12, 8, 31,
+ 5, 5, 31, 31, 31, 31, 31, 31,
+ 31, 35, 31, 31, 31, 31, 31, 31,
+ 4, 6, 31, 31, 31, 31, 31, 31,
+ 8, 31, 6, 31, 7, 7, 31, 31,
+ 31, 31, 31, 31, 31, 35, 31, 31,
+ 31, 31, 31, 31, 8, 6, 31, 36,
+ 36, 31, 31, 31, 31, 31, 31, 31,
+ 35, 31, 31, 31, 31, 31, 31, 10,
+ 6, 31, 31, 31, 4, 31, 31, 8,
+ 31, 37, 37, 31, 31, 31, 31, 31,
+ 31, 31, 35, 31, 31, 31, 31, 31,
+ 31, 12, 6, 31, 31, 31, 4, 10,
+ 31, 8, 31, 34, 34, 31, 31, 31,
+ 31, 31, 31, 31, 33, 31, 31, 31,
+ 31, 31, 31, 3, 6, 31, 31, 31,
+ 4, 10, 12, 8, 31, 28, 28, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 28, 31, 14, 14,
+ 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 15, 38,
+ 38, 38, 38, 17, 38, 40, 40, 39,
+ 39, 39, 39, 39, 39, 39, 41, 39,
+ 39, 39, 39, 39, 39, 15, 19, 39,
+ 39, 39, 17, 23, 25, 21, 39, 18,
+ 18, 39, 39, 39, 39, 39, 39, 39,
+ 41, 39, 39, 39, 39, 39, 39, 17,
+ 19, 39, 39, 39, 39, 39, 39, 21,
+ 39, 19, 39, 20, 20, 39, 39, 39,
+ 39, 39, 39, 39, 41, 39, 39, 39,
+ 39, 39, 39, 21, 19, 39, 42, 42,
+ 39, 39, 39, 39, 39, 39, 39, 41,
+ 39, 39, 39, 39, 39, 39, 23, 19,
+ 39, 39, 39, 17, 39, 39, 21, 39,
+ 43, 43, 39, 39, 39, 39, 39, 39,
+ 39, 41, 39, 39, 39, 39, 39, 39,
+ 25, 19, 39, 39, 39, 17, 23, 39,
+ 21, 39, 44, 44, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 44, 39, 45, 45, 39, 39, 39,
+ 39, 39, 39, 39, 30, 39, 39, 39,
+ 39, 39, 26, 15, 19, 39, 39, 39,
+ 17, 23, 25, 21, 39, 40, 40, 39,
+ 39, 39, 39, 39, 39, 39, 30, 39,
+ 39, 39, 39, 39, 39, 15, 19, 39,
+ 39, 39, 17, 23, 25, 21, 39, 0
+};
+
+static const char _khmer_syllable_machine_trans_targs[] = {
+ 20, 1, 28, 22, 23, 3, 24, 5,
+ 25, 7, 26, 9, 27, 20, 10, 31,
+ 20, 32, 12, 33, 14, 34, 16, 35,
+ 18, 36, 39, 20, 21, 30, 37, 20,
+ 0, 29, 2, 4, 6, 8, 20, 20,
+ 11, 13, 15, 17, 38, 19
+};
+
+static const char _khmer_syllable_machine_trans_actions[] = {
+ 1, 0, 2, 2, 2, 0, 0, 0,
+ 2, 0, 2, 0, 2, 3, 0, 4,
+ 5, 2, 0, 0, 0, 2, 0, 2,
+ 0, 2, 4, 8, 2, 9, 0, 10,
+ 0, 0, 0, 0, 0, 0, 11, 12,
+ 0, 0, 0, 0, 4, 0
+};
+
+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, 6, 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, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned char _khmer_syllable_machine_eof_trans[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 14, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 0, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 39, 40,
+ 40, 40, 40, 40, 40, 40, 40, 40
+};
+
+static const int khmer_syllable_machine_start = 20;
+static const int khmer_syllable_machine_first_final = 20;
+static const int khmer_syllable_machine_error = -1;
+
+static const int khmer_syllable_machine_en_main = 20;
+
+
+#line 36 "hb-ot-shape-complex-khmer-machine.rl"
+
+
+
+#line 80 "hb-ot-shape-complex-khmer-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+ HB_STMT_START { \
+ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+ for (unsigned int i = ts; i < te; i++) \
+ info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \
+ syllable_serial++; \
+ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+ } HB_STMT_END
+
+static 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 242 "hb-ot-shape-complex-khmer-machine.hh"
+ {
+ cs = khmer_syllable_machine_start;
+ ts = 0;
+ te = 0;
+ act = 0;
+ }
+
+#line 100 "hb-ot-shape-complex-khmer-machine.rl"
+
+
+ p = 0;
+ pe = eof = buffer->len;
+
+ unsigned int syllable_serial = 1;
+
+#line 258 "hb-ot-shape-complex-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 272 "hb-ot-shape-complex-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 76 "hb-ot-shape-complex-khmer-machine.rl"
+ {te = p+1;{ found_syllable (non_khmer_cluster); }}
+ break;
+ case 10:
+#line 74 "hb-ot-shape-complex-khmer-machine.rl"
+ {te = p;p--;{ found_syllable (consonant_syllable); }}
+ break;
+ case 12:
+#line 75 "hb-ot-shape-complex-khmer-machine.rl"
+ {te = p;p--;{ found_syllable (broken_cluster); }}
+ break;
+ case 11:
+#line 76 "hb-ot-shape-complex-khmer-machine.rl"
+ {te = p;p--;{ found_syllable (non_khmer_cluster); }}
+ break;
+ case 1:
+#line 74 "hb-ot-shape-complex-khmer-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
+ break;
+ case 5:
+#line 75 "hb-ot-shape-complex-khmer-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+ break;
+ case 3:
+#line 1 "NONE"
+ { switch( act ) {
+ case 2:
+ {{p = ((te))-1;} found_syllable (broken_cluster); }
+ break;
+ case 3:
+ {{p = ((te))-1;} found_syllable (non_khmer_cluster); }
+ break;
+ }
+ }
+ break;
+ case 4:
+#line 1 "NONE"
+ {te = p+1;}
+#line 75 "hb-ot-shape-complex-khmer-machine.rl"
+ {act = 2;}
+ break;
+ case 9:
+#line 1 "NONE"
+ {te = p+1;}
+#line 76 "hb-ot-shape-complex-khmer-machine.rl"
+ {act = 3;}
+ break;
+#line 342 "hb-ot-shape-complex-khmer-machine.hh"
+ }
+
+_again:
+ switch ( _khmer_syllable_machine_to_state_actions[cs] ) {
+ case 6:
+#line 1 "NONE"
+ {ts = 0;}
+ break;
+#line 351 "hb-ot-shape-complex-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 108 "hb-ot-shape-complex-khmer-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.rl b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.rl
new file mode 100644
index 0000000000..e7f14533dd
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer-machine.rl
@@ -0,0 +1,113 @@
+/*
+ * 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_SHAPE_COMPLEX_KHMER_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_KHMER_MACHINE_HH
+
+#include "hb.hh"
+
+%%{
+ machine khmer_syllable_machine;
+ alphtype unsigned char;
+ write data;
+}%%
+
+%%{
+
+# Same order as enum khmer_category_t. Not sure how to avoid duplication.
+C = 1;
+V = 2;
+ZWNJ = 5;
+ZWJ = 6;
+PLACEHOLDER = 11;
+DOTTEDCIRCLE = 12;
+Coeng= 14;
+Ra = 16;
+Robatic = 20;
+Xgroup = 21;
+Ygroup = 22;
+VAbv = 26;
+VBlw = 27;
+VPre = 28;
+VPst = 29;
+
+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 (Coeng.c)? ygroup;
+
+
+broken_cluster = (Coeng.cn)* (Coeng | syllable_tail);
+consonant_syllable = (cn|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster;
+other = any;
+
+main := |*
+ consonant_syllable => { found_syllable (consonant_syllable); };
+ broken_cluster => { found_syllable (broken_cluster); };
+ other => { found_syllable (non_khmer_cluster); };
+*|;
+
+
+}%%
+
+#define found_syllable(syllable_type) \
+ HB_STMT_START { \
+ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+ for (unsigned int i = ts; i < te; i++) \
+ info[i].syllable() = (syllable_serial << 4) | khmer_##syllable_type; \
+ syllable_serial++; \
+ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+ } HB_STMT_END
+
+static 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_SHAPE_COMPLEX_KHMER_MACHINE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.cc
new file mode 100644
index 0000000000..3da8374899
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.cc
@@ -0,0 +1,457 @@
+/*
+ * 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-complex-khmer.hh"
+#include "hb-ot-layout.hh"
+
+
+/*
+ * Khmer shaper.
+ */
+
+static const hb_ot_map_feature_t
+khmer_features[] =
+{
+ /*
+ * Basic features.
+ * These features are applied in order, one at a time, after reordering.
+ */
+ {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS},
+ {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS},
+ {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS},
+ {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS},
+ {HB_TAG('c','f','a','r'), F_MANUAL_JOINERS},
+ /*
+ * 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 void
+setup_syllables_khmer (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
+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'));
+ map->enable_feature (HB_TAG('c','c','m','p'));
+
+ unsigned int i = 0;
+ for (; i < KHMER_BASIC_FEATURES; i++)
+ map->add_feature (khmer_features[i]);
+
+ map->add_gsub_pause (_hb_clear_syllables);
+
+ 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
+{
+ bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
+ {
+ hb_codepoint_t glyph = virama_glyph;
+ if (unlikely (virama_glyph == (hb_codepoint_t) -1))
+ {
+ if (!font->get_nominal_glyph (0x17D2u, &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. It's safe. Don't worry! */
+ virama_glyph = glyph;
+ }
+
+ *pglyph = glyph;
+ return glyph != 0;
+ }
+
+ mutable hb_codepoint_t virama_glyph;
+
+ 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 *) calloc (1, sizeof (khmer_shape_plan_t));
+ if (unlikely (!khmer_plan))
+ return nullptr;
+
+ khmer_plan->virama_glyph = (hb_codepoint_t) -1;
+
+ 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)
+{
+ free (data);
+}
+
+
+enum khmer_syllable_type_t {
+ khmer_consonant_syllable,
+ khmer_broken_cluster,
+ khmer_non_khmer_cluster,
+};
+
+#include "hb-ot-shape-complex-khmer-machine.hh"
+
+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 void
+setup_syllables_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font HB_UNUSED,
+ hb_buffer_t *buffer)
+{
+ find_syllables_khmer (buffer);
+ foreach_syllable (buffer, start, end)
+ buffer->unsafe_to_break (start, end);
+}
+
+
+/* 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() == OT_Coeng && num_coengs <= 2 && i + 1 < end)
+ {
+ num_coengs++;
+
+ if (info[i + 1].khmer_category() == OT_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() == OT_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 inline void
+insert_dotted_circles_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
+ return;
+
+ /* Note: This loop is extra overhead, but should not be measurable.
+ * TODO Use a buffer scratch flag to remove the loop. */
+ bool has_broken_syllables = false;
+ unsigned int count = buffer->len;
+ hb_glyph_info_t *info = buffer->info;
+ for (unsigned int i = 0; i < count; i++)
+ if ((info[i].syllable() & 0x0F) == khmer_broken_cluster)
+ {
+ has_broken_syllables = true;
+ break;
+ }
+ if (likely (!has_broken_syllables))
+ return;
+
+
+ hb_codepoint_t dottedcircle_glyph;
+ if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
+ return;
+
+ hb_glyph_info_t dottedcircle = {0};
+ dottedcircle.codepoint = 0x25CCu;
+ set_khmer_properties (dottedcircle);
+ 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();
+ khmer_syllable_type_t syllable_type = (khmer_syllable_type_t) (syllable & 0x0F);
+ if (unlikely (last_syllable != syllable && syllable_type == khmer_broken_cluster))
+ {
+ 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. */
+ while (buffer->idx < buffer->len && buffer->successful &&
+ last_syllable == buffer->cur().syllable() &&
+ buffer->cur().khmer_category() == OT_Repha)
+ buffer->next_glyph ();
+
+ buffer->output_info (ginfo);
+ }
+ else
+ buffer->next_glyph ();
+ }
+ buffer->swap_buffers ();
+}
+
+static void
+reorder_khmer (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ insert_dotted_circles_khmer (plan, font, buffer);
+
+ foreach_syllable (buffer, start, end)
+ reorder_syllable_khmer (plan, font->face, buffer, start, end);
+
+ HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
+}
+
+
+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_complex_shaper_t _hb_ot_complex_shaper_khmer =
+{
+ collect_features_khmer,
+ override_features_khmer,
+ data_create_khmer,
+ data_destroy_khmer,
+ nullptr, /* preprocess_text */
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+ decompose_khmer,
+ compose_khmer,
+ setup_masks_khmer,
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+ false, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.hh
new file mode 100644
index 0000000000..11a77bfd4b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-khmer.hh
@@ -0,0 +1,113 @@
+/*
+ * 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_SHAPE_COMPLEX_KHMER_HH
+#define HB_OT_SHAPE_COMPLEX_KHMER_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape-complex-indic.hh"
+
+
+/* buffer var allocations */
+#define khmer_category() indic_category() /* khmer_category_t */
+
+
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum khmer_category_t
+{
+ OT_Robatic = 20,
+ OT_Xgroup = 21,
+ OT_Ygroup = 22,
+ //OT_VAbv = 26,
+ //OT_VBlw = 27,
+ //OT_VPre = 28,
+ //OT_VPst = 29,
+};
+
+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);
+ khmer_category_t cat = (khmer_category_t) (type & 0x7Fu);
+ indic_position_t pos = (indic_position_t) (type >> 8);
+
+
+ /*
+ * Re-assign category
+ *
+ * These categories are experimentally extracted from what Uniscribe allows.
+ */
+ switch (u)
+ {
+ case 0x179Au:
+ cat = (khmer_category_t) OT_Ra;
+ break;
+
+ case 0x17CCu:
+ case 0x17C9u:
+ case 0x17CAu:
+ cat = OT_Robatic;
+ break;
+
+ case 0x17C6u:
+ case 0x17CBu:
+ case 0x17CDu:
+ case 0x17CEu:
+ case 0x17CFu:
+ case 0x17D0u:
+ case 0x17D1u:
+ cat = OT_Xgroup;
+ break;
+
+ case 0x17C7u:
+ case 0x17C8u:
+ case 0x17DDu:
+ case 0x17D3u: /* Just guessing. Uniscribe doesn't categorize it. */
+ cat = OT_Ygroup;
+ break;
+ }
+
+ /*
+ * Re-assign position.
+ */
+ if (cat == (khmer_category_t) OT_M)
+ switch ((int) pos)
+ {
+ case POS_PRE_C: cat = (khmer_category_t) OT_VPre; break;
+ case POS_BELOW_C: cat = (khmer_category_t) OT_VBlw; break;
+ case POS_ABOVE_C: cat = (khmer_category_t) OT_VAbv; break;
+ case POS_POST_C: cat = (khmer_category_t) OT_VPst; break;
+ default: assert (0);
+ }
+
+ info.khmer_category() = cat;
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_KHMER_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh
new file mode 100644
index 0000000000..c2f4c0045c
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -0,0 +1,430 @@
+
+#line 1 "hb-ot-shape-complex-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_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+
+#include "hb.hh"
+
+
+#line 36 "hb-ot-shape-complex-myanmar-machine.hh"
+static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
+ 1u, 32u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u,
+ 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u,
+ 3u, 29u, 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u,
+ 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u,
+ 3u, 29u, 3u, 29u, 1u, 16u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u,
+ 3u, 29u, 3u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 30u,
+ 3u, 29u, 1u, 32u, 1u, 32u, 8u, 8u, 0
+};
+
+static const char _myanmar_syllable_machine_key_spans[] = {
+ 32, 28, 25, 4, 25, 23, 21, 21,
+ 27, 27, 27, 27, 16, 27, 27, 27,
+ 27, 27, 28, 27, 27, 27, 27, 27,
+ 25, 4, 25, 23, 21, 21, 27, 27,
+ 27, 27, 16, 28, 27, 27, 27, 27,
+ 27, 28, 27, 27, 27, 27, 27, 28,
+ 27, 32, 32, 1
+};
+
+static const short _myanmar_syllable_machine_index_offsets[] = {
+ 0, 33, 62, 88, 93, 119, 143, 165,
+ 187, 215, 243, 271, 299, 316, 344, 372,
+ 400, 428, 456, 485, 513, 541, 569, 597,
+ 625, 651, 656, 682, 706, 728, 750, 778,
+ 806, 834, 862, 879, 908, 936, 964, 992,
+ 1020, 1048, 1077, 1105, 1133, 1161, 1189, 1217,
+ 1246, 1274, 1307, 1340
+};
+
+static const char _myanmar_syllable_machine_indicies[] = {
+ 1, 1, 2, 3, 4, 4, 0, 5,
+ 0, 6, 1, 0, 0, 0, 0, 7,
+ 0, 8, 9, 0, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 1,
+ 0, 22, 23, 24, 24, 21, 25, 21,
+ 26, 21, 21, 21, 21, 21, 21, 21,
+ 27, 21, 21, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 21, 24, 24,
+ 21, 25, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 38, 21, 21, 21, 21,
+ 21, 21, 32, 21, 21, 21, 36, 21,
+ 24, 24, 21, 25, 21, 24, 24, 21,
+ 25, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 32, 21, 21, 21, 36, 21, 39,
+ 21, 24, 24, 21, 25, 21, 32, 21,
+ 21, 21, 21, 21, 21, 21, 40, 21,
+ 21, 21, 21, 21, 21, 32, 21, 24,
+ 24, 21, 25, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 40, 21, 21, 21,
+ 21, 21, 21, 32, 21, 24, 24, 21,
+ 25, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 32, 21, 22, 21, 24, 24, 21,
+ 25, 21, 26, 21, 21, 21, 21, 21,
+ 21, 21, 41, 21, 21, 41, 21, 21,
+ 21, 32, 42, 21, 21, 36, 21, 22,
+ 21, 24, 24, 21, 25, 21, 26, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 32, 21, 21,
+ 21, 36, 21, 22, 21, 24, 24, 21,
+ 25, 21, 26, 21, 21, 21, 21, 21,
+ 21, 21, 41, 21, 21, 21, 21, 21,
+ 21, 32, 42, 21, 21, 36, 21, 22,
+ 21, 24, 24, 21, 25, 21, 26, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 32, 42, 21,
+ 21, 36, 21, 1, 1, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 1, 21, 22, 21, 24, 24,
+ 21, 25, 21, 26, 21, 21, 21, 21,
+ 21, 21, 21, 27, 21, 21, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 21,
+ 22, 21, 24, 24, 21, 25, 21, 26,
+ 21, 21, 21, 21, 21, 21, 21, 43,
+ 21, 21, 21, 21, 21, 21, 32, 33,
+ 34, 35, 36, 21, 22, 21, 24, 24,
+ 21, 25, 21, 26, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 32, 33, 34, 35, 36, 21,
+ 22, 21, 24, 24, 21, 25, 21, 26,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 32, 33,
+ 34, 21, 36, 21, 22, 21, 24, 24,
+ 21, 25, 21, 26, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 32, 21, 34, 21, 36, 21,
+ 22, 21, 24, 24, 21, 25, 21, 26,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 32, 33,
+ 34, 35, 36, 43, 21, 22, 21, 24,
+ 24, 21, 25, 21, 26, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 28,
+ 21, 30, 21, 32, 33, 34, 35, 36,
+ 21, 22, 21, 24, 24, 21, 25, 21,
+ 26, 21, 21, 21, 21, 21, 21, 21,
+ 43, 21, 21, 28, 21, 21, 21, 32,
+ 33, 34, 35, 36, 21, 22, 21, 24,
+ 24, 21, 25, 21, 26, 21, 21, 21,
+ 21, 21, 21, 21, 44, 21, 21, 28,
+ 29, 30, 21, 32, 33, 34, 35, 36,
+ 21, 22, 21, 24, 24, 21, 25, 21,
+ 26, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 28, 29, 30, 21, 32,
+ 33, 34, 35, 36, 21, 22, 23, 24,
+ 24, 21, 25, 21, 26, 21, 21, 21,
+ 21, 21, 21, 21, 27, 21, 21, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36,
+ 21, 46, 46, 45, 5, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 47, 45,
+ 45, 45, 45, 45, 45, 14, 45, 45,
+ 45, 18, 45, 46, 46, 45, 5, 45,
+ 46, 46, 45, 5, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 14, 45, 45, 45,
+ 18, 45, 48, 45, 46, 46, 45, 5,
+ 45, 14, 45, 45, 45, 45, 45, 45,
+ 45, 49, 45, 45, 45, 45, 45, 45,
+ 14, 45, 46, 46, 45, 5, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 49,
+ 45, 45, 45, 45, 45, 45, 14, 45,
+ 46, 46, 45, 5, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 14, 45, 2, 45,
+ 46, 46, 45, 5, 45, 6, 45, 45,
+ 45, 45, 45, 45, 45, 50, 45, 45,
+ 50, 45, 45, 45, 14, 51, 45, 45,
+ 18, 45, 2, 45, 46, 46, 45, 5,
+ 45, 6, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 14, 45, 45, 45, 18, 45, 2, 45,
+ 46, 46, 45, 5, 45, 6, 45, 45,
+ 45, 45, 45, 45, 45, 50, 45, 45,
+ 45, 45, 45, 45, 14, 51, 45, 45,
+ 18, 45, 2, 45, 46, 46, 45, 5,
+ 45, 6, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 14, 51, 45, 45, 18, 45, 52, 52,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 52, 45, 2,
+ 3, 46, 46, 45, 5, 45, 6, 45,
+ 45, 45, 45, 45, 45, 45, 8, 45,
+ 45, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 45, 2, 45, 46, 46,
+ 45, 5, 45, 6, 45, 45, 45, 45,
+ 45, 45, 45, 8, 45, 45, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 45,
+ 2, 45, 46, 46, 45, 5, 45, 6,
+ 45, 45, 45, 45, 45, 45, 45, 53,
+ 45, 45, 45, 45, 45, 45, 14, 15,
+ 16, 17, 18, 45, 2, 45, 46, 46,
+ 45, 5, 45, 6, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 14, 15, 16, 17, 18, 45,
+ 2, 45, 46, 46, 45, 5, 45, 6,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 14, 15,
+ 16, 45, 18, 45, 2, 45, 46, 46,
+ 45, 5, 45, 6, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 14, 45, 16, 45, 18, 45,
+ 2, 45, 46, 46, 45, 5, 45, 6,
+ 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 14, 15,
+ 16, 17, 18, 53, 45, 2, 45, 46,
+ 46, 45, 5, 45, 6, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 10,
+ 45, 12, 45, 14, 15, 16, 17, 18,
+ 45, 2, 45, 46, 46, 45, 5, 45,
+ 6, 45, 45, 45, 45, 45, 45, 45,
+ 53, 45, 45, 10, 45, 45, 45, 14,
+ 15, 16, 17, 18, 45, 2, 45, 46,
+ 46, 45, 5, 45, 6, 45, 45, 45,
+ 45, 45, 45, 45, 54, 45, 45, 10,
+ 11, 12, 45, 14, 15, 16, 17, 18,
+ 45, 2, 45, 46, 46, 45, 5, 45,
+ 6, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 10, 11, 12, 45, 14,
+ 15, 16, 17, 18, 45, 2, 3, 46,
+ 46, 45, 5, 45, 6, 45, 45, 45,
+ 45, 45, 45, 45, 8, 45, 45, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18,
+ 45, 22, 23, 24, 24, 21, 25, 21,
+ 26, 21, 21, 21, 21, 21, 21, 21,
+ 55, 21, 21, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 21, 22, 56,
+ 24, 24, 21, 25, 21, 26, 21, 21,
+ 21, 21, 21, 21, 21, 27, 21, 21,
+ 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 21, 1, 1, 2, 3, 46, 46,
+ 45, 5, 45, 6, 1, 45, 45, 45,
+ 45, 1, 45, 8, 45, 45, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 45, 1, 45, 1, 1, 57, 57, 57,
+ 57, 57, 57, 57, 57, 1, 57, 57,
+ 57, 57, 1, 57, 57, 57, 57, 57,
+ 57, 57, 57, 57, 57, 57, 57, 57,
+ 57, 57, 1, 57, 58, 57, 0
+};
+
+static const char _myanmar_syllable_machine_trans_targs[] = {
+ 0, 1, 24, 34, 0, 25, 31, 47,
+ 36, 50, 37, 42, 43, 44, 27, 39,
+ 40, 41, 30, 46, 51, 0, 2, 12,
+ 0, 3, 9, 13, 14, 19, 20, 21,
+ 5, 16, 17, 18, 8, 23, 4, 6,
+ 7, 10, 11, 15, 22, 0, 0, 26,
+ 28, 29, 32, 33, 35, 38, 45, 48,
+ 49, 0, 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, 7, 8, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 10
+};
+
+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
+};
+
+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
+};
+
+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,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 46,
+ 46, 46, 46, 46, 46, 46, 46, 22,
+ 22, 46, 58, 58
+};
+
+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 36 "hb-ot-shape-complex-myanmar-machine.rl"
+
+
+
+#line 94 "hb-ot-shape-complex-myanmar-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+ HB_STMT_START { \
+ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+ for (unsigned int i = ts; i < te; i++) \
+ info[i].syllable() = (syllable_serial << 4) | myanmar_##syllable_type; \
+ syllable_serial++; \
+ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+ } HB_STMT_END
+
+static 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 320 "hb-ot-shape-complex-myanmar-machine.hh"
+ {
+ cs = myanmar_syllable_machine_start;
+ ts = 0;
+ te = 0;
+ act = 0;
+ }
+
+#line 114 "hb-ot-shape-complex-myanmar-machine.rl"
+
+
+ p = 0;
+ pe = eof = buffer->len;
+
+ unsigned int syllable_serial = 1;
+
+#line 336 "hb-ot-shape-complex-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 350 "hb-ot-shape-complex-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 86 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p+1;{ found_syllable (consonant_syllable); }}
+ break;
+ case 4:
+#line 87 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p+1;{ found_syllable (non_myanmar_cluster); }}
+ break;
+ case 10:
+#line 88 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p+1;{ found_syllable (punctuation_cluster); }}
+ break;
+ case 8:
+#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p+1;{ found_syllable (broken_cluster); }}
+ break;
+ case 3:
+#line 90 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p+1;{ found_syllable (non_myanmar_cluster); }}
+ break;
+ case 5:
+#line 86 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p;p--;{ found_syllable (consonant_syllable); }}
+ break;
+ case 7:
+#line 89 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p;p--;{ found_syllable (broken_cluster); }}
+ break;
+ case 9:
+#line 90 "hb-ot-shape-complex-myanmar-machine.rl"
+ {te = p;p--;{ found_syllable (non_myanmar_cluster); }}
+ break;
+#line 400 "hb-ot-shape-complex-myanmar-machine.hh"
+ }
+
+_again:
+ switch ( _myanmar_syllable_machine_to_state_actions[cs] ) {
+ case 1:
+#line 1 "NONE"
+ {ts = 0;}
+ break;
+#line 409 "hb-ot-shape-complex-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 122 "hb-ot-shape-complex-myanmar-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
new file mode 100644
index 0000000000..fc3490d716
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -0,0 +1,387 @@
+/*
+ * 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-shape-complex-myanmar.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.
+ */
+ 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 void
+setup_syllables_myanmar (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
+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'));
+ /* 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'));
+
+
+ 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);
+ map->add_gsub_pause (nullptr);
+ }
+
+ map->add_gsub_pause (_hb_clear_syllables);
+
+ for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++)
+ map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ);
+}
+
+static void
+override_features_myanmar (hb_ot_shape_planner_t *plan)
+{
+ plan->map.disable_feature (HB_TAG('l','i','g','a'));
+}
+
+
+enum myanmar_syllable_type_t {
+ myanmar_consonant_syllable,
+ myanmar_punctuation_cluster,
+ myanmar_broken_cluster,
+ myanmar_non_myanmar_cluster,
+};
+
+#include "hb-ot-shape-complex-myanmar-machine.hh"
+
+
+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);
+
+ /* 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_myanmar_properties (info[i]);
+}
+
+static void
+setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font HB_UNUSED,
+ hb_buffer_t *buffer)
+{
+ find_syllables_myanmar (buffer);
+ foreach_syllable (buffer, start, end)
+ buffer->unsafe_to_break (start, end);
+}
+
+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 a < b ? -1 : a == b ? 0 : +1;
+}
+
+
+/* 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() == OT_Ra &&
+ info[start+1].myanmar_category() == OT_As &&
+ info[start+2].myanmar_category() == OT_H)
+ {
+ limit += 3;
+ base = start;
+ has_reph = true;
+ }
+
+ {
+ if (!has_reph)
+ base = limit;
+
+ for (unsigned int i = limit; i < end; i++)
+ if (is_consonant (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++;
+ }
+ indic_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() == OT_MR) /* Pre-base reordering */
+ {
+ info[i].myanmar_position() = POS_PRE_C;
+ continue;
+ }
+ if (info[i].myanmar_position() < POS_BASE_C) /* Left matra */
+ {
+ continue;
+ }
+ if (info[i].myanmar_category() == OT_VS)
+ {
+ info[i].myanmar_position() = info[i - 1].myanmar_position();
+ continue;
+ }
+
+ if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == OT_VBlw)
+ {
+ pos = POS_BELOW_C;
+ info[i].myanmar_position() = pos;
+ continue;
+ }
+
+ if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_A)
+ {
+ info[i].myanmar_position() = POS_BEFORE_SUB;
+ continue;
+ }
+ if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_VBlw)
+ {
+ info[i].myanmar_position() = pos;
+ continue;
+ }
+ if (pos == POS_BELOW_C && info[i].myanmar_category() != OT_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);
+}
+
+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_punctuation_cluster:
+ case myanmar_non_myanmar_cluster:
+ break;
+ }
+}
+
+static inline void
+insert_dotted_circles_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
+ return;
+
+ /* Note: This loop is extra overhead, but should not be measurable.
+ * TODO Use a buffer scratch flag to remove the loop. */
+ bool has_broken_syllables = false;
+ unsigned int count = buffer->len;
+ hb_glyph_info_t *info = buffer->info;
+ for (unsigned int i = 0; i < count; i++)
+ if ((info[i].syllable() & 0x0F) == myanmar_broken_cluster)
+ {
+ has_broken_syllables = true;
+ break;
+ }
+ if (likely (!has_broken_syllables))
+ return;
+
+
+ hb_codepoint_t dottedcircle_glyph;
+ if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
+ return;
+
+ hb_glyph_info_t dottedcircle = {0};
+ dottedcircle.codepoint = 0x25CCu;
+ set_myanmar_properties (dottedcircle);
+ 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();
+ myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (syllable & 0x0F);
+ if (unlikely (last_syllable != syllable && syllable_type == myanmar_broken_cluster))
+ {
+ last_syllable = syllable;
+
+ hb_glyph_info_t ginfo = dottedcircle;
+ ginfo.cluster = buffer->cur().cluster;
+ ginfo.mask = buffer->cur().mask;
+ ginfo.syllable() = buffer->cur().syllable();
+
+ buffer->output_info (ginfo);
+ }
+ else
+ buffer->next_glyph ();
+ }
+ buffer->swap_buffers ();
+}
+
+static void
+reorder_myanmar (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ insert_dotted_circles_myanmar (plan, font, buffer);
+
+ foreach_syllable (buffer, start, end)
+ reorder_syllable_myanmar (plan, font->face, buffer, start, end);
+
+ HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
+ HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
+{
+ collect_features_myanmar,
+ override_features_myanmar,
+ nullptr, /* data_create */
+ nullptr, /* data_destroy */
+ nullptr, /* preprocess_text */
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+ nullptr, /* decompose */
+ nullptr, /* compose */
+ setup_masks_myanmar,
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
+ false, /* fallback_position */
+};
+
+
+/* Ugly Zawgyi encoding.
+ * Disable all auto processing.
+ * https://github.com/harfbuzz/harfbuzz/issues/1162 */
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_zawgyi =
+{
+ nullptr, /* collect_features */
+ nullptr, /* override_features */
+ nullptr, /* data_create */
+ nullptr, /* data_destroy */
+ nullptr, /* preprocess_text */
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
+ nullptr, /* decompose */
+ nullptr, /* compose */
+ nullptr, /* setup_masks */
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+ false, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.hh
new file mode 100644
index 0000000000..7b9821e6ba
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-myanmar.hh
@@ -0,0 +1,171 @@
+/*
+ * 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_SHAPE_COMPLEX_MYANMAR_HH
+#define HB_OT_SHAPE_COMPLEX_MYANMAR_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape-complex-indic.hh"
+
+
+/* buffer var allocations */
+#define myanmar_category() indic_category() /* myanmar_category_t */
+#define myanmar_position() indic_position() /* myanmar_position_t */
+
+
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum myanmar_category_t {
+ OT_As = 18, /* Asat */
+ OT_D0 = 20, /* Digit zero */
+ OT_DB = OT_N, /* Dot below */
+ OT_GB = OT_PLACEHOLDER,
+ OT_MH = 21, /* Various consonant medial types */
+ OT_MR = 22, /* Various consonant medial types */
+ OT_MW = 23, /* Various consonant medial types */
+ OT_MY = 24, /* Various consonant medial types */
+ OT_PT = 25, /* Pwo and other tones */
+ //OT_VAbv = 26,
+ //OT_VBlw = 27,
+ //OT_VPre = 28,
+ //OT_VPst = 29,
+ OT_VS = 30, /* Variation selectors */
+ OT_P = 31, /* Punctuation */
+ OT_D = 32, /* Digits except zero */
+};
+
+
+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);
+ unsigned int cat = type & 0x7Fu;
+ indic_position_t pos = (indic_position_t) (type >> 8);
+
+ /* Myanmar
+ * https://docs.microsoft.com/en-us/typography/script-development/myanmar#analyze
+ */
+ if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)))
+ cat = OT_VS;
+
+ switch (u)
+ {
+ case 0x104Eu:
+ cat = OT_C; /* The spec says C, IndicSyllableCategory doesn't have. */
+ break;
+
+ case 0x002Du: case 0x00A0u: case 0x00D7u: case 0x2012u:
+ case 0x2013u: case 0x2014u: case 0x2015u: case 0x2022u:
+ case 0x25CCu: case 0x25FBu: case 0x25FCu: case 0x25FDu:
+ case 0x25FEu:
+ cat = OT_GB;
+ break;
+
+ case 0x1004u: case 0x101Bu: case 0x105Au:
+ cat = OT_Ra;
+ break;
+
+ case 0x1032u: case 0x1036u:
+ cat = OT_A;
+ break;
+
+ case 0x1039u:
+ cat = OT_H;
+ break;
+
+ case 0x103Au:
+ cat = OT_As;
+ break;
+
+ case 0x1041u: case 0x1042u: case 0x1043u: case 0x1044u:
+ case 0x1045u: case 0x1046u: case 0x1047u: case 0x1048u:
+ case 0x1049u: case 0x1090u: case 0x1091u: case 0x1092u:
+ case 0x1093u: case 0x1094u: case 0x1095u: case 0x1096u:
+ case 0x1097u: case 0x1098u: case 0x1099u:
+ cat = OT_D;
+ break;
+
+ case 0x1040u:
+ cat = OT_D; /* XXX The spec says D0, but Uniscribe doesn't seem to do. */
+ break;
+
+ case 0x103Eu: case 0x1060u:
+ cat = OT_MH;
+ break;
+
+ case 0x103Cu:
+ cat = OT_MR;
+ break;
+
+ case 0x103Du: case 0x1082u:
+ cat = OT_MW;
+ break;
+
+ case 0x103Bu: case 0x105Eu: case 0x105Fu:
+ cat = OT_MY;
+ break;
+
+ case 0x1063u: case 0x1064u: case 0x1069u: case 0x106Au:
+ case 0x106Bu: case 0x106Cu: case 0x106Du: case 0xAA7Bu:
+ cat = OT_PT;
+ break;
+
+ case 0x1038u: case 0x1087u: case 0x1088u: case 0x1089u:
+ case 0x108Au: case 0x108Bu: case 0x108Cu: case 0x108Du:
+ case 0x108Fu: case 0x109Au: case 0x109Bu: case 0x109Cu:
+ cat = OT_SM;
+ break;
+
+ case 0x104Au: case 0x104Bu:
+ cat = OT_P;
+ break;
+
+ case 0xAA74u: case 0xAA75u: case 0xAA76u:
+ /* https://github.com/harfbuzz/harfbuzz/issues/218 */
+ cat = OT_C;
+ break;
+ }
+
+ if (cat == OT_M)
+ {
+ switch ((int) pos)
+ {
+ case POS_PRE_C: cat = (myanmar_category_t) OT_VPre;
+ pos = POS_PRE_M; break;
+ case POS_ABOVE_C: cat = (myanmar_category_t) OT_VAbv; break;
+ case POS_BELOW_C: cat = (myanmar_category_t) OT_VBlw; break;
+ case POS_POST_C: cat = (myanmar_category_t) OT_VPst; break;
+ }
+ }
+
+ info.myanmar_category() = cat;
+ info.myanmar_position() = pos;
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc
new file mode 100644
index 0000000000..347ea2e7ac
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-thai.cc
@@ -0,0 +1,394 @@
+/*
+ * 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-shape-complex.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 {
+ hb_codepoint_t u;
+ hb_codepoint_t win_pua;
+ hb_codepoint_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_SHAPE_COMPLEX_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 tone mark (0E48-0E4B).
+ *
+ * <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,0EC7..0ECE>
+ *
+ * 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_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u))
+
+ buffer->clear_output ();
+ unsigned int count = buffer->len;
+ for (buffer->idx = 0; buffer->idx < count && buffer->successful;)
+ {
+ hb_codepoint_t u = buffer->cur().codepoint;
+ if (likely (!IS_SARA_AM (u))) {
+ buffer->next_glyph ();
+ continue;
+ }
+
+ /* Is SARA AM. Decompose and reorder. */
+ hb_glyph_info_t &nikhahit = buffer->output_glyph (NIKHAHIT_FROM_SARA_AM (u));
+ _hb_glyph_info_set_continuation (&nikhahit);
+ buffer->replace_glyph (SARA_AA_FROM_SARA_AM (u));
+ if (unlikely (!buffer->successful))
+ return;
+
+ /* 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_TONE_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->swap_buffers ();
+
+ /* 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_complex_shaper_t _hb_ot_complex_shaper_thai =
+{
+ nullptr, /* collect_features */
+ nullptr, /* override_features */
+ nullptr, /* data_create */
+ nullptr, /* data_destroy */
+ preprocess_text_thai,
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
+ nullptr, /* decompose */
+ nullptr, /* compose */
+ nullptr, /* setup_masks */
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
+ false,/* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-use-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
new file mode 100644
index 0000000000..462342c618
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
@@ -0,0 +1,562 @@
+
+#line 1 "hb-ot-shape-complex-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_SHAPE_COMPLEX_USE_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH
+
+#include "hb.hh"
+
+
+#line 38 "hb-ot-shape-complex-use-machine.hh"
+static const unsigned char _use_syllable_machine_trans_keys[] = {
+ 12u, 48u, 1u, 15u, 1u, 1u, 12u, 48u, 1u, 1u, 0u, 48u, 21u, 21u, 11u, 48u,
+ 11u, 48u, 1u, 15u, 1u, 1u, 11u, 48u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u,
+ 26u, 47u, 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 1u, 1u, 24u, 48u,
+ 23u, 48u, 23u, 48u, 23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u,
+ 1u, 48u, 11u, 48u, 13u, 21u, 4u, 4u, 13u, 13u, 11u, 48u, 11u, 48u, 41u, 42u,
+ 42u, 42u, 11u, 48u, 11u, 48u, 22u, 48u, 23u, 48u, 24u, 47u, 25u, 47u, 26u, 47u,
+ 45u, 46u, 46u, 46u, 24u, 48u, 24u, 48u, 24u, 48u, 24u, 48u, 23u, 48u, 23u, 48u,
+ 23u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 22u, 48u, 11u, 48u, 1u, 48u, 1u, 15u,
+ 4u, 4u, 13u, 21u, 13u, 13u, 12u, 48u, 1u, 48u, 11u, 48u, 41u, 42u, 42u, 42u,
+ 21u, 42u, 1u, 5u, 0
+};
+
+static const char _use_syllable_machine_key_spans[] = {
+ 37, 15, 1, 37, 1, 49, 1, 38,
+ 38, 15, 1, 38, 27, 26, 24, 23,
+ 22, 2, 1, 25, 25, 25, 1, 25,
+ 26, 26, 26, 27, 27, 27, 27, 38,
+ 48, 38, 9, 1, 1, 38, 38, 2,
+ 1, 38, 38, 27, 26, 24, 23, 22,
+ 2, 1, 25, 25, 25, 25, 26, 26,
+ 26, 27, 27, 27, 27, 38, 48, 15,
+ 1, 9, 1, 37, 48, 38, 2, 1,
+ 22, 5
+};
+
+static const short _use_syllable_machine_index_offsets[] = {
+ 0, 38, 54, 56, 94, 96, 146, 148,
+ 187, 226, 242, 244, 283, 311, 338, 363,
+ 387, 410, 413, 415, 441, 467, 493, 495,
+ 521, 548, 575, 602, 630, 658, 686, 714,
+ 753, 802, 841, 851, 853, 855, 894, 933,
+ 936, 938, 977, 1016, 1044, 1071, 1096, 1120,
+ 1143, 1146, 1148, 1174, 1200, 1226, 1252, 1279,
+ 1306, 1333, 1361, 1389, 1417, 1445, 1484, 1533,
+ 1549, 1551, 1561, 1563, 1601, 1650, 1689, 1692,
+ 1694, 1717
+};
+
+static const char _use_syllable_machine_indicies[] = {
+ 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,
+ 1, 0, 0, 0, 1, 0, 3, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 4, 2, 3, 2,
+ 6, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 5, 5, 5, 6, 5, 7, 5,
+ 8, 9, 10, 8, 11, 12, 10, 10,
+ 10, 10, 10, 3, 13, 14, 10, 15,
+ 8, 8, 16, 17, 10, 10, 18, 19,
+ 20, 21, 22, 23, 24, 18, 25, 26,
+ 27, 28, 29, 30, 10, 31, 32, 33,
+ 10, 34, 35, 36, 37, 38, 39, 40,
+ 13, 10, 42, 41, 44, 1, 43, 43,
+ 45, 43, 43, 43, 43, 43, 46, 47,
+ 48, 49, 50, 51, 52, 53, 47, 54,
+ 46, 55, 56, 57, 58, 43, 59, 60,
+ 61, 43, 43, 43, 43, 62, 63, 64,
+ 65, 1, 43, 44, 1, 43, 43, 45,
+ 43, 43, 43, 43, 43, 66, 47, 48,
+ 49, 50, 51, 52, 53, 47, 54, 55,
+ 55, 56, 57, 58, 43, 59, 60, 61,
+ 43, 43, 43, 43, 62, 63, 64, 65,
+ 1, 43, 44, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 67,
+ 68, 67, 44, 67, 44, 1, 43, 43,
+ 45, 43, 43, 43, 43, 43, 43, 47,
+ 48, 49, 50, 51, 52, 53, 47, 54,
+ 55, 55, 56, 57, 58, 43, 59, 60,
+ 61, 43, 43, 43, 43, 62, 63, 64,
+ 65, 1, 43, 47, 48, 49, 50, 51,
+ 43, 43, 43, 43, 43, 43, 56, 57,
+ 58, 43, 59, 60, 61, 43, 43, 43,
+ 43, 48, 63, 64, 65, 69, 43, 48,
+ 49, 50, 51, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 59, 60, 61,
+ 43, 43, 43, 43, 43, 63, 64, 65,
+ 69, 43, 49, 50, 51, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 63,
+ 64, 65, 43, 50, 51, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 63,
+ 64, 65, 43, 51, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 63, 64,
+ 65, 43, 63, 64, 43, 64, 43, 49,
+ 50, 51, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 59, 60, 61, 43,
+ 43, 43, 43, 43, 63, 64, 65, 69,
+ 43, 49, 50, 51, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 60,
+ 61, 43, 43, 43, 43, 43, 63, 64,
+ 65, 69, 43, 49, 50, 51, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 61, 43, 43, 43, 43, 43,
+ 63, 64, 65, 69, 43, 71, 70, 49,
+ 50, 51, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 63, 64, 65, 69,
+ 43, 48, 49, 50, 51, 43, 43, 43,
+ 43, 43, 43, 56, 57, 58, 43, 59,
+ 60, 61, 43, 43, 43, 43, 48, 63,
+ 64, 65, 69, 43, 48, 49, 50, 51,
+ 43, 43, 43, 43, 43, 43, 43, 57,
+ 58, 43, 59, 60, 61, 43, 43, 43,
+ 43, 48, 63, 64, 65, 69, 43, 48,
+ 49, 50, 51, 43, 43, 43, 43, 43,
+ 43, 43, 43, 58, 43, 59, 60, 61,
+ 43, 43, 43, 43, 48, 63, 64, 65,
+ 69, 43, 47, 48, 49, 50, 51, 43,
+ 53, 47, 43, 43, 43, 56, 57, 58,
+ 43, 59, 60, 61, 43, 43, 43, 43,
+ 48, 63, 64, 65, 69, 43, 47, 48,
+ 49, 50, 51, 43, 72, 47, 43, 43,
+ 43, 56, 57, 58, 43, 59, 60, 61,
+ 43, 43, 43, 43, 48, 63, 64, 65,
+ 69, 43, 47, 48, 49, 50, 51, 43,
+ 43, 47, 43, 43, 43, 56, 57, 58,
+ 43, 59, 60, 61, 43, 43, 43, 43,
+ 48, 63, 64, 65, 69, 43, 47, 48,
+ 49, 50, 51, 52, 53, 47, 43, 43,
+ 43, 56, 57, 58, 43, 59, 60, 61,
+ 43, 43, 43, 43, 48, 63, 64, 65,
+ 69, 43, 44, 1, 43, 43, 45, 43,
+ 43, 43, 43, 43, 43, 47, 48, 49,
+ 50, 51, 52, 53, 47, 54, 43, 55,
+ 56, 57, 58, 43, 59, 60, 61, 43,
+ 43, 43, 43, 62, 63, 64, 65, 1,
+ 43, 44, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 68,
+ 67, 67, 67, 67, 67, 67, 67, 48,
+ 49, 50, 51, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 59, 60, 61,
+ 67, 67, 67, 67, 67, 63, 64, 65,
+ 69, 67, 44, 1, 43, 43, 45, 43,
+ 43, 43, 43, 43, 43, 47, 48, 49,
+ 50, 51, 52, 53, 47, 54, 46, 55,
+ 56, 57, 58, 43, 59, 60, 61, 43,
+ 43, 43, 43, 62, 63, 64, 65, 1,
+ 43, 74, 73, 73, 73, 73, 73, 73,
+ 73, 75, 73, 11, 76, 74, 73, 44,
+ 1, 43, 43, 45, 43, 43, 43, 43,
+ 43, 77, 47, 48, 49, 50, 51, 52,
+ 53, 47, 54, 46, 55, 56, 57, 58,
+ 43, 59, 60, 61, 43, 78, 79, 43,
+ 62, 63, 64, 65, 1, 43, 44, 1,
+ 43, 43, 45, 43, 43, 43, 43, 43,
+ 43, 47, 48, 49, 50, 51, 52, 53,
+ 47, 54, 46, 55, 56, 57, 58, 43,
+ 59, 60, 61, 43, 78, 79, 43, 62,
+ 63, 64, 65, 1, 43, 78, 79, 80,
+ 79, 80, 3, 6, 81, 81, 82, 81,
+ 81, 81, 81, 81, 83, 18, 19, 20,
+ 21, 22, 23, 24, 18, 25, 27, 27,
+ 28, 29, 30, 81, 31, 32, 33, 81,
+ 81, 81, 81, 37, 38, 39, 40, 6,
+ 81, 3, 6, 81, 81, 82, 81, 81,
+ 81, 81, 81, 81, 18, 19, 20, 21,
+ 22, 23, 24, 18, 25, 27, 27, 28,
+ 29, 30, 81, 31, 32, 33, 81, 81,
+ 81, 81, 37, 38, 39, 40, 6, 81,
+ 18, 19, 20, 21, 22, 81, 81, 81,
+ 81, 81, 81, 28, 29, 30, 81, 31,
+ 32, 33, 81, 81, 81, 81, 19, 38,
+ 39, 40, 84, 81, 19, 20, 21, 22,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 31, 32, 33, 81, 81, 81,
+ 81, 81, 38, 39, 40, 84, 81, 20,
+ 21, 22, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 38, 39, 40, 81,
+ 21, 22, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 38, 39, 40, 81,
+ 22, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 38, 39, 40, 81, 38,
+ 39, 81, 39, 81, 20, 21, 22, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 31, 32, 33, 81, 81, 81, 81,
+ 81, 38, 39, 40, 84, 81, 20, 21,
+ 22, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 32, 33, 81, 81,
+ 81, 81, 81, 38, 39, 40, 84, 81,
+ 20, 21, 22, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 33,
+ 81, 81, 81, 81, 81, 38, 39, 40,
+ 84, 81, 20, 21, 22, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 38,
+ 39, 40, 84, 81, 19, 20, 21, 22,
+ 81, 81, 81, 81, 81, 81, 28, 29,
+ 30, 81, 31, 32, 33, 81, 81, 81,
+ 81, 19, 38, 39, 40, 84, 81, 19,
+ 20, 21, 22, 81, 81, 81, 81, 81,
+ 81, 81, 29, 30, 81, 31, 32, 33,
+ 81, 81, 81, 81, 19, 38, 39, 40,
+ 84, 81, 19, 20, 21, 22, 81, 81,
+ 81, 81, 81, 81, 81, 81, 30, 81,
+ 31, 32, 33, 81, 81, 81, 81, 19,
+ 38, 39, 40, 84, 81, 18, 19, 20,
+ 21, 22, 81, 24, 18, 81, 81, 81,
+ 28, 29, 30, 81, 31, 32, 33, 81,
+ 81, 81, 81, 19, 38, 39, 40, 84,
+ 81, 18, 19, 20, 21, 22, 81, 85,
+ 18, 81, 81, 81, 28, 29, 30, 81,
+ 31, 32, 33, 81, 81, 81, 81, 19,
+ 38, 39, 40, 84, 81, 18, 19, 20,
+ 21, 22, 81, 81, 18, 81, 81, 81,
+ 28, 29, 30, 81, 31, 32, 33, 81,
+ 81, 81, 81, 19, 38, 39, 40, 84,
+ 81, 18, 19, 20, 21, 22, 23, 24,
+ 18, 81, 81, 81, 28, 29, 30, 81,
+ 31, 32, 33, 81, 81, 81, 81, 19,
+ 38, 39, 40, 84, 81, 3, 6, 81,
+ 81, 82, 81, 81, 81, 81, 81, 81,
+ 18, 19, 20, 21, 22, 23, 24, 18,
+ 25, 81, 27, 28, 29, 30, 81, 31,
+ 32, 33, 81, 81, 81, 81, 37, 38,
+ 39, 40, 6, 81, 3, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 4, 81, 81, 81, 81, 81,
+ 81, 81, 19, 20, 21, 22, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81,
+ 31, 32, 33, 81, 81, 81, 81, 81,
+ 38, 39, 40, 84, 81, 3, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 4, 86, 87, 81, 14,
+ 81, 81, 81, 81, 81, 81, 81, 88,
+ 81, 14, 81, 6, 86, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 6, 86, 86, 86, 6,
+ 86, 9, 81, 81, 81, 9, 81, 81,
+ 81, 81, 81, 3, 6, 14, 81, 82,
+ 81, 81, 81, 81, 81, 81, 18, 19,
+ 20, 21, 22, 23, 24, 18, 25, 26,
+ 27, 28, 29, 30, 81, 31, 32, 33,
+ 81, 34, 35, 81, 37, 38, 39, 40,
+ 6, 81, 3, 6, 81, 81, 82, 81,
+ 81, 81, 81, 81, 81, 18, 19, 20,
+ 21, 22, 23, 24, 18, 25, 26, 27,
+ 28, 29, 30, 81, 31, 32, 33, 81,
+ 81, 81, 81, 37, 38, 39, 40, 6,
+ 81, 34, 35, 81, 35, 81, 78, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 78, 79, 80, 9, 86, 86,
+ 86, 9, 86, 0
+};
+
+static const char _use_syllable_machine_trans_targs[] = {
+ 5, 9, 5, 41, 2, 5, 1, 53,
+ 6, 7, 5, 34, 37, 63, 64, 67,
+ 68, 72, 43, 44, 45, 46, 47, 57,
+ 58, 60, 69, 61, 54, 55, 56, 50,
+ 51, 52, 70, 71, 73, 62, 48, 49,
+ 5, 5, 5, 5, 8, 0, 33, 12,
+ 13, 14, 15, 16, 27, 28, 30, 31,
+ 24, 25, 26, 19, 20, 21, 32, 17,
+ 18, 5, 11, 5, 10, 22, 5, 23,
+ 29, 5, 35, 36, 5, 38, 39, 40,
+ 5, 5, 3, 42, 4, 59, 5, 65,
+ 66
+};
+
+static const char _use_syllable_machine_trans_actions[] = {
+ 1, 0, 2, 3, 0, 4, 0, 5,
+ 0, 5, 8, 0, 5, 9, 0, 9,
+ 3, 0, 5, 5, 0, 0, 0, 5,
+ 5, 5, 3, 3, 5, 5, 5, 5,
+ 5, 5, 0, 0, 0, 3, 0, 0,
+ 10, 11, 12, 13, 5, 0, 5, 0,
+ 0, 0, 0, 0, 0, 0, 0, 5,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 14, 5, 15, 0, 0, 16, 0,
+ 0, 17, 0, 0, 18, 5, 0, 0,
+ 19, 20, 0, 3, 0, 5, 21, 0,
+ 0
+};
+
+static const char _use_syllable_machine_to_state_actions[] = {
+ 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, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 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, 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, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 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, 3, 3, 6, 6, 0, 42, 44,
+ 44, 68, 68, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 71, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44,
+ 68, 44, 74, 77, 74, 44, 44, 81,
+ 81, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 87,
+ 82, 82, 82, 87, 82, 82, 82, 82,
+ 81, 87
+};
+
+static const int use_syllable_machine_start = 5;
+static const int use_syllable_machine_first_final = 5;
+static const int use_syllable_machine_error = -1;
+
+static const int use_syllable_machine_en_main = 5;
+
+
+#line 38 "hb-ot-shape-complex-use-machine.rl"
+
+
+
+#line 162 "hb-ot-shape-complex-use-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+ HB_STMT_START { \
+ if (0) fprintf (stderr, "syllable %d..%d %s\n", ts, te, #syllable_type); \
+ for (unsigned int i = ts; i < te; i++) \
+ info[i].syllable() = (syllable_serial << 4) | use_##syllable_type; \
+ syllable_serial++; \
+ if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+ } HB_STMT_END
+
+static void
+find_syllables_use (hb_buffer_t *buffer)
+{
+ unsigned int p, pe, eof, ts, te, act;
+ int cs;
+ hb_glyph_info_t *info = buffer->info;
+
+#line 396 "hb-ot-shape-complex-use-machine.hh"
+ {
+ cs = use_syllable_machine_start;
+ ts = 0;
+ te = 0;
+ act = 0;
+ }
+
+#line 182 "hb-ot-shape-complex-use-machine.rl"
+
+
+ p = 0;
+ pe = eof = buffer->len;
+
+ unsigned int syllable_serial = 1;
+
+#line 412 "hb-ot-shape-complex-use-machine.hh"
+ {
+ int _slen;
+ int _trans;
+ const unsigned char *_keys;
+ const char *_inds;
+ if ( p == pe )
+ goto _test_eof;
+_resume:
+ switch ( _use_syllable_machine_from_state_actions[cs] ) {
+ case 7:
+#line 1 "NONE"
+ {ts = p;}
+ break;
+#line 426 "hb-ot-shape-complex-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] <=( info[p].use_category()) &&
+ ( info[p].use_category()) <= _keys[1] ?
+ ( info[p].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 5:
+#line 1 "NONE"
+ {te = p+1;}
+ break;
+ case 12:
+#line 150 "hb-ot-shape-complex-use-machine.rl"
+ {te = p+1;{ found_syllable (independent_cluster); }}
+ break;
+ case 14:
+#line 153 "hb-ot-shape-complex-use-machine.rl"
+ {te = p+1;{ found_syllable (standard_cluster); }}
+ break;
+ case 10:
+#line 157 "hb-ot-shape-complex-use-machine.rl"
+ {te = p+1;{ found_syllable (broken_cluster); }}
+ break;
+ case 8:
+#line 158 "hb-ot-shape-complex-use-machine.rl"
+ {te = p+1;{ found_syllable (non_cluster); }}
+ break;
+ case 11:
+#line 150 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (independent_cluster); }}
+ break;
+ case 15:
+#line 151 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (virama_terminated_cluster); }}
+ break;
+ case 16:
+#line 152 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (sakot_terminated_cluster); }}
+ break;
+ case 13:
+#line 153 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (standard_cluster); }}
+ break;
+ case 18:
+#line 154 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }}
+ break;
+ case 17:
+#line 155 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (numeral_cluster); }}
+ break;
+ case 19:
+#line 156 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (symbol_cluster); }}
+ break;
+ case 20:
+#line 157 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (broken_cluster); }}
+ break;
+ case 21:
+#line 158 "hb-ot-shape-complex-use-machine.rl"
+ {te = p;p--;{ found_syllable (non_cluster); }}
+ break;
+ case 1:
+#line 153 "hb-ot-shape-complex-use-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (standard_cluster); }}
+ break;
+ case 4:
+#line 157 "hb-ot-shape-complex-use-machine.rl"
+ {{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+ break;
+ case 2:
+#line 1 "NONE"
+ { switch( act ) {
+ case 8:
+ {{p = ((te))-1;} found_syllable (broken_cluster); }
+ break;
+ case 9:
+ {{p = ((te))-1;} found_syllable (non_cluster); }
+ break;
+ }
+ }
+ break;
+ case 3:
+#line 1 "NONE"
+ {te = p+1;}
+#line 157 "hb-ot-shape-complex-use-machine.rl"
+ {act = 8;}
+ break;
+ case 9:
+#line 1 "NONE"
+ {te = p+1;}
+#line 158 "hb-ot-shape-complex-use-machine.rl"
+ {act = 9;}
+ break;
+#line 532 "hb-ot-shape-complex-use-machine.hh"
+ }
+
+_again:
+ switch ( _use_syllable_machine_to_state_actions[cs] ) {
+ case 6:
+#line 1 "NONE"
+ {ts = 0;}
+ break;
+#line 541 "hb-ot-shape-complex-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 190 "hb-ot-shape-complex-use-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-use-table.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use-table.cc
new file mode 100644
index 0000000000..aa9c350862
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use-table.cc
@@ -0,0 +1,873 @@
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ * ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
+ *
+ * on files with these headers:
+ *
+ * # IndicSyllabicCategory-13.0.0.txt
+ * # Date: 2019-07-22, 19:55:00 GMT [KW, RP]
+ * # IndicPositionalCategory-13.0.0.txt
+ * # Date: 2019-07-23, 00:01:00 GMT [KW, RP]
+ * # Blocks-13.0.0.txt
+ * # Date: 2019-07-10, 19:06:00 GMT [KW]
+ * UnicodeData.txt does not have a header.
+ */
+
+#include "hb.hh"
+
+#ifndef HB_NO_OT_SHAPE
+
+#include "hb-ot-shape-complex-use.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 GB USE_GB /* BASE_OTHER */
+#define H USE_H /* HALANT */
+#define HN USE_HN /* HALANT_NUM */
+#define HVM USE_HVM /* HALANT_OR_VOWEL_MODIFIER */
+#define IND USE_IND /* BASE_IND */
+#define N USE_N /* BASE_NUM */
+#define O USE_O /* OTHER */
+#define R USE_R /* REPHA */
+#define Rsv USE_Rsv /* Reserved */
+#define S USE_S /* SYM */
+#define SUB USE_SUB /* CONS_SUB */
+#define Sk USE_Sk /* SAKOT */
+#define VS USE_VS /* VARIATION_SELECTOR */
+#define WJ USE_WJ /* Word_Joiner */
+#define ZWJ USE_ZWJ /* ZWJ */
+#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
+
+static const USE_TABLE_ELEMENT_TYPE use_table[] = {
+
+
+#define use_offset_0x0028u 0
+
+
+ /* Basic Latin */
+ O, O, O, O, O, GB, O, O,
+ /* 0030 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+#define use_offset_0x00a0u 24
+
+
+ /* Latin-1 Supplement */
+
+ /* 00A0 */ GB, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 00B0 */ O, O, FMPst, FMPst, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 00C0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 00D0 */ O, O, O, O, O, O, O, GB,
+
+#define use_offset_0x0348u 80
+
+
+ /* Combining Diacritical Marks */
+ O, O, O, O, O, O, O, CGJ,
+
+#define use_offset_0x0900u 88
+
+
+ /* Devanagari */
+
+ /* 0900 */ VMAbv, VMAbv, VMAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0920 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0930 */ B, B, B, B, B, B, B, B, B, B, VAbv, VPst, CMBlw, B, VPst, VPre,
+ /* 0940 */ VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VPst, VPst, VPst, VPst, H, VPre, VPst,
+ /* 0950 */ O, VMAbv, VMBlw, O, O, VAbv, VBlw, VBlw, B, B, B, B, B, B, B, B,
+ /* 0960 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0970 */ O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+
+ /* Bengali */
+
+ /* 0980 */ GB, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, O, B,
+ /* 0990 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 09A0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 09B0 */ B, O, B, O, O, O, B, B, B, B, O, O, CMBlw, B, VPst, VPre,
+ /* 09C0 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPre, O, O, VPst, VPst, H, IND, O,
+ /* 09D0 */ O, O, O, O, O, O, O, VPst, O, O, O, O, B, B, O, B,
+ /* 09E0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 09F0 */ B, B, O, O, O, O, O, O, O, O, O, O, B, O, FMAbv, O,
+
+ /* Gurmukhi */
+
+ /* 0A00 */ O, VMAbv, VMAbv, VMPst, O, B, B, B, B, B, B, O, O, O, O, B,
+ /* 0A10 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0A20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 0A30 */ B, O, B, B, O, B, B, O, B, B, O, O, CMBlw, O, VPst, VPre,
+ /* 0A40 */ VPst, VBlw, VBlw, O, O, O, O, VAbv, VAbv, O, O, VAbv, VAbv, H, O, O,
+ /* 0A50 */ O, VMBlw, O, O, O, O, O, O, O, B, B, B, B, O, B, O,
+ /* 0A60 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0A70 */ VMAbv, CMAbv, GB, GB, O, MBlw, O, O, O, O, O, O, O, O, O, O,
+
+ /* Gujarati */
+
+ /* 0A80 */ O, VMAbv, VMAbv, VMPst, O, B, B, B, B, B, B, B, B, B, O, B,
+ /* 0A90 */ B, B, O, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0AA0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 0AB0 */ B, O, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VPre,
+ /* 0AC0 */ VPst, VBlw, VBlw, VBlw, VBlw, VAbv, O, VAbv, VAbv, VAbv, O, VPst, VPst, H, O, O,
+ /* 0AD0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 0AE0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0AF0 */ O, O, O, O, O, O, O, O, O, B, VMAbv, VMAbv, VMAbv, CMAbv, CMAbv, CMAbv,
+
+ /* Oriya */
+
+ /* 0B00 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, O, B,
+ /* 0B10 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0B20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 0B30 */ B, O, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv,
+ /* 0B40 */ VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPst, O, O, VPst, VPst, H, O, O,
+ /* 0B50 */ O, O, O, O, O, VAbv, VAbv, VAbv, O, O, O, O, B, B, O, B,
+ /* 0B60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0B70 */ O, B, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Tamil */
+
+ /* 0B80 */ O, O, VMAbv, IND, O, B, B, B, B, B, B, O, O, O, B, B,
+ /* 0B90 */ B, O, B, B, B, B, O, O, O, B, B, O, B, O, B, B,
+ /* 0BA0 */ O, O, O, B, B, O, O, O, B, B, B, O, O, O, B, B,
+ /* 0BB0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, VPst, VPst,
+ /* 0BC0 */ VAbv, VPst, VPst, O, O, O, VPre, VPre, VPre, O, VPst, VPst, VPst, H, O, O,
+ /* 0BD0 */ O, O, O, O, O, O, O, VPst, O, O, O, O, O, O, O, O,
+ /* 0BE0 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0BF0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Telugu */
+
+ /* 0C00 */ VMAbv, VMPst, VMPst, VMPst, VMAbv, B, B, B, B, B, B, B, B, O, B, B,
+ /* 0C10 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0C20 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 0C30 */ B, B, B, B, B, B, B, B, B, B, O, O, O, B, VAbv, VAbv,
+ /* 0C40 */ VAbv, VPst, VPst, VPst, VPst, O, VAbv, VAbv, VAbv, O, VAbv, VAbv, VAbv, H, O, O,
+ /* 0C50 */ O, O, O, O, O, VAbv, VBlw, O, B, B, B, O, O, O, O, O,
+ /* 0C60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0C70 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Kannada */
+
+ /* 0C80 */ B, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B,
+ /* 0C90 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0CA0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 0CB0 */ B, B, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv,
+ /* 0CC0 */ VAbv, VPst, VPst, VPst, VPst, O, VAbv, VAbv, VAbv, O, VAbv, VAbv, VAbv, H, O, O,
+ /* 0CD0 */ O, O, O, O, O, VPst, VPst, O, O, O, O, O, O, O, B, O,
+ /* 0CE0 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0CF0 */ O, CS, CS, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Malayalam */
+
+ /* 0D00 */ VMAbv, VMAbv, VMPst, VMPst, B, B, B, B, B, B, B, B, B, O, B, B,
+ /* 0D10 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0D20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0D30 */ B, B, B, B, B, B, B, B, B, B, B, VAbv, VAbv, B, VPst, VPst,
+ /* 0D40 */ VPst, VPst, VPst, VBlw, VBlw, O, VPre, VPre, VPre, O, VPst, VPst, VPst, H, R, O,
+ /* 0D50 */ O, O, O, O, IND, IND, IND, VPst, O, O, O, O, O, O, O, B,
+ /* 0D60 */ B, B, VBlw, VBlw, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0D70 */ O, O, O, O, O, O, O, O, O, O, IND, IND, IND, IND, IND, IND,
+
+ /* Sinhala */
+
+ /* 0D80 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0D90 */ B, B, B, B, B, B, B, O, O, O, B, B, B, B, B, B,
+ /* 0DA0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0DB0 */ B, B, O, B, B, B, B, B, B, B, B, B, O, B, O, O,
+ /* 0DC0 */ B, B, B, B, B, B, B, O, O, O, H, O, O, O, O, VPst,
+ /* 0DD0 */ VPst, VPst, VAbv, VAbv, VBlw, O, VBlw, O, VPst, VPre, VPst, VPre, VPst, VPst, VPst, VPst,
+ /* 0DE0 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B,
+ /* 0DF0 */ O, O, VPst, VPst, O, O, O, O,
+
+#define use_offset_0x0f18u 1360
+
+
+ /* Tibetan */
+ VBlw, VBlw, O, O, O, O, O, O,
+ /* 0F20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0F30 */ B, B, B, B, O, FMBlw, O, FMBlw, O, CMAbv, O, O, O, O, VPst, VPre,
+ /* 0F40 */ B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, B,
+ /* 0F50 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 0F60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, O, O,
+ /* 0F70 */ O, VBlw, VBlw, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VMAbv, VMPst,
+ /* 0F80 */ VBlw, VAbv, VMAbv, VMAbv, VBlw, IND, VMAbv, VMAbv, B, B, B, B, B, SUB, SUB, SUB,
+ /* 0F90 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB,
+ /* 0FA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB,
+ /* 0FB0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, O, O,
+ /* 0FC0 */ O, O, O, O, O, O, FMBlw, O,
+
+#define use_offset_0x1000u 1536
+
+
+ /* Myanmar */
+
+ /* 1000 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1010 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1020 */ B, B, B, B, B, B, B, B, B, B, B, VPst, VPst, VAbv, VAbv, VBlw,
+ /* 1030 */ VBlw, VPre, VAbv, VAbv, VAbv, VAbv, VMAbv, VMBlw, VMPst, H, VAbv, MPst, MPre, MBlw, MBlw, B,
+ /* 1040 */ B, B, B, B, B, B, B, B, B, B, O, GB, O, O, GB, O,
+ /* 1050 */ B, B, B, B, B, B, VPst, VPst, VBlw, VBlw, B, B, B, B, MBlw, MBlw,
+ /* 1060 */ MBlw, B, VPst, VMPst, VMPst, B, B, VPst, VPst, VMPst, VMPst, VMPst, VMPst, VMPst, B, B,
+ /* 1070 */ B, VAbv, VAbv, VAbv, VAbv, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1080 */ B, B, MBlw, VPst, VPre, VAbv, VAbv, VMPst, VMPst, VMPst, VMPst, VMPst, VMPst, VMBlw, B, VMPst,
+ /* 1090 */ B, B, B, B, B, B, B, B, B, B, VMPst, VMPst, VPst, VAbv, O, O,
+
+#define use_offset_0x1700u 1696
+
+
+ /* Tagalog */
+
+ /* 1700 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, B, B,
+ /* 1710 */ B, B, VAbv, VBlw, VBlw, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Hanunoo */
+
+ /* 1720 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1730 */ B, B, VAbv, VBlw, VBlw, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Buhid */
+
+ /* 1740 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1750 */ B, B, VAbv, VBlw, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Tagbanwa */
+
+ /* 1760 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, B, B,
+ /* 1770 */ B, O, VAbv, VBlw, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Khmer */
+
+ /* 1780 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1790 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 17A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 17B0 */ B, B, B, B, O, O, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VPst, VPst,
+ /* 17C0 */ VPst, VPre, VPre, VPre, VPst, VPst, VMAbv, VMPst, VPst, VMAbv, VMAbv, FMAbv, FAbv, CMAbv, FMAbv, FMAbv,
+ /* 17D0 */ FMAbv, VAbv, H, FMAbv, O, O, O, O, O, O, O, O, B, FMAbv, O, O,
+ /* 17E0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+#define use_offset_0x1900u 1936
+
+
+ /* Limbu */
+
+ /* 1900 */ GB, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, O,
+ /* 1920 */ VAbv, VAbv, VBlw, VPst, VPst, VAbv, VAbv, VAbv, VAbv, SUB, SUB, SUB, O, O, O, O,
+ /* 1930 */ FPst, FPst, VMBlw, FPst, FPst, FPst, FPst, FPst, FPst, FBlw, VAbv, FMBlw, O, O, O, O,
+ /* 1940 */ O, O, O, O, O, O, B, B, B, B, B, B, B, B, B, B,
+
+ /* Tai Le */
+
+ /* 1950 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1960 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, O,
+ /* 1970 */ B, B, B, B, B, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* New Tai Lue */
+
+ /* 1980 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1990 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 19A0 */ B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, O,
+ /* 19B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 19C0 */ B, B, B, B, B, B, B, B, VMPst, VMPst, O, O, O, O, O, O,
+ /* 19D0 */ B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, O,
+ /* 19E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 19F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Buginese */
+
+ /* 1A00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1A10 */ B, B, B, B, B, B, B, VAbv, VBlw, VPre, VPst, VAbv, O, O, O, O,
+
+ /* Tai Tham */
+
+ /* 1A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1A30 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1A40 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1A50 */ B, B, B, B, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, SUB, SUB, SUB, O,
+ /* 1A60 */ Sk, VPst, VAbv, VPst, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VAbv, VBlw, VPst, VPre, VPre,
+ /* 1A70 */ VPre, VPre, VPre, VAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VAbv, FMAbv, FMAbv, O, O, FMBlw,
+ /* 1A80 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+ /* 1A90 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+#define use_offset_0x1b00u 2352
+
+
+ /* Balinese */
+
+ /* 1B00 */ VMAbv, VMAbv, VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1B10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1B20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1B30 */ B, B, B, B, CMAbv, VPst, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VPre,
+ /* 1B40 */ VPst, VPst, VAbv, VAbv, H, B, B, B, B, B, B, B, O, O, O, O,
+ /* 1B50 */ B, B, B, B, B, B, B, B, B, B, O, GB, GB, O, O, GB,
+ /* 1B60 */ O, S, GB, S, S, S, S, S, GB, S, S, SMAbv, SMBlw, SMAbv, SMAbv, SMAbv,
+ /* 1B70 */ SMAbv, SMAbv, SMAbv, SMAbv, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Sundanese */
+
+ /* 1B80 */ VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1B90 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1BA0 */ B, SUB, SUB, SUB, VAbv, VBlw, VPre, VPst, VAbv, VAbv, VPst, H, SUB, SUB, B, B,
+ /* 1BB0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+
+ /* Batak */
+
+ /* 1BC0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1BD0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1BE0 */ B, B, B, B, B, B, CMAbv, VPst, VAbv, VAbv, VPst, VPst, VPst, VAbv, VPst, VAbv,
+ /* 1BF0 */ FAbv, FAbv, CMBlw, CMBlw, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Lepcha */
+
+ /* 1C00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1C10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 1C20 */ B, B, B, B, SUB, SUB, VPst, VPre, VPre, VPre, VPst, VPst, VBlw, FAbv, FAbv, FAbv,
+ /* 1C30 */ FAbv, FAbv, FAbv, FAbv, VMPre, VMPre, FMAbv, CMBlw, O, O, O, O, O, O, O, O,
+ /* 1C40 */ B, B, B, B, B, B, B, B, B, B, O, O, O, B, B, B,
+
+#define use_offset_0x1cd0u 2688
+
+
+ /* Vedic Extensions */
+
+ /* 1CD0 */ VMAbv, VMAbv, VMAbv, O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw,
+ /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, O, O, O, O, VMBlw, O, O,
+ /* 1CF0 */ O, O, IND, IND, VMAbv, CS, CS, VMPst, VMAbv, VMAbv, GB, O, O, O, O, O,
+
+#define use_offset_0x1df8u 2736
+
+
+ /* Combining Diacritical Marks Supplement */
+ O, O, O, FMAbv, O, O, O, O,
+
+#define use_offset_0x2008u 2744
+
+
+ /* General Punctuation */
+ O, O, O, O, ZWNJ, ZWJ, O, O,
+ /* 2010 */ GB, GB, GB, GB, GB, O, O, O,
+
+#define use_offset_0x2060u 2760
+
+ /* 2060 */ WJ, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Superscripts and Subscripts */
+
+ /* 2070 */ O, O, O, O, FMPst, O, O, O, O, O, O, O, O, O, O, O,
+ /* 2080 */ O, O, FMPst, FMPst, FMPst, O, O, O,
+
+#define use_offset_0x20f0u 2800
+
+
+ /* Combining Diacritical Marks for Symbols */
+
+ /* 20F0 */ VMAbv, O, O, O, O, O, O, O,
+
+#define use_offset_0x25c8u 2808
+
+
+ /* Geometric Shapes */
+ O, O, O, O, GB, O, O, O,
+
+#define use_offset_0xa800u 2816
+
+
+ /* Syloti Nagri */
+
+ /* A800 */ B, B, VAbv, B, B, B, H, B, B, B, B, VMAbv, B, B, B, B,
+ /* A810 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A820 */ B, B, B, VPst, VPst, VBlw, VAbv, VPst, O, O, O, O, VBlw, O, O, O,
+ /* A830 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Phags-pa */
+
+ /* A840 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A850 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A860 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A870 */ B, B, B, B, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Saurashtra */
+
+ /* A880 */ VMPst, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A890 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A8A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A8B0 */ B, B, B, B, MPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst, VPst,
+ /* A8C0 */ VPst, VPst, VPst, VPst, H, VMAbv, O, O, O, O, O, O, O, O, O, O,
+ /* A8D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+ /* Devanagari Extended */
+
+ /* A8E0 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv,
+ /* A8F0 */ VMAbv, VMAbv, B, B, O, O, O, O, O, O, O, O, O, O, B, VAbv,
+
+ /* Kayah Li */
+
+ /* A900 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A910 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A920 */ B, B, B, B, B, B, VAbv, VAbv, VAbv, VAbv, VAbv, VMBlw, VMBlw, VMBlw, O, O,
+
+ /* Rejang */
+
+ /* A930 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A940 */ B, B, B, B, B, B, B, VBlw, VBlw, VBlw, VAbv, VBlw, VBlw, VBlw, VBlw, FAbv,
+ /* A950 */ FAbv, FAbv, FPst, VPst, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* A960 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* A970 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Javanese */
+
+ /* A980 */ VMAbv, VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A990 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A9A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, MBlw, MBlw, MBlw,
+ /* A9C0 */ H, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* A9D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+ /* Myanmar Extended-B */
+
+ /* A9E0 */ B, B, B, B, B, VAbv, O, B, B, B, B, B, B, B, B, B,
+ /* A9F0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, O,
+
+ /* Cham */
+
+ /* AA00 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* AA10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* AA20 */ B, B, B, B, B, B, B, B, B, VMAbv, VAbv, VAbv, VAbv, VBlw, VAbv, VPre,
+ /* AA30 */ VPre, VAbv, VBlw, MPst, MPre, MBlw, MBlw, O, O, O, O, O, O, O, O, O,
+ /* AA40 */ B, B, B, FAbv, B, B, B, B, B, B, B, B, FAbv, FPst, O, O,
+ /* AA50 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+ /* Myanmar Extended-A */
+
+ /* AA60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* AA70 */ O, B, B, B, GB, GB, GB, O, O, O, B, VMPst, VMAbv, VMPst, B, B,
+
+ /* Tai Viet */
+
+ /* AA80 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* AA90 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* AAA0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* AAB0 */ VAbv, B, VAbv, VAbv, VBlw, B, B, VAbv, VAbv, B, B, B, B, B, VAbv, VMAbv,
+ /* AAC0 */ B, VMAbv, B, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* AAD0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Meetei Mayek Extensions */
+
+ /* AAE0 */ B, B, B, B, B, B, B, B, B, B, B, VPre, VBlw, VAbv, VPre, VPst,
+ /* AAF0 */ O, O, O, O, O, VMPst, H, O,
+
+#define use_offset_0xabc0u 3576
+
+
+ /* Meetei Mayek */
+
+ /* ABC0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* ABD0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* ABE0 */ B, B, B, VPst, VPst, VAbv, VPst, VPst, VBlw, VPst, VPst, O, VMPst, VBlw, O, O,
+ /* ABF0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+#define use_offset_0xfe00u 3640
+
+
+ /* Variation Selectors */
+
+ /* FE00 */ VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS, VS,
+
+#define use_offset_0x10a00u 3656
+
+
+ /* Kharoshthi */
+
+ /* 10A00 */ B, VBlw, VBlw, VBlw, O, VAbv, VBlw, O, O, O, O, O, VBlw, VBlw, VMBlw, VMAbv,
+ /* 10A10 */ B, B, B, B, O, B, B, B, O, B, B, B, B, B, B, B,
+ /* 10A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 10A30 */ B, B, B, B, B, B, O, O, CMAbv, CMBlw, CMBlw, O, O, O, O, H,
+ /* 10A40 */ B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, O,
+
+#define use_offset_0x11000u 3736
+
+
+ /* Brahmi */
+
+ /* 11000 */ VMPst, VMAbv, VMPst, CS, CS, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11010 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11020 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11030 */ B, B, B, B, B, B, B, B, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VBlw,
+ /* 11040 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, HVM, O, O, O, O, O, O, O, O, O,
+ /* 11050 */ O, O, N, N, N, N, N, N, N, N, N, N, N, N, N, N,
+ /* 11060 */ N, N, N, N, N, N, B, B, B, B, B, B, B, B, B, B,
+ /* 11070 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, HN,
+
+ /* Kaithi */
+
+ /* 11080 */ VMAbv, VMAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11090 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 110A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 110B0 */ VPst, VPre, VPst, VBlw, VBlw, VAbv, VAbv, VPst, VPst, H, CMBlw, O, O, O, O, O,
+
+#define use_offset_0x11100u 3928
+
+
+ /* Chakma */
+
+ /* 11100 */ VMAbv, VMAbv, VMAbv, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11110 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11120 */ B, B, B, B, B, B, B, VBlw, VBlw, VBlw, VAbv, VAbv, VPre, VBlw, VAbv, VAbv,
+ /* 11130 */ VBlw, VAbv, VAbv, H, CMBlw, O, B, B, B, B, B, B, B, B, B, B,
+ /* 11140 */ O, O, O, O, B, VPst, VPst, B, O, O, O, O, O, O, O, O,
+
+ /* Mahajani */
+
+ /* 11150 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11160 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11170 */ B, B, B, CMBlw, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Sharada */
+
+ /* 11180 */ VMAbv, VMAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11190 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 111A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 111B0 */ B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv,
+ /* 111C0 */ H, B, R, R, O, O, O, O, GB, FMBlw, CMBlw, VAbv, VBlw, O, VPre, VMAbv,
+ /* 111D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+ /* Sinhala Archaic Numbers */
+
+ /* 111E0 */ O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 111F0 */ B, B, B, B, B, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Khojki */
+
+ /* 11200 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11210 */ B, B, O, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11220 */ B, B, B, B, B, B, B, B, B, B, B, B, VPst, VPst, VPst, VBlw,
+ /* 11230 */ VAbv, VAbv, VAbv, VAbv, VMAbv, H, CMAbv, CMAbv, O, O, O, O, O, O, VMAbv, O,
+
+#define use_offset_0x11280u 4248
+
+
+ /* Multani */
+
+ /* 11280 */ B, B, B, B, B, B, B, O, B, O, B, B, B, B, O, B,
+ /* 11290 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, O, B,
+ /* 112A0 */ B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, O,
+
+ /* Khudawadi */
+
+ /* 112B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 112C0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 112D0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, VMAbv,
+ /* 112E0 */ VPst, VPre, VPst, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, CMBlw, VBlw, O, O, O, O, O,
+ /* 112F0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+ /* Grantha */
+
+ /* 11300 */ VMAbv, VMAbv, VMAbv, VMAbv, O, B, B, B, B, B, B, B, B, O, O, B,
+ /* 11310 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11320 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 11330 */ B, O, B, B, O, B, B, B, B, B, O, CMBlw, CMBlw, B, VPst, VPst,
+ /* 11340 */ VAbv, VPst, VPst, VPst, VPst, O, O, VPre, VPre, O, O, VPst, VPst, HVM, O, O,
+ /* 11350 */ O, O, O, O, O, O, O, VPst, O, O, O, O, O, O, B, B,
+ /* 11360 */ B, B, VPst, VPst, O, O, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, O, O, O,
+ /* 11370 */ VMAbv, VMAbv, VMAbv, VMAbv, VMAbv, O, O, O,
+
+#define use_offset_0x11400u 4496
+
+
+ /* Newa */
+
+ /* 11400 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11410 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11420 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11430 */ B, B, B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv,
+ /* 11440 */ VPst, VPst, H, VMAbv, VMAbv, VMPst, CMBlw, B, O, O, O, O, O, O, O, O,
+ /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, FMAbv, B,
+ /* 11460 */ CS, CS, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 11470 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Tirhuta */
+
+ /* 11480 */ O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11490 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 114A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 114B0 */ VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VPre, VAbv, VPst, VPst, VPst, VPst, VMAbv,
+ /* 114C0 */ VMAbv, VMAbv, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O,
+ /* 114D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+#define use_offset_0x11580u 4720
+
+
+ /* Siddham */
+
+ /* 11580 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11590 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 115A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, VPst,
+ /* 115B0 */ VPre, VPst, VBlw, VBlw, VBlw, VBlw, O, O, VPre, VPst, VPst, VPst, VMAbv, VMAbv, VMPst, H,
+ /* 115C0 */ CMBlw, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 115D0 */ O, O, O, O, O, O, O, O, B, B, B, B, VBlw, VBlw, O, O,
+ /* 115E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 115F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Modi */
+
+ /* 11600 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11610 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11620 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11630 */ VPst, VPst, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VPst, VPst, VMAbv, VMPst, H,
+ /* 11640 */ VAbv, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 11650 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+ /* 11660 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 11670 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Takri */
+
+ /* 11680 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11690 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 116A0 */ B, B, B, B, B, B, B, B, B, B, B, VMAbv, VMPst, VAbv, VPre, VPst,
+ /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, B, O, O, O, O, O, O, O,
+ /* 116C0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+ /* 116D0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 116E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 116F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Ahom */
+
+ /* 11700 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11710 */ B, B, B, B, B, B, B, B, B, B, B, O, O, MBlw, MPre, MAbv,
+ /* 11720 */ VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VAbv, VBlw, VAbv, VAbv, VAbv, O, O, O, O,
+ /* 11730 */ B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, O,
+
+#define use_offset_0x11800u 5168
+
+
+ /* Dogra */
+
+ /* 11800 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11810 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11820 */ B, B, B, B, B, B, B, B, B, B, B, B, VPst, VPre, VPst, VBlw,
+ /* 11830 */ VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VMAbv, VMPst, H, CMBlw, O, O, O, O, O,
+
+#define use_offset_0x11900u 5232
+
+
+ /* Dives Akuru */
+
+ /* 11900 */ B, B, B, B, B, B, B, O, O, B, O, O, B, B, B, B,
+ /* 11910 */ B, B, B, B, O, B, B, O, B, B, B, B, B, B, B, B,
+ /* 11920 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11930 */ VPst, VPst, VPst, VPst, VPst, VPre, O, VPre, VPst, O, O, VMAbv, VMAbv, VPst, H, R,
+ /* 11940 */ MPst, R, MBlw, CMBlw, O, O, O, O, O, O, O, O, O, O, O, O,
+ /* 11950 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+#define use_offset_0x119a0u 5328
+
+
+ /* Nandinagari */
+
+ /* 119A0 */ B, B, B, B, B, B, B, B, O, O, B, B, B, B, B, B,
+ /* 119B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 119C0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 119D0 */ B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, O, O, VAbv, VAbv, VPst, VPst, VMPst, VMPst,
+ /* 119E0 */ H, B, O, O, VPre, O, O, O, O, O, O, O, O, O, O, O,
+ /* 119F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O,
+
+ /* Zanabazar Square */
+
+ /* 11A00 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VAbv, VAbv, VBlw, B, B, B, B, B,
+ /* 11A10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11A20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11A30 */ B, B, B, FMBlw, VBlw, VMAbv, VMAbv, VMAbv, VMAbv, VMPst, R, MBlw, MBlw, MBlw, MBlw, GB,
+ /* 11A40 */ O, O, O, O, O, GB, O, H, O, O, O, O, O, O, O, O,
+
+ /* Soyombo */
+
+ /* 11A50 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VBlw, VBlw, VBlw, B, B, B, B,
+ /* 11A60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11A70 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11A80 */ B, B, B, B, R, R, R, R, R, R, FBlw, FBlw, FBlw, FBlw, FBlw, FBlw,
+ /* 11A90 */ FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, VMAbv, VMPst, CMAbv, H, O, O, O, B, O, O,
+
+#define use_offset_0x11c00u 5584
+
+
+ /* Bhaiksuki */
+
+ /* 11C00 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B,
+ /* 11C10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11C20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, VPst,
+ /* 11C30 */ VAbv, VAbv, VBlw, VBlw, VBlw, VBlw, VBlw, O, VAbv, VAbv, VAbv, VAbv, VMAbv, VMAbv, VMPst, H,
+ /* 11C40 */ B, O, O, O, GB, GB, O, O, O, O, O, O, O, O, O, O,
+ /* 11C50 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11C60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, O, O, O,
+
+ /* Marchen */
+
+ /* 11C70 */ O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11C80 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11C90 */ O, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB,
+ /* 11CA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB,
+ /* 11CB0 */ VBlw, VPre, VBlw, VAbv, VPst, VMAbv, VMAbv, O,
+
+#define use_offset_0x11d00u 5768
+
+
+ /* Masaram Gondi */
+
+ /* 11D00 */ B, B, B, B, B, B, B, O, B, B, O, B, B, B, B, B,
+ /* 11D10 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11D20 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11D30 */ B, VAbv, VAbv, VAbv, VAbv, VAbv, VBlw, O, O, O, VAbv, O, VAbv, VAbv, O, VAbv,
+ /* 11D40 */ VMAbv, VMAbv, CMBlw, VAbv, VBlw, H, R, MBlw, O, O, O, O, O, O, O, O,
+ /* 11D50 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+ /* Gunjala Gondi */
+
+ /* 11D60 */ B, B, B, B, B, B, O, B, B, O, B, B, B, B, B, B,
+ /* 11D70 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11D80 */ B, B, B, B, B, B, B, B, B, B, VPst, VPst, VPst, VPst, VPst, O,
+ /* 11D90 */ VAbv, VAbv, O, VPst, VPst, VMAbv, VMPst, H, O, O, O, O, O, O, O, O,
+ /* 11DA0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O,
+
+#define use_offset_0x11ee0u 5944
+
+
+ /* Makasar */
+
+ /* 11EE0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B,
+ /* 11EF0 */ B, B, GB, VAbv, VBlw, VPre, VPst, O,
+
+}; /* Table items: 5968; occupancy: 74% */
+
+USE_TABLE_ELEMENT_TYPE
+hb_use_get_category (hb_codepoint_t u)
+{
+ switch (u >> 12)
+ {
+ case 0x0u:
+ if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x0348u, 0x034Fu)) return use_table[u - 0x0348u + use_offset_0x0348u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x0F18u, 0x0FC7u)) return use_table[u - 0x0F18u + use_offset_0x0f18u];
+ break;
+
+ case 0x1u:
+ if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x1DF8u, 0x1DFFu)) return use_table[u - 0x1DF8u + use_offset_0x1df8u];
+ break;
+
+ case 0x2u:
+ if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x20F0u, 0x20F7u)) return use_table[u - 0x20F0u + use_offset_0x20f0u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x25C8u, 0x25CFu)) return use_table[u - 0x25C8u + use_offset_0x25c8u];
+ break;
+
+ case 0xAu:
+ if (hb_in_range<hb_codepoint_t> (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u];
+ if (hb_in_range<hb_codepoint_t> (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u];
+ break;
+
+ case 0xFu:
+ if (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u];
+ break;
+
+ case 0x10u:
+ if (hb_in_range<hb_codepoint_t> (u, 0x10A00u, 0x10A4Fu)) return use_table[u - 0x10A00u + use_offset_0x10a00u];
+ break;
+
+ case 0x11u:
+ if (hb_in_range<hb_codepoint_t> (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11100u, 0x1123Fu)) return use_table[u - 0x11100u + use_offset_0x11100u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11800u, 0x1183Fu)) return use_table[u - 0x11800u + use_offset_0x11800u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11900u, 0x1195Fu)) return use_table[u - 0x11900u + use_offset_0x11900u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x119A0u, 0x11A9Fu)) return use_table[u - 0x119A0u + use_offset_0x119a0u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11D00u, 0x11DAFu)) return use_table[u - 0x11D00u + use_offset_0x11d00u];
+ if (hb_in_range<hb_codepoint_t> (u, 0x11EE0u, 0x11EF7u)) return use_table[u - 0x11EE0u + use_offset_0x11ee0u];
+ break;
+
+ default:
+ break;
+ }
+ return USE_O;
+}
+
+#undef B
+#undef CGJ
+#undef CS
+#undef GB
+#undef H
+#undef HN
+#undef HVM
+#undef IND
+#undef N
+#undef O
+#undef R
+#undef Rsv
+#undef S
+#undef SUB
+#undef Sk
+#undef VS
+#undef WJ
+#undef ZWJ
+#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
+/* == End of generated table == */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-use.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use.cc
new file mode 100644
index 0000000000..a1e25bdd80
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use.cc
@@ -0,0 +1,569 @@
+/*
+ * 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-shape-complex-use.hh"
+#include "hb-ot-shape-complex-arabic.hh"
+#include "hb-ot-shape-complex-arabic-joining-list.hh"
+#include "hb-ot-shape-complex-vowel-constraints.hh"
+
+/* buffer var allocations */
+#define use_category() complex_var_u8_1()
+
+
+/*
+ * 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.
+ */
+ 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 {
+ USE_ISOL,
+ USE_INIT,
+ USE_MEDI,
+ USE_FINA,
+ _USE_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 void
+setup_syllables_use (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
+record_rphf_use (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
+record_pref_use (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer);
+static void
+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'));
+ map->enable_feature (HB_TAG('c','c','m','p'));
+ map->enable_feature (HB_TAG('n','u','k','t'));
+ map->enable_feature (HB_TAG('a','k','h','n'), F_MANUAL_ZWJ);
+
+ /* "Reordering group" */
+ map->add_gsub_pause (_hb_clear_substitution_flags);
+ map->add_feature (HB_TAG('r','p','h','f'), F_MANUAL_ZWJ);
+ 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);
+ 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);
+
+ map->add_gsub_pause (reorder_use);
+ map->add_gsub_pause (_hb_clear_syllables);
+
+ /* "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 *) 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))
+ {
+ 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);
+
+ free (data);
+}
+
+enum use_syllable_type_t {
+ use_independent_cluster,
+ use_virama_terminated_cluster,
+ use_sakot_terminated_cluster,
+ use_standard_cluster,
+ use_number_joiner_terminated_cluster,
+ use_numeral_cluster,
+ use_symbol_cluster,
+ use_broken_cluster,
+ use_non_cluster,
+};
+
+#include "hb-ot-shape-complex-use-machine.hh"
+
+
+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 ((USE_INIT < 4 && USE_ISOL < 4 && USE_MEDI < 4 && USE_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 = _USE_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_independent_cluster:
+ case use_symbol_cluster:
+ case use_non_cluster:
+ /* These don't join. Nothing to do. */
+ last_form = _USE_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_broken_cluster:
+
+ bool join = last_form == USE_FINA || last_form == USE_ISOL;
+
+ if (join)
+ {
+ /* Fixup previous syllable's form. */
+ last_form = last_form == USE_FINA ? USE_MEDI : USE_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 ? USE_FINA : USE_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 void
+setup_syllables_use (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font HB_UNUSED,
+ hb_buffer_t *buffer)
+{
+ 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);
+}
+
+static void
+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;
+ 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;
+ }
+ }
+}
+
+static void
+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;
+ }
+ }
+}
+
+static inline bool
+is_halant_use (const hb_glyph_info_t &info)
+{
+ return (info.use_category() == USE_H || info.use_category() == USE_HVM) &&
+ !_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_broken_cluster) |
+ 0))))
+ return;
+
+ hb_glyph_info_t *info = buffer->info;
+
+#define POST_BASE_FLAGS64 (FLAG64 (USE_FM) | \
+ FLAG64 (USE_FAbv) | \
+ FLAG64 (USE_FBlw) | \
+ FLAG64 (USE_FPst) | \
+ 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 inline void
+insert_dotted_circles_use (const hb_ot_shape_plan_t *plan HB_UNUSED,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE))
+ return;
+
+ /* Note: This loop is extra overhead, but should not be measurable.
+ * TODO Use a buffer scratch flag to remove the loop. */
+ bool has_broken_syllables = false;
+ unsigned int count = buffer->len;
+ hb_glyph_info_t *info = buffer->info;
+ for (unsigned int i = 0; i < count; i++)
+ if ((info[i].syllable() & 0x0F) == use_broken_cluster)
+ {
+ has_broken_syllables = true;
+ break;
+ }
+ if (likely (!has_broken_syllables))
+ return;
+
+ hb_glyph_info_t dottedcircle = {0};
+ if (!font->get_nominal_glyph (0x25CCu, &dottedcircle.codepoint))
+ return;
+ dottedcircle.use_category() = hb_use_get_category (0x25CC);
+
+ buffer->clear_output ();
+
+ buffer->idx = 0;
+ unsigned int last_syllable = 0;
+ while (buffer->idx < buffer->len && buffer->successful)
+ {
+ unsigned int syllable = buffer->cur().syllable();
+ use_syllable_type_t syllable_type = (use_syllable_type_t) (syllable & 0x0F);
+ if (unlikely (last_syllable != syllable && syllable_type == use_broken_cluster))
+ {
+ 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. */
+ while (buffer->idx < buffer->len && buffer->successful &&
+ last_syllable == buffer->cur().syllable() &&
+ buffer->cur().use_category() == USE_R)
+ buffer->next_glyph ();
+
+ buffer->output_info (ginfo);
+ }
+ else
+ buffer->next_glyph ();
+ }
+ buffer->swap_buffers ();
+}
+
+static void
+reorder_use (const hb_ot_shape_plan_t *plan,
+ hb_font_t *font,
+ hb_buffer_t *buffer)
+{
+ insert_dotted_circles_use (plan, font, buffer);
+
+ foreach_syllable (buffer, start, end)
+ reorder_syllable_use (buffer, start, end);
+
+ HB_BUFFER_DEALLOCATE_VAR (buffer, use_category);
+}
+
+
+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_complex_shaper_t _hb_ot_complex_shaper_use =
+{
+ collect_features_use,
+ nullptr, /* override_features */
+ data_create_use,
+ data_destroy_use,
+ preprocess_text_use,
+ nullptr, /* postprocess_glyphs */
+ HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
+ nullptr, /* decompose */
+ compose_use,
+ setup_masks_use,
+ HB_TAG_NONE, /* gpos_tag */
+ nullptr, /* reorder_marks */
+ HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
+ false, /* fallback_position */
+};
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-use.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use.hh
new file mode 100644
index 0000000000..ce6645ecd3
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-use.hh
@@ -0,0 +1,105 @@
+/*
+ * 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_SHAPE_COMPLEX_USE_HH
+#define HB_OT_SHAPE_COMPLEX_USE_HH
+
+#include "hb.hh"
+
+
+#include "hb-ot-shape-complex.hh"
+
+
+#define USE_TABLE_ELEMENT_TYPE uint8_t
+
+/* Cateories used in the Universal Shaping Engine spec:
+ * https://docs.microsoft.com/en-us/typography/script-development/use
+ */
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum use_category_t {
+ USE_O = 0, /* OTHER */
+
+ USE_B = 1, /* BASE */
+ USE_IND = 3, /* BASE_IND */
+ USE_N = 4, /* BASE_NUM */
+ USE_GB = 5, /* BASE_OTHER */
+ USE_CGJ = 6, /* CGJ */
+// USE_F = 7, /* CONS_FINAL */
+ USE_FM = 8, /* CONS_FINAL_MOD */
+// USE_M = 9, /* CONS_MED */
+// USE_CM = 10, /* CONS_MOD */
+ USE_SUB = 11, /* CONS_SUB */
+ USE_H = 12, /* HALANT */
+
+ USE_HN = 13, /* HALANT_NUM */
+ USE_ZWNJ = 14, /* Zero width non-joiner */
+ USE_ZWJ = 15, /* Zero width joiner */
+ USE_WJ = 16, /* Word joiner */
+ USE_Rsv = 17, /* Reserved characters */
+ USE_R = 18, /* REPHA */
+ USE_S = 19, /* SYM */
+// USE_SM = 20, /* SYM_MOD */
+ USE_VS = 21, /* VARIATION_SELECTOR */
+// USE_V = 36, /* VOWEL */
+// USE_VM = 40, /* VOWEL_MOD */
+ USE_CS = 43, /* CONS_WITH_STACKER */
+
+ /* https://github.com/harfbuzz/harfbuzz/issues/1102 */
+ USE_HVM = 44, /* HALANT_OR_VOWEL_MODIFIER */
+
+ USE_Sk = 48, /* SAKOT */
+
+ USE_FAbv = 24, /* CONS_FINAL_ABOVE */
+ USE_FBlw = 25, /* CONS_FINAL_BELOW */
+ USE_FPst = 26, /* CONS_FINAL_POST */
+ USE_MAbv = 27, /* CONS_MED_ABOVE */
+ USE_MBlw = 28, /* CONS_MED_BELOW */
+ USE_MPst = 29, /* CONS_MED_POST */
+ USE_MPre = 30, /* CONS_MED_PRE */
+ USE_CMAbv = 31, /* CONS_MOD_ABOVE */
+ USE_CMBlw = 32, /* CONS_MOD_BELOW */
+ USE_VAbv = 33, /* VOWEL_ABOVE / VOWEL_ABOVE_BELOW / VOWEL_ABOVE_BELOW_POST / VOWEL_ABOVE_POST */
+ USE_VBlw = 34, /* VOWEL_BELOW / VOWEL_BELOW_POST */
+ USE_VPst = 35, /* VOWEL_POST UIPC = Right */
+ USE_VPre = 22, /* VOWEL_PRE / VOWEL_PRE_ABOVE / VOWEL_PRE_ABOVE_POST / VOWEL_PRE_POST */
+ USE_VMAbv = 37, /* VOWEL_MOD_ABOVE */
+ USE_VMBlw = 38, /* VOWEL_MOD_BELOW */
+ USE_VMPst = 39, /* VOWEL_MOD_POST */
+ USE_VMPre = 23, /* VOWEL_MOD_PRE */
+ USE_SMAbv = 41, /* SYM_MOD_ABOVE */
+ USE_SMBlw = 42, /* SYM_MOD_BELOW */
+ USE_FMAbv = 45, /* CONS_FINAL_MOD UIPC = Top */
+ USE_FMBlw = 46, /* CONS_FINAL_MOD UIPC = Bottom */
+ USE_FMPst = 47, /* CONS_FINAL_MOD UIPC = Not_Applicable */
+};
+
+HB_INTERNAL USE_TABLE_ELEMENT_TYPE
+hb_use_get_category (hb_codepoint_t u);
+
+#endif /* HB_OT_SHAPE_COMPLEX_USE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc b/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc
new file mode 100644
index 0000000000..c3368c6ec2
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.cc
@@ -0,0 +1,464 @@
+/* == 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-13.0.0.txt
+ * # Date: 2020-01-22, 00:07:43 GMT
+ */
+
+#include "hb.hh"
+
+#ifndef HB_NO_OT_SHAPE
+
+#include "hb-ot-shape-complex-vowel-constraints.hh"
+
+static void
+_output_dotted_circle (hb_buffer_t *buffer)
+{
+ hb_glyph_info_t &dottedcircle = buffer->output_glyph (0x25CCu);
+ _hb_glyph_info_reset_continuation (&dottedcircle);
+}
+
+static void
+_output_with_dotted_circle (hb_buffer_t *buffer)
+{
+ _output_dotted_circle (buffer);
+ 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_SHAPE_COMPLEX_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
+ */
+ bool processed = false;
+ 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)
+ {
+ buffer->next_glyph ();
+ matched = true;
+ }
+ break;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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:
+ matched = true;
+ break;
+ }
+ break;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ 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;
+ }
+ buffer->next_glyph ();
+ if (matched) _output_with_dotted_circle (buffer);
+ }
+ processed = true;
+ break;
+
+ default:
+ break;
+ }
+ if (processed)
+ {
+ if (buffer->idx < count)
+ buffer->next_glyph ();
+ buffer->swap_buffers ();
+ }
+}
+
+
+#endif
+/* == End of generated functions == */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex-vowel-constraints.hh
new file mode 100644
index 0000000000..d9082d4ead
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex-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_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
+#define HB_OT_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH
+
+#include "hb.hh"
+
+#include "hb-ot-shape-complex.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_SHAPE_COMPLEX_VOWEL_CONSTRAINTS_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-complex.hh b/thirdparty/harfbuzz/src/hb-ot-shape-complex.hh
new file mode 100644
index 0000000000..61f4c0e158
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-complex.hh
@@ -0,0 +1,402 @@
+/*
+ * 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_SHAPE_COMPLEX_HH
+#define HB_OT_SHAPE_COMPLEX_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 complex shapers */
+#define complex_var_u8_0() var2.u8[2]
+#define complex_var_u8_1() var2.u8[3]
+
+
+#define HB_OT_SHAPE_COMPLEX_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_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
+ HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (default) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (dumber) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (khmer) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (myanmar_zawgyi) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
+ HB_COMPLEX_SHAPER_IMPLEMENT (use) \
+ /* ^--- Add new shapers here; keep sorted. */
+
+
+struct hb_ot_complex_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);
+
+
+ hb_ot_shape_normalization_mode_t normalization_preference;
+
+ /* 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);
+
+ /* 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;
+
+ /* 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);
+
+ hb_ot_shape_zero_width_marks_type_t zero_width_marks;
+
+ bool fallback_position;
+};
+
+#define HB_COMPLEX_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_complex_shaper_t _hb_ot_complex_shaper_##name;
+HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
+#undef HB_COMPLEX_SHAPER_IMPLEMENT
+
+
+static inline const hb_ot_complex_shaper_t *
+hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
+{
+ switch ((hb_tag_t) planner->props.script)
+ {
+ default:
+ return &_hb_ot_complex_shaper_default;
+
+
+ /* Unicode-1.1 additions */
+ case HB_SCRIPT_ARABIC:
+
+ /* Unicode-3.0 additions */
+ case HB_SCRIPT_MONGOLIAN:
+ case HB_SCRIPT_SYRIAC:
+
+ /* Unicode-5.0 additions */
+ case HB_SCRIPT_NKO:
+ case HB_SCRIPT_PHAGS_PA:
+
+ /* Unicode-6.0 additions */
+ case HB_SCRIPT_MANDAIC:
+
+ /* Unicode-7.0 additions */
+ case HB_SCRIPT_MANICHAEAN:
+ case HB_SCRIPT_PSALTER_PAHLAVI:
+
+ /* Unicode-9.0 additions */
+ case HB_SCRIPT_ADLAM:
+
+ /* Unicode-11.0 additions */
+ case HB_SCRIPT_HANIFI_ROHINGYA:
+ case HB_SCRIPT_SOGDIAN:
+
+ /* 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_complex_shaper_arabic;
+ else
+ return &_hb_ot_complex_shaper_default;
+
+
+ /* Unicode-1.1 additions */
+ case HB_SCRIPT_THAI:
+ case HB_SCRIPT_LAO:
+
+ return &_hb_ot_complex_shaper_thai;
+
+
+ /* Unicode-1.1 additions */
+ case HB_SCRIPT_HANGUL:
+
+ return &_hb_ot_complex_shaper_hangul;
+
+
+ /* Unicode-1.1 additions */
+ case HB_SCRIPT_HEBREW:
+
+ return &_hb_ot_complex_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:
+
+ /* Unicode-3.0 additions */
+ case HB_SCRIPT_SINHALA:
+
+ /* 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_complex_shaper_default;
+ else if ((planner->map.chosen_script[0] & 0x000000FF) == '3')
+ return &_hb_ot_complex_shaper_use;
+ else
+ return &_hb_ot_complex_shaper_indic;
+
+ case HB_SCRIPT_KHMER:
+ return &_hb_ot_complex_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_complex_shaper_default;
+ else
+ return &_hb_ot_complex_shaper_myanmar;
+
+
+ /* https://github.com/harfbuzz/harfbuzz/issues/1162 */
+ case HB_SCRIPT_MYANMAR_ZAWGYI:
+
+ return &_hb_ot_complex_shaper_myanmar_zawgyi;
+
+
+ /* 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_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:
+
+ /* 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_SOGDIAN:
+
+ /* Unicode-12.0 additions */
+ case HB_SCRIPT_NANDINAGARI:
+
+ /* Unicode-13.0 additions */
+ case HB_SCRIPT_CHORASMIAN:
+ case HB_SCRIPT_DIVES_AKURU:
+
+ /* 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_complex_shaper_default;
+ else
+ return &_hb_ot_complex_shaper_use;
+ }
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc b/thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc
new file mode 100644
index 0000000000..42bf524d16
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -0,0 +1,596 @@
+/*
+ * 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 */
+ 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 */
+ 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))
+ {
+ unsigned 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
+
+ _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);
+}
+
+
+#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;
+
+ 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 ();
+#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]))
+ {
+ 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/thirdparty/harfbuzz/src/hb-ot-shape-fallback.hh b/thirdparty/harfbuzz/src/hb-ot-shape-fallback.hh
new file mode 100644
index 0000000000..5faf5f2dfb
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc
new file mode 100644
index 0000000000..50b5829c4a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -0,0 +1,478 @@
+/*
+ * 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-shape-complex.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 complex 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)
+{
+ buffer->cur().glyph_index() = glyph;
+ buffer->output_glyph (unichar); /* This is very confusing indeed. */
+ _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;
+ 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;
+ }
+
+ unsigned int ret;
+ if ((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))
+ {
+ next_char (buffer, glyph);
+ return;
+ }
+
+ if (decompose (c, shortest, u))
+ {
+ skip_char (buffer);
+ return;
+ }
+
+ if (!shortest && c->font->get_nominal_glyph (u, &glyph))
+ {
+ 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 (0x0020u, &space_glyph))
+ {
+ _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)
+{
+ /* TODO Currently if there's a variation-selector we give-up, 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;
+ buffer->replace_glyphs (2, 1, &unicode);
+ }
+ else
+ {
+ /* Just pass on the two characters separately, let GSUB do its magic. */
+ set_glyph (buffer->cur(), font);
+ buffer->next_glyph ();
+ set_glyph (buffer->cur(), font);
+ buffer->next_glyph ();
+ }
+ /* Skip any further variation selectors. */
+ while (buffer->idx < end && unlikely (buffer->unicode->is_variation_selector (buffer->cur().codepoint)))
+ {
+ set_glyph (buffer->cur(), font);
+ buffer->next_glyph ();
+ }
+ } else {
+ set_glyph (buffer->cur(), font);
+ buffer->next_glyph ();
+ }
+ }
+ if (likely (buffer->idx < end)) {
+ set_glyph (buffer->cur(), font);
+ 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,
+ 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 (unlikely (_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]));
+ buffer->next_glyphs (done);
+ }
+ 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->swap_buffers ();
+ }
+
+
+ /* Second round, reorder (inplace) */
+
+ if (!all_simple)
+ {
+ count = buffer->len;
+ for (unsigned int i = 0; i < count; i++)
+ {
+ if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0)
+ continue;
+
+ unsigned int end;
+ for (end = i + 1; end < count; end++)
+ if (_hb_glyph_info_get_modified_combining_class (&buffer->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_COMPLEX_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;
+ }
+ }
+ 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
+ */
+ for (unsigned int i = 1; i + 1 < buffer->len; i++)
+ if (buffer->info[i].codepoint == 0x034Fu/*CGJ*/ &&
+ info_cc(buffer->info[i-1]) <= info_cc(buffer->info[i+1]))
+ {
+ _hb_glyph_info_unhide (&buffer->info[i]);
+ }
+ }
+
+
+ /* Third round, recompose */
+
+ if (!all_simple &&
+ (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS ||
+ mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT))
+ {
+ /* As noted in the comment earlier, we don't try to combine
+ * ccc=0 chars with their previous Starter. */
+
+ buffer->clear_output ();
+ count = buffer->len;
+ unsigned int starter = 0;
+ buffer->next_glyph ();
+ while (buffer->idx < count && buffer->successful)
+ {
+ hb_codepoint_t composed, glyph;
+ if (/* We don't try to compose a non-mark character with it's preceding starter.
+ * This is both an optimization to avoid trying to compose every two neighboring
+ * glyphs in most scripts AND a desired feature for Hangul. Apparently Hangul
+ * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */
+ _hb_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. */
+ buffer->next_glyph (); /* Copy to out-buffer. */
+ if (unlikely (!buffer->successful))
+ return;
+ buffer->merge_out_clusters (starter, buffer->out_len);
+ buffer->out_len--; /* Remove the second composable. */
+ /* Modify starter and carry on. */
+ buffer->out_info[starter].codepoint = composed;
+ buffer->out_info[starter].glyph_index() = glyph;
+ _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer);
+
+ continue;
+ }
+ }
+
+ /* Blocked, or doesn't compose. */
+ buffer->next_glyph ();
+
+ if (info_cc (buffer->prev()) == 0)
+ starter = buffer->out_len - 1;
+ }
+ buffer->swap_buffers ();
+ }
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-shape-normalize.hh b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.hh
new file mode 100644
index 0000000000..04f1a80091
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape-normalize.hh
@@ -0,0 +1,70 @@
+/*
+ * 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;
+ 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/thirdparty/harfbuzz/src/hb-ot-shape.cc b/thirdparty/harfbuzz/src/hb-ot-shape.cc
new file mode 100644
index 0000000000..fe0444987a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape.cc
@@ -0,0 +1,1223 @@
+/*
+ * 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-shape-complex.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"
+
+
+#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),
+ aat_map (face, props)
+#ifndef HB_NO_AAT_SHAPE
+ , apply_morx (_hb_apply_morx (face, props))
+#endif
+{
+ shaper = hb_ot_shape_complex_categorize (this);
+
+ script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE;
+ script_fallback_mark_positioning = shaper->fallback_position;
+
+ /* https://github.com/harfbuzz/harfbuzz/issues/1528 */
+ if (apply_morx && shaper != &_hb_ot_complex_shaper_default)
+ shaper = &_hb_ot_complex_shaper_dumber;
+}
+
+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_AAT_SHAPE
+ if (apply_morx)
+ aat_map.compile (plan.aat_map);
+#endif
+
+#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.
+ */
+
+ if (0)
+ ;
+#ifndef HB_NO_AAT_SHAPE
+ else if (hb_aat_layout_has_positioning (face))
+ plan.apply_kerx = true;
+#endif
+ else if (!apply_morx && !disable_gpos && hb_ot_layout_has_positioning (face))
+ plan.apply_gpos = true;
+
+ if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos))
+ {
+ /* Apparently Apple applies kerx if GPOS kern was not applied. */
+#ifndef HB_NO_AAT_SHAPE
+ if (hb_aat_layout_has_positioning (face))
+ plan.apply_kerx = true;
+ else
+#endif
+#ifndef HB_NO_OT_KERN
+ if (hb_ot_layout_has_kerning (face))
+ plan.apply_kern = true;
+#endif
+ }
+
+ 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
+ /* 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 ();
+#ifndef HB_NO_AAT_SHAPE
+ aat_map.init ();
+#endif
+
+ 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 ();
+#ifndef HB_NO_AAT_SHAPE
+ aat_map.fini ();
+#endif
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+hb_ot_shape_plan_t::fini ()
+{
+ if (shaper->data_destroy)
+ shaper->data_destroy (const_cast<void *> (data));
+
+ map.fini ();
+#ifndef HB_NO_AAT_SHAPE
+ aat_map.fini ();
+#endif
+}
+
+void
+hb_ot_shape_plan_t::substitute (hb_font_t *font,
+ hb_buffer_t *buffer) const
+{
+#ifndef HB_NO_AAT_SHAPE
+ if (unlikely (apply_morx))
+ hb_aat_layout_substitute (this, font, buffer);
+ else
+#endif
+ 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
+ else if (this->apply_kern)
+ hb_ot_layout_kern (this, font, buffer);
+#endif
+ else
+ _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->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'));
+
+ if (planner->shaper->collect_features)
+ planner->shaper->collect_features (planner);
+
+ map->enable_feature (HB_TAG ('B','U','Z','Z'));
+
+ 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 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);
+ }
+
+ 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);
+ }
+
+#ifndef HB_NO_AAT_SHAPE
+ if (planner->apply_morx)
+ {
+ hb_aat_map_builder_t *aat_map = &planner->aat_map;
+ for (unsigned int i = 0; i < num_user_features; i++)
+ {
+ const hb_feature_t *feature = &user_features[i];
+ aat_map->add_feature (feature->tag, feature->value);
+ }
+ }
+#endif
+
+ 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);
+
+ /* Marks are already set as continuation by the above line.
+ * Handle Emoji_Modifier and ZWJ-continuation. */
+ if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL &&
+ hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu)))
+ {
+ _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 11 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. Ignore Katakana for now.
+ * Tags are used for Emoji sub-region flag sequences:
+ * https://github.com/harfbuzz/harfbuzz/issues/1556
+ */
+ else if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 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;
+ buffer->output_info (info);
+ while (buffer->idx < buffer->len && buffer->successful)
+ buffer->next_glyph ();
+ buffer->swap_buffers ();
+}
+
+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);
+
+ /* 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))
+ {
+
+ if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+ foreach_grapheme (buffer, start, end)
+ {
+ buffer->merge_clusters (start, end);
+ buffer->reverse_range (start, end);
+ }
+ else
+ foreach_grapheme (buffer, start, end)
+ /* form_clusters() merged clusters already, we don't merge. */
+ buffer->reverse_range (start, end);
+
+ buffer->reverse ();
+
+ buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
+ }
+}
+
+
+/*
+ * Substitute
+ */
+
+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;
+}
+
+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;
+ }
+ }
+
+ 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;
+ }
+ }
+}
+
+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++;
+
+ 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
+ hb_ot_layout_delete_glyphs_inplace (buffer, _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_complex (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);
+
+ 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_complex (c);
+}
+
+static inline void
+hb_ot_substitute_post (const hb_ot_shape_context_t *c)
+{
+ hb_ot_hide_default_ignorables (c->buffer, c->font);
+#ifndef HB_NO_AAT_SHAPE
+ if (c->plan->apply_morx)
+ hb_aat_layout_remove_deleted_glyphs (c->buffer);
+#endif
+
+ if (c->plan->shaper->postprocess_glyphs)
+ c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font);
+}
+
+
+/*
+ * 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_complex (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 positinoing happens, we don't care about
+ * this as it will be overriden.
+ */
+ 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_complex (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_UNSAFE_TO_BREAK))
+ return;
+
+ hb_glyph_info_t *info = buffer->info;
+
+ foreach_cluster (buffer, start, end)
+ {
+ unsigned int mask = 0;
+ for (unsigned int i = start; i < end; i++)
+ if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
+ {
+ mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK;
+ break;
+ }
+ if (mask)
+ 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)
+{
+ c->buffer->deallocate_var_all ();
+ c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
+ if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR)))
+ {
+ c->buffer->max_len = hb_max (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR,
+ (unsigned) HB_BUFFER_MAX_LEN_MIN);
+ }
+ if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR)))
+ {
+ c->buffer->max_ops = hb_max (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR,
+ (unsigned) HB_BUFFER_MAX_OPS_MIN);
+ }
+
+ /* Save the original direction, we use it later. */
+ c->target_direction = c->buffer->props.direction;
+
+ _hb_buffer_allocate_unicode_vars (c->buffer);
+
+ c->buffer->clear_output ();
+
+ 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->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
+
+ 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->max_len = HB_BUFFER_MAX_LEN_DEFAULT;
+ c->buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT;
+ c->buffer->deallocate_var_all ();
+}
+
+
+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:
+ *
+ * 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:
+ *
+ * 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/thirdparty/harfbuzz/src/hb-ot-shape.h b/thirdparty/harfbuzz/src/hb-ot-shape.h
new file mode 100644
index 0000000000..7b1bcc0637
--- /dev/null
+++ b/thirdparty/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
+ */
+
+#ifndef HB_OT_H_IN
+#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/thirdparty/harfbuzz/src/hb-ot-shape.hh b/thirdparty/harfbuzz/src/hb-ot-shape.hh
new file mode 100644
index 0000000000..acc98772a9
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-shape.hh
@@ -0,0 +1,170 @@
+/*
+ * 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 == memcmp (this, other, sizeof (*this));
+ }
+};
+
+
+struct hb_shape_plan_key_t;
+
+struct hb_ot_shape_plan_t
+{
+ hb_segment_properties_t props;
+ const struct hb_ot_complex_shaper_t *shaper;
+ hb_ot_map_t map;
+ hb_aat_map_t aat_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
+#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;
+ hb_aat_map_builder_t aat_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_complex_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/thirdparty/harfbuzz/src/hb-ot-stat-table.hh b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh
new file mode 100644
index 0000000000..6aa4fa4492
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-stat-table.hh
@@ -0,0 +1,404 @@
+/*
+ * 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. */
+};
+
+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; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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. */
+ HBFixed 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; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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. */
+ HBFixed nominalValue; /* A numeric value for this attribute value. */
+ HBFixed rangeMinValue; /* The minimum value for a range associated
+ * with the specified name ID. */
+ HBFixed 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; }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (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. */
+ HBFixed value; /* A numeric value for this attribute value. */
+ HBFixed 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 (likely (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. */
+ HBFixed 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]; }
+
+ 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)));
+ }
+
+ 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;
+ }
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!c->check_struct (this)))
+ return_trace (false);
+
+ switch (u.format)
+ {
+ case 1: return_trace (u.format1.sanitize (c));
+ case 2: return_trace (u.format2.sanitize (c));
+ case 3: return_trace (u.format3.sanitize (c));
+ 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 StatAxisRecord
+{
+ int cmp (hb_tag_t key) const { return tag.cmp (key); }
+
+ hb_ot_name_id_t get_name_id () const { return nameID; }
+
+ 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 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 OffsetTo<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_set_t *nameids_to_retain) const
+ {
+ if (!has_data ()) return;
+
+ + get_design_axes ()
+ | hb_map (&StatAxisRecord::get_name_id)
+ | hb_sink (nameids_to_retain)
+ ;
+
+ + get_axis_value_offsets ()
+ | hb_map (hb_add (&(this + offsetToAxisValueOffsets)))
+ | hb_map (&AxisValue::get_value_name_id)
+ | hb_sink (nameids_to_retain)
+ ;
+ }
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (likely (c->check_struct (this) &&
+ 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 OffsetTo<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. */
+ LNNOffsetTo<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. */
+ LNNOffsetTo<UnsizedArrayOf<OffsetTo<AxisValue>>>
+ 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/thirdparty/harfbuzz/src/hb-ot-tag-table.hh b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh
new file mode 100644
index 0000000000..99937d9f69
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh
@@ -0,0 +1,2176 @@
+/* == 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="2019-05-22 06:05 PM" />
+ * File-Date: 2020-05-12
+ */
+
+#ifndef HB_OT_TAG_TABLE_HH
+#define HB_OT_TAG_TABLE_HH
+
+static const LangTag ot_languages[] = {
+ {"aa", HB_TAG('A','F','R',' ')}, /* Afar */
+ {"aae", HB_TAG('S','Q','I',' ')}, /* Arbëreshë Albanian -> Albanian */
+ {"aao", HB_TAG('A','R','A',' ')}, /* Algerian Saharan Arabic -> Arabic */
+ {"aat", HB_TAG('S','Q','I',' ')}, /* Arvanitika Albanian -> Albanian */
+ {"ab", HB_TAG('A','B','K',' ')}, /* Abkhazian */
+ {"abh", HB_TAG('A','R','A',' ')}, /* Tajiki Arabic -> Arabic */
+ {"abq", HB_TAG('A','B','A',' ')}, /* Abaza */
+ {"abv", HB_TAG('A','R','A',' ')}, /* Baharna Arabic -> Arabic */
+ {"acf", HB_TAG('F','A','N',' ')}, /* Saint Lucian Creole French -> French Antillean */
+/*{"ach", HB_TAG('A','C','H',' ')},*/ /* Acoli -> Acholi */
+ {"acm", HB_TAG('A','R','A',' ')}, /* Mesopotamian Arabic -> Arabic */
+ {"acq", HB_TAG('A','R','A',' ')}, /* Ta'izzi-Adeni Arabic -> Arabic */
+/*{"acr", HB_TAG('A','C','R',' ')},*/ /* Achi */
+ {"acw", HB_TAG('A','R','A',' ')}, /* Hijazi Arabic -> Arabic */
+ {"acx", HB_TAG('A','R','A',' ')}, /* Omani Arabic -> Arabic */
+ {"acy", HB_TAG('A','R','A',' ')}, /* Cypriot Arabic -> Arabic */
+ {"ada", HB_TAG('D','N','G',' ')}, /* Adangme -> Dangme */
+ {"adf", HB_TAG('A','R','A',' ')}, /* Dhofari Arabic -> Arabic */
+ {"adp", HB_TAG('D','Z','N',' ')}, /* Adap (retired code) -> Dzongkha */
+/*{"ady", HB_TAG('A','D','Y',' ')},*/ /* Adyghe */
+ {"aeb", HB_TAG('A','R','A',' ')}, /* Tunisian Arabic -> Arabic */
+ {"aec", HB_TAG('A','R','A',' ')}, /* Saidi Arabic -> Arabic */
+ {"af", HB_TAG('A','F','K',' ')}, /* Afrikaans */
+ {"afb", HB_TAG('A','R','A',' ')}, /* Gulf Arabic -> Arabic */
+ {"ahg", HB_TAG('A','G','W',' ')}, /* Qimant -> Agaw */
+ {"aht", HB_TAG('A','T','H',' ')}, /* Ahtena -> Athapaskan */
+ {"aii", HB_TAG('S','W','A',' ')}, /* Assyrian Neo-Aramaic -> Swadaya Aramaic */
+ {"aii", HB_TAG('S','Y','R',' ')}, /* Assyrian Neo-Aramaic -> Syriac */
+/*{"aio", HB_TAG('A','I','O',' ')},*/ /* Aiton */
+ {"aiw", HB_TAG('A','R','I',' ')}, /* Aari */
+ {"ajp", HB_TAG('A','R','A',' ')}, /* South Levantine Arabic -> Arabic */
+ {"ak", HB_TAG('A','K','A',' ')}, /* Akan [macrolanguage] */
+ {"ak", HB_TAG('T','W','I',' ')}, /* Akan [macrolanguage] -> Twi */
+ {"aln", HB_TAG('S','Q','I',' ')}, /* Gheg Albanian -> Albanian */
+ {"als", HB_TAG('S','Q','I',' ')}, /* Tosk Albanian -> Albanian */
+/*{"alt", HB_TAG('A','L','T',' ')},*/ /* Southern Altai -> Altai */
+ {"am", HB_TAG('A','M','H',' ')}, /* Amharic */
+ {"amf", HB_TAG('H','B','N',' ')}, /* Hamer-Banna -> Hammer-Banna */
+ {"amw", HB_TAG('S','Y','R',' ')}, /* Western Neo-Aramaic -> Syriac */
+ {"an", HB_TAG('A','R','G',' ')}, /* Aragonese */
+/*{"ang", HB_TAG('A','N','G',' ')},*/ /* Old English (ca. 450-1100) -> Anglo-Saxon */
+ {"apc", HB_TAG('A','R','A',' ')}, /* North Levantine Arabic -> Arabic */
+ {"apd", HB_TAG('A','R','A',' ')}, /* Sudanese Arabic -> Arabic */
+ {"apj", HB_TAG('A','T','H',' ')}, /* Jicarilla Apache -> Athapaskan */
+ {"apk", HB_TAG('A','T','H',' ')}, /* Kiowa Apache -> Athapaskan */
+ {"apl", HB_TAG('A','T','H',' ')}, /* Lipan Apache -> Athapaskan */
+ {"apm", HB_TAG('A','T','H',' ')}, /* Mescalero-Chiricahua Apache -> Athapaskan */
+ {"apw", HB_TAG('A','T','H',' ')}, /* Western Apache -> Athapaskan */
+ {"ar", HB_TAG('A','R','A',' ')}, /* Arabic [macrolanguage] */
+ {"arb", HB_TAG('A','R','A',' ')}, /* Standard Arabic -> Arabic */
+ {"arn", HB_TAG('M','A','P',' ')}, /* Mapudungun */
+ {"arq", HB_TAG('A','R','A',' ')}, /* Algerian Arabic -> Arabic */
+ {"ars", HB_TAG('A','R','A',' ')}, /* Najdi Arabic -> Arabic */
+ {"ary", HB_TAG('M','O','R',' ')}, /* Moroccan Arabic -> Moroccan */
+ {"arz", HB_TAG('A','R','A',' ')}, /* Egyptian Arabic -> Arabic */
+ {"as", HB_TAG('A','S','M',' ')}, /* Assamese */
+/*{"ast", HB_TAG('A','S','T',' ')},*/ /* Asturian */
+/*{"ath", HB_TAG('A','T','H',' ')},*/ /* Athapascan [family] -> Athapaskan */
+ {"atj", HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */
+ {"atv", HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */
+ {"auz", HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */
+ {"av", HB_TAG('A','V','R',' ')}, /* Avaric -> Avar */
+ {"avl", HB_TAG('A','R','A',' ')}, /* Eastern Egyptian Bedawi Arabic -> Arabic */
+/*{"awa", HB_TAG('A','W','A',' ')},*/ /* Awadhi */
+ {"ay", HB_TAG('A','Y','M',' ')}, /* Aymara [macrolanguage] */
+ {"ayc", HB_TAG('A','Y','M',' ')}, /* Southern Aymara -> Aymara */
+ {"ayh", HB_TAG('A','R','A',' ')}, /* Hadrami Arabic -> Arabic */
+ {"ayl", HB_TAG('A','R','A',' ')}, /* Libyan Arabic -> Arabic */
+ {"ayn", HB_TAG('A','R','A',' ')}, /* Sanaani Arabic -> Arabic */
+ {"ayp", HB_TAG('A','R','A',' ')}, /* North Mesopotamian Arabic -> Arabic */
+ {"ayr", HB_TAG('A','Y','M',' ')}, /* Central Aymara -> Aymara */
+ {"az", HB_TAG('A','Z','E',' ')}, /* Azerbaijani [macrolanguage] */
+/*{"azb", HB_TAG('A','Z','B',' ')},*/ /* South Azerbaijani -> Torki */
+ {"azj", HB_TAG('A','Z','E',' ')}, /* North Azerbaijani -> Azerbaijani */
+ {"ba", HB_TAG('B','S','H',' ')}, /* Bashkir */
+ {"bad", HB_TAG('B','A','D','0')}, /* Banda [family] */
+ {"bai", HB_TAG('B','M','L',' ')}, /* Bamileke [family] */
+ {"bal", HB_TAG('B','L','I',' ')}, /* Baluchi [macrolanguage] */
+/*{"ban", HB_TAG('B','A','N',' ')},*/ /* Balinese */
+/*{"bar", HB_TAG('B','A','R',' ')},*/ /* Bavarian */
+/*{"bbc", HB_TAG('B','B','C',' ')},*/ /* Batak Toba */
+ {"bbz", HB_TAG('A','R','A',' ')}, /* Babalia Creole Arabic (retired code) -> Arabic */
+ {"bcc", HB_TAG('B','L','I',' ')}, /* Southern Balochi -> Baluchi */
+ {"bci", HB_TAG('B','A','U',' ')}, /* Baoulé -> Baulé */
+ {"bcl", HB_TAG('B','I','K',' ')}, /* Central Bikol -> Bikol */
+ {"bcq", HB_TAG('B','C','H',' ')}, /* Bench */
+ {"bcr", HB_TAG('A','T','H',' ')}, /* Babine -> Athapaskan */
+/*{"bdy", HB_TAG('B','D','Y',' ')},*/ /* Bandjalang */
+ {"be", HB_TAG('B','E','L',' ')}, /* Belarusian -> Belarussian */
+ {"bea", HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */
+ {"beb", HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */
+/*{"bem", HB_TAG('B','E','M',' ')},*/ /* Bemba (Zambia) */
+ {"ber", HB_TAG('B','B','R',' ')}, /* Berber [family] */
+ {"bfq", HB_TAG('B','A','D',' ')}, /* Badaga */
+ {"bft", HB_TAG('B','L','T',' ')}, /* Balti */
+ {"bfu", HB_TAG('L','A','H',' ')}, /* Gahri -> Lahuli */
+ {"bfy", HB_TAG('B','A','G',' ')}, /* Bagheli -> Baghelkhandi */
+ {"bg", HB_TAG('B','G','R',' ')}, /* Bulgarian */
+/*{"bgc", HB_TAG('B','G','C',' ')},*/ /* Haryanvi */
+ {"bgn", HB_TAG('B','L','I',' ')}, /* Western Balochi -> Baluchi */
+ {"bgp", HB_TAG('B','L','I',' ')}, /* Eastern Balochi -> Baluchi */
+/*{"bgq", HB_TAG('B','G','Q',' ')},*/ /* Bagri */
+ {"bgr", HB_TAG('Q','I','N',' ')}, /* Bawm Chin -> Chin */
+ {"bhb", HB_TAG('B','H','I',' ')}, /* Bhili */
+/*{"bhi", HB_TAG('B','H','I',' ')},*/ /* Bhilali -> Bhili */
+ {"bhk", HB_TAG('B','I','K',' ')}, /* Albay Bicolano (retired code) -> Bikol */
+/*{"bho", HB_TAG('B','H','O',' ')},*/ /* Bhojpuri */
+ {"bhr", HB_TAG('M','L','G',' ')}, /* Bara Malagasy -> Malagasy */
+ {"bi", HB_TAG('B','I','S',' ')}, /* Bislama */
+/*{"bik", HB_TAG('B','I','K',' ')},*/ /* Bikol [macrolanguage] */
+ {"bin", HB_TAG('E','D','O',' ')}, /* Edo */
+/*{"bjj", HB_TAG('B','J','J',' ')},*/ /* Kanauji */
+ {"bjn", HB_TAG('M','L','Y',' ')}, /* Banjar -> Malay */
+ {"bjq", HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */
+ {"bjt", HB_TAG('B','L','N',' ')}, /* Balanta-Ganja -> Balante */
+ {"bla", HB_TAG('B','K','F',' ')}, /* Siksika -> Blackfoot */
+ {"ble", HB_TAG('B','L','N',' ')}, /* Balanta-Kentohe -> Balante */
+/*{"blk", HB_TAG('B','L','K',' ')},*/ /* Pa’o Karen */
+ {"bln", HB_TAG('B','I','K',' ')}, /* Southern Catanduanes Bikol -> Bikol */
+ {"bm", HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */
+ {"bmm", HB_TAG('M','L','G',' ')}, /* Northern Betsimisaraka Malagasy -> Malagasy */
+ {"bn", HB_TAG('B','E','N',' ')}, /* Bengali */
+ {"bo", HB_TAG('T','I','B',' ')}, /* Tibetan */
+/*{"bpy", HB_TAG('B','P','Y',' ')},*/ /* Bishnupriya -> Bishnupriya Manipuri */
+ {"bqi", HB_TAG('L','R','C',' ')}, /* Bakhtiari -> Luri */
+ {"br", HB_TAG('B','R','E',' ')}, /* Breton */
+ {"bra", HB_TAG('B','R','I',' ')}, /* Braj -> Braj Bhasha */
+/*{"brh", HB_TAG('B','R','H',' ')},*/ /* Brahui */
+/*{"brx", HB_TAG('B','R','X',' ')},*/ /* Bodo (India) */
+ {"bs", HB_TAG('B','O','S',' ')}, /* Bosnian */
+/*{"bsk", HB_TAG('B','S','K',' ')},*/ /* Burushaski */
+ {"btb", HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) (retired code) */
+ {"btj", HB_TAG('M','L','Y',' ')}, /* Bacanese Malay -> Malay */
+ {"bto", HB_TAG('B','I','K',' ')}, /* Rinconada Bikol -> Bikol */
+/*{"bts", HB_TAG('B','T','S',' ')},*/ /* Batak Simalungun */
+/*{"bug", HB_TAG('B','U','G',' ')},*/ /* Buginese -> Bugis */
+ {"bum", HB_TAG('B','T','I',' ')}, /* Bulu (Cameroon) -> Beti */
+ {"bve", HB_TAG('M','L','Y',' ')}, /* Berau Malay -> Malay */
+ {"bvu", HB_TAG('M','L','Y',' ')}, /* Bukit Malay -> Malay */
+ {"bxk", HB_TAG('L','U','H',' ')}, /* Bukusu -> Luyia */
+ {"bxp", HB_TAG('B','T','I',' ')}, /* Bebil -> Beti */
+ {"bxr", HB_TAG('R','B','U',' ')}, /* Russia Buriat -> Russian Buriat */
+ {"byn", HB_TAG('B','I','L',' ')}, /* Bilin -> Bilen */
+/*{"byv", HB_TAG('B','Y','V',' ')},*/ /* Medumba */
+ {"bzc", HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy -> Malagasy */
+ {"ca", HB_TAG('C','A','T',' ')}, /* Catalan */
+ {"caf", HB_TAG('C','R','R',' ')}, /* Southern Carrier -> Carrier */
+ {"caf", HB_TAG('A','T','H',' ')}, /* Southern Carrier -> Athapaskan */
+/*{"cak", HB_TAG('C','A','K',' ')},*/ /* Kaqchikel */
+/*{"cbk", HB_TAG('C','B','K',' ')},*/ /* Chavacano -> Zamboanga Chavacano */
+ {"cbl", HB_TAG('Q','I','N',' ')}, /* Bualkhaw Chin -> Chin */
+ {"cco", HB_TAG('C','C','H','N')}, /* Comaltepec Chinantec -> Chinantec */
+ {"ccq", HB_TAG('A','R','K',' ')}, /* Chaungtha (retired code) -> Rakhine */
+ {"cdo", HB_TAG('Z','H','S',' ')}, /* Min Dong Chinese -> Chinese Simplified */
+ {"ce", HB_TAG('C','H','E',' ')}, /* Chechen */
+/*{"ceb", HB_TAG('C','E','B',' ')},*/ /* Cebuano */
+ {"cfm", HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) */
+/*{"cgg", HB_TAG('C','G','G',' ')},*/ /* Chiga */
+ {"ch", HB_TAG('C','H','A',' ')}, /* Chamorro */
+ {"chj", HB_TAG('C','C','H','N')}, /* Ojitlán Chinantec -> Chinantec */
+ {"chk", HB_TAG('C','H','K','0')}, /* Chuukese */
+/*{"cho", HB_TAG('C','H','O',' ')},*/ /* Choctaw */
+ {"chp", HB_TAG('C','H','P',' ')}, /* Chipewyan */
+ {"chp", HB_TAG('S','A','Y',' ')}, /* Chipewyan -> Sayisi */
+ {"chp", HB_TAG('A','T','H',' ')}, /* Chipewyan -> Athapaskan */
+ {"chq", HB_TAG('C','C','H','N')}, /* Quiotepec Chinantec -> Chinantec */
+/*{"chr", HB_TAG('C','H','R',' ')},*/ /* Cherokee */
+/*{"chy", HB_TAG('C','H','Y',' ')},*/ /* Cheyenne */
+ {"chz", HB_TAG('C','C','H','N')}, /* Ozumacín Chinantec -> Chinantec */
+ {"ciw", HB_TAG('O','J','B',' ')}, /* Chippewa -> Ojibway */
+/*{"cja", HB_TAG('C','J','A',' ')},*/ /* Western Cham */
+/*{"cjm", HB_TAG('C','J','M',' ')},*/ /* Eastern Cham */
+ {"cjy", HB_TAG('Z','H','S',' ')}, /* Jinyu Chinese -> Chinese Simplified */
+ {"cka", HB_TAG('Q','I','N',' ')}, /* Khumi Awa Chin (retired code) -> Chin */
+ {"ckb", HB_TAG('K','U','R',' ')}, /* Central Kurdish -> Kurdish */
+ {"ckt", HB_TAG('C','H','K',' ')}, /* Chukot -> Chukchi */
+ {"clc", HB_TAG('A','T','H',' ')}, /* Chilcotin -> Athapaskan */
+ {"cld", HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */
+ {"cle", HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */
+ {"cmn", HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese Simplified */
+ {"cmr", HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */
+ {"cnb", HB_TAG('Q','I','N',' ')}, /* Chinbon Chin -> Chin */
+ {"cnh", HB_TAG('Q','I','N',' ')}, /* Hakha Chin -> Chin */
+ {"cnk", HB_TAG('Q','I','N',' ')}, /* Khumi Chin -> Chin */
+ {"cnl", HB_TAG('C','C','H','N')}, /* Lalana Chinantec -> Chinantec */
+ {"cnp", HB_TAG('Z','H','S',' ')}, /* Northern Ping Chinese -> Chinese Simplified */
+ {"cnt", HB_TAG('C','C','H','N')}, /* Tepetotutla Chinantec -> Chinantec */
+ {"cnw", HB_TAG('Q','I','N',' ')}, /* Ngawn Chin -> Chin */
+ {"co", HB_TAG('C','O','S',' ')}, /* Corsican */
+ {"coa", HB_TAG('M','L','Y',' ')}, /* Cocos Islands Malay -> Malay */
+/*{"cop", HB_TAG('C','O','P',' ')},*/ /* Coptic */
+ {"coq", HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */
+ {"cpa", HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */
+ {"cpe", HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [family] -> Creoles */
+ {"cpf", HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [family] -> Creoles */
+/*{"cpp", HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [family] -> Creoles */
+ {"cpx", HB_TAG('Z','H','S',' ')}, /* Pu-Xian Chinese -> Chinese Simplified */
+ {"cqd", HB_TAG('H','M','N',' ')}, /* Chuanqiandian Cluster Miao -> Hmong */
+ {"cqu", HB_TAG('Q','U','H',' ')}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */
+ {"cr", HB_TAG('C','R','E',' ')}, /* Cree [macrolanguage] */
+ {"cr", HB_TAG('Y','C','R',' ')}, /* Cree [macrolanguage] -> Y-Cree */
+ {"crh", HB_TAG('C','R','T',' ')}, /* Crimean Tatar */
+ {"crj", HB_TAG('E','C','R',' ')}, /* Southern East Cree -> Eastern Cree */
+ {"crk", HB_TAG('W','C','R',' ')}, /* Plains Cree -> West-Cree */
+ {"crl", HB_TAG('E','C','R',' ')}, /* Northern East Cree -> Eastern Cree */
+ {"crm", HB_TAG('M','C','R',' ')}, /* Moose Cree */
+ {"crm", HB_TAG('L','C','R',' ')}, /* Moose Cree -> L-Cree */
+ {"crp", HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [family] -> Creoles */
+ {"crx", HB_TAG('C','R','R',' ')}, /* Carrier */
+ {"crx", HB_TAG('A','T','H',' ')}, /* Carrier -> Athapaskan */
+ {"cs", HB_TAG('C','S','Y',' ')}, /* Czech */
+ {"csa", HB_TAG('C','C','H','N')}, /* Chiltepec Chinantec -> Chinantec */
+/*{"csb", HB_TAG('C','S','B',' ')},*/ /* Kashubian */
+ {"csh", HB_TAG('Q','I','N',' ')}, /* Asho Chin -> Chin */
+ {"cso", HB_TAG('C','C','H','N')}, /* Sochiapam Chinantec -> Chinantec */
+ {"csp", HB_TAG('Z','H','S',' ')}, /* Southern Ping Chinese -> Chinese Simplified */
+ {"csw", HB_TAG('N','C','R',' ')}, /* Swampy Cree -> N-Cree */
+ {"csw", HB_TAG('N','H','C',' ')}, /* Swampy Cree -> Norway House Cree */
+ {"csy", HB_TAG('Q','I','N',' ')}, /* Siyin Chin -> Chin */
+ {"ctc", HB_TAG('A','T','H',' ')}, /* Chetco -> Athapaskan */
+ {"ctd", HB_TAG('Q','I','N',' ')}, /* Tedim Chin -> Chin */
+ {"cte", HB_TAG('C','C','H','N')}, /* Tepinapa Chinantec -> Chinantec */
+/*{"ctg", HB_TAG('C','T','G',' ')},*/ /* Chittagonian */
+ {"ctl", HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */
+ {"cts", HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */
+ {"cu", HB_TAG('C','S','L',' ')}, /* Church Slavonic */
+ {"cuc", HB_TAG('C','C','H','N')}, /* Usila Chinantec -> Chinantec */
+/*{"cuk", HB_TAG('C','U','K',' ')},*/ /* San Blas Kuna */
+ {"cv", HB_TAG('C','H','U',' ')}, /* Chuvash */
+ {"cvn", HB_TAG('C','C','H','N')}, /* Valle Nacional Chinantec -> Chinantec */
+ {"cwd", HB_TAG('D','C','R',' ')}, /* Woods Cree */
+ {"cwd", HB_TAG('T','C','R',' ')}, /* Woods Cree -> TH-Cree */
+ {"cy", HB_TAG('W','E','L',' ')}, /* Welsh */
+ {"czh", HB_TAG('Z','H','S',' ')}, /* Huizhou Chinese -> Chinese Simplified */
+ {"czo", HB_TAG('Z','H','S',' ')}, /* Min Zhong Chinese -> Chinese Simplified */
+ {"czt", HB_TAG('Q','I','N',' ')}, /* Zotung Chin -> Chin */
+ {"da", HB_TAG('D','A','N',' ')}, /* Danish */
+ {"dao", HB_TAG('Q','I','N',' ')}, /* Daai Chin -> Chin */
+ {"dap", HB_TAG('N','I','S',' ')}, /* Nisi (India) (retired code) */
+/*{"dar", HB_TAG('D','A','R',' ')},*/ /* Dargwa */
+/*{"dax", HB_TAG('D','A','X',' ')},*/ /* Dayi */
+ {"de", HB_TAG('D','E','U',' ')}, /* German */
+ {"den", HB_TAG('S','L','A',' ')}, /* Slave (Athapascan) [macrolanguage] -> Slavey */
+ {"den", HB_TAG('A','T','H',' ')}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */
+/*{"dgo", HB_TAG('D','G','O',' ')},*/ /* Dogri */
+ {"dgr", HB_TAG('A','T','H',' ')}, /* Dogrib -> Athapaskan */
+ {"dhd", HB_TAG('M','A','W',' ')}, /* Dhundari -> Marwari */
+/*{"dhg", HB_TAG('D','H','G',' ')},*/ /* Dhangu */
+ {"dib", HB_TAG('D','N','K',' ')}, /* South Central Dinka -> Dinka */
+ {"dik", HB_TAG('D','N','K',' ')}, /* Southwestern Dinka -> Dinka */
+ {"din", HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */
+ {"dip", HB_TAG('D','N','K',' ')}, /* Northeastern Dinka -> Dinka */
+/*{"diq", HB_TAG('D','I','Q',' ')},*/ /* Dimli */
+ {"diw", HB_TAG('D','N','K',' ')}, /* Northwestern Dinka -> Dinka */
+ {"dje", HB_TAG('D','J','R',' ')}, /* Zarma */
+ {"djr", HB_TAG('D','J','R','0')}, /* Djambarrpuyngu */
+ {"dks", HB_TAG('D','N','K',' ')}, /* Southeastern Dinka -> Dinka */
+ {"dng", HB_TAG('D','U','N',' ')}, /* Dungan */
+/*{"dnj", HB_TAG('D','N','J',' ')},*/ /* Dan */
+ {"doi", HB_TAG('D','G','R',' ')}, /* Dogri [macrolanguage] */
+ {"drh", HB_TAG('M','N','G',' ')}, /* Darkhat (retired code) -> Mongolian */
+ {"drw", HB_TAG('D','R','I',' ')}, /* Darwazi (retired code) -> Dari */
+ {"dsb", HB_TAG('L','S','B',' ')}, /* Lower Sorbian */
+ {"dty", HB_TAG('N','E','P',' ')}, /* Dotyali -> Nepali */
+/*{"duj", HB_TAG('D','U','J',' ')},*/ /* Dhuwal (retired code) */
+ {"dup", HB_TAG('M','L','Y',' ')}, /* Duano -> Malay */
+ {"dv", HB_TAG('D','I','V',' ')}, /* Divehi (Dhivehi, Maldivian) */
+ {"dv", HB_TAG('D','H','V',' ')}, /* Divehi (Dhivehi, Maldivian) (deprecated) */
+ {"dwk", HB_TAG('K','U','I',' ')}, /* Dawik Kui -> Kui */
+ {"dwu", HB_TAG('D','U','J',' ')}, /* Dhuwal */
+ {"dwy", HB_TAG('D','U','J',' ')}, /* Dhuwaya -> Dhuwal */
+ {"dyu", HB_TAG('J','U','L',' ')}, /* Dyula -> Jula */
+ {"dz", HB_TAG('D','Z','N',' ')}, /* Dzongkha */
+ {"ee", HB_TAG('E','W','E',' ')}, /* Ewe */
+/*{"efi", HB_TAG('E','F','I',' ')},*/ /* Efik */
+ {"ekk", HB_TAG('E','T','I',' ')}, /* Standard Estonian -> Estonian */
+ {"el", HB_TAG('E','L','L',' ')}, /* Modern Greek (1453-) -> Greek */
+ {"emk", HB_TAG('E','M','K',' ')}, /* Eastern Maninkakan */
+ {"emk", HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan -> Maninka */
+ {"en", HB_TAG('E','N','G',' ')}, /* English */
+ {"enb", HB_TAG('K','A','L',' ')}, /* Markweeta -> Kalenjin */
+ {"enf", HB_TAG('F','N','E',' ')}, /* Forest Enets -> Forest Nenets */
+ {"enh", HB_TAG('T','N','E',' ')}, /* Tundra Enets -> Tundra Nenets */
+ {"eo", HB_TAG('N','T','O',' ')}, /* Esperanto */
+ {"es", HB_TAG('E','S','P',' ')}, /* Spanish */
+ {"esg", HB_TAG('G','O','N',' ')}, /* Aheri Gondi -> Gondi */
+ {"esi", HB_TAG('I','P','K',' ')}, /* North Alaskan Inupiatun -> Inupiat */
+ {"esk", HB_TAG('I','P','K',' ')}, /* Northwest Alaska Inupiatun -> Inupiat */
+/*{"esu", HB_TAG('E','S','U',' ')},*/ /* Central Yupik */
+ {"et", HB_TAG('E','T','I',' ')}, /* Estonian [macrolanguage] */
+ {"eto", HB_TAG('B','T','I',' ')}, /* Eton (Cameroon) -> Beti */
+ {"eu", HB_TAG('E','U','Q',' ')}, /* Basque */
+ {"eve", HB_TAG('E','V','N',' ')}, /* Even */
+ {"evn", HB_TAG('E','V','K',' ')}, /* Evenki */
+ {"ewo", HB_TAG('B','T','I',' ')}, /* Ewondo -> Beti */
+ {"eyo", HB_TAG('K','A','L',' ')}, /* Keiyo -> Kalenjin */
+ {"fa", HB_TAG('F','A','R',' ')}, /* Persian [macrolanguage] */
+ {"fan", HB_TAG('F','A','N','0')}, /* Fang (Equatorial Guinea) */
+/*{"fat", HB_TAG('F','A','T',' ')},*/ /* Fanti */
+ {"fbl", HB_TAG('B','I','K',' ')}, /* West Albay Bikol -> Bikol */
+ {"ff", HB_TAG('F','U','L',' ')}, /* Fulah [macrolanguage] */
+ {"ffm", HB_TAG('F','U','L',' ')}, /* Maasina Fulfulde -> Fulah */
+ {"fi", HB_TAG('F','I','N',' ')}, /* Finnish */
+ {"fil", HB_TAG('P','I','L',' ')}, /* Filipino */
+ {"fj", HB_TAG('F','J','I',' ')}, /* Fijian */
+ {"flm", HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) (retired code) */
+ {"flm", HB_TAG('Q','I','N',' ')}, /* Falam Chin (retired code) -> Chin */
+/*{"fmp", HB_TAG('F','M','P',' ')},*/ /* Fe’fe’ */
+ {"fo", HB_TAG('F','O','S',' ')}, /* Faroese */
+/*{"fon", HB_TAG('F','O','N',' ')},*/ /* Fon */
+ {"fr", HB_TAG('F','R','A',' ')}, /* French */
+/*{"frc", HB_TAG('F','R','C',' ')},*/ /* Cajun French */
+/*{"frp", HB_TAG('F','R','P',' ')},*/ /* Arpitan */
+ {"fub", HB_TAG('F','U','L',' ')}, /* Adamawa Fulfulde -> Fulah */
+ {"fuc", HB_TAG('F','U','L',' ')}, /* Pulaar -> Fulah */
+ {"fue", HB_TAG('F','U','L',' ')}, /* Borgu Fulfulde -> Fulah */
+ {"fuf", HB_TAG('F','T','A',' ')}, /* Pular -> Futa */
+ {"fuh", HB_TAG('F','U','L',' ')}, /* Western Niger Fulfulde -> Fulah */
+ {"fui", HB_TAG('F','U','L',' ')}, /* Bagirmi Fulfulde -> Fulah */
+ {"fuq", HB_TAG('F','U','L',' ')}, /* Central-Eastern Niger Fulfulde -> Fulah */
+ {"fur", HB_TAG('F','R','L',' ')}, /* Friulian */
+/*{"fuv", HB_TAG('F','U','V',' ')},*/ /* Nigerian Fulfulde */
+ {"fy", HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */
+ {"ga", HB_TAG('I','R','I',' ')}, /* Irish */
+ {"gaa", HB_TAG('G','A','D',' ')}, /* Ga */
+/*{"gag", HB_TAG('G','A','G',' ')},*/ /* Gagauz */
+ {"gan", HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese Simplified */
+ {"gax", HB_TAG('O','R','O',' ')}, /* Borana-Arsi-Guji Oromo -> Oromo */
+ {"gaz", HB_TAG('O','R','O',' ')}, /* West Central Oromo -> Oromo */
+ {"gbm", HB_TAG('G','A','W',' ')}, /* Garhwali */
+ {"gce", HB_TAG('A','T','H',' ')}, /* Galice -> Athapaskan */
+ {"gd", HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */
+ {"gda", HB_TAG('R','A','J',' ')}, /* Gade Lohar -> Rajasthani */
+/*{"gez", HB_TAG('G','E','Z',' ')},*/ /* Geez */
+ {"ggo", HB_TAG('G','O','N',' ')}, /* Southern Gondi (retired code) -> Gondi */
+/*{"gih", HB_TAG('G','I','H',' ')},*/ /* Githabul */
+ {"gil", HB_TAG('G','I','L','0')}, /* Kiribati (Gilbertese) */
+ {"gju", HB_TAG('R','A','J',' ')}, /* Gujari -> Rajasthani */
+/*{"gkp", HB_TAG('G','K','P',' ')},*/ /* Guinea Kpelle -> Kpelle (Guinea) */
+ {"gl", HB_TAG('G','A','L',' ')}, /* Galician */
+ {"gld", HB_TAG('N','A','N',' ')}, /* Nanai */
+/*{"glk", HB_TAG('G','L','K',' ')},*/ /* Gilaki */
+ {"gn", HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */
+/*{"gnn", HB_TAG('G','N','N',' ')},*/ /* Gumatj */
+ {"gno", HB_TAG('G','O','N',' ')}, /* Northern Gondi -> Gondi */
+ {"gnw", HB_TAG('G','U','A',' ')}, /* Western Bolivian Guaraní -> Guarani */
+/*{"gog", HB_TAG('G','O','G',' ')},*/ /* Gogo */
+ {"gom", HB_TAG('K','O','K',' ')}, /* Goan Konkani -> Konkani */
+/*{"gon", HB_TAG('G','O','N',' ')},*/ /* Gondi [macrolanguage] */
+ {"grt", HB_TAG('G','R','O',' ')}, /* Garo */
+ {"gru", HB_TAG('S','O','G',' ')}, /* Kistane -> Sodo Gurage */
+ {"gsw", HB_TAG('A','L','S',' ')}, /* Alsatian */
+ {"gu", HB_TAG('G','U','J',' ')}, /* Gujarati */
+/*{"guc", HB_TAG('G','U','C',' ')},*/ /* Wayuu */
+/*{"guf", HB_TAG('G','U','F',' ')},*/ /* Gupapuyngu */
+ {"gug", HB_TAG('G','U','A',' ')}, /* Paraguayan Guaraní -> Guarani */
+ {"gui", HB_TAG('G','U','A',' ')}, /* Eastern Bolivian Guaraní -> Guarani */
+ {"guk", HB_TAG('G','M','Z',' ')}, /* Gumuz */
+ {"guk", HB_TAG('G','U','K',' ')}, /* Gumuz (SIL fonts) */
+ {"gun", HB_TAG('G','U','A',' ')}, /* Mbyá Guaraní -> Guarani */
+/*{"guz", HB_TAG('G','U','Z',' ')},*/ /* Gusii */
+ {"gv", HB_TAG('M','N','X',' ')}, /* Manx */
+ {"gwi", HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */
+ {"ha", HB_TAG('H','A','U',' ')}, /* Hausa */
+ {"haa", HB_TAG('A','T','H',' ')}, /* Han -> Athapaskan */
+ {"hae", HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */
+ {"hak", HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese Simplified */
+ {"har", HB_TAG('H','R','I',' ')}, /* Harari */
+/*{"haw", HB_TAG('H','A','W',' ')},*/ /* Hawaiian */
+/*{"hay", HB_TAG('H','A','Y',' ')},*/ /* Haya */
+/*{"haz", HB_TAG('H','A','Z',' ')},*/ /* Hazaragi */
+ {"he", HB_TAG('I','W','R',' ')}, /* Hebrew */
+ {"hea", HB_TAG('H','M','N',' ')}, /* Northern Qiandong Miao -> Hmong */
+ {"hi", HB_TAG('H','I','N',' ')}, /* Hindi */
+/*{"hil", HB_TAG('H','I','L',' ')},*/ /* Hiligaynon */
+ {"hji", HB_TAG('M','L','Y',' ')}, /* Haji -> Malay */
+ {"hlt", HB_TAG('Q','I','N',' ')}, /* Matu Chin -> Chin */
+ {"hma", HB_TAG('H','M','N',' ')}, /* Southern Mashan Hmong -> Hmong */
+ {"hmc", HB_TAG('H','M','N',' ')}, /* Central Huishui Hmong -> Hmong */
+ {"hmd", HB_TAG('H','M','N',' ')}, /* Large Flowery Miao -> Hmong */
+ {"hme", HB_TAG('H','M','N',' ')}, /* Eastern Huishui Hmong -> Hmong */
+ {"hmg", HB_TAG('H','M','N',' ')}, /* Southwestern Guiyang Hmong -> Hmong */
+ {"hmh", HB_TAG('H','M','N',' ')}, /* Southwestern Huishui Hmong -> Hmong */
+ {"hmi", HB_TAG('H','M','N',' ')}, /* Northern Huishui Hmong -> Hmong */
+ {"hmj", HB_TAG('H','M','N',' ')}, /* Ge -> Hmong */
+ {"hml", HB_TAG('H','M','N',' ')}, /* Luopohe Hmong -> Hmong */
+ {"hmm", HB_TAG('H','M','N',' ')}, /* Central Mashan Hmong -> Hmong */
+/*{"hmn", HB_TAG('H','M','N',' ')},*/ /* Hmong [macrolanguage] */
+ {"hmp", HB_TAG('H','M','N',' ')}, /* Northern Mashan Hmong -> Hmong */
+ {"hmq", HB_TAG('H','M','N',' ')}, /* Eastern Qiandong Miao -> Hmong */
+ {"hms", HB_TAG('H','M','N',' ')}, /* Southern Qiandong Miao -> Hmong */
+ {"hmw", HB_TAG('H','M','N',' ')}, /* Western Mashan Hmong -> Hmong */
+ {"hmy", HB_TAG('H','M','N',' ')}, /* Southern Guiyang Hmong -> Hmong */
+ {"hmz", HB_TAG('H','M','N',' ')}, /* Hmong Shua -> Hmong */
+/*{"hnd", HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */
+ {"hne", HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */
+ {"hnj", HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */
+ {"hno", HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */
+ {"ho", HB_TAG('H','M','O',' ')}, /* Hiri Motu */
+ {"hoc", HB_TAG('H','O',' ',' ')}, /* Ho */
+ {"hoi", HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */
+ {"hoj", HB_TAG('H','A','R',' ')}, /* Hadothi -> Harauti */
+ {"hr", HB_TAG('H','R','V',' ')}, /* Croatian */
+ {"hrm", HB_TAG('H','M','N',' ')}, /* Horned Miao -> Hmong */
+ {"hsb", HB_TAG('U','S','B',' ')}, /* Upper Sorbian */
+ {"hsn", HB_TAG('Z','H','S',' ')}, /* Xiang Chinese -> Chinese Simplified */
+ {"ht", HB_TAG('H','A','I',' ')}, /* Haitian (Haitian Creole) */
+ {"hu", HB_TAG('H','U','N',' ')}, /* Hungarian */
+ {"huj", HB_TAG('H','M','N',' ')}, /* Northern Guiyang Hmong -> Hmong */
+ {"hup", HB_TAG('A','T','H',' ')}, /* Hupa -> Athapaskan */
+ {"hy", HB_TAG('H','Y','E','0')}, /* Armenian -> Armenian East */
+ {"hy", HB_TAG('H','Y','E',' ')}, /* Armenian */
+ {"hyw", HB_TAG('H','Y','E',' ')}, /* Western Armenian -> Armenian */
+ {"hz", HB_TAG('H','E','R',' ')}, /* Herero */
+ {"ia", HB_TAG('I','N','A',' ')}, /* Interlingua (International Auxiliary Language Association) */
+/*{"iba", HB_TAG('I','B','A',' ')},*/ /* Iban */
+/*{"ibb", HB_TAG('I','B','B',' ')},*/ /* Ibibio */
+ {"id", HB_TAG('I','N','D',' ')}, /* Indonesian */
+ {"ida", HB_TAG('L','U','H',' ')}, /* Idakho-Isukha-Tiriki -> Luyia */
+ {"ie", HB_TAG('I','L','E',' ')}, /* Interlingue */
+ {"ig", HB_TAG('I','B','O',' ')}, /* Igbo */
+ {"igb", HB_TAG('E','B','I',' ')}, /* Ebira */
+ {"ii", HB_TAG('Y','I','M',' ')}, /* Sichuan Yi -> Yi Modern */
+ {"ijc", HB_TAG('I','J','O',' ')}, /* Izon -> Ijo */
+/*{"ijo", HB_TAG('I','J','O',' ')},*/ /* Ijo [family] */
+ {"ik", HB_TAG('I','P','K',' ')}, /* Inupiaq [macrolanguage] -> Inupiat */
+ {"ike", HB_TAG('I','N','U',' ')}, /* Eastern Canadian Inuktitut -> Inuktitut */
+ {"ikt", HB_TAG('I','N','U',' ')}, /* Inuinnaqtun -> Inuktitut */
+/*{"ilo", HB_TAG('I','L','O',' ')},*/ /* Iloko -> Ilokano */
+ {"in", HB_TAG('I','N','D',' ')}, /* Indonesian (retired code) */
+ {"ing", HB_TAG('A','T','H',' ')}, /* Degexit'an -> Athapaskan */
+ {"inh", HB_TAG('I','N','G',' ')}, /* Ingush */
+ {"io", HB_TAG('I','D','O',' ')}, /* Ido */
+ {"is", HB_TAG('I','S','L',' ')}, /* Icelandic */
+ {"it", HB_TAG('I','T','A',' ')}, /* Italian */
+ {"iu", HB_TAG('I','N','U',' ')}, /* Inuktitut [macrolanguage] */
+ {"iw", HB_TAG('I','W','R',' ')}, /* Hebrew (retired code) */
+ {"ja", HB_TAG('J','A','N',' ')}, /* Japanese */
+ {"jak", HB_TAG('M','L','Y',' ')}, /* Jakun -> Malay */
+/*{"jam", HB_TAG('J','A','M',' ')},*/ /* Jamaican Creole English -> Jamaican Creole */
+ {"jax", HB_TAG('M','L','Y',' ')}, /* Jambi Malay -> Malay */
+/*{"jbo", HB_TAG('J','B','O',' ')},*/ /* Lojban */
+/*{"jct", HB_TAG('J','C','T',' ')},*/ /* Krymchak */
+ {"ji", HB_TAG('J','I','I',' ')}, /* Yiddish (retired code) */
+ {"jv", HB_TAG('J','A','V',' ')}, /* Javanese */
+ {"jw", HB_TAG('J','A','V',' ')}, /* Javanese (retired code) */
+ {"ka", HB_TAG('K','A','T',' ')}, /* Georgian */
+ {"kaa", HB_TAG('K','R','K',' ')}, /* Karakalpak */
+ {"kab", HB_TAG('K','A','B','0')}, /* Kabyle */
+ {"kam", HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */
+ {"kar", HB_TAG('K','R','N',' ')}, /* Karen [family] */
+ {"kbd", HB_TAG('K','A','B',' ')}, /* Kabardian */
+ {"kby", HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */
+ {"kca", HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */
+ {"kca", HB_TAG('K','H','S',' ')}, /* Khanty -> Khanty-Shurishkar */
+ {"kca", HB_TAG('K','H','V',' ')}, /* Khanty -> Khanty-Vakhi */
+/*{"kde", HB_TAG('K','D','E',' ')},*/ /* Makonde */
+ {"kdr", HB_TAG('K','R','M',' ')}, /* Karaim */
+ {"kdt", HB_TAG('K','U','Y',' ')}, /* Kuy */
+/*{"kea", HB_TAG('K','E','A',' ')},*/ /* Kabuverdianu (Crioulo) */
+/*{"kek", HB_TAG('K','E','K',' ')},*/ /* Kekchi */
+ {"kex", HB_TAG('K','K','N',' ')}, /* Kukna -> Kokni */
+ {"kfa", HB_TAG('K','O','D',' ')}, /* Kodava -> Kodagu */
+ {"kfr", HB_TAG('K','A','C',' ')}, /* Kachhi -> Kachchi */
+ {"kfx", HB_TAG('K','U','L',' ')}, /* Kullu Pahari -> Kulvi */
+ {"kfy", HB_TAG('K','M','N',' ')}, /* Kumaoni */
+ {"kg", HB_TAG('K','O','N','0')}, /* Kongo [macrolanguage] */
+ {"kha", HB_TAG('K','S','I',' ')}, /* Khasi */
+ {"khb", HB_TAG('X','B','D',' ')}, /* Lü */
+ {"khk", HB_TAG('M','N','G',' ')}, /* Halh Mongolian -> Mongolian */
+ {"kht", HB_TAG('K','H','N',' ')}, /* Khamti -> Khamti Shan (Microsoft fonts) */
+ {"kht", HB_TAG('K','H','T',' ')}, /* Khamti -> Khamti Shan (OpenType spec and SIL fonts) */
+/*{"khw", HB_TAG('K','H','W',' ')},*/ /* Khowar */
+ {"ki", HB_TAG('K','I','K',' ')}, /* Kikuyu (Gikuyu) */
+/*{"kiu", HB_TAG('K','I','U',' ')},*/ /* Kirmanjki */
+ {"kj", HB_TAG('K','U','A',' ')}, /* Kuanyama */
+/*{"kjd", HB_TAG('K','J','D',' ')},*/ /* Southern Kiwai */
+ {"kjh", HB_TAG('K','H','A',' ')}, /* Khakas -> Khakass */
+/*{"kjp", HB_TAG('K','J','P',' ')},*/ /* Pwo Eastern Karen -> Eastern Pwo Karen */
+/*{"kjz", HB_TAG('K','J','Z',' ')},*/ /* Bumthangkha */
+ {"kk", HB_TAG('K','A','Z',' ')}, /* Kazakh */
+ {"kkz", HB_TAG('A','T','H',' ')}, /* Kaska -> Athapaskan */
+ {"kl", HB_TAG('G','R','N',' ')}, /* Greenlandic */
+ {"kln", HB_TAG('K','A','L',' ')}, /* Kalenjin [macrolanguage] */
+ {"km", HB_TAG('K','H','M',' ')}, /* Khmer */
+ {"kmb", HB_TAG('M','B','N',' ')}, /* Kimbundu -> Mbundu */
+ {"kmr", HB_TAG('K','U','R',' ')}, /* Northern Kurdish -> Kurdish */
+ {"kmw", HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */
+/*{"kmz", HB_TAG('K','M','Z',' ')},*/ /* Khorasani Turkish -> Khorasani Turkic */
+ {"kn", HB_TAG('K','A','N',' ')}, /* Kannada */
+ {"knc", HB_TAG('K','N','R',' ')}, /* Central Kanuri -> Kanuri */
+ {"kng", HB_TAG('K','O','N','0')}, /* Koongo -> Kongo */
+ {"knn", HB_TAG('K','O','K',' ')}, /* Konkani */
+ {"ko", HB_TAG('K','O','R',' ')}, /* Korean */
+ {"koi", HB_TAG('K','O','P',' ')}, /* Komi-Permyak */
+/*{"kok", HB_TAG('K','O','K',' ')},*/ /* Konkani [macrolanguage] */
+/*{"kos", HB_TAG('K','O','S',' ')},*/ /* Kosraean */
+ {"koy", HB_TAG('A','T','H',' ')}, /* Koyukon -> Athapaskan */
+ {"kpe", HB_TAG('K','P','L',' ')}, /* Kpelle [macrolanguage] */
+ {"kpv", HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */
+ {"kpy", HB_TAG('K','Y','K',' ')}, /* Koryak */
+ {"kqs", HB_TAG('K','I','S',' ')}, /* Northern Kissi -> Kisii */
+ {"kqy", HB_TAG('K','R','T',' ')}, /* Koorete */
+ {"kr", HB_TAG('K','N','R',' ')}, /* Kanuri [macrolanguage] */
+ {"krc", HB_TAG('K','A','R',' ')}, /* Karachay-Balkar -> Karachay */
+ {"krc", HB_TAG('B','A','L',' ')}, /* Karachay-Balkar -> Balkar */
+/*{"kri", HB_TAG('K','R','I',' ')},*/ /* Krio */
+/*{"krl", HB_TAG('K','R','L',' ')},*/ /* Karelian */
+ {"krt", HB_TAG('K','N','R',' ')}, /* Tumari Kanuri -> Kanuri */
+ {"kru", HB_TAG('K','U','U',' ')}, /* Kurukh */
+ {"ks", HB_TAG('K','S','H',' ')}, /* Kashmiri */
+ {"ksh", HB_TAG('K','S','H','0')}, /* Kölsch -> Ripuarian */
+ {"kss", HB_TAG('K','I','S',' ')}, /* Southern Kisi -> Kisii */
+/*{"ksw", HB_TAG('K','S','W',' ')},*/ /* S’gaw Karen */
+ {"ktb", HB_TAG('K','E','B',' ')}, /* Kambaata -> Kebena */
+ {"ktu", HB_TAG('K','O','N',' ')}, /* Kituba (Democratic Republic of Congo) -> Kikongo */
+ {"ktw", HB_TAG('A','T','H',' ')}, /* Kato -> Athapaskan */
+ {"ku", HB_TAG('K','U','R',' ')}, /* Kurdish [macrolanguage] */
+/*{"kum", HB_TAG('K','U','M',' ')},*/ /* Kumyk */
+ {"kuu", HB_TAG('A','T','H',' ')}, /* Upper Kuskokwim -> Athapaskan */
+ {"kv", HB_TAG('K','O','M',' ')}, /* Komi [macrolanguage] */
+ {"kvb", HB_TAG('M','L','Y',' ')}, /* Kubu -> Malay */
+ {"kvr", HB_TAG('M','L','Y',' ')}, /* Kerinci -> Malay */
+ {"kw", HB_TAG('C','O','R',' ')}, /* Cornish */
+ {"kwy", HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */
+ {"kxc", HB_TAG('K','M','S',' ')}, /* Konso -> Komso */
+ {"kxd", HB_TAG('M','L','Y',' ')}, /* Brunei -> Malay */
+ {"kxl", HB_TAG('K','U','U',' ')}, /* Nepali Kurux (retired code) -> Kurukh */
+ {"kxu", HB_TAG('K','U','I',' ')}, /* Kui (India) (retired code) */
+ {"ky", HB_TAG('K','I','R',' ')}, /* Kirghiz (Kyrgyz) */
+/*{"kyu", HB_TAG('K','Y','U',' ')},*/ /* Western Kayah */
+ {"la", HB_TAG('L','A','T',' ')}, /* Latin */
+ {"lad", HB_TAG('J','U','D',' ')}, /* Ladino */
+ {"lb", HB_TAG('L','T','Z',' ')}, /* Luxembourgish */
+ {"lbe", HB_TAG('L','A','K',' ')}, /* Lak */
+ {"lbj", HB_TAG('L','D','K',' ')}, /* Ladakhi */
+ {"lbl", HB_TAG('B','I','K',' ')}, /* Libon Bikol -> Bikol */
+ {"lce", HB_TAG('M','L','Y',' ')}, /* Loncong -> Malay */
+ {"lcf", HB_TAG('M','L','Y',' ')}, /* Lubu -> Malay */
+ {"ldi", HB_TAG('K','O','N','0')}, /* Laari -> Kongo */
+/*{"lez", HB_TAG('L','E','Z',' ')},*/ /* Lezghian -> Lezgi */
+ {"lg", HB_TAG('L','U','G',' ')}, /* Ganda */
+ {"li", HB_TAG('L','I','M',' ')}, /* Limburgish */
+ {"lif", HB_TAG('L','M','B',' ')}, /* Limbu */
+/*{"lij", HB_TAG('L','I','J',' ')},*/ /* Ligurian */
+/*{"lis", HB_TAG('L','I','S',' ')},*/ /* Lisu */
+ {"liw", HB_TAG('M','L','Y',' ')}, /* Col -> Malay */
+/*{"ljp", HB_TAG('L','J','P',' ')},*/ /* Lampung Api -> Lampung */
+ {"lkb", HB_TAG('L','U','H',' ')}, /* Kabras -> Luyia */
+/*{"lki", HB_TAG('L','K','I',' ')},*/ /* Laki */
+ {"lko", HB_TAG('L','U','H',' ')}, /* Khayo -> Luyia */
+ {"lks", HB_TAG('L','U','H',' ')}, /* Kisa -> Luyia */
+ {"lld", HB_TAG('L','A','D',' ')}, /* Ladin */
+ {"lmn", HB_TAG('L','A','M',' ')}, /* Lambadi -> Lambani */
+/*{"lmo", HB_TAG('L','M','O',' ')},*/ /* Lombard */
+ {"ln", HB_TAG('L','I','N',' ')}, /* Lingala */
+ {"lo", HB_TAG('L','A','O',' ')}, /* Lao */
+/*{"lom", HB_TAG('L','O','M',' ')},*/ /* Loma (Liberia) */
+/*{"lrc", HB_TAG('L','R','C',' ')},*/ /* Northern Luri -> Luri */
+ {"lri", HB_TAG('L','U','H',' ')}, /* Marachi -> Luyia */
+ {"lrm", HB_TAG('L','U','H',' ')}, /* Marama -> Luyia */
+ {"lsm", HB_TAG('L','U','H',' ')}, /* Saamia -> Luyia */
+ {"lt", HB_TAG('L','T','H',' ')}, /* Lithuanian */
+ {"ltg", HB_TAG('L','V','I',' ')}, /* Latgalian -> Latvian */
+ {"lto", HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */
+ {"lts", HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */
+ {"lu", HB_TAG('L','U','B',' ')}, /* Luba-Katanga */
+/*{"lua", HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */
+/*{"luo", HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */
+ {"lus", HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */
+ {"luy", HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */
+ {"luz", HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */
+ {"lv", HB_TAG('L','V','I',' ')}, /* Latvian [macrolanguage] */
+ {"lvs", HB_TAG('L','V','I',' ')}, /* Standard Latvian -> Latvian */
+ {"lwg", HB_TAG('L','U','H',' ')}, /* Wanga -> Luyia */
+ {"lzh", HB_TAG('Z','H','T',' ')}, /* Literary Chinese -> Chinese Traditional */
+ {"lzz", HB_TAG('L','A','Z',' ')}, /* Laz */
+/*{"mad", HB_TAG('M','A','D',' ')},*/ /* Madurese -> Madura */
+/*{"mag", HB_TAG('M','A','G',' ')},*/ /* Magahi */
+ {"mai", HB_TAG('M','T','H',' ')}, /* Maithili */
+ {"mak", HB_TAG('M','K','R',' ')}, /* Makasar */
+/*{"mam", HB_TAG('M','A','M',' ')},*/ /* Mam */
+ {"man", HB_TAG('M','N','K',' ')}, /* Mandingo [macrolanguage] -> Maninka */
+ {"max", HB_TAG('M','L','Y',' ')}, /* North Moluccan Malay -> Malay */
+/*{"mbo", HB_TAG('M','B','O',' ')},*/ /* Mbo (Cameroon) */
+ {"mct", HB_TAG('B','T','I',' ')}, /* Mengisa -> Beti */
+ {"mdf", HB_TAG('M','O','K',' ')}, /* Moksha */
+/*{"mdr", HB_TAG('M','D','R',' ')},*/ /* Mandar */
+ {"mdy", HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */
+ {"men", HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */
+ {"meo", HB_TAG('M','L','Y',' ')}, /* Kedah Malay -> Malay */
+/*{"mer", HB_TAG('M','E','R',' ')},*/ /* Meru */
+/*{"mfa", HB_TAG('M','F','A',' ')},*/ /* Pattani Malay */
+ {"mfb", HB_TAG('M','L','Y',' ')}, /* Bangka -> Malay */
+/*{"mfe", HB_TAG('M','F','E',' ')},*/ /* Morisyen */
+ {"mg", HB_TAG('M','L','G',' ')}, /* Malagasy [macrolanguage] */
+ {"mh", HB_TAG('M','A','H',' ')}, /* Marshallese */
+ {"mhr", HB_TAG('L','M','A',' ')}, /* Eastern Mari -> Low Mari */
+ {"mhv", HB_TAG('A','R','K',' ')}, /* Arakanese (retired code) -> Rakhine */
+ {"mi", HB_TAG('M','R','I',' ')}, /* Maori */
+/*{"min", HB_TAG('M','I','N',' ')},*/ /* Minangkabau */
+ {"mk", HB_TAG('M','K','D',' ')}, /* Macedonian */
+ {"mku", HB_TAG('M','N','K',' ')}, /* Konyanka Maninka -> Maninka */
+/*{"mkw", HB_TAG('M','K','W',' ')},*/ /* Kituba (Congo) */
+ {"ml", HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */
+ {"ml", HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */
+ {"mlq", HB_TAG('M','L','N',' ')}, /* Western Maninkakan -> Malinke */
+ {"mlq", HB_TAG('M','N','K',' ')}, /* Western Maninkakan -> Maninka */
+ {"mmr", HB_TAG('H','M','N',' ')}, /* Western Xiangxi Miao -> Hmong */
+ {"mn", HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */
+ {"mnc", HB_TAG('M','C','H',' ')}, /* Manchu */
+/*{"mni", HB_TAG('M','N','I',' ')},*/ /* Manipuri */
+ {"mnk", HB_TAG('M','N','D',' ')}, /* Mandinka */
+ {"mnk", HB_TAG('M','N','K',' ')}, /* Mandinka -> Maninka */
+ {"mnp", HB_TAG('Z','H','S',' ')}, /* Min Bei Chinese -> Chinese Simplified */
+ {"mns", HB_TAG('M','A','N',' ')}, /* Mansi */
+ {"mnw", HB_TAG('M','O','N',' ')}, /* Mon */
+ {"mo", HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */
+/*{"moh", HB_TAG('M','O','H',' ')},*/ /* Mohawk */
+/*{"mos", HB_TAG('M','O','S',' ')},*/ /* Mossi */
+ {"mpe", HB_TAG('M','A','J',' ')}, /* Majang */
+ {"mqg", HB_TAG('M','L','Y',' ')}, /* Kota Bangun Kutai Malay -> Malay */
+ {"mr", HB_TAG('M','A','R',' ')}, /* Marathi */
+ {"mrh", HB_TAG('Q','I','N',' ')}, /* Mara Chin -> Chin */
+ {"mrj", HB_TAG('H','M','A',' ')}, /* Western Mari -> High Mari */
+ {"ms", HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */
+ {"msc", HB_TAG('M','N','K',' ')}, /* Sankaran Maninka -> Maninka */
+ {"msh", HB_TAG('M','L','G',' ')}, /* Masikoro Malagasy -> Malagasy */
+ {"msi", HB_TAG('M','L','Y',' ')}, /* Sabah Malay -> Malay */
+ {"mt", HB_TAG('M','T','S',' ')}, /* Maltese */
+ {"mtr", HB_TAG('M','A','W',' ')}, /* Mewari -> Marwari */
+ {"mui", HB_TAG('M','L','Y',' ')}, /* Musi -> Malay */
+ {"mup", HB_TAG('R','A','J',' ')}, /* Malvi -> Rajasthani */
+ {"muq", HB_TAG('H','M','N',' ')}, /* Eastern Xiangxi Miao -> Hmong */
+/*{"mus", HB_TAG('M','U','S',' ')},*/ /* Creek -> Muscogee */
+ {"mvb", HB_TAG('A','T','H',' ')}, /* Mattole -> Athapaskan */
+ {"mve", HB_TAG('M','A','W',' ')}, /* Marwari (Pakistan) */
+ {"mvf", HB_TAG('M','N','G',' ')}, /* Peripheral Mongolian -> Mongolian */
+ {"mwk", HB_TAG('M','N','K',' ')}, /* Kita Maninkakan -> Maninka */
+/*{"mwl", HB_TAG('M','W','L',' ')},*/ /* Mirandese */
+ {"mwr", HB_TAG('M','A','W',' ')}, /* Marwari [macrolanguage] */
+/*{"mww", HB_TAG('M','W','W',' ')},*/ /* Hmong Daw */
+ {"my", HB_TAG('B','R','M',' ')}, /* Burmese */
+ {"mym", HB_TAG('M','E','N',' ')}, /* Me’en */
+/*{"myn", HB_TAG('M','Y','N',' ')},*/ /* Mayan [family] */
+ {"myq", HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) -> Maninka */
+ {"myv", HB_TAG('E','R','Z',' ')}, /* Erzya */
+/*{"mzn", HB_TAG('M','Z','N',' ')},*/ /* Mazanderani */
+ {"na", HB_TAG('N','A','U',' ')}, /* Nauru -> Nauruan */
+/*{"nag", HB_TAG('N','A','G',' ')},*/ /* Naga Pidgin -> Naga-Assamese */
+/*{"nah", HB_TAG('N','A','H',' ')},*/ /* Nahuatl [family] */
+ {"nan", HB_TAG('Z','H','S',' ')}, /* Min Nan Chinese -> Chinese Simplified */
+/*{"nap", HB_TAG('N','A','P',' ')},*/ /* Neapolitan */
+ {"nb", HB_TAG('N','O','R',' ')}, /* Norwegian Bokmål -> Norwegian */
+ {"nd", HB_TAG('N','D','B',' ')}, /* North Ndebele -> Ndebele */
+/*{"ndc", HB_TAG('N','D','C',' ')},*/ /* Ndau */
+/*{"nds", HB_TAG('N','D','S',' ')},*/ /* Low Saxon */
+ {"ne", HB_TAG('N','E','P',' ')}, /* Nepali [macrolanguage] */
+/*{"new", HB_TAG('N','E','W',' ')},*/ /* Newari */
+ {"ng", HB_TAG('N','D','G',' ')}, /* Ndonga */
+/*{"nga", HB_TAG('N','G','A',' ')},*/ /* Ngbaka */
+ {"ngl", HB_TAG('L','M','W',' ')}, /* Lomwe */
+ {"ngo", HB_TAG('S','X','T',' ')}, /* Ngoni -> Sutu */
+ {"nhd", HB_TAG('G','U','A',' ')}, /* Chiripá -> Guarani */
+ {"niq", HB_TAG('K','A','L',' ')}, /* Nandi -> Kalenjin */
+/*{"niu", HB_TAG('N','I','U',' ')},*/ /* Niuean */
+ {"niv", HB_TAG('G','I','L',' ')}, /* Gilyak */
+ {"njz", HB_TAG('N','I','S',' ')}, /* Nyishi -> Nisi */
+ {"nl", HB_TAG('N','L','D',' ')}, /* Dutch */
+ {"nle", HB_TAG('L','U','H',' ')}, /* East Nyala -> Luyia */
+ {"nn", HB_TAG('N','Y','N',' ')}, /* Norwegian Nynorsk (Nynorsk, Norwegian) */
+ {"no", HB_TAG('N','O','R',' ')}, /* Norwegian [macrolanguage] */
+ {"nod", HB_TAG('N','T','A',' ')}, /* Northern Thai -> Northern Tai */
+/*{"noe", HB_TAG('N','O','E',' ')},*/ /* Nimadi */
+/*{"nog", HB_TAG('N','O','G',' ')},*/ /* Nogai */
+/*{"nov", HB_TAG('N','O','V',' ')},*/ /* Novial */
+ {"npi", HB_TAG('N','E','P',' ')}, /* Nepali */
+ {"nqo", HB_TAG('N','K','O',' ')}, /* N’Ko */
+ {"nr", HB_TAG('N','D','B',' ')}, /* South Ndebele -> Ndebele */
+ {"nsk", HB_TAG('N','A','S',' ')}, /* Naskapi */
+/*{"nso", HB_TAG('N','S','O',' ')},*/ /* Pedi -> Sotho, Northern */
+ {"nv", HB_TAG('N','A','V',' ')}, /* Navajo */
+ {"nv", HB_TAG('A','T','H',' ')}, /* Navajo -> Athapaskan */
+ {"ny", HB_TAG('C','H','I',' ')}, /* Chichewa (Chewa, Nyanja) */
+ {"nyd", HB_TAG('L','U','H',' ')}, /* Nyore -> Luyia */
+/*{"nym", HB_TAG('N','Y','M',' ')},*/ /* Nyamwezi */
+ {"nyn", HB_TAG('N','K','L',' ')}, /* Nyankole */
+/*{"nza", HB_TAG('N','Z','A',' ')},*/ /* Tigon Mbembe -> Mbembe Tigon */
+ {"oc", HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */
+ {"oj", HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */
+/*{"ojb", HB_TAG('O','J','B',' ')},*/ /* Northwestern Ojibwa -> Ojibway */
+ {"ojc", HB_TAG('O','J','B',' ')}, /* Central Ojibwa -> Ojibway */
+ {"ojg", HB_TAG('O','J','B',' ')}, /* Eastern Ojibwa -> Ojibway */
+ {"ojs", HB_TAG('O','C','R',' ')}, /* Severn Ojibwa -> Oji-Cree */
+ {"ojw", HB_TAG('O','J','B',' ')}, /* Western Ojibwa -> Ojibway */
+ {"oki", HB_TAG('K','A','L',' ')}, /* Okiek -> Kalenjin */
+ {"okm", HB_TAG('K','O','H',' ')}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */
+ {"om", HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */
+ {"or", HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */
+ {"orc", HB_TAG('O','R','O',' ')}, /* Orma -> Oromo */
+ {"orn", HB_TAG('M','L','Y',' ')}, /* Orang Kanaq -> Malay */
+ {"ors", HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */
+ {"ory", HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */
+ {"os", HB_TAG('O','S','S',' ')}, /* Ossetian */
+ {"otw", HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */
+ {"pa", HB_TAG('P','A','N',' ')}, /* Punjabi */
+/*{"pag", HB_TAG('P','A','G',' ')},*/ /* Pangasinan */
+/*{"pam", HB_TAG('P','A','M',' ')},*/ /* Pampanga -> Pampangan */
+ {"pap", HB_TAG('P','A','P','0')}, /* Papiamento -> Papiamentu */
+/*{"pau", HB_TAG('P','A','U',' ')},*/ /* Palauan */
+ {"pbt", HB_TAG('P','A','S',' ')}, /* Southern Pashto -> Pashto */
+ {"pbu", HB_TAG('P','A','S',' ')}, /* Northern Pashto -> Pashto */
+/*{"pcc", HB_TAG('P','C','C',' ')},*/ /* Bouyei */
+/*{"pcd", HB_TAG('P','C','D',' ')},*/ /* Picard */
+ {"pce", HB_TAG('P','L','G',' ')}, /* Ruching Palaung -> Palaung */
+ {"pck", HB_TAG('Q','I','N',' ')}, /* Paite Chin -> Chin */
+/*{"pdc", HB_TAG('P','D','C',' ')},*/ /* Pennsylvania German */
+ {"pel", HB_TAG('M','L','Y',' ')}, /* Pekal -> Malay */
+ {"pes", HB_TAG('F','A','R',' ')}, /* Iranian Persian -> Persian */
+ {"pga", HB_TAG('A','R','A',' ')}, /* Sudanese Creole Arabic -> Arabic */
+/*{"phk", HB_TAG('P','H','K',' ')},*/ /* Phake */
+ {"pi", HB_TAG('P','A','L',' ')}, /* Pali */
+/*{"pih", HB_TAG('P','I','H',' ')},*/ /* Pitcairn-Norfolk -> Norfolk */
+ {"pko", HB_TAG('K','A','L',' ')}, /* Pökoot -> Kalenjin */
+ {"pl", HB_TAG('P','L','K',' ')}, /* Polish */
+ {"pll", HB_TAG('P','L','G',' ')}, /* Shwe Palaung -> Palaung */
+ {"plp", HB_TAG('P','A','P',' ')}, /* Palpa (retired code) */
+ {"plt", HB_TAG('M','L','G',' ')}, /* Plateau Malagasy -> Malagasy */
+/*{"pms", HB_TAG('P','M','S',' ')},*/ /* Piemontese */
+/*{"pnb", HB_TAG('P','N','B',' ')},*/ /* Western Panjabi */
+/*{"poh", HB_TAG('P','O','H',' ')},*/ /* Poqomchi' -> Pocomchi */
+/*{"pon", HB_TAG('P','O','N',' ')},*/ /* Pohnpeian */
+ {"ppa", HB_TAG('B','A','G',' ')}, /* Pao (retired code) -> Baghelkhandi */
+/*{"pro", HB_TAG('P','R','O',' ')},*/ /* Old Provençal (to 1500) -> Provençal / Old Provençal */
+ {"prs", HB_TAG('D','R','I',' ')}, /* Dari */
+ {"ps", HB_TAG('P','A','S',' ')}, /* Pashto [macrolanguage] */
+ {"pse", HB_TAG('M','L','Y',' ')}, /* Central Malay -> Malay */
+ {"pst", HB_TAG('P','A','S',' ')}, /* Central Pashto -> Pashto */
+ {"pt", HB_TAG('P','T','G',' ')}, /* Portuguese */
+/*{"pwo", HB_TAG('P','W','O',' ')},*/ /* Pwo Western Karen -> Western Pwo Karen */
+ {"qu", HB_TAG('Q','U','Z',' ')}, /* Quechua [macrolanguage] */
+ {"qub", HB_TAG('Q','W','H',' ')}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */
+/*{"quc", HB_TAG('Q','U','C',' ')},*/ /* K’iche’ */
+ {"qud", HB_TAG('Q','V','I',' ')}, /* Calderón Highland Quichua -> Quechua (Ecuador) */
+ {"quf", HB_TAG('Q','U','Z',' ')}, /* Lambayeque Quechua -> Quechua */
+ {"qug", HB_TAG('Q','V','I',' ')}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */
+/*{"quh", HB_TAG('Q','U','H',' ')},*/ /* South Bolivian Quechua -> Quechua (Bolivia) */
+ {"quk", HB_TAG('Q','U','Z',' ')}, /* Chachapoyas Quechua -> Quechua */
+ {"qul", HB_TAG('Q','U','Z',' ')}, /* North Bolivian Quechua -> Quechua */
+ {"qup", HB_TAG('Q','V','I',' ')}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */
+ {"qur", HB_TAG('Q','W','H',' ')}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */
+ {"qus", HB_TAG('Q','U','H',' ')}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */
+ {"quw", HB_TAG('Q','V','I',' ')}, /* Tena Lowland Quichua -> Quechua (Ecuador) */
+ {"qux", HB_TAG('Q','W','H',' ')}, /* Yauyos Quechua -> Quechua (Peru) */
+ {"quy", HB_TAG('Q','U','Z',' ')}, /* Ayacucho Quechua -> Quechua */
+/*{"quz", HB_TAG('Q','U','Z',' ')},*/ /* Cusco Quechua -> Quechua */
+ {"qva", HB_TAG('Q','W','H',' ')}, /* Ambo-Pasco Quechua -> Quechua (Peru) */
+ {"qvc", HB_TAG('Q','U','Z',' ')}, /* Cajamarca Quechua -> Quechua */
+ {"qve", HB_TAG('Q','U','Z',' ')}, /* Eastern Apurímac Quechua -> Quechua */
+ {"qvh", HB_TAG('Q','W','H',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */
+/*{"qvi", HB_TAG('Q','V','I',' ')},*/ /* Imbabura Highland Quichua -> Quechua (Ecuador) */
+ {"qvj", HB_TAG('Q','V','I',' ')}, /* Loja Highland Quichua -> Quechua (Ecuador) */
+ {"qvl", HB_TAG('Q','W','H',' ')}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */
+ {"qvm", HB_TAG('Q','W','H',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */
+ {"qvn", HB_TAG('Q','W','H',' ')}, /* North Junín Quechua -> Quechua (Peru) */
+ {"qvo", HB_TAG('Q','V','I',' ')}, /* Napo Lowland Quechua -> Quechua (Ecuador) */
+ {"qvp", HB_TAG('Q','W','H',' ')}, /* Pacaraos Quechua -> Quechua (Peru) */
+ {"qvs", HB_TAG('Q','U','Z',' ')}, /* San Martín Quechua -> Quechua */
+ {"qvw", HB_TAG('Q','W','H',' ')}, /* Huaylla Wanca Quechua -> Quechua (Peru) */
+ {"qvz", HB_TAG('Q','V','I',' ')}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */
+ {"qwa", HB_TAG('Q','W','H',' ')}, /* Corongo Ancash Quechua -> Quechua (Peru) */
+ {"qwc", HB_TAG('Q','U','Z',' ')}, /* Classical Quechua -> Quechua */
+/*{"qwh", HB_TAG('Q','W','H',' ')},*/ /* Huaylas Ancash Quechua -> Quechua (Peru) */
+ {"qws", HB_TAG('Q','W','H',' ')}, /* Sihuas Ancash Quechua -> Quechua (Peru) */
+ {"qxa", HB_TAG('Q','W','H',' ')}, /* Chiquián Ancash Quechua -> Quechua (Peru) */
+ {"qxc", HB_TAG('Q','W','H',' ')}, /* Chincha Quechua -> Quechua (Peru) */
+ {"qxh", HB_TAG('Q','W','H',' ')}, /* Panao Huánuco Quechua -> Quechua (Peru) */
+ {"qxl", HB_TAG('Q','V','I',' ')}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */
+ {"qxn", HB_TAG('Q','W','H',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */
+ {"qxo", HB_TAG('Q','W','H',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */
+ {"qxp", HB_TAG('Q','U','Z',' ')}, /* Puno Quechua -> Quechua */
+ {"qxr", HB_TAG('Q','V','I',' ')}, /* Cañar Highland Quichua -> Quechua (Ecuador) */
+ {"qxt", HB_TAG('Q','W','H',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */
+ {"qxu", HB_TAG('Q','U','Z',' ')}, /* Arequipa-La Unión Quechua -> Quechua */
+ {"qxw", HB_TAG('Q','W','H',' ')}, /* Jauja Wanca Quechua -> Quechua (Peru) */
+ {"rag", HB_TAG('L','U','H',' ')}, /* Logooli -> Luyia */
+/*{"raj", HB_TAG('R','A','J',' ')},*/ /* Rajasthani [macrolanguage] */
+/*{"rar", HB_TAG('R','A','R',' ')},*/ /* Rarotongan */
+ {"rbb", HB_TAG('P','L','G',' ')}, /* Rumai Palaung -> Palaung */
+ {"rbl", HB_TAG('B','I','K',' ')}, /* Miraya Bikol -> Bikol */
+/*{"rej", HB_TAG('R','E','J',' ')},*/ /* Rejang */
+/*{"ria", HB_TAG('R','I','A',' ')},*/ /* Riang (India) */
+/*{"rif", HB_TAG('R','I','F',' ')},*/ /* Tarifit */
+/*{"rit", HB_TAG('R','I','T',' ')},*/ /* Ritharrngu -> Ritarungo */
+ {"rki", HB_TAG('A','R','K',' ')}, /* Rakhine */
+/*{"rkw", HB_TAG('R','K','W',' ')},*/ /* Arakwal */
+ {"rm", HB_TAG('R','M','S',' ')}, /* Romansh */
+ {"rmc", HB_TAG('R','O','Y',' ')}, /* Carpathian Romani -> Romany */
+ {"rmf", HB_TAG('R','O','Y',' ')}, /* Kalo Finnish Romani -> Romany */
+ {"rml", HB_TAG('R','O','Y',' ')}, /* Baltic Romani -> Romany */
+ {"rmn", HB_TAG('R','O','Y',' ')}, /* Balkan Romani -> Romany */
+ {"rmo", HB_TAG('R','O','Y',' ')}, /* Sinte Romani -> Romany */
+ {"rmw", HB_TAG('R','O','Y',' ')}, /* Welsh Romani -> Romany */
+/*{"rmy", HB_TAG('R','M','Y',' ')},*/ /* Vlax Romani */
+ {"rmz", HB_TAG('A','R','K',' ')}, /* Marma -> Rakhine */
+ {"rn", HB_TAG('R','U','N',' ')}, /* Rundi */
+ {"rnl", HB_TAG('H','A','L',' ')}, /* Ranglong -> Halam (Falam Chin) */
+ {"ro", HB_TAG('R','O','M',' ')}, /* Romanian */
+ {"rom", HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */
+/*{"rtm", HB_TAG('R','T','M',' ')},*/ /* Rotuman */
+ {"ru", HB_TAG('R','U','S',' ')}, /* Russian */
+ {"rue", HB_TAG('R','S','Y',' ')}, /* Rusyn */
+/*{"rup", HB_TAG('R','U','P',' ')},*/ /* Aromanian */
+ {"rw", HB_TAG('R','U','A',' ')}, /* Kinyarwanda */
+ {"rwr", HB_TAG('M','A','W',' ')}, /* Marwari (India) */
+ {"sa", HB_TAG('S','A','N',' ')}, /* Sanskrit */
+ {"sah", HB_TAG('Y','A','K',' ')}, /* Yakut -> Sakha */
+ {"sam", HB_TAG('P','A','A',' ')}, /* Samaritan Aramaic -> Palestinian Aramaic */
+/*{"sas", HB_TAG('S','A','S',' ')},*/ /* Sasak */
+/*{"sat", HB_TAG('S','A','T',' ')},*/ /* Santali */
+ {"sc", HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */
+ {"sck", HB_TAG('S','A','D',' ')}, /* Sadri */
+/*{"scn", HB_TAG('S','C','N',' ')},*/ /* Sicilian */
+/*{"sco", HB_TAG('S','C','O',' ')},*/ /* Scots */
+ {"scs", HB_TAG('S','C','S',' ')}, /* North Slavey */
+ {"scs", HB_TAG('S','L','A',' ')}, /* North Slavey -> Slavey */
+ {"scs", HB_TAG('A','T','H',' ')}, /* North Slavey -> Athapaskan */
+ {"sd", HB_TAG('S','N','D',' ')}, /* Sindhi */
+ {"sdc", HB_TAG('S','R','D',' ')}, /* Sassarese Sardinian -> Sardinian */
+ {"sdh", HB_TAG('K','U','R',' ')}, /* Southern Kurdish -> Kurdish */
+ {"sdn", HB_TAG('S','R','D',' ')}, /* Gallurese Sardinian -> Sardinian */
+ {"se", HB_TAG('N','S','M',' ')}, /* Northern Sami */
+ {"seh", HB_TAG('S','N','A',' ')}, /* Sena */
+ {"sek", HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */
+/*{"sel", HB_TAG('S','E','L',' ')},*/ /* Selkup */
+ {"sez", HB_TAG('Q','I','N',' ')}, /* Senthang Chin -> Chin */
+ {"sfm", HB_TAG('H','M','N',' ')}, /* Small Flowery Miao -> Hmong */
+ {"sg", HB_TAG('S','G','O',' ')}, /* Sango */
+/*{"sga", HB_TAG('S','G','A',' ')},*/ /* Old Irish (to 900) */
+ {"sgc", HB_TAG('K','A','L',' ')}, /* Kipsigis -> Kalenjin */
+/*{"sgs", HB_TAG('S','G','S',' ')},*/ /* Samogitian */
+ {"sgw", HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage -> Chaha Gurage */
+ {"sgw", HB_TAG('S','G','W',' ')}, /* Sebat Bet Gurage -> Chaha Gurage (SIL fonts) */
+/*{"shi", HB_TAG('S','H','I',' ')},*/ /* Tachelhit */
+/*{"shn", HB_TAG('S','H','N',' ')},*/ /* Shan */
+ {"shu", HB_TAG('A','R','A',' ')}, /* Chadian Arabic -> Arabic */
+ {"si", HB_TAG('S','N','H',' ')}, /* Sinhala (Sinhalese) */
+/*{"sid", HB_TAG('S','I','D',' ')},*/ /* Sidamo */
+ {"sjd", HB_TAG('K','S','M',' ')}, /* Kildin Sami */
+ {"sjo", HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */
+ {"sk", HB_TAG('S','K','Y',' ')}, /* Slovak */
+ {"skg", HB_TAG('M','L','G',' ')}, /* Sakalava Malagasy -> Malagasy */
+ {"skr", HB_TAG('S','R','K',' ')}, /* Saraiki */
+ {"sl", HB_TAG('S','L','V',' ')}, /* Slovenian */
+ {"sm", HB_TAG('S','M','O',' ')}, /* Samoan */
+ {"sma", HB_TAG('S','S','M',' ')}, /* Southern Sami */
+ {"smj", HB_TAG('L','S','M',' ')}, /* Lule Sami */
+ {"smn", HB_TAG('I','S','M',' ')}, /* Inari Sami */
+ {"sms", HB_TAG('S','K','S',' ')}, /* Skolt Sami */
+ {"sn", HB_TAG('S','N','A','0')}, /* Shona */
+/*{"snk", HB_TAG('S','N','K',' ')},*/ /* Soninke */
+ {"so", HB_TAG('S','M','L',' ')}, /* Somali */
+/*{"sop", HB_TAG('S','O','P',' ')},*/ /* Songe */
+ {"spv", HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */
+ {"spy", HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */
+ {"sq", HB_TAG('S','Q','I',' ')}, /* Albanian [macrolanguage] */
+ {"sr", HB_TAG('S','R','B',' ')}, /* Serbian */
+ {"src", HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */
+ {"sro", HB_TAG('S','R','D',' ')}, /* Campidanese Sardinian -> Sardinian */
+/*{"srr", HB_TAG('S','R','R',' ')},*/ /* Serer */
+ {"srs", HB_TAG('A','T','H',' ')}, /* Sarsi -> Athapaskan */
+ {"ss", HB_TAG('S','W','Z',' ')}, /* Swati */
+ {"ssh", HB_TAG('A','R','A',' ')}, /* Shihhi Arabic -> Arabic */
+ {"st", HB_TAG('S','O','T',' ')}, /* Southern Sotho -> Sotho, Southern */
+/*{"stq", HB_TAG('S','T','Q',' ')},*/ /* Saterfriesisch -> Saterland Frisian */
+ {"stv", HB_TAG('S','I','G',' ')}, /* Silt'e -> Silte Gurage */
+ {"su", HB_TAG('S','U','N',' ')}, /* Sundanese */
+/*{"suk", HB_TAG('S','U','K',' ')},*/ /* Sukuma */
+ {"suq", HB_TAG('S','U','R',' ')}, /* Suri */
+ {"sv", HB_TAG('S','V','E',' ')}, /* Swedish */
+/*{"sva", HB_TAG('S','V','A',' ')},*/ /* Svan */
+ {"sw", HB_TAG('S','W','K',' ')}, /* Swahili [macrolanguage] */
+ {"swb", HB_TAG('C','M','R',' ')}, /* Maore Comorian -> Comorian */
+ {"swc", HB_TAG('S','W','K',' ')}, /* Congo Swahili -> Swahili */
+ {"swh", HB_TAG('S','W','K',' ')}, /* Swahili */
+ {"swv", HB_TAG('M','A','W',' ')}, /* Shekhawati -> Marwari */
+/*{"sxu", HB_TAG('S','X','U',' ')},*/ /* Upper Saxon */
+ {"syc", HB_TAG('S','Y','R',' ')}, /* Classical Syriac -> Syriac */
+/*{"syl", HB_TAG('S','Y','L',' ')},*/ /* Sylheti */
+/*{"syr", HB_TAG('S','Y','R',' ')},*/ /* Syriac [macrolanguage] */
+/*{"szl", HB_TAG('S','Z','L',' ')},*/ /* Silesian */
+ {"ta", HB_TAG('T','A','M',' ')}, /* Tamil */
+ {"taa", HB_TAG('A','T','H',' ')}, /* Lower Tanana -> Athapaskan */
+/*{"tab", HB_TAG('T','A','B',' ')},*/ /* Tabassaran -> Tabasaran */
+ {"taq", HB_TAG('T','M','H',' ')}, /* Tamasheq -> Tamashek */
+ {"tau", HB_TAG('A','T','H',' ')}, /* Upper Tanana -> Athapaskan */
+ {"tcb", HB_TAG('A','T','H',' ')}, /* Tanacross -> Athapaskan */
+ {"tce", HB_TAG('A','T','H',' ')}, /* Southern Tutchone -> Athapaskan */
+ {"tcp", HB_TAG('Q','I','N',' ')}, /* Tawr Chin -> Chin */
+ {"tcy", HB_TAG('T','U','L',' ')}, /* Tulu -> Tumbuka */
+ {"tcz", HB_TAG('Q','I','N',' ')}, /* Thado Chin -> Chin */
+/*{"tdd", HB_TAG('T','D','D',' ')},*/ /* Tai Nüa -> Dehong Dai */
+ {"tdx", HB_TAG('M','L','G',' ')}, /* Tandroy-Mahafaly Malagasy -> Malagasy */
+ {"te", HB_TAG('T','E','L',' ')}, /* Telugu */
+ {"tec", HB_TAG('K','A','L',' ')}, /* Terik -> Kalenjin */
+ {"tem", HB_TAG('T','M','N',' ')}, /* Timne -> Temne */
+/*{"tet", HB_TAG('T','E','T',' ')},*/ /* Tetum */
+ {"tfn", HB_TAG('A','T','H',' ')}, /* Tanaina -> Athapaskan */
+ {"tg", HB_TAG('T','A','J',' ')}, /* Tajik -> Tajiki */
+ {"tgj", HB_TAG('N','I','S',' ')}, /* Tagin -> Nisi */
+ {"tgx", HB_TAG('A','T','H',' ')}, /* Tagish -> Athapaskan */
+ {"th", HB_TAG('T','H','A',' ')}, /* Thai */
+ {"tht", HB_TAG('A','T','H',' ')}, /* Tahltan -> Athapaskan */
+ {"thv", HB_TAG('T','M','H',' ')}, /* Tahaggart Tamahaq -> Tamashek */
+ {"thz", HB_TAG('T','M','H',' ')}, /* Tayart Tamajeq -> Tamashek */
+ {"ti", HB_TAG('T','G','Y',' ')}, /* Tigrinya */
+ {"tig", HB_TAG('T','G','R',' ')}, /* Tigre */
+/*{"tiv", HB_TAG('T','I','V',' ')},*/ /* Tiv */
+ {"tk", HB_TAG('T','K','M',' ')}, /* Turkmen */
+ {"tkg", HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */
+ {"tl", HB_TAG('T','G','L',' ')}, /* Tagalog */
+/*{"tmh", HB_TAG('T','M','H',' ')},*/ /* Tamashek [macrolanguage] */
+ {"tmw", HB_TAG('M','L','Y',' ')}, /* Temuan -> Malay */
+ {"tn", HB_TAG('T','N','A',' ')}, /* Tswana */
+ {"tnf", HB_TAG('D','R','I',' ')}, /* Tangshewi (retired code) -> Dari */
+ {"to", HB_TAG('T','G','N',' ')}, /* Tonga (Tonga Islands) -> Tongan */
+ {"tod", HB_TAG('T','O','D','0')}, /* Toma */
+ {"toi", HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */
+ {"tol", HB_TAG('A','T','H',' ')}, /* Tolowa -> Athapaskan */
+/*{"tpi", HB_TAG('T','P','I',' ')},*/ /* Tok Pisin */
+ {"tr", HB_TAG('T','R','K',' ')}, /* Turkish */
+ {"tru", HB_TAG('T','U','A',' ')}, /* Turoyo -> Turoyo Aramaic */
+ {"tru", HB_TAG('S','Y','R',' ')}, /* Turoyo -> Syriac */
+ {"ts", HB_TAG('T','S','G',' ')}, /* Tsonga */
+/*{"tsj", HB_TAG('T','S','J',' ')},*/ /* Tshangla */
+ {"tt", HB_TAG('T','A','T',' ')}, /* Tatar */
+ {"ttm", HB_TAG('A','T','H',' ')}, /* Northern Tutchone -> Athapaskan */
+ {"ttq", HB_TAG('T','M','H',' ')}, /* Tawallammat Tamajaq -> Tamashek */
+/*{"tum", HB_TAG('T','U','M',' ')},*/ /* Tumbuka -> Tulu */
+ {"tuu", HB_TAG('A','T','H',' ')}, /* Tututni -> Athapaskan */
+ {"tuy", HB_TAG('K','A','L',' ')}, /* Tugen -> Kalenjin */
+/*{"tvl", HB_TAG('T','V','L',' ')},*/ /* Tuvalu */
+ {"tw", HB_TAG('T','W','I',' ')}, /* Twi */
+ {"tw", HB_TAG('A','K','A',' ')}, /* Twi -> Akan */
+ {"txc", HB_TAG('A','T','H',' ')}, /* Tsetsaut -> Athapaskan */
+ {"txy", HB_TAG('M','L','G',' ')}, /* Tanosy Malagasy -> Malagasy */
+ {"ty", HB_TAG('T','H','T',' ')}, /* Tahitian */
+ {"tyv", HB_TAG('T','U','V',' ')}, /* Tuvinian -> Tuvin */
+/*{"tyz", HB_TAG('T','Y','Z',' ')},*/ /* Tày */
+/*{"tzm", HB_TAG('T','Z','M',' ')},*/ /* Central Atlas Tamazight -> Tamazight */
+/*{"tzo", HB_TAG('T','Z','O',' ')},*/ /* Tzotzil */
+ {"ubl", HB_TAG('B','I','K',' ')}, /* Buhi'non Bikol -> Bikol */
+/*{"udm", HB_TAG('U','D','M',' ')},*/ /* Udmurt */
+ {"ug", HB_TAG('U','Y','G',' ')}, /* Uyghur */
+ {"uk", HB_TAG('U','K','R',' ')}, /* Ukrainian */
+ {"uki", HB_TAG('K','U','I',' ')}, /* Kui (India) */
+/*{"umb", HB_TAG('U','M','B',' ')},*/ /* Umbundu */
+ {"unr", HB_TAG('M','U','N',' ')}, /* Mundari */
+ {"ur", HB_TAG('U','R','D',' ')}, /* Urdu */
+ {"urk", HB_TAG('M','L','Y',' ')}, /* Urak Lawoi' -> Malay */
+ {"uz", HB_TAG('U','Z','B',' ')}, /* Uzbek [macrolanguage] */
+ {"uzn", HB_TAG('U','Z','B',' ')}, /* Northern Uzbek -> Uzbek */
+ {"uzs", HB_TAG('U','Z','B',' ')}, /* Southern Uzbek -> Uzbek */
+ {"ve", HB_TAG('V','E','N',' ')}, /* Venda */
+/*{"vec", HB_TAG('V','E','C',' ')},*/ /* Venetian */
+ {"vi", HB_TAG('V','I','T',' ')}, /* Vietnamese */
+ {"vkk", HB_TAG('M','L','Y',' ')}, /* Kaur -> Malay */
+ {"vkt", HB_TAG('M','L','Y',' ')}, /* Tenggarong Kutai Malay -> Malay */
+ {"vls", HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */
+ {"vmw", HB_TAG('M','A','K',' ')}, /* Makhuwa */
+ {"vo", HB_TAG('V','O','L',' ')}, /* Volapük */
+/*{"vro", HB_TAG('V','R','O',' ')},*/ /* Võro */
+ {"wa", HB_TAG('W','L','N',' ')}, /* Walloon */
+/*{"war", HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */
+ {"wbm", HB_TAG('W','A',' ',' ')}, /* Wa */
+ {"wbr", HB_TAG('W','A','G',' ')}, /* Wagdi */
+ {"wlc", HB_TAG('C','M','R',' ')}, /* Mwali Comorian -> Comorian */
+ {"wle", HB_TAG('S','I','G',' ')}, /* Wolane -> Silte Gurage */
+ {"wlk", HB_TAG('A','T','H',' ')}, /* Wailaki -> Athapaskan */
+ {"wni", HB_TAG('C','M','R',' ')}, /* Ndzwani Comorian -> Comorian */
+ {"wo", HB_TAG('W','L','F',' ')}, /* Wolof */
+ {"wry", HB_TAG('M','A','W',' ')}, /* Merwari -> Marwari */
+ {"wsg", HB_TAG('G','O','N',' ')}, /* Adilabad Gondi -> Gondi */
+/*{"wtm", HB_TAG('W','T','M',' ')},*/ /* Mewati */
+ {"wuu", HB_TAG('Z','H','S',' ')}, /* Wu Chinese -> Chinese Simplified */
+ {"xal", HB_TAG('K','L','M',' ')}, /* Kalmyk */
+ {"xal", HB_TAG('T','O','D',' ')}, /* Kalmyk -> Todo */
+ {"xan", HB_TAG('S','E','K',' ')}, /* Xamtanga -> Sekota */
+ {"xh", HB_TAG('X','H','S',' ')}, /* Xhosa */
+/*{"xjb", HB_TAG('X','J','B',' ')},*/ /* Minjungbal -> Minjangbal */
+/*{"xkf", HB_TAG('X','K','F',' ')},*/ /* Khengkha */
+ {"xmm", HB_TAG('M','L','Y',' ')}, /* Manado Malay -> Malay */
+ {"xmv", HB_TAG('M','L','G',' ')}, /* Antankarana Malagasy -> Malagasy */
+ {"xmw", HB_TAG('M','L','G',' ')}, /* Tsimihety Malagasy -> Malagasy */
+ {"xnr", HB_TAG('D','G','R',' ')}, /* Kangri -> Dogri */
+/*{"xog", HB_TAG('X','O','G',' ')},*/ /* Soga */
+/*{"xpe", HB_TAG('X','P','E',' ')},*/ /* Liberia Kpelle -> Kpelle (Liberia) */
+ {"xsl", HB_TAG('S','S','L',' ')}, /* South Slavey */
+ {"xsl", HB_TAG('S','L','A',' ')}, /* South Slavey -> Slavey */
+ {"xsl", HB_TAG('A','T','H',' ')}, /* South Slavey -> Athapaskan */
+ {"xst", HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) -> Silte Gurage */
+ {"xwo", HB_TAG('T','O','D',' ')}, /* Written Oirat -> Todo */
+/*{"yao", HB_TAG('Y','A','O',' ')},*/ /* Yao */
+/*{"yap", HB_TAG('Y','A','P',' ')},*/ /* Yapese */
+ {"ybd", HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */
+ {"ydd", HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */
+ {"yi", HB_TAG('J','I','I',' ')}, /* Yiddish [macrolanguage] */
+ {"yih", HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */
+ {"yo", HB_TAG('Y','B','A',' ')}, /* Yoruba */
+ {"yos", HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */
+ {"yrk", HB_TAG('T','N','E',' ')}, /* Nenets -> Tundra Nenets */
+ {"yrk", HB_TAG('F','N','E',' ')}, /* Nenets -> Forest Nenets */
+ {"yue", HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Hong Kong SAR */
+ {"za", HB_TAG('Z','H','A',' ')}, /* Zhuang [macrolanguage] */
+ {"zch", HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */
+ {"zdj", HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */
+/*{"zea", HB_TAG('Z','E','A',' ')},*/ /* Zeeuws -> Zealandic */
+ {"zeh", HB_TAG('Z','H','A',' ')}, /* Eastern Hongshuihe Zhuang -> Zhuang */
+ {"zgb", HB_TAG('Z','H','A',' ')}, /* Guibei Zhuang -> Zhuang */
+/*{"zgh", HB_TAG('Z','G','H',' ')},*/ /* Standard Moroccan Tamazight */
+ {"zgm", HB_TAG('Z','H','A',' ')}, /* Minz Zhuang -> Zhuang */
+ {"zgn", HB_TAG('Z','H','A',' ')}, /* Guibian Zhuang -> Zhuang */
+ {"zh", HB_TAG('Z','H','S',' ')}, /* Chinese [macrolanguage] -> Chinese Simplified */
+ {"zhd", HB_TAG('Z','H','A',' ')}, /* Dai Zhuang -> Zhuang */
+ {"zhn", HB_TAG('Z','H','A',' ')}, /* Nong Zhuang -> Zhuang */
+ {"zlj", HB_TAG('Z','H','A',' ')}, /* Liujiang Zhuang -> Zhuang */
+ {"zlm", HB_TAG('M','L','Y',' ')}, /* Malay */
+ {"zln", HB_TAG('Z','H','A',' ')}, /* Lianshan Zhuang -> Zhuang */
+ {"zlq", HB_TAG('Z','H','A',' ')}, /* Liuqian Zhuang -> Zhuang */
+ {"zmi", HB_TAG('M','L','Y',' ')}, /* Negeri Sembilan Malay -> Malay */
+ {"zne", HB_TAG('Z','N','D',' ')}, /* Zande */
+ {"zom", HB_TAG('Q','I','N',' ')}, /* Zou -> Chin */
+ {"zqe", HB_TAG('Z','H','A',' ')}, /* Qiubei Zhuang -> Zhuang */
+ {"zsm", HB_TAG('M','L','Y',' ')}, /* Standard Malay -> Malay */
+ {"zu", HB_TAG('Z','U','L',' ')}, /* Zulu */
+ {"zum", HB_TAG('L','R','C',' ')}, /* Kumzari -> Luri */
+ {"zyb", HB_TAG('Z','H','A',' ')}, /* Yongbei Zhuang -> Zhuang */
+ {"zyg", HB_TAG('Z','H','A',' ')}, /* Yang Zhuang -> Zhuang */
+ {"zyj", HB_TAG('Z','H','A',' ')}, /* Youjiang Zhuang -> Zhuang */
+ {"zyn", HB_TAG('Z','H','A',' ')}, /* Yongnan Zhuang -> Zhuang */
+/*{"zza", HB_TAG('Z','Z','A',' ')},*/ /* Zazaki [macrolanguage] */
+ {"zzj", HB_TAG('Z','H','A',' ')}, /* Zuojiang Zhuang -> Zhuang */
+};
+
+/**
+ * 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 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 (subtag_matches (lang_str, limit, "-fonnapa"))
+ {
+ /* Undetermined; North American Phonetic Alphabet */
+ tags[0] = HB_TAG('A','P','P','H'); /* Phonetic transcription—Americanist conventions */
+ *count = 1;
+ return true;
+ }
+ if (subtag_matches (lang_str, limit, "-polyton"))
+ {
+ /* Modern Greek (1453-); Polytonic Greek */
+ tags[0] = HB_TAG('P','G','R',' '); /* Polytonic Greek */
+ *count = 1;
+ return true;
+ }
+ if (subtag_matches (lang_str, limit, "-provenc"))
+ {
+ /* Occitan (post 1500); Provençal */
+ tags[0] = HB_TAG('P','R','O',' '); /* Provençal / Old Provençal */
+ *count = 1;
+ return true;
+ }
+ if (subtag_matches (lang_str, limit, "-fonipa"))
+ {
+ /* Undetermined; International Phonetic Alphabet */
+ tags[0] = HB_TAG('I','P','P','H'); /* Phonetic transcription—IPA conventions */
+ *count = 1;
+ return true;
+ }
+ if (subtag_matches (lang_str, limit, "-geok"))
+ {
+ /* Undetermined; Khutsuri (Asomtavruli and Nuskhuri) */
+ tags[0] = HB_TAG('K','G','E',' '); /* Khutsuri Georgian */
+ *count = 1;
+ return true;
+ }
+ if (subtag_matches (lang_str, limit, "-syre"))
+ {
+ /* 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 (lang_str, limit, "-syrj"))
+ {
+ /* 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 (lang_str, limit, "-syrn"))
+ {
+ /* 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;
+ }
+ switch (lang_str[0])
+ {
+ case 'a':
+ if (0 == strcmp (&lang_str[1], "rt-lojban"))
+ {
+ /* Lojban */
+ tags[0] = HB_TAG('J','B','O',' '); /* Lojban */
+ *count = 1;
+ return true;
+ }
+ break;
+ case 'c':
+ if (lang_matches (&lang_str[1], "do-hant-hk"))
+ {
+ /* Min Dong Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "do-hant-mo"))
+ {
+ /* Min Dong Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "jy-hant-hk"))
+ {
+ /* Jinyu Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "jy-hant-mo"))
+ {
+ /* Jinyu Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "mn-hant-hk"))
+ {
+ /* Mandarin Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "mn-hant-mo"))
+ {
+ /* Mandarin Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "np-hant-hk"))
+ {
+ /* Northern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "np-hant-mo"))
+ {
+ /* Northern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "px-hant-hk"))
+ {
+ /* Pu-Xian Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "px-hant-mo"))
+ {
+ /* Pu-Xian Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sp-hant-hk"))
+ {
+ /* Southern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sp-hant-mo"))
+ {
+ /* Southern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zh-hant-hk"))
+ {
+ /* Huizhou Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zh-hant-mo"))
+ {
+ /* Huizhou Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zo-hant-hk"))
+ {
+ /* Min Zhong Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zo-hant-mo"))
+ {
+ /* Min Zhong Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "do-hans"))
+ {
+ /* Min Dong Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "do-hant"))
+ {
+ /* Min Dong Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "jy-hans"))
+ {
+ /* Jinyu Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "jy-hant"))
+ {
+ /* Jinyu Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "mn-hans"))
+ {
+ /* Mandarin Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "mn-hant"))
+ {
+ /* Mandarin Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "np-hans"))
+ {
+ /* Northern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "np-hant"))
+ {
+ /* Northern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "px-hans"))
+ {
+ /* Pu-Xian Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "px-hant"))
+ {
+ /* Pu-Xian Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sp-hans"))
+ {
+ /* Southern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sp-hant"))
+ {
+ /* Southern Ping Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zh-hans"))
+ {
+ /* Huizhou Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zh-hant"))
+ {
+ /* Huizhou Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zo-hans"))
+ {
+ /* Min Zhong Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "zo-hant"))
+ {
+ /* Min Zhong Chinese */
+ 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"))
+ {
+ /* Min Dong Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "do-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Min Dong Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "do-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Jinyu Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "jy-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Jinyu Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "jy-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Mandarin Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "mn-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Mandarin Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "mn-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Northern Ping Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "np-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Northern Ping Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "np-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Pu-Xian Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "px-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Pu-Xian Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "px-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Southern Ping Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "sp-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Southern Ping Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "sp-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Huizhou Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "zh-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Huizhou Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "zh-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Min Zhong Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "zo-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Min Zhong Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "zo-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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], "an-hant-hk"))
+ {
+ /* Gan Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "an-hant-mo"))
+ {
+ /* Gan Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "an-hans"))
+ {
+ /* Gan Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "an-hant"))
+ {
+ /* Gan Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "a-latg"))
+ {
+ /* Irish */
+ 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"))
+ {
+ /* Gan Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "an-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Gan Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "an-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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], "ak-hant-hk"))
+ {
+ /* Hakka Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "ak-hant-mo"))
+ {
+ /* Hakka Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sn-hant-hk"))
+ {
+ /* Xiang Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sn-hant-mo"))
+ {
+ /* Xiang Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "ak-hans"))
+ {
+ /* Hakka Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "ak-hant"))
+ {
+ /* Hakka Chinese */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sn-hans"))
+ {
+ /* Xiang Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "sn-hant"))
+ {
+ /* Xiang Chinese */
+ 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"))
+ {
+ /* Hakka Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "ak-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Hakka Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "ak-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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"))
+ {
+ /* Xiang Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "sn-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Xiang Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "sn-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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 */
+ 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 */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (0 == strcmp (&lang_str[1], "-lux"))
+ {
+ /* Luxembourgish */
+ tags[0] = HB_TAG('L','T','Z',' '); /* Luxembourgish */
+ *count = 1;
+ return true;
+ }
+ break;
+ case 'l':
+ if (lang_matches (&lang_str[1], "zh-hans"))
+ {
+ /* Literary Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ break;
+ case 'm':
+ if (lang_matches (&lang_str[1], "np-hant-hk"))
+ {
+ /* Min Bei Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "np-hant-mo"))
+ {
+ /* Min Bei Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "np-hans"))
+ {
+ /* Min Bei Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "np-hant"))
+ {
+ /* Min Bei Chinese */
+ 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"))
+ {
+ /* Min Bei Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "np-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Min Bei Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "np-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* Min Bei Chinese; Taiwan, Province of China */
+ tags[0] = HB_TAG('Z','H','T',' '); /* Chinese Traditional */
+ *count = 1;
+ return true;
+ }
+ break;
+ case 'n':
+ if (lang_matches (&lang_str[1], "an-hant-hk"))
+ {
+ /* Min Nan Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "an-hant-mo"))
+ {
+ /* Min Nan Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "an-hans"))
+ {
+ /* Min Nan Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "an-hant"))
+ {
+ /* Min Nan Chinese */
+ 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"))
+ {
+ /* Min Nan Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "an-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Min Nan Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "an-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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 */
+ tags[0] = HB_TAG('N','O','R',' '); /* Norwegian */
+ *count = 1;
+ return true;
+ }
+ if (0 == strcmp (&lang_str[1], "o-nyn"))
+ {
+ /* Norwegian Nynorsk */
+ 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"))
+ {
+ /* Romanian; Moldova */
+ tags[0] = HB_TAG('M','O','L',' '); /* Moldavian */
+ *count = 1;
+ return true;
+ }
+ break;
+ case 'w':
+ if (lang_matches (&lang_str[1], "uu-hant-hk"))
+ {
+ /* Wu Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "uu-hant-mo"))
+ {
+ /* Wu Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "uu-hans"))
+ {
+ /* Wu Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "uu-hant"))
+ {
+ /* Wu Chinese */
+ 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"))
+ {
+ /* Wu Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "uu-", 3)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Wu Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "uu-", 3)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* 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], "ue-hans"))
+ {
+ /* Yue Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ break;
+ case 'z':
+ if (lang_matches (&lang_str[1], "h-hant-hk"))
+ {
+ /* Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "h-hant-mo"))
+ {
+ /* Chinese */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strcmp (&lang_str[1], "h-min-nan"))
+ {
+ /* Minnan, Hokkien, Amoy, Taiwanese, Southern Min, Southern Fujian, Hoklo, Southern Fukien, Ho-lo */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "h-hans"))
+ {
+ /* Chinese */
+ tags[0] = HB_TAG('Z','H','S',' '); /* Chinese Simplified */
+ *count = 1;
+ return true;
+ }
+ if (lang_matches (&lang_str[1], "h-hant"))
+ {
+ /* Chinese */
+ 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 */
+ 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"))
+ {
+ /* Chinese; Hong Kong */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "h-", 2)
+ && subtag_matches (lang_str, limit, "-mo"))
+ {
+ /* Chinese; Macao */
+ tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Hong Kong SAR */
+ *count = 1;
+ return true;
+ }
+ if (0 == strncmp (&lang_str[1], "h-", 2)
+ && subtag_matches (lang_str, limit, "-tw"))
+ {
+ /* Chinese; 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 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 */
+ 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 */
+ case HB_TAG('B','I','K',' '): /* Bikol */
+ return hb_language_from_string ("bik", -1); /* Bikol */
+ case HB_TAG('C','P','P',' '): /* Creoles */
+ return hb_language_from_string ("crp", -1); /* Creoles and pidgins */
+ case HB_TAG('C','R','R',' '): /* Carrier */
+ return hb_language_from_string ("crx", -1); /* Carrier */
+ case HB_TAG('D','N','K',' '): /* Dinka */
+ return hb_language_from_string ("din", -1); /* Dinka */
+ 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 */
+ case HB_TAG('G','O','N',' '): /* Gondi */
+ return hb_language_from_string ("gon", -1); /* Gondi */
+ case HB_TAG('H','M','N',' '): /* Hmong */
+ return hb_language_from_string ("hmn", -1); /* Hmong */
+ case HB_TAG('H','N','D',' '): /* Hindko */
+ return hb_language_from_string ("hnd", -1); /* Southern Hindko */
+ case HB_TAG('I','J','O',' '): /* Ijo */
+ return hb_language_from_string ("ijo", -1); /* Ijo */
+ case HB_TAG('I','N','U',' '): /* Inuktitut */
+ return hb_language_from_string ("iu", -1); /* Inuktitut */
+ case HB_TAG('I','P','K',' '): /* Inupiat */
+ return hb_language_from_string ("ik", -1); /* Inupiaq */
+ 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 */
+ case HB_TAG('K','A','L',' '): /* Kalenjin */
+ return hb_language_from_string ("kln", -1); /* Kalenjin */
+ 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 */
+ case HB_TAG('K','O','K',' '): /* Konkani */
+ return hb_language_from_string ("kok", -1); /* Konkani */
+ 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 */
+ case HB_TAG('L','U','H',' '): /* Luyia */
+ return hb_language_from_string ("luy", -1); /* Luyia */
+ case HB_TAG('L','V','I',' '): /* Latvian */
+ return hb_language_from_string ("lv", -1); /* Latvian */
+ case HB_TAG('M','A','W',' '): /* Marwari */
+ return hb_language_from_string ("mwr", -1); /* Marwari */
+ case HB_TAG('M','L','G',' '): /* Malagasy */
+ return hb_language_from_string ("mg", -1); /* Malagasy */
+ case HB_TAG('M','L','Y',' '): /* Malay */
+ return hb_language_from_string ("ms", -1); /* Malay */
+ case HB_TAG('M','N','G',' '): /* Mongolian */
+ return hb_language_from_string ("mn", -1); /* Mongolian */
+ case HB_TAG('M','O','L',' '): /* Moldavian */
+ return hb_language_from_string ("ro-MD", -1); /* Romanian; Moldova */
+ case HB_TAG('N','E','P',' '): /* Nepali */
+ return hb_language_from_string ("ne", -1); /* Nepali */
+ 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 */
+ case HB_TAG('O','J','B',' '): /* Ojibway */
+ return hb_language_from_string ("oj", -1); /* Ojibwa */
+ case HB_TAG('O','R','O',' '): /* Oromo */
+ return hb_language_from_string ("om", -1); /* Oromo */
+ case HB_TAG('P','A','S',' '): /* Pashto */
+ return hb_language_from_string ("ps", -1); /* Pashto */
+ 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','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 */
+ case HB_TAG('R','O','Y',' '): /* Romany */
+ return hb_language_from_string ("rom", -1); /* Romany */
+ case HB_TAG('S','Q','I',' '): /* Albanian */
+ return hb_language_from_string ("sq", -1); /* Albanian */
+ case HB_TAG('S','Y','R',' '): /* Syriac */
+ return hb_language_from_string ("syr", -1); /* Syriac */
+ 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 */
+ case HB_TAG('T','N','E',' '): /* Tundra Nenets */
+ return hb_language_from_string ("yrk", -1); /* Nenets */
+ case HB_TAG('Z','H','H',' '): /* Chinese, Hong Kong SAR */
+ return hb_language_from_string ("zh-HK", -1); /* Chinese; Hong Kong */
+ case HB_TAG('Z','H','S',' '): /* Chinese Simplified */
+ return hb_language_from_string ("zh-Hans", -1); /* Chinese; Han (Simplified variant) */
+ case HB_TAG('Z','H','T',' '): /* Chinese Traditional */
+ return hb_language_from_string ("zh-Hant", -1); /* Chinese; Han (Traditional variant) */
+ default:
+ return HB_LANGUAGE_INVALID;
+ }
+}
+
+#endif /* HB_OT_TAG_TABLE_HH */
+
+/* == End of generated table == */
diff --git a/thirdparty/harfbuzz/src/hb-ot-tag.cc b/thirdparty/harfbuzz/src/hb-ot-tag.cc
new file mode 100644
index 0000000000..7ec91c5815
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-tag.cc
@@ -0,0 +1,567 @@
+/*
+ * 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;
+
+ /* 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;
+
+ /* 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
+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_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 bool
+subtag_matches (const char *lang_str,
+ const char *limit,
+ const char *subtag)
+{
+ do {
+ const char *s = strstr (lang_str, subtag);
+ if (!s || s >= limit)
+ return false;
+ if (!ISALNUM (s[strlen (subtag)]))
+ return true;
+ lang_str = s + strlen (subtag);
+ } while (true);
+}
+
+static hb_bool_t
+lang_matches (const char *lang_str, const char *spec)
+{
+ unsigned int len = strlen (spec);
+
+ return strncmp (lang_str, spec, len) == 0 &&
+ (lang_str[len] == '\0' || lang_str[len] == '-');
+}
+
+struct LangTag
+{
+ char language[4];
+ hb_tag_t tag;
+
+ int cmp (const char *a) const
+ {
+ const char *b = this->language;
+ unsigned int da, db;
+ const char *p;
+
+ p = strchr (a, '-');
+ da = p ? (unsigned int) (p - a) : strlen (a);
+
+ p = strchr (b, '-');
+ db = p ? (unsigned int) (p - b) : strlen (b);
+
+ return strncmp (a, b, hb_max (da, db));
+ }
+ 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_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)
+{
+ const char *s;
+ unsigned int tag_idx;
+
+ /* Check for matches of multiple subtags. */
+ if (hb_ot_tags_from_complex_language (lang_str, limit, count, tags))
+ return;
+
+ /* Find a language matching in the first component. */
+ s = strchr (lang_str, '-');
+ {
+ 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;
+ }
+ if (hb_sorted_array (ot_languages).bfind (lang_str, &tag_idx))
+ {
+ unsigned int i;
+ while (tag_idx != 0 &&
+ 0 == strcmp (ot_languages[tag_idx].language, ot_languages[tag_idx - 1].language))
+ tag_idx--;
+ for (i = 0;
+ i < *count &&
+ tag_idx + i < ARRAY_LENGTH (ot_languages) &&
+ 0 == strcmp (ot_languages[tag_idx + i].language, ot_languages[tag_idx].language);
+ i++)
+ tags[i] = ot_languages[tag_idx + i].tag;
+ *count = i;
+ return;
+ }
+ }
+
+ 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;
+ }
+
+ *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: an #hb_language_t to convert.
+ * @script_count: (allow-none): maximum number of script tags to retrieve (IN)
+ * and actual number of script tags retrieved (OUT)
+ * @script_tags: (out) (allow-none): array of size at least @script_count to store the
+ * script tag results
+ * @language_count: (allow-none): maximum number of language tags to retrieve
+ * (IN) and actual number of language tags retrieved (OUT)
+ * @language_tags: (out) (allow-none): 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:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * 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;
+
+ {
+ hb_language_t disambiguated_tag = hb_ot_ambiguous_tag_to_language (tag);
+ if (disambiguated_tag != HB_LANGUAGE_INVALID)
+ return disambiguated_tag;
+ }
+
+ for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
+ if (ot_languages[i].tag == tag)
+ return hb_language_from_string (ot_languages[i].language, -1);
+
+ /* 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: (allow-none): the #hb_script_t corresponding to @script_tag (OUT).
+ * @language: (allow-none): the #hb_language_t corresponding to @script_tag and
+ * @language_tag (OUT).
+ *
+ * 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 *) malloc (len + 16);
+ if (unlikely (!buf))
+ {
+ *language = nullptr;
+ }
+ else
+ {
+ int shift;
+ 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);
+ free (buf);
+ }
+ }
+ }
+}
+
+#ifdef MAIN
+static inline void
+test_langs_sorted ()
+{
+ for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++)
+ {
+ int c = ot_languages[i].cmp (&ot_languages[i - 1]);
+ if (c > 0)
+ {
+ fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n",
+ i, ot_languages[i-1].language, c, ot_languages[i].language);
+ abort();
+ }
+ }
+}
+
+int
+main ()
+{
+ test_langs_sorted ();
+ return 0;
+}
+
+#endif
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh
new file mode 100644
index 0000000000..29219adb0a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-var-avar-table.hh
@@ -0,0 +1,169 @@
+/*
+ * 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"
+
+/*
+ * 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 {
+
+
+struct AxisValueMap
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (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 : ArrayOf<AxisValueMap>
+{
+ int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const
+ {
+#define fromCoord coords[from_offset]
+#define toCoord coords[to_offset]
+ /* 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); }
+
+ public:
+ DEFINE_SIZE_ARRAY (2, *this);
+};
+
+struct avar
+{
+ static constexpr hb_tag_t tableTag = HB_OT_TAG_avar;
+
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ if (unlikely (!(version.sanitize (c) &&
+ version.major == 1 &&
+ 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);
+ }
+
+ 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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
new file mode 100644
index 0000000000..f9e933fb2b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-var-fvar-table.hh
@@ -0,0 +1,327 @@
+/*
+ * 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 {
+
+
+struct InstanceRecord
+{
+ friend struct fvar;
+
+ hb_array_t<const HBFixed> get_coordinates (unsigned int axis_count) const
+ { return coordinatesZ.as_array (axis_count); }
+
+ bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ 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<HBFixed>
+ 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;
+ }
+
+ 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));
+ }
+
+ protected:
+ void get_coordinates (float &min, float &default_, float &max) const
+ {
+ default_ = defaultValue / 65536.f;
+ /* Ensure order, to simplify client math. */
+ min = hb_min (default_, minValue / 65536.f);
+ max = hb_max (default_, maxValue / 65536.f);
+ }
+
+ protected:
+ Tag axisTag; /* Tag identifying the design variation for the axis. */
+ HBFixed minValue; /* The minimum coordinate value for the axis. */
+ HBFixed defaultValue; /* The default coordinate value for the axis. */
+ HBFixed maxValue; /* The maximum coordinate value for the axis. */
+ 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) &&
+ likely (version.major == 1) &&
+ c->check_struct (this) &&
+ axisSize == 20 && /* Assumed in our code. */
+ instanceSize >= axisCount * 4 + 4 &&
+ get_axes ().sanitize (c) &&
+ c->check_range (get_instance (0), 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) && (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) && (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 HBFixed> 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_set_t *nameids) const
+ {
+ if (!has_data ()) return;
+
+ + get_axes ()
+ | hb_map (&AxisRecord::get_name_id)
+ | hb_sink (nameids)
+ ;
+
+ + hb_range ((unsigned) instanceCount)
+ | hb_map ([this] (const unsigned _) { return get_instance_subfamily_name_id (_); })
+ | hb_sink (nameids)
+ ;
+
+ + hb_range ((unsigned) instanceCount)
+ | hb_map ([this] (const unsigned _) { return get_instance_postscript_name_id (_); })
+ | hb_sink (nameids)
+ ;
+ }
+
+ protected:
+ 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 */
+ OffsetTo<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(HBFixed) + 4, or to
+ * axisCount * sizeof(HBFixed) + 6. */
+
+ public:
+ DEFINE_SIZE_STATIC (16);
+};
+
+} /* namespace OT */
+
+
+#endif /* HB_OT_VAR_FVAR_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
new file mode 100644
index 0000000000..4d4e6dcae4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh
@@ -0,0 +1,701 @@
+/*
+ * 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"
+
+/*
+ * 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 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 translate (const contour_point_t &p) { x += p.x; y += p.y; }
+
+ uint8_t flag;
+ float x, y;
+ 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;
+ resize (old_len + a.length);
+ for (unsigned int i = 0; i < a.length; i++)
+ (*this)[old_len + i] = a[i];
+ }
+
+ void transform (const float (&matrix)[4])
+ {
+ for (unsigned int i = 0; i < length; i++)
+ {
+ contour_point_t &p = (*this)[i];
+ float x_ = p.x * matrix[0] + p.y * matrix[2];
+ p.y = p.x * matrix[1] + p.y * matrix[3];
+ p.x = x_;
+ }
+ }
+
+ void translate (const contour_point_t& delta)
+ {
+ for (unsigned int i = 0; i < length; i++)
+ (*this)[i].translate (delta);
+ }
+};
+
+/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */
+struct TupleVariationHeader
+{
+ 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)); }
+
+ float calculate_scalar (const int *coords, unsigned int coord_count,
+ const hb_array_t<const F2DOT14> shared_tuples) const
+ {
+ hb_array_t<const F2DOT14> peak_tuple;
+
+ if (has_peak ())
+ peak_tuple = get_peak_tuple (coord_count);
+ else
+ {
+ unsigned int index = get_index ();
+ if (unlikely (index * coord_count >= shared_tuples.length))
+ return 0.f;
+ peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count);
+ }
+
+ hb_array_t<const F2DOT14> start_tuple;
+ hb_array_t<const F2DOT14> end_tuple;
+ if (has_intermediate ())
+ {
+ start_tuple = get_start_tuple (coord_count);
+ end_tuple = get_end_tuple (coord_count);
+ }
+
+ float scalar = 1.f;
+ for (unsigned int i = 0; i < coord_count; i++)
+ {
+ int v = coords[i];
+ int peak = peak_tuple[i];
+ if (!peak || v == peak) continue;
+
+ if (has_intermediate ())
+ {
+ int start = start_tuple[i];
+ int end = end_tuple[i];
+ 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
+ };
+
+ 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);
+};
+
+struct GlyphVariationData
+{
+ const TupleVariationHeader &get_tuple_var_header (void) const
+ { return StructAfter<TupleVariationHeader> (data); }
+
+ struct tuple_iterator_t
+ {
+ void init (hb_bytes_t var_data_bytes_, unsigned int axis_count_)
+ {
+ var_data_bytes = var_data_bytes_;
+ var_data = var_data_bytes_.as<GlyphVariationData> ();
+ index = 0;
+ axis_count = axis_count_;
+ current_tuple = &var_data->get_tuple_var_header ();
+ data_offset = 0;
+ }
+
+ bool get_shared_indices (hb_vector_t<unsigned int> &shared_indices /* OUT */)
+ {
+ if (var_data->has_shared_point_numbers ())
+ {
+ const HBUINT8 *base = &(var_data+var_data->data);
+ const HBUINT8 *p = base;
+ if (!unpack_points (p, shared_indices, var_data_bytes)) 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))) &&
+ current_tuple->get_size (axis_count);
+ }
+
+ bool move_to_next ()
+ {
+ data_offset += current_tuple->get_data_size ();
+ current_tuple = &current_tuple->get_next (axis_count);
+ index++;
+ return is_valid ();
+ }
+
+ const HBUINT8 *get_serialized_data () const
+ { return &(var_data+var_data->data) + data_offset; }
+
+ private:
+ const GlyphVariationData *var_data;
+ unsigned int index;
+ unsigned int axis_count;
+ unsigned int data_offset;
+
+ 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,
+ hb_vector_t<unsigned int> &shared_indices /* OUT */,
+ tuple_iterator_t *iterator /* OUT */)
+ {
+ iterator->init (var_data_bytes, axis_count);
+ 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 hb_bytes_t &bytes)
+ {
+ enum packed_point_flag_t
+ {
+ POINTS_ARE_WORDS = 0x80,
+ POINT_RUN_COUNT_MASK = 0x7F
+ };
+
+ if (unlikely (!bytes.check_range (p))) return false;
+
+ uint16_t count = *p++;
+ if (count & POINTS_ARE_WORDS)
+ {
+ if (unlikely (!bytes.check_range (p))) return false;
+ count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++;
+ }
+ points.resize (count);
+
+ unsigned int n = 0;
+ uint16_t i = 0;
+ while (i < count)
+ {
+ if (unlikely (!bytes.check_range (p))) return false;
+ uint16_t j;
+ uint8_t control = *p++;
+ uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1;
+ if (control & POINTS_ARE_WORDS)
+ {
+ for (j = 0; j < run_count && i < count; j++, i++)
+ {
+ if (unlikely (!bytes.check_range ((const HBUINT16 *) p)))
+ return false;
+ n += *(const HBUINT16 *)p;
+ points[i] = n;
+ p += HBUINT16::static_size;
+ }
+ }
+ else
+ {
+ for (j = 0; j < run_count && i < count; j++, i++)
+ {
+ if (unlikely (!bytes.check_range (p))) return false;
+ n += *p++;
+ points[i] = n;
+ }
+ }
+ if (j < run_count) return false;
+ }
+ return true;
+ }
+
+ static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */,
+ hb_vector_t<int> &deltas /* IN/OUT */,
+ const hb_bytes_t &bytes)
+ {
+ enum packed_delta_flag_t
+ {
+ DELTAS_ARE_ZERO = 0x80,
+ DELTAS_ARE_WORDS = 0x40,
+ DELTA_RUN_COUNT_MASK = 0x3F
+ };
+
+ unsigned int i = 0;
+ unsigned int count = deltas.length;
+ while (i < count)
+ {
+ if (unlikely (!bytes.check_range (p))) return false;
+ uint8_t control = *p++;
+ unsigned int run_count = (control & DELTA_RUN_COUNT_MASK) + 1;
+ unsigned int j;
+ if (control & DELTAS_ARE_ZERO)
+ for (j = 0; j < run_count && i < count; j++, i++)
+ deltas[i] = 0;
+ else if (control & DELTAS_ARE_WORDS)
+ for (j = 0; j < run_count && i < count; j++, i++)
+ {
+ if (unlikely (!bytes.check_range ((const HBUINT16 *) p)))
+ return false;
+ deltas[i] = *(const HBINT16 *) p;
+ p += HBUINT16::static_size;
+ }
+ else
+ for (j = 0; j < run_count && i < count; j++, i++)
+ {
+ if (unlikely (!bytes.check_range (p)))
+ return false;
+ deltas[i] = *(const HBINT8 *) p++;
+ }
+ if (j < run_count)
+ return false;
+ }
+ return true;
+ }
+
+ bool has_data () const { return tupleVarCount; }
+
+ protected:
+ struct TupleVarCount : HBUINT16
+ {
+ bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); }
+ unsigned int get_count () const { return (*this) & CountMask; }
+
+ 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. */
+ OffsetTo<HBUINT8>
+ data; /* Offset from the start of the GlyphVariationData table
+ * to the serialized data. */
+ /* TupleVariationHeader tupleVariationHeaders[] *//* Array of tuple variation headers. */
+ public:
+ DEFINE_SIZE_MIN (4);
+};
+
+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) && (version.major == 1) &&
+ (glyphCount == c->get_num_glyphs ()) &&
+ sharedTuples.sanitize (c, this, axisCount * sharedTupleCount) &&
+ (is_long_offset () ?
+ c->check_array (get_long_offset_array (), glyphCount+1) :
+ c->check_array (get_short_offset_array (), glyphCount+1)) &&
+ c->check_array (((const HBUINT8*)&(this+dataZ)) + get_offset (0),
+ get_offset (glyphCount) - get_offset (0)));
+ }
+
+ /* GlyphVariationData not sanitized here; must be checked while accessing each glyph varation data */
+ bool sanitize (hb_sanitize_context_t *c) const
+ { return sanitize_shallow (c); }
+
+ bool subset (hb_subset_context_t *c) const
+ {
+ TRACE_SUBSET (this);
+
+ 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->glyphCount = num_glyphs;
+
+ unsigned int subset_data_size = 0;
+ for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
+ {
+ hb_codepoint_t old_gid;
+ if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue;
+ subset_data_size += get_glyph_var_data_bytes (c->source_blob, old_gid).length;
+ }
+
+ bool long_offset = subset_data_size & ~0xFFFFu;
+ out->flags = long_offset ? 1 : 0;
+
+ HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1));
+ 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;
+ memcpy (tuples, this+sharedTuples, shared_tuple_size);
+ }
+
+ char *subset_data = c->serializer->allocate_size<char> (subset_data_size);
+ if (!subset_data) return_trace (false);
+ out->dataZ = subset_data - (char *) out;
+
+ unsigned int glyph_offset = 0;
+ for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
+ {
+ hb_codepoint_t old_gid;
+ hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid)
+ ? get_glyph_var_data_bytes (c->source_blob, old_gid)
+ : hb_bytes_t ();
+
+ if (long_offset)
+ ((HBUINT32 *) subset_offsets)[gid] = glyph_offset;
+ else
+ ((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2;
+
+ if (var_data_bytes.length > 0)
+ 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)[num_glyphs] = glyph_offset;
+ else
+ ((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2;
+
+ return_trace (true);
+ }
+
+ protected:
+ const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, hb_codepoint_t glyph) const
+ {
+ unsigned start_offset = get_offset (glyph);
+ unsigned length = get_offset (glyph+1) - 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 i) const
+ { 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
+ {
+ void init (hb_face_t *face)
+ { table = hb_sanitize_context_t ().reference_table<gvar> (face); }
+ void fini () { table.destroy (); }
+
+ private:
+ struct x_getter { static float get (const contour_point_t &p) { return p.x; } };
+ struct y_getter { static float get (const contour_point_t &p) { return p.y; } };
+
+ template <typename T>
+ 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 target_val = T::get (points[target]);
+ float prev_val = T::get (points[prev]);
+ float next_val = T::get (points[next]);
+ float prev_delta = T::get (deltas[prev]);
+ float next_delta = T::get (deltas[next]);
+
+ 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 (1.f - r) * prev_delta + r * next_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_font_t *font,
+ const hb_array_t<contour_point_t> points) const
+ {
+ /* num_coords should exactly match gvar's axisCount due to how GlyphVariationData tuples are aligned */
+ if (!font->num_coords || font->num_coords != table->axisCount) return true;
+
+ if (unlikely (glyph >= table->glyphCount)) return true;
+
+ hb_bytes_t var_data_bytes = table->get_glyph_var_data_bytes (table.get_blob (), 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,
+ shared_indices, &iterator))
+ return true; /* so isn't applied at all */
+
+ /* Save original points for inferred delta calculation */
+ contour_point_vector_t orig_points;
+ orig_points.resize (points.length);
+ for (unsigned int i = 0; i < orig_points.length; i++)
+ orig_points[i] = points[i];
+
+ contour_point_vector_t deltas; /* flag is used to indicate referenced point */
+ deltas.resize (points.length);
+
+ hb_vector_t<unsigned> end_points;
+ for (unsigned i = 0; i < points.length; ++i)
+ if (points[i].is_end_point)
+ end_points.push (i);
+
+ int *coords = font->coords;
+ unsigned num_coords = font->num_coords;
+ hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount);
+ do
+ {
+ float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, 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;
+
+ hb_bytes_t bytes ((const char *) p, length);
+ hb_vector_t<unsigned int> private_indices;
+ if (iterator.current_tuple->has_private_points () &&
+ !GlyphVariationData::unpack_points (p, private_indices, bytes))
+ return false;
+ const hb_array_t<unsigned int> &indices = private_indices.length ? private_indices : shared_indices;
+
+ bool apply_to_all = (indices.length == 0);
+ unsigned int num_deltas = apply_to_all ? points.length : indices.length;
+ hb_vector_t<int> x_deltas;
+ x_deltas.resize (num_deltas);
+ if (!GlyphVariationData::unpack_deltas (p, x_deltas, bytes))
+ return false;
+ hb_vector_t<int> y_deltas;
+ y_deltas.resize (num_deltas);
+ if (!GlyphVariationData::unpack_deltas (p, y_deltas, bytes))
+ return false;
+
+ for (unsigned int i = 0; i < deltas.length; i++)
+ deltas[i].init ();
+ for (unsigned int i = 0; i < num_deltas; i++)
+ {
+ unsigned int pt_index = apply_to_all ? i : indices[i];
+ deltas[pt_index].flag = 1; /* this point is referenced, i.e., explicit deltas specified */
+ deltas[pt_index].x += x_deltas[i] * scalar;
+ deltas[pt_index].y += y_deltas[i] * scalar;
+ }
+
+ /* infer deltas for unreferenced points */
+ unsigned start_point = 0;
+ for (unsigned c = 0; c < end_points.length; c++)
+ {
+ unsigned end_point = end_points[c];
+
+ /* 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; i++)
+ if (!deltas[i].flag) 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[i].flag && !deltas[j].flag) break;
+ }
+ prev = j = i;
+ for (;;)
+ {
+ i = j;
+ j = next_index (i, start_point, end_point);
+ if (!deltas[i].flag && deltas[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[i].x = infer_delta<x_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next);
+ deltas[i].y = infer_delta<y_getter> (orig_points.as_array (), deltas.as_array (), i, prev, next);
+ if (--unref_count == 0) goto no_more_gaps;
+ }
+ }
+no_more_gaps:
+ start_point = end_point + 1;
+ }
+
+ /* apply specified / inferred deltas to points */
+ for (unsigned int i = 0; i < points.length; i++)
+ {
+ points[i].x += roundf (deltas[i].x);
+ points[i].y += roundf (deltas[i].y);
+ }
+ } while (iterator.move_to_next ());
+
+ return true;
+ }
+
+ unsigned int get_axis_count () const { return table->axisCount; }
+
+ private:
+ hb_blob_ptr_t<gvar> table;
+ };
+
+ 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. */
+ LNNOffsetTo<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 glyphCount; /* 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. */
+ LOffsetTo<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_MIN (20);
+};
+
+struct gvar_accelerator_t : gvar::accelerator_t {};
+
+} /* namespace OT */
+
+#endif /* HB_OT_VAR_GVAR_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
new file mode 100644
index 0000000000..fdcc88d674
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-var-hvar-table.hh
@@ -0,0 +1,488 @@
+/*
+ * 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"
+
+
+namespace OT {
+
+
+struct DeltaSetIndexMap
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this) &&
+ c->check_range (mapDataZ.arrayZ,
+ mapCount,
+ get_width ()));
+ }
+
+ 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 unsigned int> 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);
+
+ format = ((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[i];
+ 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);
+ }
+
+ unsigned int 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 int get_map_count () const { return mapCount; }
+ unsigned int get_width () const { return ((format >> 4) & 3) + 1; }
+ unsigned int get_inner_bit_count () const { return (format & 0xF) + 1; }
+
+ protected:
+ HBUINT16 format; /* A packed field that describes the compressed
+ * representation of delta-set indices. */
+ HBUINT16 mapCount; /* The number of mapping entries. */
+ UnsizedArrayOf<HBUINT8>
+ mapDataZ; /* The delta-set index mapping data. */
+
+ public:
+ DEFINE_SIZE_ARRAY (4, mapDataZ);
+};
+
+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)
+ {
+ map_count = 0;
+ outer_bit_count = 0;
+ inner_bit_count = 1;
+ max_inners.init ();
+ output_map.init ();
+
+ if (&index_map == &Null (DeltaSetIndexMap)) return;
+
+ unsigned int last_val = (unsigned int)-1;
+ hb_codepoint_t last_gid = (hb_codepoint_t)-1;
+ hb_codepoint_t gid = (hb_codepoint_t) hb_min (index_map.get_map_count (), plan->num_output_glyphs ());
+
+ 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 */
+ for (; gid > 0; gid--)
+ {
+ hb_codepoint_t old_gid;
+ if (!plan->old_gid_for_new_gid (gid - 1, &old_gid))
+ {
+ if (last_gid == (hb_codepoint_t) -1)
+ continue;
+ else
+ break;
+ }
+
+ unsigned int v = index_map.map (old_gid);
+ if (last_gid == (hb_codepoint_t) -1)
+ {
+ 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;
+ for (gid = 0; gid < map_count; gid++)
+ {
+ hb_codepoint_t old_gid;
+ if (plan->old_gid_for_new_gid (gid, &old_gid))
+ {
+ 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)
+ {
+ if (input_map == &Null (DeltaSetIndexMap)) return;
+
+ 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;
+ }
+
+ output_map.resize (map_count);
+ for (hb_codepoint_t gid = 0; gid < output_map.length; gid++)
+ {
+ hb_codepoint_t old_gid;
+ if (plan->old_gid_for_new_gid (gid, &old_gid))
+ {
+ unsigned int v = input_map->map (old_gid);
+ unsigned int outer = v >> 16;
+ output_map[gid] = (outer_map[outer] << 16) | (inner_maps[outer][v & 0xFFFF]);
+ }
+ else
+ output_map[gid] = 0; /* Map unused glyph to outer/inner=0/0 */
+ }
+ }
+
+ 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 unsigned int> 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<unsigned int> 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 ());
+
+ for (unsigned int i = 0; i < inner_maps.length; i++)
+ inner_maps[i].init ();
+
+ 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);
+ if (index_maps[0] == &Null (DeltaSetIndexMap))
+ {
+ retain_adv_map = plan->retain_gids;
+ outer_map.add (0);
+ for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++)
+ {
+ hb_codepoint_t old_gid;
+ if (plan->old_gid_for_new_gid (gid, &old_gid))
+ 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 (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++)
+ if (inner_sets[0]->has (gid))
+ inner_maps[0].add (gid);
+ else
+ inner_maps[0].skip ();
+ }
+ 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);
+ }
+
+ 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_deep ();
+ index_map_plans.fini_deep ();
+ }
+
+ 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) &&
+ likely (version.major == 1) &&
+ varStore.sanitize (c, this) &&
+ advMap.sanitize (c, this) &&
+ lsbMap.sanitize (c, this) &&
+ rsbMap.sanitize (c, this));
+ }
+
+ 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 (c, this).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 (c, this).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 (c, this).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);
+ 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 (unlikely (!out->varStore.serialize (c->serializer, out)
+ .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_var (hb_codepoint_t glyph, hb_font_t *font) const
+ {
+ unsigned int varidx = (this+advMap).map (glyph);
+ return (this+varStore).get_delta (varidx, font->coords, font->num_coords);
+ }
+
+ float get_side_bearing_var (hb_codepoint_t glyph,
+ const int *coords, unsigned int coord_count) const
+ {
+ if (!has_side_bearing_deltas ()) return 0.f;
+ unsigned int varidx = (this+lsbMap).map (glyph);
+ return (this+varStore).get_delta (varidx, coords, coord_count);
+ }
+
+ bool has_side_bearing_deltas () const { return lsbMap && rsbMap; }
+
+ protected:
+ FixedVersion<>version; /* Version of the metrics variation table
+ * initially set to 0x00010000u */
+ LOffsetTo<VariationStore>
+ varStore; /* Offset to item variation store table. */
+ LOffsetTo<DeltaSetIndexMap>
+ advMap; /* Offset to advance var-idx mapping. */
+ LOffsetTo<DeltaSetIndexMap>
+ lsbMap; /* Offset to lsb/tsb var-idx mapping. */
+ LOffsetTo<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 (c, this).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); }
+
+ protected:
+ LOffsetTo<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/thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh
new file mode 100644
index 0000000000..1b7fad9cec
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-var-mvar-table.hh
@@ -0,0 +1,119 @@
+/*
+ * 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-layout-common.hh"
+
+
+namespace OT {
+
+
+struct VariationValueRecord
+{
+ bool sanitize (hb_sanitize_context_t *c) const
+ {
+ TRACE_SANITIZE (this);
+ return_trace (c->check_struct (this));
+ }
+
+ public:
+ Tag valueTag; /* Four-byte tag identifying a font-wide measure. */
+ HBUINT32 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) &&
+ likely (version.major == 1) &&
+ c->check_struct (this) &&
+ valueRecordSize >= VariationValueRecord::static_size &&
+ varStore.sanitize (c, this) &&
+ c->check_range (valuesZ.arrayZ,
+ valueRecordCount,
+ valueRecordSize));
+ }
+
+ 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. */
+ OffsetTo<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 */
+
+
+#endif /* HB_OT_VAR_MVAR_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot-var.cc b/thirdparty/harfbuzz/src/hb-ot-var.cc
new file mode 100644
index 0000000000..6b8b09b6ba
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-var.cc
@@ -0,0 +1,220 @@
+/*
+ * 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: #hb_face_t to test
+ *
+ * This function allows to verify the presence of OpenType variation data on the face.
+ *
+ * Return value: true if face has a `fvar' table and 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:
+ *
+ * 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:
+ *
+ * Since: 1.4.2
+ * Deprecated: 2.2.0
+ **/
+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:
+ *
+ * Since: 1.4.2
+ * Deprecated: 2.2.0
+ **/
+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:
+ *
+ * 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:
+ *
+ * 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.
+ */
+
+unsigned int
+hb_ot_var_get_named_instance_count (hb_face_t *face)
+{
+ return face->table.fvar->get_instance_count ();
+}
+
+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_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);
+}
+
+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:
+ *
+ * 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:
+ *
+ * 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/thirdparty/harfbuzz/src/hb-ot-var.h b/thirdparty/harfbuzz/src/hb-ot-var.h
new file mode 100644
index 0000000000..df89bc5a23
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-var.h
@@ -0,0 +1,146 @@
+/*
+ * 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
+ */
+
+#ifndef HB_OT_H_IN
+#error "Include <hb-ot.h> instead."
+#endif
+
+#ifndef HB_OT_VAR_H
+#define HB_OT_VAR_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_OT_TAG_VAR_AXIS_ITALIC HB_TAG('i','t','a','l')
+#define HB_OT_TAG_VAR_AXIS_OPTICAL_SIZE HB_TAG('o','p','s','z')
+#define HB_OT_TAG_VAR_AXIS_SLANT HB_TAG('s','l','n','t')
+#define HB_OT_TAG_VAR_AXIS_WIDTH HB_TAG('w','d','t','h')
+#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.
+ *
+ * Since: 2.2.0
+ */
+typedef enum { /*< flags >*/
+ HB_OT_VAR_AXIS_FLAG_HIDDEN = 0x00000001u,
+
+ _HB_OT_VAR_AXIS_FLAG_MAX_VALUE= HB_TAG_MAX_SIGNED /*< skip >*/
+} hb_ot_var_axis_flags_t;
+
+/**
+ * hb_ot_var_axis_info_t:
+ *
+ * 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/thirdparty/harfbuzz/src/hb-ot-vorg-table.hh b/thirdparty/harfbuzz/src/hb-ot-vorg-table.hh
new file mode 100644
index 0000000000..c6803200f9
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ot-vorg-table.hh
@@ -0,0 +1,136 @@
+/*
+ * 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:
+ HBGlyphID 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);
+ VORG *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) &&
+ version.major == 1 &&
+ vertYOrigins.sanitize (c));
+ }
+
+ protected:
+ FixedVersion<>version; /* Version of VORG table. Set to 0x00010000u. */
+ FWORD defaultVertOriginY;
+ /* The default vertical origin. */
+ SortedArrayOf<VertOriginMetric>
+ vertYOrigins; /* The array of vertical origins. */
+
+ public:
+ DEFINE_SIZE_ARRAY(8, vertYOrigins);
+};
+} /* namespace OT */
+
+#endif /* HB_OT_VORG_TABLE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-ot.h b/thirdparty/harfbuzz/src/hb-ot.h
new file mode 100644
index 0000000000..f2dbaa1b31
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-pool.hh b/thirdparty/harfbuzz/src/hb-pool.hh
new file mode 100644
index 0000000000..dcf0faf2a9
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-pool.hh
@@ -0,0 +1,100 @@
+/*
+ * 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. */
+
+template <typename T, unsigned ChunkLen = 16>
+struct hb_pool_t
+{
+ hb_pool_t () : next (nullptr) {}
+ ~hb_pool_t () { fini (); }
+
+ void fini ()
+ {
+ next = nullptr;
+
+ for (chunk_t *_ : chunks) ::free (_);
+
+ chunks.fini ();
+ }
+
+ T* alloc ()
+ {
+ if (unlikely (!next))
+ {
+ if (unlikely (!chunks.alloc (chunks.length + 1))) return nullptr;
+ chunk_t *chunk = (chunk_t *) calloc (1, sizeof (chunk_t));
+ if (unlikely (!chunk)) return nullptr;
+ chunks.push (chunk);
+ next = chunk->thread ();
+ }
+
+ T* obj = next;
+ next = * ((T**) next);
+
+ memset (obj, 0, sizeof (T));
+
+ return obj;
+ }
+
+ void free (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/thirdparty/harfbuzz/src/hb-sanitize.hh b/thirdparty/harfbuzz/src/hb-sanitize.hh
new file mode 100644
index 0000000000..024b4d1c99
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-sanitize.hh
@@ -0,0 +1,412 @@
+/*
+ * 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 8
+#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_SUTABLES
+#define HB_SANITIZE_MAX_SUTABLES 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),
+ max_ops (0), max_subtables (0),
+ writable (false), edit_count (0),
+ blob (nullptr),
+ num_glyphs (65536),
+ num_glyphs_set (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); }
+ 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_SUTABLES;
+ }
+
+ private:
+ template <typename T, typename ...Ts> auto
+ _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
+ ( obj.sanitize (this, hb_forward<Ts> (ds)...) )
+ template <typename T, typename ...Ts> auto
+ _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
+ ( obj.dispatch (this, hb_forward<Ts> (ds)...) )
+ public:
+ template <typename T, typename ...Ts> auto
+ dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
+ ( _dispatch (obj, hb_prioritize, hb_forward<Ts> (ds)...) )
+
+
+ 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;
+ else
+ {
+ this->start = obj_start;
+ this->end = obj_start + hb_min (size_t (this->end - obj_start), obj->get_size ());
+ }
+ }
+
+ void reset_object ()
+ {
+ this->start = this->blob->data;
+ this->end = this->start + this->blob->length;
+ assert (this->start <= this->end); /* Must not overflow. */
+ }
+
+ void start_processing ()
+ {
+ reset_object ();
+ if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR)))
+ this->max_ops = HB_SANITIZE_MAX_OPS_MAX;
+ else
+ this->max_ops = hb_clamp ((unsigned) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR,
+ (unsigned) HB_SANITIZE_MAX_OPS_MIN,
+ (unsigned) HB_SANITIZE_MAX_OPS_MAX);
+ this->edit_count = 0;
+ this->debug_depth = 0;
+
+ DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1,
+ "start [%p..%p] (%lu bytes)",
+ this->start, this->end,
+ (unsigned long) (this->end - this->start));
+ }
+
+ 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;
+ }
+
+ unsigned get_edit_count () { return edit_count; }
+
+ bool check_range (const void *base,
+ unsigned int len) const
+ {
+ const char *p = (const char *) base;
+ bool ok = !len ||
+ (this->start <= p &&
+ p <= this->end &&
+ (unsigned int) (this->end - p) >= len &&
+ this->max_ops-- > 0);
+
+ DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0,
+ "check_range [%p..%p]"
+ " (%d bytes) in [%p..%p] -> %s",
+ p, p + len, len,
+ this->start, this->end,
+ ok ? "OK" : "OUT-OF-RANGE");
+
+ return likely (ok);
+ }
+
+ template <typename T>
+ bool check_range (const T *base,
+ unsigned int a,
+ unsigned int b) const
+ {
+ return !hb_unsigned_mul_overflows (a, b) &&
+ this->check_range (base, a * b);
+ }
+
+ template <typename T>
+ bool check_range (const T *base,
+ unsigned int a,
+ unsigned int b,
+ unsigned int c) const
+ {
+ return !hb_unsigned_mul_overflows (a, b) &&
+ this->check_range (base, a * b, c);
+ }
+
+ 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, a, b, hb_static_size (T));
+ }
+
+ template <typename Type>
+ bool check_struct (const Type *obj) const
+ { return likely (this->check_range (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] (%d bytes) in [%p..%p] -> %s",
+ this->edit_count,
+ p, p + len, len,
+ this->start, this->end,
+ this->writable ? "GRANTED" : "DENIED");
+
+ return this->writable;
+ }
+
+ template <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 %d 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 %d edits in second round; FAILLING", 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;
+ mutable int max_ops, max_subtables;
+ private:
+ bool writable;
+ unsigned int edit_count;
+ hb_blob_t *blob;
+ unsigned int num_glyphs;
+ bool num_glyphs_set;
+};
+
+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/thirdparty/harfbuzz/src/hb-serialize.hh b/thirdparty/harfbuzz/src/hb-serialize.hh
new file mode 100644
index 0000000000..4566153a59
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-serialize.hh
@@ -0,0 +1,553 @@
+/*
+ * 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"
+
+
+/*
+ * Serialize
+ */
+
+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 () { links.fini (); }
+
+ bool operator == (const object_t &o) const
+ {
+ return (tail - head == o.tail - o.head)
+ && (links.length == o.links.length)
+ && 0 == hb_memcmp (head, o.head, tail - head)
+ && links.as_bytes () == o.links.as_bytes ();
+ }
+ uint32_t hash () const
+ {
+ return hb_bytes_t (head, tail - head).hash () ^
+ links.as_bytes ().hash ();
+ }
+
+ struct link_t
+ {
+ bool is_wide: 1;
+ bool is_signed: 1;
+ unsigned whence: 2;
+ unsigned position: 28;
+ unsigned bias;
+ objidx_t objidx;
+ };
+
+ char *head;
+ char *tail;
+ hb_vector_t<link_t> links;
+ object_t *next;
+ };
+
+ struct snapshot_t
+ {
+ char *head;
+ char *tail;
+ object_t *current; // Just for sanity check
+ unsigned num_links;
+ };
+
+ snapshot_t snapshot ()
+ { return snapshot_t { head, tail, current, current->links.length }; }
+
+ 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 ();
+ }
+ object_pool.fini ();
+ }
+
+ bool in_error () const { return !this->successful; }
+
+ void reset ()
+ {
+ this->successful = true;
+ this->ran_out_of_room = false;
+ this->head = this->start;
+ this->tail = this->end;
+ this->debug_depth = 0;
+
+ fini ();
+ this->packed.push (nullptr);
+ }
+
+ bool check_success (bool success)
+ { return this->successful && (success || (err_other_error (), false)); }
+
+ template <typename T1, typename T2>
+ bool check_equal (T1 &&v1, T2 &&v2)
+ { return check_success ((long long) v1 == (long long) v2); }
+
+ template <typename T1, typename T2>
+ bool check_assign (T1 &v1, T2 &&v2)
+ { return check_equal (v1 = v2, v2); }
+
+ 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 (hb_forward<T1> (o1)) &&
+ propagate_error (hb_forward<Ts> (os)...); }
+
+ /* To be called around main operation. */
+ template <typename Type>
+ 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),
+ this->successful ? "successful" : "UNSUCCESSFUL");
+
+ propagate_error (packed, packed_map);
+
+ if (unlikely (!current)) return;
+ if (unlikely (in_error())) 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>
+ 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;
+ if (unlikely (in_error())) return;
+
+ current = current->next;
+ revert (obj->head, obj->tail);
+ obj->fini ();
+ object_pool.free (obj);
+ }
+
+ /* Set share to false when an object is unlikely sharable 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;
+ if (unlikely (in_error())) return 0;
+
+ current = current->next;
+ obj->tail = head;
+ obj->next = nullptr;
+ unsigned len = obj->tail - obj->head;
+ head = obj->head; /* Rewind head. */
+
+ if (!len)
+ {
+ assert (!obj->links.length);
+ return 0;
+ }
+
+ objidx_t objidx;
+ if (share)
+ {
+ objidx = packed_map.get (obj);
+ if (objidx)
+ {
+ obj->fini ();
+ return objidx;
+ }
+ }
+
+ tail -= len;
+ memmove (tail, obj->head, len);
+
+ obj->head = tail;
+ obj->tail = tail + len;
+
+ packed.push (obj);
+
+ if (unlikely (packed.in_error ())) {
+ // obj wasn't successfully added to packed, so clean it up otherwise it's
+ // links will be leaked.
+ propagate_error (packed);
+ obj->fini ();
+ return 0;
+ }
+
+ objidx = packed.length - 1;
+
+ if (share) packed_map.set (obj, objidx);
+ propagate_error (packed_map);
+
+ return objidx;
+ }
+
+ void revert (snapshot_t snap)
+ {
+ if (unlikely (in_error ())) return;
+ assert (snap.current == current);
+ current->links.shrink (snap.num_links);
+ 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);
+ }
+
+ template <typename T>
+ void add_link (T &ofs, objidx_t objidx,
+ whence_t whence = Head,
+ unsigned bias = 0)
+ {
+ static_assert (sizeof (T) == 2 || sizeof (T) == 4, "");
+ if (unlikely (in_error ())) return;
+
+ if (!objidx)
+ return;
+
+ assert (current);
+ assert (current->head <= (const char *) &ofs);
+
+ auto& link = *current->links.push ();
+
+ link.is_wide = sizeof (T) == 4;
+ link.is_signed = hb_is_signed (hb_unwrap_type (T));
+ link.whence = (unsigned) whence;
+ link.position = (const char *) &ofs - current->head;
+ link.bias = bias;
+ link.objidx = objidx;
+ }
+
+ 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->links)
+ {
+ const object_t* child = packed[link.objidx];
+ if (unlikely (!child)) { err_other_error(); 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)
+ {
+ if (link.is_wide)
+ assign_offset<int32_t> (parent, link, offset);
+ else
+ assign_offset<int16_t> (parent, link, offset);
+ }
+ else
+ {
+ if (link.is_wide)
+ assign_offset<uint32_t> (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)
+ allocate_size<void> (alignment - l);
+ }
+
+ template <typename Type = void>
+ Type *start_embed (const Type *obj HB_UNUSED = nullptr) const
+ { return reinterpret_cast<Type *> (this->head); }
+ template <typename Type>
+ Type *start_embed (const Type &obj) const
+ { return start_embed (hb_addressof (obj)); }
+
+ /* Following two functions exist to allow setting breakpoint on. */
+ void err_ran_out_of_room () { this->ran_out_of_room = true; }
+ void err_other_error () { this->successful = false; }
+
+ template <typename Type>
+ Type *allocate_size (unsigned int size)
+ {
+ if (unlikely (!this->successful)) return nullptr;
+
+ if (this->tail - this->head < ptrdiff_t (size))
+ {
+ err_ran_out_of_room ();
+ this->successful = false;
+ return nullptr;
+ }
+ 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>
+ Type *embed (const Type *obj)
+ {
+ unsigned int size = obj->get_size ();
+ Type *ret = this->allocate_size<Type> (size);
+ if (unlikely (!ret)) return nullptr;
+ memcpy (ret, obj, size);
+ return ret;
+ }
+ template <typename Type>
+ Type *embed (const Type &obj)
+ { return embed (hb_addressof (obj)); }
+
+ template <typename Type, typename ...Ts> auto
+ _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN
+ (Type *, src.copy (this, hb_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 memcpy(). */
+ template <typename Type, typename ...Ts>
+ Type *copy (const Type &src, Ts&&... ds)
+ { return _copy (src, hb_prioritize, hb_forward<Ts> (ds)...); }
+ template <typename Type, typename ...Ts>
+ Type *copy (const Type *src, Ts&&... ds)
+ { return copy (*src, hb_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 (_, hb_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, unsigned int size)
+ {
+ if (unlikely (in_error ())) return nullptr;
+
+ assert (this->start <= (char *) obj);
+ assert ((char *) obj <= this->head);
+ assert ((char *) obj + size >= this->head);
+ if (unlikely (!this->allocate_size<Type> (((char *) obj) + size - this->head))) return nullptr;
+ return reinterpret_cast<Type *> (obj);
+ }
+ template <typename Type>
+ Type *extend_size (Type &obj, unsigned int size)
+ { return extend_size (hb_addressof (obj), size); }
+
+ 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 (hb_addressof (obj)); }
+
+ template <typename Type, typename ...Ts>
+ Type *extend (Type *obj, Ts&&... ds)
+ { return extend_size (obj, obj->get_size (hb_forward<Ts> (ds)...)); }
+ template <typename Type, typename ...Ts>
+ Type *extend (Type &obj, Ts&&... ds)
+ { return extend (hb_addressof (obj), hb_forward<Ts> (ds)...); }
+
+ /* Output routines. */
+ hb_bytes_t copy_bytes () const
+ {
+ assert (this->successful);
+ /* Copy both items from head side and tail side... */
+ unsigned int len = (this->head - this->start)
+ + (this->end - this->tail);
+
+ char *p = (char *) malloc (len);
+ if (unlikely (!p)) return hb_bytes_t ();
+
+ memcpy (p, this->start, this->head - this->start);
+ 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, free);
+ }
+
+ private:
+ template <typename T>
+ void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset)
+ {
+ auto &off = * ((BEInt<T, sizeof (T)> *) (parent->head + link.position));
+ assert (0 == off);
+ check_assign (off, offset);
+ }
+
+ public: /* TODO Make private. */
+ char *start, *head, *tail, *end;
+ unsigned int debug_depth;
+ bool successful;
+ bool ran_out_of_room;
+
+ private:
+
+ /* 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, nullptr, 0> packed_map;
+};
+
+
+#endif /* HB_SERIALIZE_HH */
diff --git a/thirdparty/harfbuzz/src/hb-set-digest.hh b/thirdparty/harfbuzz/src/hb-set-digest.hh
new file mode 100644
index 0000000000..b97526f775
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-set-digest.hh
@@ -0,0 +1,174 @@
+/*
+ * 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"
+
+/*
+ * 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 frozen-set can be used instead of a digest, to trade more
+ * memory for 100% accuracy, but in practice, that doesn't look like
+ * an attractive trade-off.
+ */
+
+template <typename mask_t, unsigned int shift>
+struct hb_set_digest_lowest_bits_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 (hb_codepoint_t g) { mask |= mask_for (g); }
+
+ bool add_range (hb_codepoint_t a, hb_codepoint_t b)
+ {
+ if ((b >> shift) - (a >> shift) >= mask_bits - 1)
+ mask = (mask_t) -1;
+ 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 = (const T *) (stride + (const char *) array);
+ }
+ }
+ template <typename T>
+ bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+ {
+ for (unsigned int i = 0; i < count; i++)
+ {
+ add (*array);
+ array = (const T *) (stride + (const char *) array);
+ }
+ return true;
+ }
+
+ 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 (hb_codepoint_t g)
+ {
+ head.add (g);
+ tail.add (g);
+ }
+
+ bool add_range (hb_codepoint_t a, hb_codepoint_t b)
+ {
+ head.add_range (a, b);
+ tail.add_range (a, b);
+ return true;
+ }
+ 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>
+ bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
+ {
+ head.add_sorted_array (array, count, stride);
+ tail.add_sorted_array (array, count, stride);
+ return true;
+ }
+
+ 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.
+ */
+typedef hb_set_digest_combiner_t
+<
+ hb_set_digest_lowest_bits_t<unsigned long, 4>,
+ hb_set_digest_combiner_t
+ <
+ hb_set_digest_lowest_bits_t<unsigned long, 0>,
+ hb_set_digest_lowest_bits_t<unsigned long, 9>
+ >
+> hb_set_digest_t;
+
+
+#endif /* HB_SET_DIGEST_HH */
diff --git a/thirdparty/harfbuzz/src/hb-set.cc b/thirdparty/harfbuzz/src/hb-set.cc
new file mode 100644
index 0000000000..0551ed80f2
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-set.cc
@@ -0,0 +1,541 @@
+/*
+ * 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: Object representing a set of integers
+ * @include: hb.h
+ *
+ * Set objects represent a mathematical set of integer values. They are
+ * used in non-shaping API to query certain set of characters or glyphs,
+ * or other integer values.
+ **/
+
+
+/**
+ * hb_set_create: (Xconstructor)
+ *
+ * Return value: (transfer full):
+ *
+ * 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 ();
+
+ set->init_shallow ();
+
+ return set;
+}
+
+/**
+ * hb_set_get_empty:
+ *
+ * Return value: (transfer full):
+ *
+ * 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.
+ *
+ * Return value: (transfer full):
+ *
+ * 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.
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_destroy (hb_set_t *set)
+{
+ if (!hb_object_destroy (set)) return;
+
+ set->fini_shallow ();
+
+ free (set);
+}
+
+/**
+ * hb_set_set_user_data: (skip)
+ * @set: a set.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ * Return value:
+ *
+ * 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:
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.2
+ **/
+void *
+hb_set_get_user_data (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.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 0.9.2
+ **/
+hb_bool_t
+hb_set_allocation_successful (const hb_set_t *set)
+{
+ return set->successful;
+}
+
+/**
+ * hb_set_clear:
+ * @set: a set.
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_clear (hb_set_t *set)
+{
+ set->clear ();
+}
+
+/**
+ * hb_set_is_empty:
+ * @set: a set.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_add (hb_set_t *set,
+ hb_codepoint_t codepoint)
+{
+ set->add (codepoint);
+}
+
+/**
+ * hb_set_add_range:
+ * @set: a set.
+ * @first:
+ * @last:
+ *
+ *
+ *
+ * Since: 0.9.7
+ **/
+void
+hb_set_add_range (hb_set_t *set,
+ hb_codepoint_t first,
+ hb_codepoint_t last)
+{
+ set->add_range (first, last);
+}
+
+/**
+ * hb_set_del:
+ * @set: a set.
+ * @codepoint:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_del (hb_set_t *set,
+ hb_codepoint_t codepoint)
+{
+ set->del (codepoint);
+}
+
+/**
+ * hb_set_del_range:
+ * @set: a set.
+ * @first:
+ * @last:
+ *
+ *
+ *
+ * Since: 0.9.7
+ **/
+void
+hb_set_del_range (hb_set_t *set,
+ hb_codepoint_t first,
+ hb_codepoint_t last)
+{
+ set->del_range (first, last);
+}
+
+/**
+ * hb_set_is_equal:
+ * @set: a set.
+ * @other: other set.
+ *
+ *
+ *
+ * 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_is_subset:
+ * @set: a set.
+ * @larger_set: other 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:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_set (hb_set_t *set,
+ const hb_set_t *other)
+{
+ set->set (other);
+}
+
+/**
+ * hb_set_union:
+ * @set: a set.
+ * @other:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_union (hb_set_t *set,
+ const hb_set_t *other)
+{
+ set->union_ (other);
+}
+
+/**
+ * hb_set_intersect:
+ * @set: a set.
+ * @other:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_intersect (hb_set_t *set,
+ const hb_set_t *other)
+{
+ set->intersect (other);
+}
+
+/**
+ * hb_set_subtract:
+ * @set: a set.
+ * @other:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_subtract (hb_set_t *set,
+ const hb_set_t *other)
+{
+ set->subtract (other);
+}
+
+/**
+ * hb_set_symmetric_difference:
+ * @set: a set.
+ * @other:
+ *
+ *
+ *
+ * Since: 0.9.2
+ **/
+void
+hb_set_symmetric_difference (hb_set_t *set,
+ const hb_set_t *other)
+{
+ set->symmetric_difference (other);
+}
+
+#ifndef HB_DISABLE_DEPRECATED
+/**
+ * hb_set_invert:
+ * @set: a set.
+ *
+ *
+ *
+ * Since: 0.9.10
+ *
+ * Deprecated: 1.6.1
+ **/
+void
+hb_set_invert (hb_set_t *set HB_UNUSED)
+{
+}
+#endif
+
+/**
+ * hb_set_get_population:
+ * @set: a set.
+ *
+ * Returns the number of numbers in the set.
+ *
+ * Return value: set population.
+ *
+ * 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 minimum number in the set.
+ *
+ * Return value: minimum of the 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 maximum number in the set.
+ *
+ * Return value: minimum of the 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):
+ *
+ * Gets the next number in @set that is greater than current value of @codepoint.
+ *
+ * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a next value.
+ *
+ * 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):
+ *
+ * Gets the previous number in @set that is lower than current value of @codepoint.
+ *
+ * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a previous value.
+ *
+ * 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): output first codepoint in the range.
+ * @last: (inout): input current last and output last codepoint in the range.
+ *
+ * Gets the next consecutive range of numbers in @set that
+ * are greater than current value of @last.
+ *
+ * Set @last to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a next range.
+ *
+ * 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 current first and output first codepoint in the range.
+ * @last: (out): output last codepoint in the range.
+ *
+ * Gets the previous consecutive range of numbers in @set that
+ * are less than current value of @first.
+ *
+ * Set @first to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a previous range.
+ *
+ * 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);
+}
diff --git a/thirdparty/harfbuzz/src/hb-set.h b/thirdparty/harfbuzz/src/hb-set.h
new file mode 100644
index 0000000000..ed0e05db2e
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-set.h
@@ -0,0 +1,167 @@
+/*
+ * 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_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_SET_H
+#define HB_SET_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * Since: 0.9.21
+ */
+#define HB_SET_VALUE_INVALID ((hb_codepoint_t) -1)
+
+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 (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 void
+hb_set_clear (hb_set_t *set);
+
+HB_EXTERN hb_bool_t
+hb_set_is_empty (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_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 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);
+
+
+HB_END_DECLS
+
+#endif /* HB_SET_H */
diff --git a/thirdparty/harfbuzz/src/hb-set.hh b/thirdparty/harfbuzz/src/hb-set.hh
new file mode 100644
index 0000000000..b6e2086a2e
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-set.hh
@@ -0,0 +1,884 @@
+/*
+ * Copyright © 2012,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_SET_HH
+#define HB_SET_HH
+
+#include "hb.hh"
+#include "hb-machinery.hh"
+
+
+/*
+ * hb_set_t
+ */
+
+/* TODO Keep a free-list so we can free pages that are completely zeroed. At that
+ * point maybe also use a sentinel value for "all-1" pages? */
+
+struct hb_set_t
+{
+ HB_DELETE_COPY_ASSIGN (hb_set_t);
+ hb_set_t () { init (); }
+ ~hb_set_t () { fini (); }
+
+ struct page_map_t
+ {
+ int cmp (const page_map_t &o) const { return (int) o.major - (int) major; }
+
+ uint32_t major;
+ uint32_t index;
+ };
+
+ struct page_t
+ {
+ void init0 () { v.clear (); }
+ void init1 () { v.clear (0xFF); }
+
+ unsigned int len () const
+ { return ARRAY_LENGTH_CONST (v); }
+
+ bool is_empty () const
+ {
+ for (unsigned int i = 0; i < len (); i++)
+ if (v[i])
+ return false;
+ return true;
+ }
+
+ void add (hb_codepoint_t g) { elt (g) |= mask (g); }
+ void del (hb_codepoint_t g) { elt (g) &= ~mask (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) - 1);
+ la++;
+
+ memset (la, 0xff, (char *) lb - (char *) la);
+
+ *lb |= ((mask (b) << 1) - 1);
+ }
+ }
+
+ 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) << 1) - mask(a));
+ else
+ {
+ *la &= mask (a) - 1;
+ la++;
+
+ memset (la, 0, (char *) lb - (char *) la);
+
+ *lb &= ~((mask (b) << 1) - 1);
+ }
+ }
+
+ bool is_equal (const page_t *other) const
+ {
+ return 0 == hb_memcmp (&v, &other->v, sizeof (v));
+ }
+
+ unsigned int get_population () const
+ {
+ unsigned int pop = 0;
+ for (unsigned int i = 0; i < len (); i++)
+ pop += hb_popcount (v[i]);
+ return pop;
+ }
+
+ 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;
+ }
+
+ typedef unsigned long long elt_t;
+ static constexpr unsigned PAGE_BITS = 512;
+ static_assert ((PAGE_BITS & ((PAGE_BITS) - 1)) == 0, "");
+
+ 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]; }
+ elt_t const &elt (hb_codepoint_t g) const { return v[(g & MASK) / ELT_BITS]; }
+ elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & ELT_MASK); }
+
+ vector_t v;
+ };
+ static_assert (page_t::PAGE_BITS == sizeof (page_t) * 8, "");
+
+ hb_object_header_t header;
+ bool successful; /* Allocations successful */
+ mutable unsigned int population;
+ hb_sorted_vector_t<page_map_t> page_map;
+ hb_vector_t<page_t> pages;
+
+ void init_shallow ()
+ {
+ successful = true;
+ population = 0;
+ page_map.init ();
+ pages.init ();
+ }
+ void init ()
+ {
+ hb_object_init (this);
+ init_shallow ();
+ }
+ void fini_shallow ()
+ {
+ population = 0;
+ page_map.fini ();
+ pages.fini ();
+ }
+ void fini ()
+ {
+ hb_object_fini (this);
+ fini_shallow ();
+ }
+
+ bool in_error () const { return !successful; }
+
+ bool resize (unsigned int count)
+ {
+ if (unlikely (!successful)) return false;
+ if (!pages.resize (count) || !page_map.resize (count))
+ {
+ pages.resize (page_map.length);
+ successful = false;
+ return false;
+ }
+ return true;
+ }
+
+ void reset ()
+ {
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+ clear ();
+ successful = true;
+ }
+
+ void clear ()
+ {
+ if (unlikely (hb_object_is_immutable (this)))
+ return;
+ population = 0;
+ page_map.resize (0);
+ pages.resize (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;
+ }
+
+ void dirty () { population = UINT_MAX; }
+
+ void add (hb_codepoint_t g)
+ {
+ if (unlikely (!successful)) return;
+ if (unlikely (g == INVALID)) return;
+ dirty ();
+ page_t *page = page_for_insert (g); 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_insert (a); if (unlikely (!page)) return false;
+ page->add_range (a, b);
+ }
+ else
+ {
+ page_t *page = page_for_insert (a); 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_insert (major_start (m)); if (unlikely (!page)) return false;
+ page->init1 ();
+ }
+
+ page = page_for_insert (b); if (unlikely (!page)) return false;
+ page->add_range (major_start (mb), b);
+ }
+ return true;
+ }
+
+ template <typename T>
+ void add_array (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_insert (g); if (unlikely (!page)) return;
+ unsigned int start = major_start (m);
+ unsigned int end = major_start (m + 1);
+ do
+ {
+ page->add (g);
+
+ array = &StructAtOffsetUnaligned<T> (array, stride);
+ count--;
+ }
+ while (count && (g = *array, start <= g && g < end));
+ }
+ }
+
+ /* 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))
+ {
+ if (unlikely (!successful)) return true; /* https://github.com/harfbuzz/harfbuzz/issues/657 */
+ if (!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_insert (g); if (unlikely (!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;
+ page->add (g);
+
+ array = (const T *) ((const char *) array + stride);
+ count--;
+ }
+ while (count && (g = *array, g < end));
+ }
+ return true;
+ }
+
+ void del (hb_codepoint_t g)
+ {
+ /* TODO perform op even if !successful. */
+ 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)
+ {
+ 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 (write_index);
+ resize (write_index);
+ }
+ }
+
+ public:
+ void del_range (hb_codepoint_t a, hb_codepoint_t b)
+ {
+ /* TODO perform op even if !successful. */
+ if (unlikely (!successful)) return;
+ if (unlikely (a > b || a == INVALID || b == 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. */
+ static constexpr bool SENTINEL = false;
+ typedef bool value_t;
+ value_t operator [] (hb_codepoint_t k) const { return get (k); }
+ bool has (hb_codepoint_t k) const { return (*this)[k] != SENTINEL; }
+ /* Predicate. */
+ bool operator () (hb_codepoint_t k) const { return has (k); }
+
+ /* Sink interface. */
+ hb_set_t& operator << (hb_codepoint_t v)
+ { add (v); return *this; }
+ hb_set_t& operator << (const hb_pair_t<hb_codepoint_t, hb_codepoint_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_set_t *other)
+ {
+ if (unlikely (!successful)) return;
+ unsigned int count = other->pages.length;
+ if (!resize (count))
+ return;
+ population = other->population;
+ memcpy ((void *) pages, (const void *) other->pages, count * pages.item_size);
+ memcpy ((void *) page_map, (const void *) other->page_map, count * page_map.item_size);
+ }
+
+ bool is_equal (const hb_set_t *other) const
+ {
+ if (get_population () != other->get_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_set_t *larger_set) const
+ {
+ if (get_population () > larger_set->get_population ())
+ return false;
+
+ /* TODO Optimize to use pages. */
+ hb_codepoint_t c = INVALID;
+ while (next (&c))
+ if (!larger_set->has (c))
+ return false;
+
+ return true;
+ }
+
+ void compact (unsigned int length)
+ {
+ hb_vector_t<uint32_t> old_index_to_page_map_index;
+ old_index_to_page_map_index.resize(pages.length);
+ for (uint32_t i = 0; i < old_index_to_page_map_index.length; i++)
+ old_index_to_page_map_index[i] = 0xFFFFFFFF;
+
+ for (uint32_t 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<uint32_t>& old_index_to_page_map_index)
+ {
+ unsigned int write_index = 0;
+ for (unsigned int i = 0; i < pages.length; i++)
+ {
+ if (old_index_to_page_map_index[i] == 0xFFFFFFFF) continue;
+
+ if (write_index < i)
+ pages[write_index] = pages[i];
+
+ page_map[old_index_to_page_map_index[i]].index = write_index;
+ write_index++;
+ }
+ }
+
+ template <typename Op>
+ void process (const Op& op, const hb_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;
+ for (; a < na && b < nb; )
+ {
+ if (page_map[a].major == other->page_map[b].major)
+ {
+ if (!Op::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 (Op::passthru_left)
+ count++;
+ a++;
+ }
+ else
+ {
+ if (Op::passthru_right)
+ count++;
+ b++;
+ }
+ }
+ if (Op::passthru_left)
+ count += na - a;
+ if (Op::passthru_right)
+ count += nb - b;
+
+ if (!Op::passthru_left)
+ {
+ na = write_index;
+ next_page = write_index;
+ compact (write_index);
+ }
+
+ if (!resize (count))
+ return;
+
+ newCount = count;
+
+ /* Process in-place backward. */
+ a = na;
+ b = nb;
+ for (; a && b; )
+ {
+ if (page_map[a - 1].major == other->page_map[b - 1].major)
+ {
+ a--;
+ b--;
+ count--;
+ page_map[count] = page_map[a];
+ page_at (count).v = op (page_at (a).v, other->page_at (b).v);
+ }
+ else if (page_map[a - 1].major > other->page_map[b - 1].major)
+ {
+ a--;
+ if (Op::passthru_left)
+ {
+ count--;
+ page_map[count] = page_map[a];
+ }
+ }
+ else
+ {
+ b--;
+ if (Op::passthru_right)
+ {
+ count--;
+ page_map[count].major = other->page_map[b].major;
+ page_map[count].index = next_page++;
+ page_at (count).v = other->page_at (b).v;
+ }
+ }
+ }
+ if (Op::passthru_left)
+ while (a)
+ {
+ a--;
+ count--;
+ page_map[count] = page_map [a];
+ }
+ if (Op::passthru_right)
+ while (b)
+ {
+ b--;
+ count--;
+ page_map[count].major = other->page_map[b].major;
+ page_map[count].index = next_page++;
+ page_at (count).v = other->page_at (b).v;
+ }
+ assert (!count);
+ if (pages.length > newCount)
+ resize (newCount);
+ }
+
+ void union_ (const hb_set_t *other)
+ {
+ process (hb_bitwise_or, other);
+ }
+ void intersect (const hb_set_t *other)
+ {
+ process (hb_bitwise_and, other);
+ }
+ void subtract (const hb_set_t *other)
+ {
+ process (hb_bitwise_sub, other);
+ }
+ void symmetric_difference (const hb_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;
+ }
+
+ page_map_t map = {get_major (*codepoint), 0};
+ unsigned int i;
+ page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST);
+ if (i < page_map.length && page_map[i].major == map.major)
+ {
+ if (pages[page_map[i].index].next (codepoint))
+ {
+ *codepoint += page_map[i].major * page_t::PAGE_BITS;
+ return true;
+ }
+ i++;
+ }
+ for (; i < page_map.length; i++)
+ {
+ hb_codepoint_t m = pages[page_map[i].index].get_min ();
+ if (m != INVALID)
+ {
+ *codepoint = page_map[i].major * page_t::PAGE_BITS + m;
+ 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_BFIND_NOT_FOUND_STORE_CLOSEST);
+ if (i < page_map.length && page_map[i].major == map.major)
+ {
+ if (pages[page_map[i].index].previous (codepoint))
+ {
+ *codepoint += page_map[i].major * page_t::PAGE_BITS;
+ return true;
+ }
+ }
+ i--;
+ for (; (int) i >= 0; i--)
+ {
+ hb_codepoint_t m = pages[page_map[i].index].get_max ();
+ if (m != INVALID)
+ {
+ *codepoint = page_map[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 get_population () const
+ {
+ if (population != UINT_MAX)
+ 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 int count = pages.length;
+ for (unsigned int i = 0; i < count; i++)
+ if (!page_at (i).is_empty ())
+ return page_map[i].major * page_t::PAGE_BITS + page_at (i).get_min ();
+ return INVALID;
+ }
+ hb_codepoint_t get_max () const
+ {
+ unsigned int count = pages.length;
+ for (int i = count - 1; i >= 0; i++)
+ if (!page_at (i).is_empty ())
+ return page_map[(unsigned) i].major * page_t::PAGE_BITS + page_at (i).get_max ();
+ return INVALID;
+ }
+
+ static constexpr hb_codepoint_t INVALID = HB_SET_VALUE_INVALID;
+
+ /*
+ * Iterator implementation.
+ */
+ struct iter_t : hb_iter_with_fallback_t<iter_t, hb_codepoint_t>
+ {
+ static constexpr bool is_sorted_iterator = true;
+ iter_t (const hb_set_t &s_ = Null (hb_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_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_insert (hb_codepoint_t g)
+ {
+ page_map_t map = {get_major (g), pages.length};
+ unsigned int i;
+ if (!page_map.bfind (map, &i, HB_BFIND_NOT_FOUND_STORE_CLOSEST))
+ {
+ if (!resize (pages.length + 1))
+ return nullptr;
+
+ pages[map.index].init0 ();
+ memmove (page_map + i + 1,
+ page_map + i,
+ (page_map.length - 1 - i) * page_map.item_size);
+ page_map[i] = map;
+ }
+ return &pages[page_map[i].index];
+ }
+ page_t *page_for (hb_codepoint_t g)
+ {
+ page_map_t key = {get_major (g)};
+ const page_map_t *found = page_map.bsearch (key);
+ if (found)
+ return &pages[found->index];
+ return nullptr;
+ }
+ const page_t *page_for (hb_codepoint_t g) const
+ {
+ page_map_t key = {get_major (g)};
+ const page_map_t *found = page_map.bsearch (key);
+ if (found)
+ return &pages[found->index];
+ return nullptr;
+ }
+ page_t &page_at (unsigned int i) { return pages[page_map[i].index]; }
+ const page_t &page_at (unsigned int i) const { return pages[page_map[i].index]; }
+ unsigned int get_major (hb_codepoint_t g) const { return g / page_t::PAGE_BITS; }
+ hb_codepoint_t major_start (unsigned int major) const { return major * page_t::PAGE_BITS; }
+};
+
+
+#endif /* HB_SET_HH */
diff --git a/thirdparty/harfbuzz/src/hb-shape-plan.cc b/thirdparty/harfbuzz/src/hb-shape-plan.cc
new file mode 100644
index 0000000000..666470b4f1
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-shape-plan.cc
@@ -0,0 +1,513 @@
+/*
+ * 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"
+
+
+/**
+ * SECTION:hb-shape-plan
+ * @title: hb-shape-plan
+ * @short_description: Object representing a shaping plan
+ * @include: hb.h
+ *
+ * Shape plans are not used for shaping directly, but can be access to query
+ * certain information about how shaping will perform given a set of input
+ * parameters (script, language, direction, features, etc.)
+ * Most client would 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 *) 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)
+ {
+ 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_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:
+ ::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: (Xconstructor)
+ * @face:
+ * @props:
+ * @user_features: (array length=num_user_features):
+ * @num_user_features:
+ * @shaper_list: (array zero-terminated=1):
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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_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=%d num_coords=%d shaper_list=%p",
+ face,
+ num_user_features,
+ num_coords,
+ shaper_list);
+
+ assert (props->direction != HB_DIRECTION_INVALID);
+
+ 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.free ();
+bail2:
+ free (shape_plan);
+bail:
+ return hb_shape_plan_get_empty ();
+}
+
+/**
+ * hb_shape_plan_get_empty:
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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 shape plan.
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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 shape plan.
+ *
+ *
+ *
+ * Since: 0.9.7
+ **/
+void
+hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
+{
+ if (!hb_object_destroy (shape_plan)) return;
+
+#ifndef HB_NO_OT_SHAPE
+ shape_plan->ot.fini ();
+#endif
+ shape_plan->key.free ();
+ free (shape_plan);
+}
+
+/**
+ * hb_shape_plan_set_user_data: (skip)
+ * @shape_plan: a shape plan.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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 shape plan.
+ * @key:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.7
+ **/
+void *
+hb_shape_plan_get_user_data (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 shape plan.
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.7
+ **/
+const char *
+hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
+{
+ return shape_plan->key.shaper_name;
+}
+
+
+/**
+ * hb_shape_plan_execute:
+ * @shape_plan: a shape plan.
+ * @font: a font.
+ * @buffer: a buffer.
+ * @features: (array length=num_features):
+ * @num_features:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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)
+{
+ DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
+ "num_features=%d 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));
+ assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+
+ if (unlikely (hb_object_is_inert (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;
+}
+
+
+/*
+ * Caching
+ */
+
+/**
+ * hb_shape_plan_create_cached:
+ * @face:
+ * @props:
+ * @user_features: (array length=num_user_features):
+ * @num_user_features:
+ * @shaper_list: (array zero-terminated=1):
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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_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=%d 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_inert (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 *) 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);
+ free (node);
+ goto retry;
+ }
+ DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
+
+ return hb_shape_plan_reference (shape_plan);
+}
diff --git a/thirdparty/harfbuzz/src/hb-shape-plan.h b/thirdparty/harfbuzz/src/hb-shape-plan.h
new file mode 100644
index 0000000000..b62ae7ca35
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-shape-plan.h
@@ -0,0 +1,108 @@
+/*
+ * 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_H_IN
+#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
+
+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 (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/thirdparty/harfbuzz/src/hb-shape-plan.hh b/thirdparty/harfbuzz/src/hb-shape-plan.hh
new file mode 100644
index 0000000000..6da7edb2f8
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-shape-plan.hh
@@ -0,0 +1,76 @@
+/*
+ * 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 free () { ::free ((void *) user_features); }
+
+ 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_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/thirdparty/harfbuzz/src/hb-shape.cc b/thirdparty/harfbuzz/src/hb-shape.cc
new file mode 100644
index 0000000000..017fb91b69
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-shape.cc
@@ -0,0 +1,172 @@
+/*
+ * 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"
+
+
+/**
+ * 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.
+ **/
+
+
+#if HB_USE_ATEXIT
+static void free_static_shaper_list ();
+#endif
+
+static const char *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 **) 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;
+
+#if HB_USE_ATEXIT
+ atexit (free_static_shaper_list);
+#endif
+
+ return shaper_list;
+ }
+ static void destroy (const char **l)
+ { free (l); }
+ static const char ** get_null ()
+ { return nil_shaper_list; }
+} static_shaper_list;
+
+#if HB_USE_ATEXIT
+static
+void free_static_shaper_list ()
+{
+ static_shaper_list.free_instance ();
+}
+#endif
+
+
+/**
+ * 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) (allow-none): an array of user
+ * specified #hb_feature_t or %NULL
+ * @num_features: the length of @features array
+ * @shaper_list: (array zero-terminated=1) (allow-none): 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 (hb_object_is_immutable (buffer))) return false;
+
+ 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);
+ hb_shape_plan_destroy (shape_plan);
+
+ if (res)
+ buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
+ 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) (allow-none): 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);
+}
diff --git a/thirdparty/harfbuzz/src/hb-shape.h b/thirdparty/harfbuzz/src/hb-shape.h
new file mode 100644
index 0000000000..39507ff744
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-shape.h
@@ -0,0 +1,62 @@
+/*
+ * 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
+ */
+
+#ifndef HB_H_IN
+#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 const char **
+hb_shape_list_shapers (void);
+
+
+HB_END_DECLS
+
+#endif /* HB_SHAPE_H */
diff --git a/thirdparty/harfbuzz/src/hb-shaper-impl.hh b/thirdparty/harfbuzz/src/hb-shaper-impl.hh
new file mode 100644
index 0000000000..b674fceb6a
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-shaper-list.hh b/thirdparty/harfbuzz/src/hb-shaper-list.hh
new file mode 100644
index 0000000000..0d63933a76
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-shaper-list.hh
@@ -0,0 +1,60 @@
+/*
+ * 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_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 OpenType 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/thirdparty/harfbuzz/src/hb-shaper.cc b/thirdparty/harfbuzz/src/hb-shaper.cc
new file mode 100644
index 0000000000..0ea68ad1f5
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-shaper.cc
@@ -0,0 +1,108 @@
+/*
+ * 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 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 (all_shapers), "No shaper enabled.");
+#endif
+
+#if HB_USE_ATEXIT
+static void free_static_shapers ();
+#endif
+
+static struct hb_shapers_lazy_loader_t : hb_lazy_loader_t<const 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 *) calloc (1, sizeof (all_shapers));
+ if (unlikely (!shapers))
+ return nullptr;
+
+ memcpy (shapers, all_shapers, sizeof (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 (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;
+ }
+
+#if HB_USE_ATEXIT
+ atexit (free_static_shapers);
+#endif
+
+ return shapers;
+ }
+ static void destroy (const hb_shaper_entry_t *p) { free ((void *) p); }
+ static const hb_shaper_entry_t *get_null () { return all_shapers; }
+} static_shapers;
+
+#if HB_USE_ATEXIT
+static
+void free_static_shapers ()
+{
+ static_shapers.free_instance ();
+}
+#endif
+
+const hb_shaper_entry_t *
+_hb_shapers_get ()
+{
+ return static_shapers.get_unconst ();
+}
diff --git a/thirdparty/harfbuzz/src/hb-shaper.hh b/thirdparty/harfbuzz/src/hb-shaper.hh
new file mode 100644
index 0000000000..b4138a324f
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-static.cc b/thirdparty/harfbuzz/src/hb-static.cc
new file mode 100644
index 0000000000..f5b7fa50a0
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-static.cc
@@ -0,0 +1,112 @@
+/*
+ * 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-ot-layout-common.hh"
+#include "hb-ot-cmap-table.hh"
+#include "hb-ot-head-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, LangSys) = {0x00,0x00, 0xFF,0xFF, 0x00,0x00};
+DEFINE_NULL_NAMESPACE_BYTES (OT, RangeRecord) = {0x00,0x01, 0x00,0x00, 0x00, 0x00};
+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};
+/* Hand-coded because Lookup is a template. Sad. */
+const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF};
+
+
+
+/* hb_face_t */
+
+unsigned int
+hb_face_t::load_num_glyphs () const
+{
+ hb_sanitize_context_t c = hb_sanitize_context_t ();
+ c.set_num_glyphs (0); /* So we don't recurse ad infinitum. */
+ hb_blob_t *maxp_blob = c.reference_table<OT::maxp> (this);
+ const OT::maxp *maxp_table = maxp_blob->as<OT::maxp> ();
+
+ unsigned int ret = maxp_table->get_num_glyphs ();
+ num_glyphs.set_relaxed (ret);
+ hb_blob_destroy (maxp_blob);
+ return ret;
+}
+
+unsigned int
+hb_face_t::load_upem () const
+{
+ unsigned int ret = table.head->get_upem ();
+ upem.set_relaxed (ret);
+ return ret;
+}
+
+
+/* hb_user_data_array_t */
+
+bool
+hb_user_data_array_t::set (hb_user_data_key_t *key,
+ void * data,
+ hb_destroy_func_t destroy,
+ hb_bool_t replace)
+{
+ if (!key)
+ return false;
+
+ if (replace) {
+ if (!data && !destroy) {
+ items.remove (key, lock);
+ return true;
+ }
+ }
+ hb_user_data_item_t item = {key, data, destroy};
+ bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
+
+ return ret;
+}
+
+void *
+hb_user_data_array_t::get (hb_user_data_key_t *key)
+{
+ hb_user_data_item_t item = {nullptr, nullptr, nullptr};
+
+ return items.find (key, &item, lock) ? item.data : nullptr;
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-string-array.hh b/thirdparty/harfbuzz/src/hb-string-array.hh
new file mode 100644
index 0000000000..e7ac119232
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-style.cc b/thirdparty/harfbuzz/src/hb-style.cc
new file mode 100644
index 0000000000..86b9f7da5f
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-style.cc
@@ -0,0 +1,135 @@
+/*
+ * 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
+#ifdef HB_EXPERIMENTAL_API
+
+#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"
+
+/**
+ * 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: 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.
+ * @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 https://docs.microsoft.com/en-us/typography/opentype/spec/dvaraxisreg
+ *
+ * Since: EXPERIMENTAL
+ **/
+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 = 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'),
+
+ _HB_STYLE_TAG_MAX_VALUE = HB_TAG_MAX_SIGNED /*< skip >*/
+} hb_style_tag_t;
+
+/**
+ * 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: EXPERIMENTAL
+ **/
+float
+hb_style_get_value (hb_font_t *font, hb_tag_t tag)
+{
+ hb_style_tag_t style_tag = (hb_style_tag_t) tag;
+ 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, upper;
+ return face->table.OS2->v5 ().get_optical_size (&lower, &upper)
+ ? (float) (lower + upper) / 2.f
+ : 12.f;
+ }
+ case HB_STYLE_TAG_SLANT:
+ return face->table.post->table->italicAngle.to_float ();
+ case HB_STYLE_TAG_WIDTH:
+ return face->table.OS2->has_data ()
+ ? face->table.OS2->get_width ()
+ : (face->table.head->is_condensed () ? 75 : 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
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-style.h b/thirdparty/harfbuzz/src/hb-style.h
new file mode 100644
index 0000000000..1209c79e94
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-style.h
@@ -0,0 +1,43 @@
+/*
+ * 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_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_STYLE_H
+#define HB_STYLE_H
+
+#include "hb.h"
+
+HB_BEGIN_DECLS
+
+#ifdef HB_EXPERIMENTAL_API
+HB_EXTERN float
+hb_style_get_value (hb_font_t *font, hb_tag_t style_tag);
+#endif
+
+HB_END_DECLS
+
+#endif /* HB_STYLE_H */
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.cc b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc
new file mode 100644
index 0000000000..04e1db24ac
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc
@@ -0,0 +1,227 @@
+/*
+ * 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;
+
+/**
+ * hb_plan_subset_cff_fdselect
+ * 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_set_create ();
+ if (unlikely (set == &Null (hb_set_t))) return false;
+ hb_codepoint_t prev_fd = CFF_UNDEF_CODE;
+ for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
+ {
+ hb_codepoint_t glyph;
+ hb_codepoint_t fd;
+ if (!plan->old_gid_for_new_gid (i, &glyph))
+ {
+ /* fonttools retains FDSelect & font dicts for missing glyphs. do the same */
+ glyph = i;
+ }
+ fd = src.get_fd (glyph);
+ set->add (fd);
+
+ if (fd != prev_fd)
+ {
+ num_ranges++;
+ prev_fd = fd;
+ code_pair_t pair = { fd, i };
+ fdselect_ranges.push (pair);
+ }
+ }
+
+ 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);
+ hb_set_destroy (set);
+ }
+ else
+ {
+ /* create a fdmap */
+ fdmap.reset ();
+
+ hb_codepoint_t fd = CFF_UNDEF_CODE;
+ while (set->next (&fd))
+ fdmap.add (fd);
+ hb_set_destroy (set);
+ 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);
+}
+
+/**
+ * hb_serialize_cff_fdselect
+ * 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/thirdparty/harfbuzz/src/hb-subset-cff-common.hh b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
new file mode 100644
index 0000000000..422b20b8d0
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh
@@ -0,0 +1,989 @@
+/*
+ * 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_), error (false) {}
+
+ void reset () { buff.resize (0); }
+
+ void encode_byte (unsigned char b)
+ {
+ if (unlikely (buff.push (b) == &Crap (unsigned char)))
+ set_error ();
+ }
+
+ 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);
+ }
+ }
+
+ void encode_num (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);
+ }
+ }
+
+ 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 byte_str_t &str)
+ {
+ unsigned int offset = buff.length;
+ if (unlikely (!buff.resize (offset + str.length)))
+ {
+ set_error ();
+ return;
+ }
+ if (unlikely (buff.length < offset + str.length))
+ {
+ set_error ();
+ return;
+ }
+ memcpy (&buff[offset], &str[0], str.length);
+ }
+
+ bool is_error () const { return error; }
+
+ protected:
+ void set_error () { error = true; }
+
+ str_buff_t &buff;
+ bool error;
+};
+
+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
+ {
+ HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length);
+ if (unlikely (!d)) return_trace (false);
+ memcpy (d, &opstr.str[0], opstr.str.length);
+ }
+ return_trace (true);
+ }
+};
+
+struct cff_private_dict_op_serializer_t : op_serializer_t
+{
+ cff_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 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));
+ }
+ else
+ return_trace (copy_opstr (c, opstr));
+ }
+
+ protected:
+ const bool desubroutinize;
+ const bool drop_hints;
+};
+
+struct flatten_param_t
+{
+ str_buff_t &flatStr;
+ bool drop_hints;
+};
+
+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)
+ {
+ if (!flat_charstrings.resize (plan->num_output_glyphs ()))
+ return false;
+ for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ flat_charstrings[i].init ();
+ for (unsigned int i = 0; i < plan->num_output_glyphs (); 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 byte_str_t str = (*acc.charStrings)[glyph];
+ unsigned int fd = acc.fdSelect->get_fd (glyph);
+ if (unlikely (fd >= acc.fdCount))
+ return false;
+ cs_interpreter_t<ENV, OPSET, flatten_param_t> interp;
+ interp.env.init (str, acc, fd);
+ flatten_param_t param = { flat_charstrings[i], plan->drop_hints };
+ 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 () : valid (false), global_closure (nullptr)
+ { local_closures.init (); }
+
+ void init (unsigned int fd_count)
+ {
+ valid = true;
+ global_closure = hb_set_create ();
+ if (global_closure == hb_set_get_empty ())
+ valid = false;
+ if (!local_closures.resize (fd_count))
+ valid = false;
+
+ for (unsigned int i = 0; i < local_closures.length; i++)
+ {
+ local_closures[i] = hb_set_create ();
+ if (local_closures[i] == hb_set_get_empty ())
+ valid = false;
+ }
+ }
+
+ void fini ()
+ {
+ hb_set_destroy (global_closure);
+ for (unsigned int i = 0; i < local_closures.length; i++)
+ hb_set_destroy (local_closures[i]);
+ local_closures.fini ();
+ }
+
+ void reset ()
+ {
+ hb_set_clear (global_closure);
+ for (unsigned int i = 0; i < local_closures.length; i++)
+ hb_set_clear (local_closures[i]);
+ }
+
+ bool is_valid () const { return valid; }
+ bool valid;
+ hb_set_t *global_closure;
+ hb_vector_t<hb_set_t *> local_closures;
+};
+
+struct parsed_cs_op_t : op_str_t
+{
+ void init (unsigned int subr_num_ = 0)
+ {
+ op_str_t::init ();
+ subr_num = subr_num_;
+ drop_flag = false;
+ keep_flag = false;
+ skip_flag = false;
+ }
+
+ void fini () { op_str_t::fini (); }
+
+ bool for_drop () const { return drop_flag; }
+ void set_drop () { if (!for_keep ()) drop_flag = true; }
+
+ bool for_keep () const { return keep_flag; }
+ void set_keep () { keep_flag = true; }
+
+ bool for_skip () const { return skip_flag; }
+ void set_skip () { skip_flag = true; }
+
+ unsigned int subr_num;
+
+ protected:
+ bool drop_flag : 1;
+ bool keep_flag : 1;
+ bool skip_flag : 1;
+};
+
+struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t>
+{
+ void init ()
+ {
+ SUPER::init ();
+ parsed = false;
+ hint_dropped = false;
+ has_prefix_ = false;
+ }
+
+ 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 ())
+ {
+ unsigned int parsed_len = get_count ();
+ if (likely (parsed_len > 0))
+ values[parsed_len-1].set_skip ();
+
+ parsed_cs_op_t val;
+ val.init (subr_num);
+ SUPER::add_op (op, str_ref, val);
+ }
+ }
+
+ 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_; }
+
+ protected:
+ bool parsed;
+ bool hint_dropped;
+ bool vsindex_dropped;
+ bool has_prefix_;
+ 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>
+{
+ void init (unsigned int len_ = 0)
+ {
+ SUPER::init ();
+ if (unlikely (!resize (len_)))
+ return;
+ for (unsigned int i = 0; i < length; i++)
+ (*this)[i].init ();
+ }
+ void fini () { SUPER::fini_deep (); }
+
+ private:
+ typedef hb_vector_t<parsed_cs_str_t> SUPER;
+};
+
+struct subr_subset_param_t
+{
+ void init (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_)
+ {
+ parsed_charstring = parsed_charstring_;
+ current_parsed_str = 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
+ 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 (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.
+ */
+
+ hb_codepoint_t old_num = HB_SET_VALUE_INVALID;
+ while (hb_set_next (closure, &old_num))
+ 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 ()
+ {
+ global_remap.init ();
+ local_remaps.init ();
+ }
+
+ ~subr_remaps_t () { fini (); }
+
+ void init (unsigned int fdCount)
+ {
+ if (unlikely (!local_remaps.resize (fdCount))) return;
+ for (unsigned int i = 0; i < fdCount; i++)
+ local_remaps[i].init ();
+ }
+
+ 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[i].create (closures.local_closures[i]);
+ }
+
+ void fini ()
+ {
+ global_remap.fini ();
+ local_remaps.fini_deep ();
+ }
+
+ 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_)
+ {
+ parsed_charstrings.init ();
+ parsed_global_subrs.init ();
+ parsed_local_subrs.init ();
+ }
+
+ ~subr_subsetter_t ()
+ {
+ closures.fini ();
+ remaps.fini ();
+ parsed_charstrings.fini_deep ();
+ parsed_global_subrs.fini_deep ();
+ parsed_local_subrs.fini_deep ();
+ }
+
+ /* 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)
+ {
+ closures.init (acc.fdCount);
+ remaps.init (acc.fdCount);
+
+ parsed_charstrings.init (plan->num_output_glyphs ());
+ parsed_global_subrs.init (acc.globalSubrs->count);
+
+ if (unlikely (remaps.in_error()
+ || parsed_charstrings.in_error ()
+ || parsed_global_subrs.in_error ())) {
+ return false;
+ }
+
+ if (unlikely (!parsed_local_subrs.resize (acc.fdCount))) return false;
+
+ for (unsigned int i = 0; i < acc.fdCount; i++)
+ {
+ parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count);
+ if (unlikely (parsed_local_subrs[i].in_error ())) return false;
+ }
+ if (unlikely (!closures.valid))
+ return false;
+
+ /* phase 1 & 2 */
+ for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ {
+ hb_codepoint_t glyph;
+ if (!plan->old_gid_for_new_gid (i, &glyph))
+ continue;
+ const byte_str_t str = (*acc.charStrings)[glyph];
+ unsigned int fd = acc.fdSelect->get_fd (glyph);
+ if (unlikely (fd >= acc.fdCount))
+ return false;
+
+ cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp;
+ interp.env.init (str, acc, fd);
+
+ subr_subset_param_t param;
+ param.init (&parsed_charstrings[i],
+ &parsed_global_subrs, &parsed_local_subrs[fd],
+ closures.global_closure, closures.local_closures[fd],
+ plan->drop_hints);
+
+ 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[i]);
+ }
+
+ if (plan->drop_hints)
+ {
+ /* mark hint ops and arguments for drop */
+ for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ {
+ hb_codepoint_t glyph;
+ if (!plan->old_gid_for_new_gid (i, &glyph))
+ continue;
+ unsigned int fd = acc.fdSelect->get_fd (glyph);
+ if (unlikely (fd >= acc.fdCount))
+ return false;
+ subr_subset_param_t param;
+ param.init (&parsed_charstrings[i],
+ &parsed_global_subrs, &parsed_local_subrs[fd],
+ closures.global_closure, closures.local_closures[fd],
+ plan->drop_hints);
+
+ drop_hints_param_t drop;
+ if (drop_hints_in_str (parsed_charstrings[i], param, drop))
+ {
+ parsed_charstrings[i].set_hint_dropped ();
+ if (drop.vsindex_dropped)
+ parsed_charstrings[i].set_vsindex_dropped ();
+ }
+ }
+
+ /* after dropping hints recreate closures of actually used subrs */
+ closures.reset ();
+ for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
+ {
+ hb_codepoint_t glyph;
+ if (!plan->old_gid_for_new_gid (i, &glyph))
+ continue;
+ unsigned int fd = acc.fdSelect->get_fd (glyph);
+ if (unlikely (fd >= acc.fdCount))
+ return false;
+ subr_subset_param_t param;
+ param.init (&parsed_charstrings[i],
+ &parsed_global_subrs, &parsed_local_subrs[fd],
+ closures.global_closure, closures.local_closures[fd],
+ plan->drop_hints);
+ collect_subr_refs_in_str (parsed_charstrings[i], param);
+ }
+ }
+
+ remaps.create (closures);
+
+ return true;
+ }
+
+ bool encode_charstrings (str_buff_vec_t &buffArray) const
+ {
+ if (unlikely (!buffArray.resize (plan->num_output_glyphs ())))
+ return false;
+ for (unsigned int i = 0; i < plan->num_output_glyphs (); 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) buffArray[i].push (endchar_op);
+ continue;
+ }
+ unsigned int fd = acc.fdSelect->get_fd (glyph);
+ if (unlikely (fd >= acc.fdCount))
+ return false;
+ if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i])))
+ return false;
+ }
+ 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 (count)))
+ return false;
+ for (unsigned int old_num = 0; old_num < subrs.length; old_num++)
+ {
+ hb_codepoint_t new_num = remap[old_num];
+ if (new_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 &param, 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_drop ();
+ /* 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_drop ();
+ }
+
+ 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 &param, drop_hints_param_t &drop)
+ {
+ bool seen_hint = false;
+
+ for (unsigned int pos = 0; pos < str.values.length; pos++)
+ {
+ bool has_hint = false;
+ switch (str.values[pos].op)
+ {
+ case OpCode_callsubr:
+ has_hint = drop_hints_in_subr (str, pos,
+ *param.parsed_local_subrs, str.values[pos].subr_num,
+ param, drop);
+ break;
+
+ case OpCode_callgsubr:
+ has_hint = drop_hints_in_subr (str, pos,
+ *param.parsed_global_subrs, str.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)
+ {
+ str.values[pos].set_drop ();
+ break;
+ }
+ HB_FALLTHROUGH;
+
+ case OpCode_hstemhm:
+ case OpCode_vstemhm:
+ case OpCode_hstem:
+ case OpCode_vstem:
+ has_hint = true;
+ str.values[pos].set_drop ();
+ if (str.at_end (pos))
+ drop.ends_in_hint = true;
+ break;
+
+ case OpCode_dotsection:
+ str.values[pos].set_drop ();
+ break;
+
+ default:
+ /* NONE */
+ break;
+ }
+ if (has_hint)
+ {
+ for (int i = pos - 1; i >= 0; i--)
+ {
+ parsed_cs_op_t &csop = str.values[(unsigned)i];
+ if (csop.for_drop ())
+ break;
+ csop.set_drop ();
+ 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 < str.values.length; pos++)
+ {
+ parsed_cs_op_t &csop = str.values[pos];
+ if (csop.op == OpCode_return)
+ break;
+ if (!csop.for_drop ())
+ {
+ drop.all_dropped = false;
+ break;
+ }
+ }
+
+ return seen_hint;
+ }
+
+ void collect_subr_refs_in_subr (parsed_cs_str_t &str, unsigned int pos,
+ unsigned int subr_num, parsed_cs_str_vec_t &subrs,
+ hb_set_t *closure,
+ const subr_subset_param_t &param)
+ {
+ closure->add (subr_num);
+ collect_subr_refs_in_str (subrs[subr_num], param);
+ }
+
+ void collect_subr_refs_in_str (parsed_cs_str_t &str, const subr_subset_param_t &param)
+ {
+ for (unsigned int pos = 0; pos < str.values.length; pos++)
+ {
+ if (!str.values[pos].for_drop ())
+ {
+ switch (str.values[pos].op)
+ {
+ case OpCode_callsubr:
+ collect_subr_refs_in_subr (str, pos,
+ str.values[pos].subr_num, *param.parsed_local_subrs,
+ param.local_closure, param);
+ break;
+
+ case OpCode_callgsubr:
+ collect_subr_refs_in_subr (str, pos,
+ str.values[pos].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) const
+ {
+ buff.init ();
+ str_encoder_t encoder (buff);
+ encoder.reset ();
+ /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
+ * re-insert it at the beginning of charstreing */
+ if (str.has_prefix () && str.is_hint_dropped ())
+ {
+ encoder.encode_num (str.prefix_num ());
+ if (str.prefix_op () != OpCode_Invalid)
+ encoder.encode_op (str.prefix_op ());
+ }
+ for (unsigned int i = 0; i < str.get_count(); i++)
+ {
+ const parsed_cs_op_t &opstr = str.values[i];
+ if (!opstr.for_drop () && !opstr.for_skip ())
+ {
+ switch (opstr.op)
+ {
+ case OpCode_callsubr:
+ encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
+ encoder.encode_op (OpCode_callsubr);
+ break;
+
+ case OpCode_callgsubr:
+ encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
+ encoder.encode_op (OpCode_callgsubr);
+ break;
+
+ default:
+ encoder.copy_str (opstr.str);
+ break;
+ }
+ }
+ }
+ return !encoder.is_error ();
+ }
+
+ protected:
+ const ACC &acc;
+ const hb_subset_plan_t *plan;
+
+ subr_closures_t closures;
+
+ 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;
+
+ subr_remaps_t remaps;
+
+ private:
+ 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/thirdparty/harfbuzz/src/hb-subset-cff1.cc b/thirdparty/harfbuzz/src/hb-subset-cff1.cc
new file mode 100644
index 0000000000..df322f8451
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-cff1.cc
@@ -0,0 +1,940 @@
+/*
+ * 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-cff1.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 : hb_inc_bimap_t
+{
+ unsigned int add (unsigned int sid)
+ {
+ if ((sid != CFF_UNDEF_SID) && !is_std_std (sid))
+ return offset_sid (hb_inc_bimap_t::add (unoffset_sid (sid)));
+ else
+ return sid;
+ }
+
+ unsigned int operator[] (unsigned int sid) const
+ {
+ if (is_std_std (sid) || (sid == CFF_UNDEF_SID))
+ return sid;
+ else
+ return offset_sid (get (unoffset_sid (sid)));
+ }
+
+ static const unsigned int num_std_strings = 391;
+
+ static bool is_std_std (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; }
+};
+
+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.str.length >= opstr.last_arg_offset + 3)))
+ return_trace (false);
+ supp_op.str = byte_str_t (&opstr.str + opstr.last_arg_offset, opstr.str.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 (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 (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)
+ {
+ bool two_byte = false;
+ for (unsigned int i = (*this).length; i > 0; i--)
+ {
+ code_pair_t &pair = (*this)[i - 1];
+ unsigned int nLeft = last_glyph - pair.glyph - 1;
+ if (nLeft >= 0x100)
+ two_byte = true;
+ last_glyph = pair.glyph;
+ pair.glyph = nLeft;
+ }
+ 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_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 ();
+ }
+ }
+};
+
+struct cff_subset_plan {
+ cff_subset_plan ()
+ : info (),
+ orig_fdcount (0),
+ subset_fdcount (1),
+ subset_fdselect_format (0),
+ drop_hints (false),
+ desubroutinize(false)
+ {
+ topdict_mod.init ();
+ subset_fdselect_ranges.init ();
+ fdmap.init ();
+ subset_charstrings.init ();
+ subset_globalsubrs.init ();
+ subset_localsubrs.init ();
+ fontdicts_mod.init ();
+ subset_enc_code_ranges.init ();
+ subset_enc_supp_codes.init ();
+ subset_charset_ranges.init ();
+ sidmap.init ();
+ for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
+ topDictModSIDs[i] = CFF_UNDEF_SID;
+ }
+
+ ~cff_subset_plan ()
+ {
+ topdict_mod.fini ();
+ subset_fdselect_ranges.fini ();
+ fdmap.fini ();
+ subset_charstrings.fini_deep ();
+ subset_globalsubrs.fini_deep ();
+ subset_localsubrs.fini_deep ();
+ fontdicts_mod.fini ();
+ subset_enc_code_ranges.fini ();
+ subset_enc_supp_codes.fini ();
+ subset_charset_ranges.fini ();
+ sidmap.fini ();
+ }
+
+ 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, supp_size;
+ hb_codepoint_t code, last_code = CFF_UNDEF_CODE;
+ hb_vector_t<hb_codepoint_t> supp_codes;
+
+ if (unlikely (!subset_enc_code_ranges.resize (0)))
+ {
+ plan->check_success (false);
+ return;
+ }
+
+ supp_size = 0;
+ supp_codes.init ();
+
+ subset_enc_num_codes = plan->num_output_glyphs () - 1;
+ unsigned int glyph;
+ for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
+ {
+ hb_codepoint_t old_glyph;
+ if (!plan->old_gid_for_new_gid (glyph, &old_glyph))
+ {
+ /* Retain the code for the old missing glyph ID */
+ old_glyph = glyph;
+ }
+ code = acc.glyph_to_code (old_glyph);
+ if (code == CFF_UNDEF_CODE)
+ {
+ subset_enc_num_codes = glyph - 1;
+ break;
+ }
+
+ if ((last_code == CFF_UNDEF_CODE) || (code != last_code + 1))
+ {
+ code_pair_t pair = { code, glyph };
+ subset_enc_code_ranges.push (pair);
+ }
+ last_code = code;
+
+ if (encoding != &Null (Encoding))
+ {
+ hb_codepoint_t sid = acc.glyph_to_sid (old_glyph);
+ encoding->get_supplement_codes (sid, supp_codes);
+ for (unsigned int i = 0; i < supp_codes.length; i++)
+ {
+ code_pair_t pair = { supp_codes[i], sid };
+ subset_enc_supp_codes.push (pair);
+ }
+ supp_size += SuppEncoding::static_size * supp_codes.length;
+ }
+ }
+ 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;
+ }
+
+ void plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
+ {
+ unsigned int size0, size_ranges;
+ hb_codepoint_t sid, last_sid = CFF_UNDEF_CODE;
+
+ if (unlikely (!subset_charset_ranges.resize (0)))
+ {
+ plan->check_success (false);
+ return;
+ }
+
+ unsigned int glyph;
+ for (glyph = 1; glyph < plan->num_output_glyphs (); glyph++)
+ {
+ hb_codepoint_t old_glyph;
+ if (!plan->old_gid_for_new_gid (glyph, &old_glyph))
+ {
+ /* Retain the SID for the old missing glyph ID */
+ old_glyph = glyph;
+ }
+ sid = acc.glyph_to_sid (old_glyph);
+
+ if (!acc.is_CID ())
+ sid = sidmap.add (sid);
+
+ if ((last_sid == CFF_UNDEF_CODE) || (sid != last_sid + 1))
+ {
+ code_pair_t pair = { sid, glyph };
+ subset_charset_ranges.push (pair);
+ }
+ last_sid = sid;
+ }
+
+ bool two_byte = subset_charset_ranges.complete (glyph);
+
+ size0 = Charset0::min_size + HBUINT16::static_size * (plan->num_output_glyphs () - 1);
+ if (!two_byte)
+ size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.length;
+ else
+ size_ranges = Charset2::min_size + Charset2_Range::static_size * 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;
+ }
+
+ bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
+ {
+ sidmap.reset ();
+
+ for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++)
+ {
+ unsigned int sid = acc.topDict.nameSIDs[i];
+ if (sid != CFF_UNDEF_SID)
+ {
+ (void)sidmap.add (sid);
+ topDictModSIDs[i] = sidmap[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->drop_hints;
+ desubroutinize = plan->desubroutinize;
+
+ /* check whether the subset renumbers any glyph IDs */
+ gid_renum = false;
+ for (hb_codepoint_t new_glyph = 0; new_glyph < plan->num_output_glyphs (); new_glyph++)
+ {
+ if (!plan->old_gid_for_new_gid(new_glyph, &old_glyph))
+ continue;
+ if (new_glyph != old_glyph) {
+ gid_renum = true;
+ break;
+ }
+ }
+
+ subset_charset = gid_renum || !acc.is_predef_charset ();
+ 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);
+
+ 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 ((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;
+ unsigned int subset_fdcount;
+ unsigned int subset_fdselect_format;
+ 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;
+
+ 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;
+};
+
+static bool _serialize_cff1 (hb_serialize_context_t *c,
+ cff_subset_plan &plan,
+ const OT::cff1::accelerator_subset_t &acc,
+ unsigned int num_glyphs)
+{
+ /* private dicts & local subrs */
+ for (int i = (int)acc.privateDicts.length; --i >= 0 ;)
+ {
+ if (plan.fdmap.has (i))
+ {
+ objidx_t subrs_link = 0;
+ if (plan.subset_localsubrs[i].length > 0)
+ {
+ CFF1Subrs *dest = c->start_embed <CFF1Subrs> ();
+ if (unlikely (!dest)) return false;
+ c->push ();
+ if (likely (dest && dest->serialize (c, plan.subset_localsubrs[i])))
+ subrs_link = c->pop_pack ();
+ else
+ {
+ c->pop_discard ();
+ return false;
+ }
+ }
+
+ PrivateDict *pd = c->start_embed<PrivateDict> ();
+ if (unlikely (!pd)) return false;
+ c->push ();
+ cff_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, acc.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 (!acc.is_CID ())
+ plan.info.privateDictInfo = plan.fontdicts_mod[0].privateDictInfo;
+
+ /* CharStrings */
+ {
+ CFF1CharStrings *cs = c->start_embed<CFF1CharStrings> ();
+ if (unlikely (!cs)) return false;
+ c->push ();
+ if (likely (cs->serialize (c, plan.subset_charstrings)))
+ plan.info.char_strings_link = c->pop_pack ();
+ else
+ {
+ c->pop_discard ();
+ return false;
+ }
+ }
+
+ /* FDArray (FD Index) */
+ if (acc.fdArray != &Null (CFF1FDArray))
+ {
+ CFF1FDArray *fda = c->start_embed<CFF1FDArray> ();
+ if (unlikely (!fda)) return false;
+ c->push ();
+ 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 (acc.fdSelect != &Null (CFF1FDSelect))
+ {
+ c->push ();
+ if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *acc.fdSelect, acc.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)
+ {
+ Charset *dest = c->start_embed<Charset> ();
+ if (unlikely (!dest)) return false;
+ c->push ();
+ 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)
+ {
+ Encoding *dest = c->start_embed<Encoding> ();
+ if (unlikely (!dest)) return false;
+ c->push ();
+ 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 */
+ {
+ c->push ();
+ CFF1Subrs *dest = c->start_embed <CFF1Subrs> ();
+ if (unlikely (!dest)) return false;
+ if (likely (dest->serialize (c, plan.subset_globalsubrs)))
+ c->pop_pack ();
+ else
+ {
+ c->pop_discard ();
+ return false;
+ }
+ }
+
+ /* String INDEX */
+ {
+ CFF1StringIndex *dest = c->start_embed<CFF1StringIndex> ();
+ if (unlikely (!dest)) return false;
+ c->push ();
+ if (likely (dest->serialize (c, *acc.stringIndex, plan.sidmap)))
+ 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 (!(*acc.nameIndex).copy (c))) return false;
+
+ /* top dict INDEX */
+ {
+ /* serialize singleton TopDict */
+ TopDict *top = c->start_embed<TopDict> ();
+ if (!top) return false;
+ c->push ();
+ 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 */
+ CFF1Index *dest = c->start_embed<CFF1Index> ();
+ if (!dest) return false;
+ return dest->serialize_header (c, hb_iter (hb_array_t<unsigned> (&top_size, 1)));
+ }
+}
+
+static bool
+_hb_subset_cff1 (const OT::cff1::accelerator_subset_t &acc,
+ hb_subset_context_t *c)
+{
+ cff_subset_plan cff_plan;
+
+ if (unlikely (!cff_plan.create (acc, c->plan)))
+ {
+ DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
+ return false;
+ }
+
+ return _serialize_cff1 (c->serializer, cff_plan, acc, c->plan->num_output_glyphs ());
+}
+
+/**
+ * hb_subset_cff1:
+ * Subsets the CFF table according to a provided plan.
+ *
+ * Return value: subsetted cff table.
+ **/
+bool
+hb_subset_cff1 (hb_subset_context_t *c)
+{
+ OT::cff1::accelerator_subset_t acc;
+ acc.init (c->plan->source);
+ bool result = likely (acc.is_valid ()) && _hb_subset_cff1 (acc, c);
+ acc.fini ();
+
+ return result;
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff1.hh b/thirdparty/harfbuzz/src/hb-subset-cff1.hh
new file mode 100644
index 0000000000..aaf5def1ed
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-cff1.hh
@@ -0,0 +1,37 @@
+/*
+ * 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_CFF1_HH
+#define HB_SUBSET_CFF1_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff1 (hb_subset_context_t *c);
+
+#endif /* HB_SUBSET_CFF1_HH */
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2.cc b/thirdparty/harfbuzz/src/hb-subset-cff2.cc
new file mode 100644
index 0000000000..17ee040deb
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-cff2.cc
@@ -0,0 +1,488 @@
+/*
+ * 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-cff2.hh"
+#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:
+ return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link));
+
+ 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>
+{
+ static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_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 &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 (arg);
+ i++;
+ }
+ }
+ SUPER::flush_args (env, param);
+ }
+
+ static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_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 (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 (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 &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);
+ }
+ }
+
+ private:
+ typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> SUPER;
+ typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_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>
+{
+ static void process_op (op_code_t op, cff2_cs_interp_env_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 &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> 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, 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 &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_subset_plan {
+ cff2_subset_plan ()
+ : orig_fdcount (0),
+ subset_fdcount(1),
+ subset_fdselect_size (0),
+ subset_fdselect_format (0),
+ drop_hints (false),
+ desubroutinize (false)
+ {
+ subset_fdselect_ranges.init ();
+ fdmap.init ();
+ subset_charstrings.init ();
+ subset_globalsubrs.init ();
+ subset_localsubrs.init ();
+ }
+
+ ~cff2_subset_plan ()
+ {
+ subset_fdselect_ranges.fini ();
+ fdmap.fini ();
+ subset_charstrings.fini_deep ();
+ subset_globalsubrs.fini_deep ();
+ subset_localsubrs.fini_deep ();
+ }
+
+ bool create (const OT::cff2::accelerator_subset_t &acc,
+ hb_subset_plan_t *plan)
+ {
+ orig_fdcount = acc.fdArray->count;
+
+ drop_hints = plan->drop_hints;
+ desubroutinize = plan->desubroutinize;
+
+ if (desubroutinize)
+ {
+ /* Flatten global & local subrs */
+ subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_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))
+ 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 orig_fdcount;
+ unsigned int subset_fdcount;
+ unsigned int subset_fdselect_size;
+ unsigned int subset_fdselect_format;
+ 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;
+ bool desubroutinize;
+};
+
+static bool _serialize_cff2 (hb_serialize_context_t *c,
+ cff2_subset_plan &plan,
+ const OT::cff2::accelerator_subset_t &acc,
+ unsigned int num_glyphs)
+{
+ /* 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)acc.privateDicts.length; --i >= 0 ;)
+ {
+ if (plan.fdmap.has (i))
+ {
+ objidx_t subrs_link = 0;
+
+ if (plan.subset_localsubrs[i].length > 0)
+ {
+ CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
+ if (unlikely (!dest)) return false;
+ c->push ();
+ if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
+ subrs_link = c->pop_pack ();
+ else
+ {
+ c->pop_discard ();
+ return false;
+ }
+ }
+ PrivateDict *pd = c->start_embed<PrivateDict> ();
+ if (unlikely (!pd)) return false;
+ c->push ();
+ cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
+ if (likely (pd->serialize (c, acc.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;
+ }
+ }
+ }
+
+ /* CharStrings */
+ {
+ CFF2CharStrings *cs = c->start_embed<CFF2CharStrings> ();
+ if (unlikely (!cs)) return false;
+ c->push ();
+ if (likely (cs->serialize (c, plan.subset_charstrings)))
+ plan.info.char_strings_link = c->pop_pack ();
+ else
+ {
+ c->pop_discard ();
+ return false;
+ }
+ }
+
+ /* FDSelect */
+ if (acc.fdSelect != &Null (CFF2FDSelect))
+ {
+ c->push ();
+ if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.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) */
+ {
+ c->push ();
+ CFF2FDArray *fda = c->start_embed<CFF2FDArray> ();
+ if (unlikely (!fda)) return false;
+ cff_font_dict_op_serializer_t fontSzr;
+ auto it =
+ + hb_zip (+ hb_iter (acc.fontDicts)
+ | hb_filter ([&] (const cff2_font_dict_values_t &_)
+ { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }),
+ hb_iter (private_dict_infos))
+ ;
+ if (unlikely (!fda->serialize (c, it, fontSzr))) return false;
+ plan.info.fd_array_link = c->pop_pack ();
+ }
+
+ /* variation store */
+ if (acc.varStore != &Null (CFF2VariationStore))
+ {
+ c->push ();
+ CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> ();
+ if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false;
+ plan.info.var_store_link = c->pop_pack ();
+ }
+
+ 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, acc.topDict, topSzr, plan.info))) return false;
+ cff2->topDictSize = c->head - (const char *)&dict;
+ }
+
+ /* global subrs */
+ {
+ CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
+ if (unlikely (!dest)) return false;
+ return dest->serialize (c, plan.subset_globalsubrs);
+ }
+}
+
+static bool
+_hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc,
+ hb_subset_context_t *c)
+{
+ cff2_subset_plan cff2_plan;
+
+ if (unlikely (!cff2_plan.create (acc, c->plan))) return false;
+ return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ());
+}
+
+/**
+ * hb_subset_cff2:
+ * Subsets the CFF2 table according to a provided subset context.
+ **/
+bool
+hb_subset_cff2 (hb_subset_context_t *c)
+{
+ OT::cff2::accelerator_subset_t acc;
+ acc.init (c->plan->source);
+ bool result = likely (acc.is_valid ()) && _hb_subset_cff2 (acc, c);
+ acc.fini ();
+
+ return result;
+}
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2.hh b/thirdparty/harfbuzz/src/hb-subset-cff2.hh
new file mode 100644
index 0000000000..f10556ddd7
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-cff2.hh
@@ -0,0 +1,37 @@
+/*
+ * 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_CFF2_HH
+#define HB_SUBSET_CFF2_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff2 (hb_subset_context_t *c);
+
+#endif /* HB_SUBSET_CFF2_HH */
diff --git a/thirdparty/harfbuzz/src/hb-subset-input.cc b/thirdparty/harfbuzz/src/hb-subset-input.cc
new file mode 100644
index 0000000000..fe9be3ce02
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-input.cc
@@ -0,0 +1,229 @@
+/*
+ * 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"
+
+/**
+ * hb_subset_input_create_or_fail:
+ *
+ * Return value: New subset input.
+ *
+ * Since: 1.8.0
+ **/
+hb_subset_input_t *
+hb_subset_input_create_or_fail ()
+{
+ hb_subset_input_t *input = hb_object_create<hb_subset_input_t>();
+
+ if (unlikely (!input))
+ return nullptr;
+
+ input->unicodes = hb_set_create ();
+ input->glyphs = hb_set_create ();
+ input->name_ids = hb_set_create ();
+ hb_set_add_range (input->name_ids, 0, 6);
+ input->name_languages = hb_set_create ();
+ hb_set_add (input->name_languages, 0x0409);
+ input->drop_tables = hb_set_create ();
+ input->drop_hints = false;
+ input->desubroutinize = false;
+ input->retain_gids = false;
+ input->name_legacy = false;
+
+ hb_tag_t default_drop_tables[] = {
+ // Layout disabled by default
+ HB_TAG ('G', 'S', 'U', 'B'),
+ HB_TAG ('G', 'P', 'O', 'S'),
+ HB_TAG ('G', 'D', 'E', 'F'),
+ 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'),
+ };
+
+ input->drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables));
+
+ return input;
+}
+
+/**
+ * hb_subset_input_reference: (skip)
+ * @subset_input: a subset_input.
+ *
+ *
+ *
+ * Return value:
+ *
+ * Since: 1.8.0
+ **/
+hb_subset_input_t *
+hb_subset_input_reference (hb_subset_input_t *subset_input)
+{
+ return hb_object_reference (subset_input);
+}
+
+/**
+ * hb_subset_input_destroy:
+ * @subset_input: a subset_input.
+ *
+ * Since: 1.8.0
+ **/
+void
+hb_subset_input_destroy (hb_subset_input_t *subset_input)
+{
+ if (!hb_object_destroy (subset_input)) return;
+
+ hb_set_destroy (subset_input->unicodes);
+ hb_set_destroy (subset_input->glyphs);
+ hb_set_destroy (subset_input->name_ids);
+ hb_set_destroy (subset_input->name_languages);
+ hb_set_destroy (subset_input->drop_tables);
+
+ free (subset_input);
+}
+
+/**
+ * hb_subset_input_unicode_set:
+ * @subset_input: a subset_input.
+ *
+ * Since: 1.8.0
+ **/
+HB_EXTERN hb_set_t *
+hb_subset_input_unicode_set (hb_subset_input_t *subset_input)
+{
+ return subset_input->unicodes;
+}
+
+/**
+ * hb_subset_input_glyph_set:
+ * @subset_input: a subset_input.
+ *
+ * Since: 1.8.0
+ **/
+HB_EXTERN hb_set_t *
+hb_subset_input_glyph_set (hb_subset_input_t *subset_input)
+{
+ return subset_input->glyphs;
+}
+
+HB_EXTERN hb_set_t *
+hb_subset_input_nameid_set (hb_subset_input_t *subset_input)
+{
+ return subset_input->name_ids;
+}
+
+HB_EXTERN hb_set_t *
+hb_subset_input_namelangid_set (hb_subset_input_t *subset_input)
+{
+ return subset_input->name_languages;
+}
+
+HB_EXTERN hb_set_t *
+hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input)
+{
+ return subset_input->drop_tables;
+}
+
+HB_EXTERN void
+hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
+ hb_bool_t drop_hints)
+{
+ subset_input->drop_hints = drop_hints;
+}
+
+HB_EXTERN hb_bool_t
+hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input)
+{
+ return subset_input->drop_hints;
+}
+
+HB_EXTERN void
+hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
+ hb_bool_t desubroutinize)
+{
+ subset_input->desubroutinize = desubroutinize;
+}
+
+HB_EXTERN hb_bool_t
+hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input)
+{
+ return subset_input->desubroutinize;
+}
+
+/**
+ * hb_subset_input_set_retain_gids:
+ * @subset_input: a subset_input.
+ * @retain_gids: If true the subsetter will not renumber glyph ids.
+ * Since: 2.4.0
+ **/
+HB_EXTERN void
+hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input,
+ hb_bool_t retain_gids)
+{
+ subset_input->retain_gids = retain_gids;
+}
+
+/**
+ * hb_subset_input_get_retain_gids:
+ * Returns: value of retain_gids.
+ * Since: 2.4.0
+ **/
+HB_EXTERN hb_bool_t
+hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input)
+{
+ return subset_input->retain_gids;
+}
+
+HB_EXTERN void
+hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input,
+ hb_bool_t name_legacy)
+{
+ subset_input->name_legacy = name_legacy;
+}
+
+HB_EXTERN hb_bool_t
+hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input)
+{
+ return subset_input->name_legacy;
+}
diff --git a/thirdparty/harfbuzz/src/hb-subset-input.hh b/thirdparty/harfbuzz/src/hb-subset-input.hh
new file mode 100644
index 0000000000..0aeb96695b
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-input.hh
@@ -0,0 +1,61 @@
+/*
+ * 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-font.hh"
+
+struct hb_subset_input_t
+{
+ hb_object_header_t header;
+
+ hb_set_t *unicodes;
+ hb_set_t *glyphs;
+ hb_set_t *name_ids;
+ hb_set_t *name_languages;
+ hb_set_t *drop_tables;
+
+ bool drop_hints;
+ bool desubroutinize;
+ bool retain_gids;
+ bool name_legacy;
+ /* TODO
+ *
+ * features
+ * lookups
+ * name_ids
+ * ...
+ */
+};
+
+
+#endif /* HB_SUBSET_INPUT_HH */
diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.cc b/thirdparty/harfbuzz/src/hb-subset-plan.cc
new file mode 100644
index 0000000000..24beada3e8
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-plan.cc
@@ -0,0 +1,395 @@
+/*
+ * 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-map.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-color-colr-table.hh"
+#include "hb-ot-var-fvar-table.hh"
+#include "hb-ot-stat-table.hh"
+
+
+#ifndef HB_NO_SUBSET_CFF
+static inline void
+_add_cff_seac_components (const OT::cff1::accelerator_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);
+ }
+}
+#endif
+
+#ifndef HB_NO_SUBSET_LAYOUT
+static void
+_remap_indexes (const hb_set_t *indexes,
+ hb_map_t *mapping /* OUT */)
+{
+ unsigned count = indexes->get_population ();
+
+ for (auto _ : + hb_zip (indexes->iter (), hb_range (count)))
+ mapping->set (_.first, _.second);
+
+}
+
+static inline void
+_gsub_closure_glyphs_lookups_features (hb_face_t *face,
+ hb_set_t *gids_to_retain,
+ hb_map_t *gsub_lookups,
+ hb_map_t *gsub_features)
+{
+ hb_set_t lookup_indices;
+ hb_ot_layout_collect_lookups (face,
+ HB_OT_TAG_GSUB,
+ nullptr,
+ nullptr,
+ nullptr,
+ &lookup_indices);
+ hb_ot_layout_lookups_substitute_closure (face,
+ &lookup_indices,
+ gids_to_retain);
+ hb_blob_ptr_t<OT::GSUB> gsub = hb_sanitize_context_t ().reference_table<OT::GSUB> (face);
+ gsub->closure_lookups (face,
+ gids_to_retain,
+ &lookup_indices);
+ _remap_indexes (&lookup_indices, gsub_lookups);
+
+ //closure features
+ hb_set_t feature_indices;
+ gsub->closure_features (gsub_lookups, &feature_indices);
+ _remap_indexes (&feature_indices, gsub_features);
+ gsub.destroy ();
+}
+
+static inline void
+_gpos_closure_lookups_features (hb_face_t *face,
+ const hb_set_t *gids_to_retain,
+ hb_map_t *gpos_lookups,
+ hb_map_t *gpos_features)
+{
+ hb_set_t lookup_indices;
+ hb_ot_layout_collect_lookups (face,
+ HB_OT_TAG_GPOS,
+ nullptr,
+ nullptr,
+ nullptr,
+ &lookup_indices);
+ hb_blob_ptr_t<OT::GPOS> gpos = hb_sanitize_context_t ().reference_table<OT::GPOS> (face);
+ gpos->closure_lookups (face,
+ gids_to_retain,
+ &lookup_indices);
+ _remap_indexes (&lookup_indices, gpos_lookups);
+
+ //closure features
+ hb_set_t feature_indices;
+ gpos->closure_features (gpos_lookups, &feature_indices);
+ _remap_indexes (&feature_indices, gpos_features);
+ gpos.destroy ();
+}
+#endif
+
+#ifndef HB_NO_VAR
+static inline void
+ _collect_layout_variation_indices (hb_face_t *face,
+ const hb_set_t *glyphset,
+ const hb_map_t *gpos_lookups,
+ hb_set_t *layout_variation_indices,
+ hb_map_t *layout_variation_idx_map)
+{
+ hb_blob_ptr_t<OT::GDEF> gdef = hb_sanitize_context_t ().reference_table<OT::GDEF> (face);
+ hb_blob_ptr_t<OT::GPOS> gpos = hb_sanitize_context_t ().reference_table<OT::GPOS> (face);
+
+ if (!gdef->has_data ())
+ {
+ gdef.destroy ();
+ gpos.destroy ();
+ return;
+ }
+ OT::hb_collect_variation_indices_context_t c (layout_variation_indices, glyphset, gpos_lookups);
+ gdef->collect_variation_indices (&c);
+
+ if (hb_ot_layout_has_positioning (face))
+ gpos->collect_variation_indices (&c);
+
+ gdef->remap_layout_variation_indices (layout_variation_indices, layout_variation_idx_map);
+
+ 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;
+ cmap.init (face);
+ cmap.table->closure_glyphs (unicodes, glyphset);
+ cmap.fini ();
+}
+
+static inline void
+_remove_invalid_gids (hb_set_t *glyphs,
+ unsigned int num_glyphs)
+{
+ hb_codepoint_t gid = HB_SET_VALUE_INVALID;
+ while (glyphs->next (&gid))
+ {
+ if (gid >= num_glyphs)
+ glyphs->del (gid);
+ }
+}
+
+static void
+_populate_gids_to_retain (hb_subset_plan_t* plan,
+ const hb_set_t *unicodes,
+ const hb_set_t *input_glyphs_to_retain,
+ bool close_over_gsub,
+ bool close_over_gpos,
+ bool close_over_gdef)
+{
+ OT::cmap::accelerator_t cmap;
+ OT::glyf::accelerator_t glyf;
+#ifndef HB_NO_SUBSET_CFF
+ OT::cff1::accelerator_t cff;
+#endif
+ OT::COLR::accelerator_t colr;
+ cmap.init (plan->source);
+ glyf.init (plan->source);
+#ifndef HB_NO_SUBSET_CFF
+ cff.init (plan->source);
+#endif
+ colr.init (plan->source);
+
+ plan->_glyphset_gsub->add (0); // Not-def
+ hb_set_union (plan->_glyphset_gsub, input_glyphs_to_retain);
+
+ hb_codepoint_t cp = HB_SET_VALUE_INVALID;
+ while (unicodes->next (&cp))
+ {
+ hb_codepoint_t gid;
+ if (!cmap.get_nominal_glyph (cp, &gid))
+ {
+ DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
+ continue;
+ }
+ plan->unicodes->add (cp);
+ plan->codepoint_to_glyph->set (cp, gid);
+ plan->_glyphset_gsub->add (gid);
+ }
+
+ _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub);
+
+#ifndef HB_NO_SUBSET_LAYOUT
+ if (close_over_gsub)
+ // closure all glyphs/lookups/features needed for GSUB substitutions.
+ _gsub_closure_glyphs_lookups_features (plan->source, plan->_glyphset_gsub, plan->gsub_lookups, plan->gsub_features);
+
+ if (close_over_gpos)
+ _gpos_closure_lookups_features (plan->source, plan->_glyphset_gsub, plan->gpos_lookups, plan->gpos_features);
+#endif
+ _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ());
+
+ // Populate a full set of glyphs to retain by adding all referenced
+ // composite glyphs.
+ hb_codepoint_t gid = HB_SET_VALUE_INVALID;
+ while (plan->_glyphset_gsub->next (&gid))
+ {
+ glyf.add_gid_and_children (gid, plan->_glyphset);
+#ifndef HB_NO_SUBSET_CFF
+ if (cff.is_valid ())
+ _add_cff_seac_components (cff, gid, plan->_glyphset);
+#endif
+ if (colr.is_valid ())
+ colr.closure_glyphs (gid, plan->_glyphset);
+ }
+
+ _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ());
+
+#ifndef HB_NO_VAR
+ if (close_over_gdef)
+ _collect_layout_variation_indices (plan->source, plan->_glyphset, plan->gpos_lookups, plan->layout_variation_indices, plan->layout_variation_idx_map);
+#endif
+
+#ifndef HB_NO_SUBSET_CFF
+ cff.fini ();
+#endif
+ glyf.fini ();
+ cmap.fini ();
+}
+
+static void
+_create_old_gid_to_new_gid_map (const hb_face_t *face,
+ bool retain_gids,
+ const hb_set_t *all_gids_to_retain,
+ hb_map_t *glyph_map, /* OUT */
+ hb_map_t *reverse_glyph_map, /* OUT */
+ unsigned int *num_glyphs /* OUT */)
+{
+ if (!retain_gids)
+ {
+ + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
+ | hb_sink (reverse_glyph_map)
+ ;
+ *num_glyphs = reverse_glyph_map->get_population ();
+ } else {
+ + hb_iter (all_gids_to_retain)
+ | hb_map ([] (hb_codepoint_t _) {
+ return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
+ })
+ | hb_sink (reverse_glyph_map)
+ ;
+
+ unsigned max_glyph =
+ + hb_iter (all_gids_to_retain)
+ | hb_reduce (hb_max, 0u)
+ ;
+ *num_glyphs = max_glyph + 1;
+ }
+
+ + reverse_glyph_map->iter ()
+ | hb_map (&hb_pair_t<hb_codepoint_t, hb_codepoint_t>::reverse)
+ | hb_sink (glyph_map)
+ ;
+}
+
+static void
+_nameid_closure (hb_face_t *face,
+ hb_set_t *nameids)
+{
+#ifndef HB_NO_STYLE
+ face->table.STAT->collect_name_ids (nameids);
+#endif
+#ifndef HB_NO_VAR
+ face->table.fvar->collect_name_ids (nameids);
+#endif
+}
+
+/**
+ * hb_subset_plan_create:
+ * 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: New subset plan.
+ *
+ * Since: 1.7.5
+ **/
+hb_subset_plan_t *
+hb_subset_plan_create (hb_face_t *face,
+ hb_subset_input_t *input)
+{
+ hb_subset_plan_t *plan;
+ if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> ())))
+ return const_cast<hb_subset_plan_t *> (&Null (hb_subset_plan_t));
+
+ plan->successful = true;
+ plan->drop_hints = input->drop_hints;
+ plan->desubroutinize = input->desubroutinize;
+ plan->retain_gids = input->retain_gids;
+ plan->name_legacy = input->name_legacy;
+ plan->unicodes = hb_set_create ();
+ plan->name_ids = hb_set_reference (input->name_ids);
+ _nameid_closure (face, plan->name_ids);
+ plan->name_languages = hb_set_reference (input->name_languages);
+ plan->glyphs_requested = hb_set_reference (input->glyphs);
+ plan->drop_tables = hb_set_reference (input->drop_tables);
+ plan->source = hb_face_reference (face);
+ plan->dest = hb_face_builder_create ();
+
+ plan->_glyphset = hb_set_create ();
+ plan->_glyphset_gsub = hb_set_create ();
+ plan->codepoint_to_glyph = hb_map_create ();
+ plan->glyph_map = hb_map_create ();
+ plan->reverse_glyph_map = hb_map_create ();
+ plan->gsub_lookups = hb_map_create ();
+ plan->gpos_lookups = hb_map_create ();
+ plan->gsub_features = hb_map_create ();
+ plan->gpos_features = hb_map_create ();
+ plan->layout_variation_indices = hb_set_create ();
+ plan->layout_variation_idx_map = hb_map_create ();
+
+ _populate_gids_to_retain (plan,
+ input->unicodes,
+ input->glyphs,
+ !input->drop_tables->has (HB_OT_TAG_GSUB),
+ !input->drop_tables->has (HB_OT_TAG_GPOS),
+ !input->drop_tables->has (HB_OT_TAG_GDEF));
+
+ _create_old_gid_to_new_gid_map (face,
+ input->retain_gids,
+ plan->_glyphset,
+ plan->glyph_map,
+ plan->reverse_glyph_map,
+ &plan->_num_output_glyphs);
+
+ return plan;
+}
+
+/**
+ * hb_subset_plan_destroy:
+ *
+ * Since: 1.7.5
+ **/
+void
+hb_subset_plan_destroy (hb_subset_plan_t *plan)
+{
+ if (!hb_object_destroy (plan)) return;
+
+ hb_set_destroy (plan->unicodes);
+ hb_set_destroy (plan->name_ids);
+ hb_set_destroy (plan->name_languages);
+ hb_set_destroy (plan->glyphs_requested);
+ hb_set_destroy (plan->drop_tables);
+ hb_face_destroy (plan->source);
+ hb_face_destroy (plan->dest);
+ hb_map_destroy (plan->codepoint_to_glyph);
+ hb_map_destroy (plan->glyph_map);
+ hb_map_destroy (plan->reverse_glyph_map);
+ hb_set_destroy (plan->_glyphset);
+ hb_set_destroy (plan->_glyphset_gsub);
+ hb_map_destroy (plan->gsub_lookups);
+ hb_map_destroy (plan->gpos_lookups);
+ hb_map_destroy (plan->gsub_features);
+ hb_map_destroy (plan->gpos_features);
+ hb_set_destroy (plan->layout_variation_indices);
+ hb_map_destroy (plan->layout_variation_idx_map);
+
+
+ free (plan);
+}
diff --git a/thirdparty/harfbuzz/src/hb-subset-plan.hh b/thirdparty/harfbuzz/src/hb-subset-plan.hh
new file mode 100644
index 0000000000..e9f603dd1d
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset-plan.hh
@@ -0,0 +1,194 @@
+/*
+ * 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-map.hh"
+#include "hb-set.hh"
+
+struct hb_subset_plan_t
+{
+ hb_object_header_t header;
+
+ bool successful : 1;
+ bool drop_hints : 1;
+ bool desubroutinize : 1;
+ bool retain_gids : 1;
+ bool name_legacy : 1;
+
+ // For each cp that we'd like to retain maps to the corresponding gid.
+ hb_set_t *unicodes;
+
+ // name_ids we would like to retain
+ hb_set_t *name_ids;
+
+ // name_languages we would like to retain
+ hb_set_t *name_languages;
+
+ //glyph ids requested to retain
+ hb_set_t *glyphs_requested;
+
+ // Tables which should be dropped.
+ hb_set_t *drop_tables;
+
+ // The glyph subset
+ hb_map_t *codepoint_to_glyph;
+
+ // Old -> New glyph id mapping
+ hb_map_t *glyph_map;
+ hb_map_t *reverse_glyph_map;
+
+ // Plan is only good for a specific source/dest so keep them with it
+ hb_face_t *source;
+ hb_face_t *dest;
+
+ unsigned int _num_output_glyphs;
+ hb_set_t *_glyphset;
+ hb_set_t *_glyphset_gsub;
+
+ //active lookups we'd like to retain
+ hb_map_t *gsub_lookups;
+ hb_map_t *gpos_lookups;
+
+ //active features we'd like to retain
+ hb_map_t *gsub_features;
+ hb_map_t *gpos_features;
+
+ //The set of layout item variation store delta set indices to be retained
+ hb_set_t *layout_variation_indices;
+ //Old -> New layout item variation store delta set index mapping
+ hb_map_t *layout_variation_idx_map;
+
+ public:
+
+ 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;
+ }
+
+ /*
+ * Given an output gid , returns true if that glyph id is an empty
+ * glyph (ie. it's a gid that we are dropping all data for).
+ */
+ inline bool is_empty_glyph (hb_codepoint_t gid) const
+ {
+ return !_glyphset->has (gid);
+ }
+
+ 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)
+ {
+ hb_blob_t *source_blob = source->reference_table (tag);
+ DEBUG_MSG(SUBSET, nullptr, "add table %c%c%c%c, dest %d bytes, source %d 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);
+ }
+};
+
+typedef struct hb_subset_plan_t hb_subset_plan_t;
+
+HB_INTERNAL hb_subset_plan_t *
+hb_subset_plan_create (hb_face_t *face,
+ hb_subset_input_t *input);
+
+HB_INTERNAL void
+hb_subset_plan_destroy (hb_subset_plan_t *plan);
+
+#endif /* HB_SUBSET_PLAN_HH */
diff --git a/thirdparty/harfbuzz/src/hb-subset.cc b/thirdparty/harfbuzz/src/hb-subset.cc
new file mode 100644
index 0000000000..8b77ecd45a
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset.cc
@@ -0,0 +1,269 @@
+/*
+ * 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 "hb-ot-color-sbix-table.hh"
+#include "hb-ot-color-colr-table.hh"
+#include "hb-ot-os2-table.hh"
+#include "hb-ot-post-table.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-color-cbdt-table.hh"
+#include "hb-ot-layout-gsub-table.hh"
+#include "hb-ot-layout-gpos-table.hh"
+#include "hb-ot-var-gvar-table.hh"
+#include "hb-ot-var-hvar-table.hh"
+
+
+static unsigned
+_plan_estimate_subset_table_size (hb_subset_plan_t *plan, unsigned table_len)
+{
+ unsigned src_glyphs = plan->source->get_num_glyphs ();
+ unsigned dst_glyphs = plan->glyphset ()->get_population ();
+
+ if (unlikely (!src_glyphs))
+ return 512 + table_len;
+
+ return 512 + (unsigned) (table_len * sqrt ((double) dst_glyphs / src_glyphs));
+}
+
+template<typename TableType>
+static bool
+_subset (hb_subset_plan_t *plan)
+{
+ bool result = false;
+ hb_blob_t *source_blob = hb_sanitize_context_t ().reference_table<TableType> (plan->source);
+ const TableType *table = source_blob->as<TableType> ();
+
+ hb_tag_t tag = TableType::tableTag;
+ if (source_blob->data)
+ {
+ hb_vector_t<char> buf;
+ /* TODO Not all tables are glyph-related. 'name' table size for example should not be
+ * affected by number of glyphs. Accommodate that. */
+ unsigned buf_size = _plan_estimate_subset_table_size (plan, source_blob->length);
+ 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);
+ hb_blob_destroy (source_blob);
+ return false;
+ }
+ retry:
+ hb_serialize_context_t serializer ((void *) buf, buf_size);
+ serializer.start_serialize<TableType> ();
+ hb_subset_context_t c (source_blob, plan, &serializer, tag);
+ bool needed = table->subset (&c);
+ if (serializer.ran_out_of_room)
+ {
+ buf_size += (buf_size >> 1) + 32;
+ DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size);
+ if (unlikely (!buf.alloc (buf_size)))
+ {
+ DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", HB_UNTAG (tag), buf_size);
+ hb_blob_destroy (source_blob);
+ return false;
+ }
+ goto retry;
+ }
+ serializer.end_serialize ();
+
+ result = !serializer.in_error ();
+
+ if (result)
+ {
+ if (needed)
+ {
+ hb_blob_t *dest_blob = serializer.copy_blob ();
+ DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c final subset table size: %u bytes.", HB_UNTAG (tag), dest_blob->length);
+ result = c.plan->add_table (tag, dest_blob);
+ hb_blob_destroy (dest_blob);
+ }
+ else
+ {
+ DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset table subsetted to empty.", HB_UNTAG (tag));
+ }
+ }
+ }
+ else
+ DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c::subset sanitize failed on source table.", HB_UNTAG (tag));
+
+ hb_blob_destroy (source_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)
+{
+ hb_tag_t table_tags[32];
+ unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
+ while ((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 */
+ 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->drop_hints;
+
+#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
+
+ default:
+ return false;
+ }
+}
+
+static bool
+_subset_table (hb_subset_plan_t *plan, hb_tag_t 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);
+ case HB_OT_TAG_hdmx: return _subset<const OT::hdmx> (plan);
+ case HB_OT_TAG_name: return _subset<const OT::name> (plan);
+ 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);
+ case HB_OT_TAG_hhea: return true; /* skip hhea, handled by hmtx */
+ case HB_OT_TAG_hmtx: return _subset<const OT::hmtx> (plan);
+ case HB_OT_TAG_vhea: return true; /* skip vhea, handled by vmtx */
+ case HB_OT_TAG_vmtx: return _subset<const OT::vmtx> (plan);
+ case HB_OT_TAG_maxp: return _subset<const OT::maxp> (plan);
+ case HB_OT_TAG_sbix: return _subset<const OT::sbix> (plan);
+ case HB_OT_TAG_loca: return true; /* skip loca, handled by glyf */
+ case HB_OT_TAG_cmap: return _subset<const OT::cmap> (plan);
+ case HB_OT_TAG_OS2 : return _subset<const OT::OS2 > (plan);
+ case HB_OT_TAG_post: return _subset<const OT::post> (plan);
+ case HB_OT_TAG_COLR: return _subset<const OT::COLR> (plan);
+ case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan);
+ case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */
+
+#ifndef HB_NO_SUBSET_CFF
+ case HB_OT_TAG_cff1: return _subset<const OT::cff1> (plan);
+ case HB_OT_TAG_cff2: return _subset<const OT::cff2> (plan);
+ case HB_OT_TAG_VORG: return _subset<const OT::VORG> (plan);
+#endif
+
+#ifndef HB_NO_SUBSET_LAYOUT
+ case HB_OT_TAG_GDEF: return _subset<const OT::GDEF> (plan);
+ case HB_OT_TAG_GSUB: return _subset<const OT::GSUB> (plan);
+ case HB_OT_TAG_GPOS: return _subset<const OT::GPOS> (plan);
+ case HB_OT_TAG_gvar: return _subset<const OT::gvar> (plan);
+ case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan);
+ case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan);
+#endif
+
+ default:
+ 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;
+ }
+}
+
+/**
+ * hb_subset:
+ * @source: font face data to be subset.
+ * @input: input to use for the subsetting.
+ *
+ * Subsets a font according to provided input.
+ **/
+hb_face_t *
+hb_subset (hb_face_t *source, hb_subset_input_t *input)
+{
+ if (unlikely (!input || !source)) return hb_face_get_empty ();
+
+ hb_subset_plan_t *plan = hb_subset_plan_create (source, input);
+ if (unlikely (plan->in_error ()))
+ return hb_face_get_empty ();
+
+ hb_set_t tags_set;
+ bool success = true;
+ hb_tag_t table_tags[32];
+ unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
+ while ((hb_face_get_table_tags (source, 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) && !tags_set.has (tag)) continue;
+ tags_set.add (tag);
+ success = _subset_table (plan, tag);
+ if (unlikely (!success)) goto end;
+ }
+ offset += num_tables;
+ }
+end:
+
+ hb_face_t *result = success ? hb_face_reference (plan->dest) : hb_face_get_empty ();
+
+ hb_subset_plan_destroy (plan);
+ return result;
+}
diff --git a/thirdparty/harfbuzz/src/hb-subset.h b/thirdparty/harfbuzz/src/hb-subset.h
new file mode 100644
index 0000000000..ddf4409734
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset.h
@@ -0,0 +1,97 @@
+/*
+ * 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"
+
+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_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 *subset_input);
+
+HB_EXTERN void
+hb_subset_input_destroy (hb_subset_input_t *subset_input);
+
+HB_EXTERN hb_set_t *
+hb_subset_input_unicode_set (hb_subset_input_t *subset_input);
+
+HB_EXTERN hb_set_t *
+hb_subset_input_glyph_set (hb_subset_input_t *subset_input);
+
+HB_EXTERN hb_set_t *
+hb_subset_input_nameid_set (hb_subset_input_t *subset_input);
+
+HB_EXTERN hb_set_t *
+hb_subset_input_namelangid_set (hb_subset_input_t *subset_input);
+
+HB_EXTERN hb_set_t *
+hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input);
+
+HB_EXTERN void
+hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input,
+ hb_bool_t drop_hints);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input);
+
+HB_EXTERN void
+hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input,
+ hb_bool_t desubroutinize);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_desubroutinize (hb_subset_input_t *subset_input);
+
+HB_EXTERN void
+hb_subset_input_set_retain_gids (hb_subset_input_t *subset_input,
+ hb_bool_t retain_gids);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_retain_gids (hb_subset_input_t *subset_input);
+
+HB_EXTERN void
+hb_subset_input_set_name_legacy (hb_subset_input_t *subset_input,
+ hb_bool_t name_legacy);
+HB_EXTERN hb_bool_t
+hb_subset_input_get_name_legacy (hb_subset_input_t *subset_input);
+
+/* hb_subset () */
+HB_EXTERN hb_face_t *
+hb_subset (hb_face_t *source, hb_subset_input_t *input);
+
+
+HB_END_DECLS
+
+#endif /* HB_SUBSET_H */
diff --git a/thirdparty/harfbuzz/src/hb-subset.hh b/thirdparty/harfbuzz/src/hb-subset.hh
new file mode 100644
index 0000000000..c9b01c67f3
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-subset.hh
@@ -0,0 +1,73 @@
+/*
+ * 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-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, hb_forward<Ts> (ds)...) )
+ template <typename T, typename ...Ts> auto
+ _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
+ ( obj.dispatch (this, hb_forward<Ts> (ds)...) )
+ public:
+ template <typename T, typename ...Ts> auto
+ dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
+ ( _dispatch (obj, hb_prioritize, hb_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/thirdparty/harfbuzz/src/hb-ucd-table.hh b/thirdparty/harfbuzz/src/hb-ucd-table.hh
new file mode 100644
index 0000000000..88623db338
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ucd-table.hh
@@ -0,0 +1,6780 @@
+/* == 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 13.0.0
+ */
+
+#ifndef HB_UCD_TABLE_HH
+#define HB_UCD_TABLE_HH
+
+#include "hb.hh"
+
+static const hb_script_t
+_hb_ucd_sc_map[157] =
+{
+ 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,
+};
+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[32480] =
+{
+ 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, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 27, 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, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 28,
+ 29, 26, 30, 31, 32, 33, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 34, 35, 35, 35, 35,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 26, 57, 58, 59, 59, 59, 59, 59, 26, 26, 60, 59, 59, 59, 59, 59,
+ 59, 59, 26, 61, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 26, 62, 59, 63, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 64, 26, 26, 65, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 66, 67, 59, 59, 59, 59, 68, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 69, 70, 71, 72, 73, 74, 59, 59,
+ 75, 76, 59, 59, 77, 59, 78, 79, 80, 81, 73, 82, 83, 84, 59, 59,
+ 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, 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, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 85, 26, 26, 26, 26, 26, 26, 26, 86, 87, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 88, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 89, 59, 59, 59, 59, 59, 59, 26, 90, 59, 59,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 91, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 92, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 93,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 94,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 29, 21, 21, 21, 23, 21, 21, 21, 22, 18, 21, 25, 21, 17, 21, 21,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 25, 25, 25, 21,
+ 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, 22, 21, 18, 24, 16,
+ 24, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 25, 18, 25, 0,
+ 29, 21, 23, 23, 23, 23, 26, 21, 24, 26, 7, 20, 25, 1, 26, 24,
+ 26, 25, 15, 15, 24, 5, 21, 21, 24, 15, 7, 19, 15, 15, 15, 21,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 5, 5,
+ 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5,
+ 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9,
+ 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5,
+ 9, 5, 9, 5, 9, 5, 9, 5, 9, 9, 5, 9, 5, 9, 5, 5,
+ 5, 9, 9, 5, 9, 5, 9, 9, 5, 9, 9, 9, 5, 5, 9, 9,
+ 9, 9, 5, 9, 9, 5, 9, 9, 9, 5, 5, 5, 9, 9, 5, 9,
+ 9, 5, 9, 5, 9, 5, 9, 9, 5, 9, 5, 5, 9, 5, 9, 9,
+ 5, 9, 9, 9, 5, 9, 5, 9, 9, 5, 5, 7, 9, 5, 5, 5,
+ 7, 7, 7, 7, 9, 8, 5, 9, 8, 5, 9, 8, 5, 9, 5, 9,
+ 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5,
+ 5, 9, 8, 5, 9, 5, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5,
+ 9, 5, 9, 5, 5, 5, 5, 5, 5, 5, 9, 9, 5, 9, 9, 5,
+ 5, 9, 5, 9, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5,
+ 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 24, 24, 24, 24, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 6, 6, 6, 6, 6, 24, 24, 24, 24, 24, 24, 24, 6, 24, 6, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 9, 5, 9, 5, 6, 24, 9, 5, 2, 2, 6, 5, 5, 5, 21, 9,
+ 2, 2, 2, 2, 24, 24, 9, 21, 9, 9, 9, 2, 9, 2, 9, 9,
+ 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9,
+ 5, 5, 9, 9, 9, 5, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5,
+ 5, 5, 5, 5, 9, 5, 25, 9, 5, 9, 9, 5, 5, 9, 9, 9,
+ 9, 5, 26, 12, 12, 12, 12, 12, 11, 11, 9, 5, 9, 5, 9, 5,
+ 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5,
+ 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 2, 2, 6, 21, 21, 21, 21, 21, 21,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 21, 17, 2, 2, 26, 26, 23,
+ 2, 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, 17, 12,
+ 21, 12, 12, 21, 12, 12, 21, 12, 2, 2, 2, 2, 2, 2, 2, 2,
+ 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, 2, 2, 2, 2, 7,
+ 7, 7, 7, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 25, 25, 25, 21, 21, 23, 21, 21, 26, 26,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 21, 1, 2, 21, 21,
+ 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 7, 7,
+ 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 21, 7, 12, 12, 12, 12, 12, 12, 12, 1, 26, 12,
+ 12, 12, 12, 12, 12, 6, 6, 12, 12, 26, 12, 12, 12, 12, 7, 7,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 26, 26, 7,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 1,
+ 7, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 6, 6, 26, 21, 21, 21, 6, 2, 2, 12, 23, 23,
+ 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 6, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 6, 12, 12, 12, 6, 12, 12, 12, 12, 12, 2, 2,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 2, 2, 21, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 1, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 12, 7, 10, 10,
+ 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 10, 10, 12, 10, 10,
+ 7, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 12, 12, 21, 21, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 21, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7,
+ 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7,
+ 7, 2, 7, 2, 2, 2, 7, 7, 7, 7, 2, 2, 12, 7, 10, 10,
+ 10, 12, 12, 12, 12, 2, 2, 10, 10, 2, 2, 10, 10, 12, 7, 2,
+ 2, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 7, 7, 2, 7,
+ 7, 7, 12, 12, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 7, 7, 23, 23, 15, 15, 15, 15, 15, 15, 26, 23, 7, 21, 12, 2,
+ 2, 12, 12, 10, 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7,
+ 7, 2, 7, 7, 2, 7, 7, 2, 7, 7, 2, 2, 12, 2, 10, 10,
+ 10, 12, 12, 2, 2, 2, 2, 12, 12, 2, 2, 12, 12, 12, 2, 2,
+ 2, 12, 2, 2, 2, 2, 2, 2, 2, 7, 7, 7, 7, 2, 7, 2,
+ 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 12, 12, 7, 7, 7, 12, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 12, 12, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7,
+ 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 10,
+ 10, 12, 12, 12, 12, 12, 2, 12, 12, 10, 2, 10, 10, 12, 2, 2,
+ 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 21, 23, 2, 2, 2, 2, 2, 2, 2, 7, 12, 12, 12, 12, 12, 12,
+ 2, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7,
+ 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 12,
+ 10, 12, 12, 12, 12, 2, 2, 10, 10, 2, 2, 10, 10, 12, 2, 2,
+ 2, 2, 2, 2, 2, 12, 12, 10, 2, 2, 2, 2, 7, 7, 2, 7,
+ 26, 7, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 12, 7, 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 7,
+ 7, 2, 7, 7, 7, 7, 2, 2, 2, 7, 7, 2, 7, 2, 7, 7,
+ 2, 2, 2, 7, 7, 2, 2, 2, 7, 7, 7, 2, 2, 2, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 10, 10,
+ 12, 10, 10, 2, 2, 2, 10, 10, 10, 2, 10, 10, 10, 12, 2, 2,
+ 7, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2,
+ 15, 15, 15, 26, 26, 26, 26, 26, 26, 23, 26, 2, 2, 2, 2, 2,
+ 12, 10, 10, 10, 12, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7,
+ 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 12, 12,
+ 12, 10, 10, 10, 10, 2, 12, 12, 12, 2, 12, 12, 12, 12, 2, 2,
+ 2, 2, 2, 2, 2, 12, 12, 2, 7, 7, 7, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 21, 15, 15, 15, 15, 15, 15, 15, 26,
+ 7, 12, 10, 10, 21, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7,
+ 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 2, 12, 7, 10, 12,
+ 10, 10, 10, 10, 10, 2, 12, 10, 10, 2, 10, 10, 12, 12, 2, 2,
+ 2, 2, 2, 2, 2, 10, 10, 2, 2, 2, 2, 2, 2, 2, 7, 2,
+ 2, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 12, 12, 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 7, 10, 10,
+ 10, 12, 12, 12, 12, 2, 10, 10, 10, 2, 10, 10, 10, 12, 7, 26,
+ 2, 2, 2, 2, 7, 7, 7, 10, 15, 15, 15, 15, 15, 15, 15, 7,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 7, 7, 7, 7, 7, 7,
+ 2, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 7, 7, 7, 7, 7, 7,
+ 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 12, 2, 2, 2, 2, 10,
+ 10, 10, 12, 12, 12, 2, 12, 2, 10, 10, 10, 10, 10, 10, 10, 10,
+ 2, 2, 10, 10, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 12, 7, 7, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 23,
+ 7, 7, 7, 7, 7, 7, 6, 12, 12, 12, 12, 12, 12, 12, 12, 21,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 2, 2, 2, 2,
+ 2, 7, 7, 2, 7, 2, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7,
+ 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 12, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 2, 2,
+ 7, 7, 7, 7, 7, 2, 6, 2, 12, 12, 12, 12, 12, 12, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 7, 7, 7, 7,
+ 7, 26, 26, 26, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 26, 21, 26, 26, 26, 12, 12, 26, 26, 26, 26, 26, 26,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 26, 12, 26, 12, 26, 12, 22, 18, 22, 18, 10, 10,
+ 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2,
+ 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10,
+ 12, 12, 12, 12, 12, 21, 12, 12, 7, 7, 7, 7, 7, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 26, 26,
+ 26, 26, 26, 26, 26, 26, 12, 26, 26, 26, 26, 26, 26, 2, 26, 26,
+ 21, 21, 21, 21, 21, 26, 26, 26, 26, 21, 21, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 12, 12, 12,
+ 12, 10, 12, 12, 12, 12, 12, 12, 10, 12, 12, 10, 10, 12, 12, 7,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 21, 21, 21, 21,
+ 7, 7, 7, 7, 7, 7, 10, 10, 12, 12, 7, 7, 7, 7, 12, 12,
+ 12, 7, 10, 10, 10, 7, 7, 10, 10, 10, 10, 10, 10, 10, 7, 7,
+ 7, 12, 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 12, 10, 10, 12, 12, 10, 10, 10, 10, 10, 10, 12, 7, 10,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 10, 10, 10, 12, 26, 26,
+ 9, 9, 9, 9, 9, 9, 2, 9, 2, 2, 2, 2, 2, 9, 2, 2,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 21, 6, 5, 5, 5,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 2, 2,
+ 7, 2, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 2,
+ 7, 2, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 12,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2,
+ 9, 9, 9, 9, 9, 9, 2, 2, 5, 5, 5, 5, 5, 5, 2, 2,
+ 17, 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, 26, 21, 7,
+ 29, 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, 22, 18, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 21, 21, 14, 14,
+ 14, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7,
+ 7, 7, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 12, 12, 12, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 2, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 12, 12, 10, 12, 12, 12, 12, 12, 12, 12, 10, 10,
+ 10, 10, 10, 10, 10, 10, 12, 10, 10, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 21, 21, 21, 6, 21, 21, 21, 23, 7, 12, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 2,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2,
+ 21, 21, 21, 21, 21, 21, 17, 21, 21, 21, 21, 12, 12, 12, 1, 2,
+ 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 7, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2,
+ 12, 12, 12, 10, 10, 10, 10, 12, 12, 10, 10, 10, 2, 2, 2, 2,
+ 10, 10, 12, 10, 10, 10, 10, 10, 10, 12, 12, 12, 2, 2, 2, 2,
+ 26, 2, 2, 2, 21, 21, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2,
+ 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 2, 2, 2, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 7, 7, 7, 7, 7, 7, 7, 12, 12, 10, 10, 12, 2, 2, 21, 21,
+ 7, 7, 7, 7, 7, 10, 12, 10, 12, 12, 12, 12, 12, 12, 12, 2,
+ 12, 10, 12, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 10,
+ 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 12,
+ 21, 21, 21, 21, 21, 21, 21, 6, 21, 21, 21, 21, 21, 21, 2, 2,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 12,
+ 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 12, 12, 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 12, 10, 12, 12, 12, 12, 12, 10, 12, 10, 10, 10,
+ 10, 10, 12, 10, 10, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2,
+ 21, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2,
+ 12, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 10, 12, 12, 12, 12, 10, 10, 12, 12, 10, 12, 12, 12, 7, 7,
+ 7, 7, 7, 7, 7, 7, 12, 10, 12, 12, 10, 10, 10, 12, 10, 12,
+ 12, 12, 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 21,
+ 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12,
+ 12, 12, 12, 12, 10, 10, 12, 12, 2, 2, 2, 21, 21, 21, 21, 21,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 21, 21,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9,
+ 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2,
+ 12, 12, 12, 21, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 10, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 12, 7, 7,
+ 7, 7, 7, 7, 12, 7, 7, 10, 12, 12, 7, 2, 2, 2, 2, 2,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12,
+ 9, 5, 9, 5, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9,
+ 5, 5, 5, 5, 5, 5, 2, 2, 9, 9, 9, 9, 9, 9, 2, 2,
+ 5, 5, 5, 5, 5, 5, 5, 5, 2, 9, 2, 9, 2, 9, 2, 9,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2,
+ 5, 5, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, 8, 8, 8,
+ 5, 5, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 5, 24,
+ 24, 24, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 24, 24,
+ 5, 5, 5, 5, 2, 2, 5, 5, 9, 9, 9, 9, 2, 24, 24, 24,
+ 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 24, 24, 24,
+ 2, 2, 5, 5, 5, 2, 5, 5, 9, 9, 9, 9, 8, 24, 24, 2,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 1, 1, 1, 1, 1,
+ 17, 17, 17, 17, 17, 17, 21, 21, 20, 19, 22, 20, 20, 19, 22, 20,
+ 21, 21, 21, 21, 21, 21, 21, 21, 27, 28, 1, 1, 1, 1, 1, 29,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 19, 21, 21, 21, 21, 16,
+ 16, 21, 21, 21, 25, 22, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 25, 21, 16, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 29,
+ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 15, 6, 2, 2, 15, 15, 15, 15, 15, 15, 25, 25, 25, 22, 18, 6,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 25, 25, 25, 22, 18, 2,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11,
+ 11, 12, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 26, 26, 9, 26, 26, 26, 26, 9, 26, 26, 5, 9, 9, 9, 5, 5,
+ 9, 9, 9, 5, 26, 9, 26, 26, 25, 9, 9, 9, 9, 9, 26, 26,
+ 26, 26, 26, 26, 9, 26, 9, 26, 9, 26, 9, 9, 9, 9, 26, 5,
+ 9, 9, 9, 9, 5, 7, 7, 7, 7, 5, 26, 26, 5, 5, 9, 9,
+ 25, 25, 25, 25, 25, 9, 5, 5, 5, 5, 26, 25, 26, 26, 5, 26,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 9, 5, 14, 14, 14, 14, 15, 26, 26, 2, 2, 2, 2,
+ 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 25, 25, 26, 26, 26, 26,
+ 25, 26, 26, 25, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 25, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25,
+ 26, 26, 25, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 26, 26, 26, 26, 26, 26, 26, 26, 22, 18, 22, 18, 26, 26, 26, 26,
+ 25, 25, 26, 26, 26, 26, 26, 26, 26, 22, 18, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25,
+ 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 15, 15, 15, 15,
+ 26, 26, 26, 26, 26, 26, 26, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25,
+ 26, 26, 26, 26, 26, 26, 26, 26, 22, 18, 22, 18, 22, 18, 22, 18,
+ 22, 18, 22, 18, 22, 18, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 25, 25, 25, 25, 25, 22, 18, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18,
+ 25, 25, 25, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22,
+ 18, 22, 18, 22, 18, 22, 18, 22, 18, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 22, 18, 22, 18, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 22, 18, 25, 25,
+ 25, 25, 25, 25, 25, 26, 26, 25, 25, 25, 25, 25, 25, 26, 26, 26,
+ 26, 26, 26, 26, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 2,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2,
+ 9, 5, 9, 9, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, 9, 9,
+ 9, 5, 9, 5, 5, 9, 5, 5, 5, 5, 5, 5, 6, 6, 9, 9,
+ 9, 5, 9, 5, 5, 26, 26, 26, 26, 26, 26, 9, 5, 9, 5, 12,
+ 12, 12, 9, 5, 2, 2, 2, 2, 2, 21, 21, 21, 21, 15, 21, 21,
+ 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 6,
+ 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12,
+ 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 2,
+ 21, 21, 20, 19, 20, 19, 21, 21, 21, 20, 19, 21, 20, 19, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 17, 21, 21, 17, 21, 20, 19, 21, 21,
+ 20, 19, 22, 18, 22, 18, 22, 18, 22, 18, 21, 21, 21, 21, 21, 6,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 17, 17, 21, 21, 21, 21,
+ 17, 21, 22, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 26, 26, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2,
+ 29, 21, 21, 21, 26, 6, 7, 14, 22, 18, 22, 18, 22, 18, 22, 18,
+ 22, 18, 26, 26, 22, 18, 22, 18, 22, 18, 22, 18, 17, 22, 18, 18,
+ 26, 14, 14, 14, 14, 14, 14, 14, 14, 14, 12, 12, 12, 12, 10, 10,
+ 17, 6, 6, 6, 6, 6, 26, 26, 14, 14, 14, 6, 7, 21, 26, 26,
+ 7, 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 24, 24, 6, 6, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 6, 6, 6, 7,
+ 2, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 26, 26, 15, 15, 15, 15, 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, 2,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 15, 15, 15, 15, 15, 15,
+ 26, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 21, 21, 21,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 2, 2, 2, 2,
+ 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 7, 12,
+ 11, 11, 11, 21, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 21, 6,
+ 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 6, 6, 12, 12,
+ 7, 7, 7, 7, 7, 7, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 12, 12, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2,
+ 24, 24, 24, 24, 24, 24, 24, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 24, 24, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5,
+ 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5,
+ 6, 5, 5, 5, 5, 5, 5, 5, 5, 9, 5, 9, 5, 9, 9, 5,
+ 9, 5, 9, 5, 9, 5, 9, 5, 6, 24, 24, 9, 5, 9, 5, 7,
+ 9, 5, 9, 5, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5,
+ 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 9, 9, 9, 9, 5,
+ 9, 9, 9, 9, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5,
+ 2, 2, 9, 5, 9, 9, 9, 9, 5, 9, 5, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 9, 5, 7, 6, 6, 5, 7, 7, 7, 7, 7,
+ 7, 7, 12, 7, 7, 7, 12, 7, 7, 7, 7, 12, 7, 7, 7, 7,
+ 7, 7, 7, 10, 10, 12, 12, 10, 26, 26, 26, 26, 12, 2, 2, 2,
+ 15, 15, 15, 15, 15, 15, 26, 26, 23, 26, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2,
+ 10, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 21, 21,
+ 12, 12, 7, 7, 7, 7, 7, 7, 21, 21, 21, 7, 21, 7, 7, 12,
+ 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 21, 21,
+ 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21,
+ 7, 7, 7, 12, 10, 10, 12, 12, 12, 12, 10, 10, 12, 12, 10, 10,
+ 10, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 6,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 21, 21,
+ 7, 7, 7, 7, 7, 12, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 7, 7, 7, 7, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 10,
+ 10, 12, 12, 10, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 12, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 21, 21, 21, 21,
+ 6, 7, 7, 7, 7, 7, 7, 26, 26, 26, 7, 10, 12, 10, 7, 7,
+ 12, 7, 12, 12, 12, 7, 7, 12, 12, 7, 7, 7, 7, 7, 12, 12,
+ 7, 12, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 6, 21, 21,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 12, 12, 10, 10,
+ 21, 21, 7, 6, 6, 10, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7, 2,
+ 2, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 6, 6, 6, 6,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 24, 24, 2, 2, 2, 2,
+ 7, 7, 7, 10, 10, 12, 10, 10, 12, 10, 10, 21, 10, 12, 2, 2,
+ 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 7, 7, 7, 7, 7,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 7, 12, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 25, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 7, 2,
+ 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 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, 18, 22,
+ 2, 2, 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, 23, 26, 2, 2,
+ 21, 21, 21, 21, 21, 21, 21, 22, 18, 21, 2, 2, 2, 2, 2, 2,
+ 21, 17, 17, 16, 16, 22, 18, 22, 18, 22, 18, 22, 18, 22, 18, 22,
+ 18, 22, 18, 22, 18, 21, 21, 22, 18, 21, 21, 21, 21, 16, 16, 16,
+ 21, 21, 21, 2, 21, 21, 21, 21, 17, 22, 18, 22, 18, 22, 18, 21,
+ 21, 21, 25, 17, 25, 25, 25, 2, 21, 23, 21, 21, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 1,
+ 2, 21, 21, 21, 23, 21, 21, 21, 22, 18, 21, 25, 21, 17, 21, 21,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 25, 18, 25, 22,
+ 18, 21, 22, 18, 21, 21, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 6, 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, 6, 6,
+ 2, 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7,
+ 2, 2, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 2, 2, 2,
+ 23, 23, 25, 24, 26, 23, 23, 2, 26, 25, 25, 25, 25, 26, 26, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 26, 26, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7,
+ 21, 21, 21, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 14, 14, 14, 14, 14, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 15, 15, 26, 26, 26, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2,
+ 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 2, 2,
+ 12, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2,
+ 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 7,
+ 7, 14, 7, 7, 7, 7, 7, 7, 7, 7, 14, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 21,
+ 7, 7, 7, 7, 2, 2, 2, 2, 7, 7, 7, 7, 7, 7, 7, 7,
+ 21, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, 5, 5,
+ 9, 9, 9, 9, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2,
+ 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21,
+ 7, 7, 7, 7, 7, 7, 2, 2, 7, 2, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 2, 2, 7, 2, 2, 7,
+ 7, 7, 7, 7, 7, 7, 2, 21, 15, 15, 15, 15, 15, 15, 15, 15,
+ 7, 7, 7, 7, 7, 7, 7, 26, 26, 15, 15, 15, 15, 15, 15, 15,
+ 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 7, 7, 7, 2, 7, 7, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15,
+ 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 2, 2, 2, 21,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 21,
+ 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 15, 15, 7, 7,
+ 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 7, 12, 12, 12, 2, 12, 12, 2, 2, 2, 2, 2, 12, 12, 12, 12,
+ 7, 7, 7, 7, 2, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 2, 2, 12, 12, 12, 2, 2, 2, 2, 12,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 21,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15,
+ 7, 7, 7, 7, 7, 7, 7, 7, 26, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 12, 12, 2, 2, 2, 2, 15, 15, 15, 15, 15,
+ 21, 21, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 2, 2, 2, 21, 21, 21, 21, 21, 21, 21,
+ 7, 7, 7, 7, 7, 7, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15,
+ 7, 7, 7, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15,
+ 7, 7, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 21, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15, 15,
+ 9, 9, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 15, 15, 15, 15, 15, 15,
+ 7, 7, 7, 7, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 12, 12, 17, 2, 2,
+ 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 15, 15, 15, 15, 15, 15, 15, 7, 2, 2, 2, 2, 2, 2, 2, 2,
+ 12, 15, 15, 15, 15, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 2,
+ 10, 12, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 21, 21, 21, 21, 21, 21, 21, 2, 2,
+ 15, 15, 15, 15, 15, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12,
+ 10, 10, 10, 12, 12, 12, 12, 10, 10, 12, 12, 21, 21, 1, 21, 21,
+ 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2,
+ 12, 12, 12, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 10, 12, 12, 12,
+ 12, 12, 12, 12, 12, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 21, 21, 21, 21, 7, 10, 10, 7, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 12, 21, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10,
+ 10, 7, 7, 7, 7, 21, 21, 21, 21, 12, 12, 12, 12, 21, 10, 12,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 7, 21, 7, 21, 21, 21,
+ 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 12,
+ 12, 12, 10, 10, 12, 10, 12, 12, 21, 21, 21, 21, 21, 21, 12, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 2, 7, 7, 7, 7, 2, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12,
+ 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2,
+ 12, 12, 10, 10, 2, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7,
+ 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 2, 12, 12, 7, 10, 10,
+ 12, 10, 10, 10, 10, 2, 2, 10, 10, 2, 2, 10, 10, 10, 2, 2,
+ 7, 2, 2, 2, 2, 2, 2, 10, 2, 2, 2, 2, 2, 7, 7, 7,
+ 7, 7, 10, 10, 2, 2, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2,
+ 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12,
+ 10, 10, 12, 12, 12, 10, 12, 7, 7, 7, 7, 21, 21, 21, 21, 21,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 21, 21, 2, 21, 12, 7,
+ 10, 10, 10, 12, 12, 12, 12, 12, 12, 10, 12, 10, 10, 10, 10, 12,
+ 12, 10, 12, 12, 7, 7, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10,
+ 10, 10, 12, 12, 12, 12, 2, 2, 10, 10, 10, 10, 12, 12, 10, 12,
+ 12, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 7, 7, 7, 7, 12, 12, 2, 2,
+ 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 12, 10, 12,
+ 12, 21, 21, 21, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 10, 12, 10, 10,
+ 12, 12, 12, 12, 12, 12, 10, 12, 7, 2, 2, 2, 2, 2, 2, 2,
+ 10, 10, 12, 12, 12, 12, 10, 12, 12, 12, 12, 12, 2, 2, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 15, 15, 21, 21, 21, 26,
+ 12, 12, 12, 12, 12, 12, 12, 12, 10, 12, 12, 21, 2, 2, 2, 2,
+ 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7,
+ 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, 2, 2, 7, 7, 7, 7,
+ 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7,
+ 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 2, 12, 12, 10, 12, 7,
+ 10, 7, 10, 12, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 7, 7, 7, 7, 7, 7,
+ 7, 10, 10, 10, 12, 12, 12, 12, 2, 2, 12, 12, 10, 10, 10, 10,
+ 12, 7, 21, 7, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 7, 7, 7, 7, 7,
+ 7, 7, 7, 12, 12, 12, 12, 12, 12, 10, 7, 12, 12, 12, 12, 21,
+ 21, 21, 21, 21, 21, 21, 21, 12, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 12, 12, 12, 12, 12, 12, 10, 10, 12, 12, 12, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 10, 12, 12, 21, 21, 21, 7, 21, 21,
+ 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 10, 12,
+ 7, 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 21, 21, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 2, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 2, 10, 12, 12, 12, 12, 12, 12,
+ 12, 10, 12, 12, 10, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7,
+ 7, 12, 12, 12, 12, 12, 12, 2, 2, 2, 12, 2, 12, 12, 2, 12,
+ 12, 12, 12, 12, 12, 12, 7, 12, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 2, 7, 7, 2, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10, 2,
+ 12, 12, 2, 10, 10, 12, 10, 12, 7, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 12, 12, 10, 10, 21, 21, 2, 2, 2, 2, 2, 2, 2,
+ 15, 15, 15, 15, 15, 26, 26, 26, 26, 26, 26, 26, 26, 23, 23, 23,
+ 23, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2,
+ 21, 21, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
+ 12, 12, 12, 12, 12, 21, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 12, 12, 12, 12, 12, 12, 12, 21, 21, 21, 21, 21, 26, 26, 26, 26,
+ 6, 6, 6, 6, 21, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 15, 15, 15, 15, 15,
+ 15, 15, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 2, 7, 7, 7,
+ 15, 15, 15, 15, 15, 15, 15, 21, 21, 21, 21, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 2, 12,
+ 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, 12,
+ 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 21, 6, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 10, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 7, 7, 7, 7, 2, 2, 2, 2, 2, 2, 2, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 26, 12, 12, 21,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 2, 2, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 10, 10, 12, 12, 12, 26, 26, 26, 10, 10, 10,
+ 10, 10, 10, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 12, 12, 12,
+ 12, 12, 12, 26, 26, 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 12, 12, 12, 12, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 12, 12, 12, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 5,
+ 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 2, 9, 9,
+ 2, 2, 9, 2, 2, 9, 9, 2, 2, 9, 9, 9, 9, 2, 9, 9,
+ 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 2, 5, 2, 5, 5, 5,
+ 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 9, 9, 2, 9, 9, 9, 9, 2, 2, 9, 9, 9,
+ 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9, 2, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 2, 9, 9, 9, 9, 2,
+ 9, 9, 9, 9, 9, 2, 9, 2, 2, 2, 9, 9, 9, 9, 9, 9,
+ 9, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 2, 2, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 25, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5,
+ 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 25,
+ 5, 5, 5, 5, 5, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5,
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 9, 5, 2, 2, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 12, 12, 12, 12, 12, 12, 12, 26, 26, 26, 26, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 26, 26, 26,
+ 26, 26, 26, 26, 26, 12, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 12, 26, 26, 21, 21, 21, 21, 21, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 12, 12,
+ 12, 12, 2, 12, 12, 2, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2,
+ 12, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 2, 2,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 7, 26,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, 12,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 23,
+ 7, 7, 7, 7, 7, 2, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 5, 5, 5, 5, 12, 12, 12, 12, 12, 12, 12, 6, 2, 2, 2, 2,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 15, 15, 15,
+ 23, 15, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2,
+ 7, 7, 7, 7, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 2, 7, 7, 2, 7, 2, 2, 7, 2, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 2, 7, 2, 2, 2, 2,
+ 2, 2, 7, 2, 2, 2, 2, 7, 2, 7, 2, 7, 2, 7, 7, 7,
+ 2, 7, 7, 2, 7, 2, 2, 7, 2, 7, 2, 7, 2, 7, 2, 7,
+ 2, 7, 7, 2, 7, 2, 2, 7, 7, 7, 7, 2, 7, 7, 7, 7,
+ 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 7, 7, 7, 2, 7, 2,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7,
+ 2, 7, 7, 7, 2, 7, 7, 7, 7, 7, 2, 7, 7, 7, 7, 7,
+ 25, 25, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 2,
+ 2, 2, 2, 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 24, 24, 24, 24, 24,
+ 26, 26, 26, 26, 26, 26, 26, 26, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 26, 26, 26,
+ 26, 26, 26, 26, 26, 2, 2, 2, 26, 26, 26, 2, 2, 2, 2, 2,
+ 26, 26, 26, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 12, 12, 13, 14, 12, 15, 16, 17, 18, 19, 20,
+ 21, 22, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 24, 25,
+ 0, 26, 27, 0, 28, 29, 30, 31, 32, 33, 0, 34, 0, 0, 0, 0,
+ 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 0, 0, 0, 0,
+ 39, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0,
+ 43, 44, 45, 46, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 50, 0, 0, 0,
+ 0, 0, 0, 51, 0, 52, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 55, 0, 0, 0, 0, 56, 0, 0, 57, 58, 0,
+ 59, 60, 61, 62, 63, 64, 65, 0, 66, 67, 0, 68, 69, 70, 71, 0,
+ 60, 0, 72, 73, 74, 75, 0, 0, 69, 0, 76, 77, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 0, 0, 0, 0, 80,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, 0, 79, 0, 0, 86, 0, 0, 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, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 1, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 14, 15, 16, 17, 18, 19, 20, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 21, 0, 0, 0, 0, 0, 22, 23, 24,
+ 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 27,
+ 28, 29, 0, 0, 0, 0, 30, 0, 0, 0, 31, 32, 33, 34, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 13, 35, 36, 0, 0, 26, 37, 38, 39, 0, 0, 0, 0, 0, 40,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 1,
+ 42, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 48, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 0, 0,
+ 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 46, 0, 47, 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, 0, 0, 0, 0, 0, 0, 47, 50, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 47, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 54, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 0, 56, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 58, 59, 0, 0, 0, 0,
+ 0, 0, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, 0, 0, 0, 0,
+ 0, 0, 0, 0, 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, 66,
+ 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 67, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 68,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 71, 72, 0, 0, 0, 0, 0, 0, 0, 0,
+ 73, 0, 66, 74, 0, 0, 0, 0, 0, 0, 75, 76, 72, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 67, 0, 0, 0,
+ 0, 77, 78, 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, 0, 0, 0,
+ 80, 0, 79, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 82,
+ 83, 84, 85, 86, 0, 0, 0, 0, 0, 0, 0, 0, 87, 88, 89, 1,
+ 1, 1, 90, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 93,
+ 94, 95, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 71, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 71,100,101, 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 86, 0,102, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0,
+ 1, 1, 86, 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0,104, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105, 0, 73, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106,107,108, 0, 0, 0,
+ 0, 0,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 47, 0, 0, 0, 0, 0,109, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,110,111, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 26,112, 0,113, 0, 0, 0, 0, 0,114, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 115, 0, 0, 0, 0, 0, 0, 0,100, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117,118, 72, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,102, 0, 0, 0,
+ 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0,
+ 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0,
+ 0, 0,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73,120, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,121, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,122, 0, 0, 0, 0, 0, 0, 0, 0, 0,123, 0, 47, 0, 0,
+ 26,124,124, 0, 0, 0, 0, 0, 0, 0, 0, 0,125, 0, 0, 49,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,126, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97,127, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,129,105, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 73, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 0, 97, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,130, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,131, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,132, 0, 0, 0, 0, 0, 0, 0,133, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,134, 0, 0, 0, 0,135, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 136,137,138,139,140,141, 0, 0, 0,142, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,143, 0, 0, 0,
+ 0, 0, 0, 0,133, 1, 1,144,145,112, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,146, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,100,147, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,
+ 230,230,230,230,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,
+ 220,220,220,220,220,220,220,220, 1, 1, 1, 1, 1,220,220,220,
+ 220,230,230,230,230,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,230,230,230,230, 0, 0, 0,230,
+ 230,230,230,230, 0,220,230,230,230,230,220,230,230,230,222,220,
+ 230,230,230,230,230,230,220,220,220,220,220,220,230,230,220,230,
+ 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, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230,
+ 230,220,220,230,230,230,230,230,220,230,230,220, 35, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,230,
+ 230, 0, 0,230,230,230,230,220,230, 0, 0,230,230, 0,220,230,
+ 230,220, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0,230,220,230,230,
+ 220,230,230,220,220,220,230,220,220,230,220,230,230,230,220,230,
+ 220,230,220,230,220,230,230, 0, 0, 0, 0, 0,230,230,220,230,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,220, 0, 0,230,230, 0,230,
+ 230,230,230,230,230,230,230,230, 0,230,230,230, 0,230,230,230,
+ 230,230, 0, 0, 0,220,220,220, 0, 0, 0, 0, 0, 0, 0,220,
+ 230,230,230,230,230,230, 0,220,230,230,220,230,230,220,230,230,
+ 230,220,220,220, 27, 28, 29,230,230,230,220,230,230,220,220,230,
+ 230,230,230,230, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,230, 0, 0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 9,
+ 9, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0,103,103, 9, 0,
+ 0, 0, 0, 0,107,107,107,107, 0, 0, 0, 0,118,118, 9, 0,
+ 0, 0, 0, 0,122,122,122,122, 0, 0, 0, 0,220,220, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,220, 0,220, 0,216, 0, 0,
+ 0, 0, 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, 0, 0,
+ 0, 0,220, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 9, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,230,230,230, 0, 0, 0, 0,
+ 9, 0, 0, 0, 0, 0, 0, 0, 0,230, 0, 0, 0,228, 0, 0,
+ 0, 0, 0, 0, 0,222,230,220, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0,230,220, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
+ 0, 0, 0, 0,230,230,230,230,230, 0, 0,220,230,230,230,230,
+ 230,220,220,220,220,220,220,230,230,220, 0,220, 0, 0, 0,230,
+ 220,230,230,230,230,230,230,230, 0, 0, 0, 0, 0, 0, 9, 9,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0,230,230,230, 0,
+ 1,220,220,220,220,220,230,230,220,220,220,220,230, 0, 1, 1,
+ 1, 1, 1, 1, 1, 0, 0, 0, 0,220, 0, 0, 0, 0, 0, 0,
+ 230, 0, 0, 0,230,230, 0, 0, 0, 0, 0, 0,230,230,220,230,
+ 230,230,230,230,230,230,220,230,230,234,214,220,202,230,230,230,
+ 230,230,230,230,230,230,230,230,230,230,232,228,228,220, 0,230,
+ 233,220,230,220,230,230, 1, 1,230,230,230,230, 1, 1, 1,230,
+ 230, 0, 0, 0, 0,230, 0, 0, 0, 1, 1,230,220,230, 1, 1,
+ 220,220,220,220,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 9, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,230,230,230,
+ 230,230, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0,220,
+ 220,220, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 7,
+ 0, 0, 0, 0,230, 0,230,230,220, 0, 0,230,230, 0, 0, 0,
+ 0, 0,230,230, 0,230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 26, 0,230,230,230,230,230,230,230,220,220,220,220,220,
+ 220,220,230,230,230,230,230, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,220, 0,230,230, 1,220, 0, 0, 0, 0, 9, 0, 0, 0, 0,
+ 0,230,220, 0, 0, 0, 0,230,230, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,220,220,230,230,230,220,230,220,220,220, 0, 9, 7, 0,
+ 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 0, 0, 0, 0, 0, 7,
+ 7, 0, 0, 0,230,230,230,230,230, 0, 0, 0, 0, 0, 9, 0,
+ 0, 0, 7, 0, 0, 0, 9, 7, 0, 0, 0, 0, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 0, 0, 0, 0,
+ 0, 9, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0,
+ 9, 9, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0,230,230,230,230,
+ 230,230,230, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0, 0,216,216, 1, 1, 1, 0, 0,
+ 0,226,216,216,216,216,216, 0, 0, 0, 0, 0, 0, 0, 0,220,
+ 220,220,220,220,220,220,220, 0, 0,230,230,230,230,230,220,220,
+ 0, 0, 0, 0, 0, 0,230,230,230,230, 0, 0, 0, 0,230,230,
+ 230, 0, 0, 0,230, 0, 0,230,230,230,230,230,230,230, 0,230,
+ 230, 0,230,230,220,220,220,220,220,220,220, 0,230,230, 7, 0,
+ 0, 0, 0, 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, 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, 0, 0,
+ 0, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 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, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 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, 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, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 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, 20,
+ 20, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 21, 32, 4, 0, 10, 0, 33, 7, 20, 20, 20,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 0, 0, 0, 0, 0, 8, 21,
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 20, 1, 20, 20, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 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, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 3, 4, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0,
+ 0, 0, 0, 0, 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, 0, 0,
+ 0, 0, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 17, 19, 20, 21, 22, 23, 24, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 27, 28, 28, 29, 30, 31, 32, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 35, 35, 35, 35, 35, 59, 59, 60, 35,
+ 35, 35, 35, 35, 35, 35, 61, 62, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 63, 64, 35, 65, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 67, 66, 68, 69, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 70, 71, 35, 35,
+ 35, 35, 72, 35, 35, 35, 35, 35, 35, 35, 35, 35, 73, 74, 75, 76,
+ 77, 78, 35, 35, 79, 80, 35, 35, 81, 35, 82, 83, 84, 85, 17, 86,
+ 87, 88, 35, 35, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 89, 25, 25, 25, 25, 25, 25, 25, 90,
+ 91, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 92, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 93, 35, 35, 35, 35, 35, 35,
+ 25, 94, 35, 35, 25, 25, 25, 25, 25, 25, 25, 25, 25, 95, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 19, 19, 19, 19, 19, 19, 19,
+ 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 19, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 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, 19, 19, 19, 19, 0, 0, 0, 0, 0,
+ 26, 26, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 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,
+ 9, 9, 9, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 9,
+ 9, 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, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 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, 14, 14, 14, 14, 14, 14, 14,
+ 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2,
+ 2, 2, 2, 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, 2, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 0, 3, 3, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 2, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+ 38, 38, 38, 38, 38, 38, 38, 38, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 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, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90,
+ 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
+ 95, 95, 2, 2, 95, 2, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
+ 37, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 7, 7,
+ 7, 7, 7, 7, 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, 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, 5, 5, 5, 5, 2, 2, 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, 5, 5, 5, 5, 5, 5, 5, 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, 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, 11, 11, 2, 2, 2, 2, 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, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 2,
+ 2, 2, 2, 2, 2, 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, 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, 10, 10, 10, 10, 2, 10, 10, 10,
+ 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10,
+ 10, 10, 10, 10, 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, 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, 21, 21, 21, 21, 2, 2, 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, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2,
+ 2, 2, 2, 2, 2, 2, 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, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 2, 2, 2, 2, 22, 22, 22, 22, 22, 2, 2, 2, 22, 22, 22, 2,
+ 22, 22, 22, 22, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 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,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 2,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 2, 23, 23, 23, 2,
+ 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, 23,
+ 23, 2, 2, 2, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 23,
+ 23, 23, 23, 23, 23, 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, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 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, 16, 16, 16, 16, 2, 16, 16, 16, 2,
+ 16, 16, 16, 16, 2, 2, 2, 2, 2, 2, 2, 16, 16, 2, 2, 2,
+ 2, 2, 2, 2, 16, 2, 16, 16, 16, 16, 2, 2, 16, 16, 16, 16,
+ 16, 16, 16, 16, 16, 16, 2, 16, 16, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 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, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 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, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 20, 20,
+ 20, 20, 20, 20, 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, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 2, 36, 2, 2, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2,
+ 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 36, 36,
+ 36, 36, 36, 36, 36, 36, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 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, 24, 24, 24, 24, 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, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18,
+ 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 2, 2, 2, 2, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 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, 33, 33,
+ 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 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, 8, 8, 8, 8, 8, 8, 8,
+ 8, 0, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 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, 2,
+ 30, 30, 30, 30, 2, 2, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30,
+ 30, 30, 30, 30, 30, 2, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 2, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 2, 2, 29, 29,
+ 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 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, 2, 2, 2, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 0, 0, 0, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 2,
+ 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 2, 45, 45, 45, 45, 45, 45, 45, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 2, 2,
+ 2, 2, 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, 2, 2,
+ 2, 2, 2, 2, 2, 2, 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, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 2, 2, 2, 2, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ 2, 2, 2, 2, 2, 2, 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, 2,
+ 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 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, 48,
+ 48, 48, 48, 48, 48, 2, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
+ 52, 52, 52, 52, 2, 2, 52, 52, 52, 52, 52, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 58, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 2, 2, 2, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58,
+ 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+ 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+ 91, 91, 91, 2, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+ 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+ 91, 91, 91, 91, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 2, 2, 2, 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 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, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 2, 2,
+ 2, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,
+ 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2,
+ 2, 2, 2, 2, 2, 2, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 2, 2, 8, 8, 8, 76, 76, 76, 76, 76, 76, 76, 76, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 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, 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, 19, 19, 19, 19, 19, 19, 19, 19, 6, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9,
+ 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, 9, 9, 9, 9, 2, 9,
+ 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, 9, 2, 9, 9, 9, 9,
+ 9, 9, 9, 9, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 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, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0,
+ 19, 19, 0, 0, 0, 0, 0, 0, 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, 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0,
+ 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 2, 2, 2, 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 2, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
+ 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55,
+ 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61,
+ 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2,
+ 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 61, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30,
+ 30, 30, 30, 30, 30, 2, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 1, 1, 1, 1, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 17, 17, 17, 17, 17, 17, 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, 26, 26, 26, 26, 26, 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, 12, 12, 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, 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0,
+ 0, 0, 0, 0, 0, 0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 2, 2, 2, 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, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
+ 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0,
+ 0, 19, 19, 19, 19, 19, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19,
+ 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
+ 60, 60, 60, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
+ 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 2, 2,
+ 2, 2, 2, 2, 2, 2, 75, 75, 75, 75, 75, 75, 75, 75, 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, 75, 75, 75, 75, 75, 75, 75, 75,
+ 2, 2, 2, 2, 2, 2, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,
+ 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 74, 74, 74, 74, 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, 12, 12, 12, 12, 12,
+ 12, 12, 12, 2, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 2, 0, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
+ 68, 68, 68, 68, 2, 2, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68,
+ 2, 2, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92,
+ 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 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, 87, 87, 87, 87, 87, 87, 87, 87, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 30, 30, 2, 2, 30,
+ 30, 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19, 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, 87, 87, 87, 87,
+ 87, 87, 87, 87, 2, 2, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
+ 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2,
+ 2, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2,
+ 2, 2, 2, 2, 2, 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, 14, 14,
+ 14, 14, 14, 14, 14, 14, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 0, 0, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
+ 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2,
+ 12, 12, 12, 12, 12, 12, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2,
+ 12, 12, 12, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 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, 49, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49,
+ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 2, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 2, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ 49, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
+ 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71,
+ 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 42, 42, 42, 42, 42, 42, 42, 42, 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, 41, 41, 41, 41, 41, 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,118,118,118,118,118,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, 53, 53, 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, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+ 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
+ 51, 51, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 50, 50, 50, 50, 2, 2, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
+ 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135,
+ 135,135,135,135,135,135,135,135,135,135, 2, 2, 2, 2,135,135,
+ 135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,
+ 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,106,106,
+ 106,106,106,106,106,106,106,106,106,106,106,106,106,106, 2, 2,
+ 2, 2, 2, 2, 2, 2,104,104,104,104,104,104,104,104,104,104,
+ 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2,104,110,110,110,110,110,110,110,110,110,110,
+ 110,110,110,110,110,110,110,110,110,110,110,110,110, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,110,110,110,110,110,110, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,110,110,110,110,110,110,110,110, 2, 2,
+ 2, 2, 2, 2, 2, 2, 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, 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, 81, 81, 81, 81, 81, 81, 81, 81, 2, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81,120,120,120,120,120,120,120,120,120,120,
+ 120,120,120,120,120,120,116,116,116,116,116,116,116,116,116,116,
+ 116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,116,
+ 116,116,116,116,116, 2, 2, 2, 2, 2, 2, 2, 2,116,116,116,
+ 116,116,116,116,116,116,128,128,128,128,128,128,128,128,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, 66, 66, 66, 66, 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,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 2, 2, 2, 2, 2, 72, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2,
+ 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 97, 97,
+ 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, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57,
+ 57, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2,
+ 2, 2, 2, 2, 2, 2, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,117,117,
+ 117,117,117,117,117,117,112,112,112,112,112,112,112,112,112,112,
+ 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2,
+ 2,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 78, 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, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 83, 83,
+ 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 82, 82, 82,
+ 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2, 82, 82,
+ 82, 82, 82, 82, 82, 82,122,122,122,122,122,122,122,122,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, 2, 2, 2, 2, 2, 2, 2, 2,122,
+ 122,122,122,122,122,122, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 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,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,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,
+ 144,144,144,144,144,144,144,144,144,144,144,144,144,144, 2, 2,
+ 2, 2, 2, 2, 2, 2,144,144,144,144,144,144,144,144,144,144,
+ 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 2,156,156,156,156,156,156,156,156,156,156,
+ 156,156,156,156,156,156,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, 2, 2, 2,147,147,147,147,147,147,147,147,147,147,
+ 147,147,147,147,147,147,147,147,147,147,147,147,147,147, 2, 2,
+ 2, 2, 2, 2, 2, 2,148,148,148,148,148,148,148,148,148,148,
+ 148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,
+ 2, 2, 2, 2, 2, 2,153,153,153,153,153,153,153,153,153,153,
+ 153,153,153,153,153,153,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,149,149,149,149,149,149,149,149, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 94, 94, 94, 94, 94, 94,
+ 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 94, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85,
+ 85, 85, 85, 85, 85, 85, 85, 85, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 85, 2, 2,101,101,101,101,101,101,101,101,101,101,
+ 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101, 2,
+ 2, 2, 2, 2, 2, 2,101,101,101,101,101,101,101,101,101,101,
+ 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 96, 96, 96, 96,
+ 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 2,
+ 2, 2, 2, 2, 2, 2,111,111,111,111,111,111,111,111,111,111,
+ 111,111,111,111,111,111,111,111,111,111,111,111,111, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,108,108,108,108,108,108,108,108,108,108,
+ 108,108,108,108,108,108,108,108, 2,108,108,108,108,108,108,108,
+ 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,
+ 108,108,108,108,108, 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,129,129,129,129,129,129,129,129,
+ 2, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109,
+ 109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,109,
+ 109, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,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,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,107,107,107,107, 2, 2,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,107,107,107,107,107, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,137,137,137,137,137,137,137,137,137,137,
+ 137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,
+ 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,124,124,124,124,124,124,124,124,124,124,
+ 124,124,124,124,124,124,124,124,124,124,124,124,124,124, 2, 2,
+ 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,123,123,123,123,123,123,123,123, 2, 2,123,123,
+ 123,123,123,123,123,123,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,114,114,114,114,114,114,114,114, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,114,114,114,114,114,114,114,114,114,114,
+ 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 2, 2, 2,102,102,102,102,102,102,102,102,102,102,
+ 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102, 2,
+ 2, 2, 2, 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,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,
+ 126, 2, 2,126,126,126,126,126,126,126,126,126,126,126,126,126,
+ 126,126, 2, 2, 2, 2,142,142,142,142,142,142,142,142,142,142,
+ 142,142,142,142,142,142,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,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,154,154,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,154,154,154,154,154, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,154,154,154,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,150,150,150,150,150,
+ 150,150,150,150,150,150,150,150,150,150,150, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,141,141,141,141,141,141,141,141,141,141,
+ 141,141,141,141,141,141,141,141,141,141,141,141,141,141, 2, 2,
+ 2, 2, 2, 2, 2, 2,140,140,140,140,140,140,140,140,140,140,
+ 140,140,140,140,140,140,140,140,140, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,121,121,121,121,121,121,121,121,121,121,
+ 121,121,121,121,121,121,121,121,121,121,121,121,121,121,121, 2,
+ 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,133,133,133,
+ 133,133,133,133,133,133,133,133,133,133,133,133,133, 2,133,133,
+ 133,133,133,133,133,133,133,133,133,133,133,133, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,133,133,133,133,133,133,133,133,133,133,
+ 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134,134,134,
+ 134,134,134,134,134,134, 2, 2,134,134,134,134,134,134,134,134,
+ 134,134,134,134,134,134,134,134,134,134,134,134,134,134, 2,134,
+ 134,134,134,134,134,134,134,134,134,134,134,134,134, 2, 2, 2,
+ 2, 2, 2, 2, 2, 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,138,138,
+ 138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, 2,
+ 138, 2,138,138, 2,138,138,138,138,138,138,138,138,138, 2, 2,
+ 2, 2, 2, 2, 2, 2,138,138,138,138,138,138,138,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,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,143,143,143,143,143,143,143,143,
+ 2, 2, 2, 2, 2, 2,145,145,145,145,145,145,145,145,145,145,
+ 145,145,145,145,145,145,145,145,145,145,145,145,145,145,145, 2,
+ 2, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
+ 22, 22, 22, 22, 22, 22, 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,
+ 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
+ 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 2, 63, 63, 63, 63, 63, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 2, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2,
+ 2, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127,
+ 127,127,127,127,127,127,127,127,127,127,127,127,127, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 79, 2,
+ 2, 2, 2, 2, 2, 2,115,115,115,115,115,115,115,115,115,115,
+ 115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,115,
+ 115,115,115,115,115, 2,115,115,115,115,115,115,115,115,115,115,
+ 2, 2, 2, 2,115,115,103,103,103,103,103,103,103,103,103,103,
+ 103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,103,
+ 103,103,103,103, 2, 2,103,103,103,103,103,103, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,119,119,119,119,119,119,119,119,119,119,
+ 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,119,119,119,119,119,119,119,119,119,119,
+ 2,119,119,119,119,119,119,119, 2,119,119,119,119,119,119,119,
+ 119,119,119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,
+ 2, 2, 2,119,119,119,146,146,146,146,146,146,146,146,146,146,
+ 146,146,146,146,146,146,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, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 2, 2,
+ 2, 2, 2, 2, 2, 99,136,139, 0, 0,155, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 13, 13, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,136,136,136,136,136,136,136,136,136,136,
+ 136,136,136,136,136,136,136,136,136,136,136,136,136,136, 2, 2,
+ 2, 2, 2, 2, 2, 2,155,155,155,155,155,155,155,155,155,155,
+ 155,155,155,155,155,155,155,155,155,155,155,155, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,136,136,136,136,136,136,136,136,136, 2,
+ 2, 2, 2, 2, 2, 2, 17, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 2, 15, 15, 15, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 17, 17, 17, 17, 2, 2,
+ 2, 2, 2, 2, 2, 2,139,139,139,139,139,139,139,139,139,139,
+ 139,139,139,139,139,139,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,105,105,105,105,105,105,105,105,105,105,105,105,105,105,105,
+ 105, 2, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105,
+ 105,105,105, 2, 2, 2,105,105,105,105,105,105,105,105,105, 2,
+ 2, 2, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105,
+ 2, 2,105,105,105,105, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+ 2, 2, 2, 2, 2, 2, 9, 9, 9, 9, 9, 9, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
+ 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 0, 0,131,131,131,131,131,131,131,131,131,131,
+ 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
+ 131,131, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2,131,131,131,131,131, 2,131,131,131,131,131,131,131,131,131,
+ 131,131,131,131,131,131, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 2,
+ 2, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, 2, 56, 56, 56, 56,
+ 56, 2, 2, 2, 2, 2,151,151,151,151,151,151,151,151,151,151,
+ 151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,
+ 151,151,151, 2, 2, 2,151,151,151,151,151,151,151,151,151,151,
+ 151,151,151,151, 2, 2,151,151,151,151,151,151,151,151,151,151,
+ 2, 2, 2, 2,151,151,152,152,152,152,152,152,152,152,152,152,
+ 152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+ 2, 2, 2, 2, 2,152,113,113,113,113,113,113,113,113,113,113,
+ 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113,
+ 113,113,113,113,113,113,113,113,113,113,113,113,113, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,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, 2, 2, 2, 2,132,132,132,132,132,132,132,132,132,132,
+ 2, 2, 2, 2,132,132, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3,
+ 3, 3, 3, 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, 3, 2, 3, 2, 2, 3, 2, 3,
+ 2, 3, 2, 3, 2, 3, 2, 3, 3, 2, 3, 2, 2, 3, 3, 3,
+ 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3,
+ 3, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3,
+ 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0,
+ 0, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 13, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 3, 4, 5, 6, 0,
+ 0, 0, 0, 7, 8, 9, 10, 11, 0, 12, 0, 0, 0, 0, 13, 0,
+ 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 15, 16, 0, 17, 18, 19,
+ 0, 0, 0, 20, 21, 22, 0, 23, 0, 24, 0, 25, 0, 26, 0, 0,
+ 0, 0, 0, 27, 28, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 41, 0, 42, 43, 44, 45,
+ 46, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 50, 51, 52, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 65, 0, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 70, 71, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, 78, 79, 80, 0,
+};
+static const uint16_t
+_hb_ucd_u16[11328] =
+{
+ 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, 72, 72, 72, 48, 73, 74, 75, 76, 32,
+ 77, 48, 48, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ 91, 84, 85, 92, 93, 94, 95, 96, 97, 98, 85, 99, 100, 101, 89, 102,
+ 103, 84, 85, 104, 105, 106, 89, 107, 108, 109, 110, 111, 112, 113, 95, 114,
+ 115, 116, 85, 117, 118, 119, 89, 120, 121, 116, 85, 122, 123, 124, 89, 125,
+ 126, 116, 48, 127, 128, 129, 89, 130, 131, 132, 48, 133, 134, 135, 95, 136,
+ 137, 48, 48, 138, 139, 140, 72, 72, 141, 48, 142, 143, 144, 145, 72, 72,
+ 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 72, 72,
+ 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, 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, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183,
+ 184, 185, 48, 186, 48, 187, 184, 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, 72, 72, 72,
+ 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, 240,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13,
+ 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251,
+ 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 72, 263, 264, 216,
+ 265, 266, 267, 268, 269, 270, 271, 271, 272, 273, 274, 209, 275, 276, 209, 277,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 279, 209, 280, 209, 209, 209, 209, 281, 209, 282, 278, 283, 209, 284, 285, 209,
+ 209, 209, 286, 72, 287, 72, 270, 270, 270, 288, 209, 209, 209, 209, 289, 270,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 290, 291, 209, 209, 292,
+ 209, 209, 209, 209, 209, 209, 293, 209, 209, 209, 209, 209, 209, 209, 209, 209,
+ 209, 209, 209, 209, 209, 209, 294, 295, 270, 296, 209, 209, 297, 278, 298, 278,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
+ 278, 278, 278, 278, 278, 278, 278, 278, 299, 300, 278, 278, 278, 301, 278, 302,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 209, 209, 209, 278, 303, 209, 209, 304, 209, 305, 209, 209, 209, 209, 209, 209,
+ 9, 9, 306, 11, 11, 307, 308, 309, 13, 13, 13, 13, 13, 13, 310, 311,
+ 11, 11, 312, 48, 48, 48, 313, 314, 48, 315, 316, 316, 316, 316, 32, 32,
+ 317, 318, 319, 320, 321, 322, 72, 72, 209, 323, 209, 209, 209, 209, 209, 324,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 325, 72, 326,
+ 327, 328, 329, 330, 137, 48, 48, 48, 48, 331, 178, 48, 48, 48, 48, 332,
+ 333, 48, 48, 137, 48, 48, 48, 48, 200, 334, 48, 48, 209, 209, 324, 48,
+ 209, 335, 336, 209, 337, 338, 209, 209, 336, 209, 209, 338, 209, 209, 209, 209,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
+ 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, 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, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209,
+ 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, 48, 48, 48, 48, 48, 48, 151,
+ 48, 339, 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, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 286, 48, 48, 229,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 340, 48, 341, 72, 13, 13, 342, 343, 13, 344, 48, 48, 48, 48, 345, 346,
+ 31, 347, 348, 349, 13, 13, 13, 350, 351, 352, 353, 354, 355, 72, 72, 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, 77, 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, 316, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192,
+ 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, 48, 388, 48, 389, 48, 48, 206,
+ 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390,
+ 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390,
+ 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391,
+ 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391,
+ 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 72, 72,
+ 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 400, 72, 48, 48, 48, 48, 401, 48, 48, 74, 72, 72, 402,
+ 32, 403, 32, 404, 405, 406, 407, 73, 48, 48, 48, 48, 48, 48, 48, 408,
+ 409, 2, 3, 4, 5, 410, 411, 412, 48, 413, 48, 200, 414, 415, 416, 417,
+ 418, 48, 172, 419, 204, 204, 72, 72, 48, 48, 48, 48, 48, 48, 48, 71,
+ 420, 270, 270, 421, 271, 271, 271, 422, 423, 424, 425, 72, 72, 209, 209, 426,
+ 72, 72, 72, 72, 72, 72, 72, 72, 48, 151, 48, 48, 48, 101, 427, 428,
+ 48, 48, 429, 48, 430, 48, 48, 431, 48, 432, 48, 48, 433, 434, 72, 72,
+ 9, 9, 435, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 436, 11, 437,
+ 48, 48, 74, 48, 48, 48, 438, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 315, 48, 199, 74, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 439, 48, 48, 440, 48, 441, 48, 442, 48, 200, 443, 72, 72, 72, 48, 444,
+ 48, 445, 48, 446, 72, 72, 72, 72, 48, 48, 48, 447, 270, 448, 270, 270,
+ 449, 450, 48, 451, 452, 453, 48, 454, 48, 455, 72, 72, 456, 48, 457, 458,
+ 48, 48, 48, 459, 48, 460, 48, 461, 48, 462, 463, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 196, 72, 72, 72, 9, 9, 9, 464, 11, 11, 11, 465,
+ 48, 48, 466, 192, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 270, 467, 48, 48, 468, 469, 72, 72, 72, 72,
+ 48, 455, 470, 48, 62, 471, 72, 72, 72, 72, 72, 48, 472, 72, 48, 315,
+ 473, 48, 48, 474, 475, 448, 476, 477, 222, 48, 48, 478, 479, 48, 196, 192,
+ 480, 48, 481, 482, 483, 48, 48, 484, 222, 48, 48, 485, 486, 487, 488, 489,
+ 48, 98, 490, 491, 72, 72, 72, 72, 492, 493, 494, 48, 48, 495, 496, 192,
+ 497, 84, 85, 498, 499, 500, 501, 502, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 503, 504, 505, 469, 72, 48, 48, 48, 506, 507, 192, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 48, 48, 508, 509, 510, 511, 72, 72,
+ 48, 48, 48, 512, 513, 192, 514, 72, 48, 48, 515, 516, 192, 72, 72, 72,
+ 48, 173, 517, 518, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 490, 519, 72, 72, 72, 72, 72, 72, 9, 9, 11, 11, 148, 520,
+ 521, 522, 48, 523, 524, 192, 72, 72, 72, 72, 525, 48, 48, 526, 527, 72,
+ 528, 48, 48, 529, 530, 531, 48, 48, 532, 533, 534, 72, 48, 48, 48, 196,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 85, 48, 508, 535, 536, 148, 175, 537, 48, 538, 539, 540, 72, 72, 72, 72,
+ 541, 48, 48, 542, 543, 192, 544, 48, 545, 546, 192, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 547,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 101, 270, 548, 549, 550,
+ 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, 207, 72, 72, 72, 72, 72, 72,
+ 271, 271, 271, 271, 271, 271, 551, 552, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 388, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 200, 553, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 315, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 196, 48, 200, 370, 72, 72, 72, 72, 72, 72, 48, 204, 554,
+ 48, 48, 48, 555, 556, 557, 558, 559, 48, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 9, 9, 11, 11, 270, 560, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 561, 562, 563, 563, 564, 565, 72, 72, 72, 72, 566, 567,
+ 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, 48, 48, 48, 48, 48, 48, 74,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 72, 72,
+ 196, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 200, 72, 72, 72, 568, 569, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 48, 48, 71, 151, 196, 570, 571, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 325,
+ 209, 209, 572, 209, 209, 209, 573, 574, 575, 209, 576, 209, 209, 209, 577, 72,
+ 209, 209, 209, 209, 578, 72, 72, 72, 72, 72, 72, 72, 72, 72, 270, 579,
+ 209, 209, 209, 209, 209, 286, 270, 452, 72, 72, 72, 72, 72, 72, 72, 72,
+ 9, 580, 11, 581, 582, 583, 242, 9, 584, 585, 586, 587, 588, 9, 580, 11,
+ 589, 590, 11, 591, 592, 593, 594, 9, 595, 11, 9, 580, 11, 581, 582, 11,
+ 242, 9, 584, 594, 9, 595, 11, 9, 580, 11, 596, 9, 597, 598, 599, 600,
+ 11, 601, 9, 602, 603, 604, 605, 11, 606, 9, 607, 11, 608, 609, 609, 609,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209,
+ 32, 32, 32, 610, 32, 32, 611, 612, 613, 614, 45, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 615, 616, 617, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 151, 618, 619, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 48, 620, 621,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 622, 623, 72, 72,
+ 9, 9, 584, 11, 624, 370, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 488, 270, 270, 625, 626, 72, 72, 72, 72,
+ 488, 270, 627, 628, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 629, 48, 630, 631, 632, 633, 634, 635, 636, 206, 637, 206, 72, 72, 72, 638,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 209, 209, 326, 209, 209, 209, 209, 209, 209, 324, 335, 639, 639, 639, 209, 325,
+ 640, 209, 209, 209, 209, 209, 209, 209, 209, 209, 641, 72, 72, 72, 642, 209,
+ 643, 209, 209, 326, 577, 644, 325, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 645,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 646, 424, 424,
+ 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 577, 326, 72,
+ 326, 209, 209, 209, 646, 176, 209, 209, 646, 209, 641, 644, 72, 72, 72, 72,
+ 209, 209, 209, 209, 209, 209, 209, 647, 209, 209, 209, 209, 648, 209, 209, 209,
+ 209, 209, 209, 209, 209, 324, 641, 649, 286, 209, 577, 286, 643, 286, 72, 72,
+ 209, 209, 209, 209, 209, 209, 209, 209, 209, 650, 209, 209, 287, 72, 72, 192,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 72, 72,
+ 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, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 205, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 204, 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, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 469, 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, 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, 48, 101, 72,
+ 48, 204, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
+ 48, 48, 48, 48, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 651, 72, 652, 652, 652, 652, 652, 652, 72, 72, 72, 72, 72, 72, 72, 72,
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 72,
+ 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391,
+ 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 653,
+ 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391,
+ 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 654,
+ 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, 4, 4, 4, 4, 4,
+ 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, 45, 45, 45, 27, 46, 47, 48, 49, 27,
+ 50, 50, 50, 50, 50, 51, 52, 50, 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, 111, 112, 113, 114, 111, 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 124, 125, 124, 126, 45, 45, 127, 128, 129, 130, 131, 132, 45, 45,
+ 133, 133, 133, 133, 134, 133, 135, 136, 133, 134, 133, 137, 137, 138, 45, 45,
+ 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 141, 140, 140, 142,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 144, 144, 144, 144, 145, 146, 144, 144, 145, 144, 144, 147, 148, 149, 144, 144,
+ 144, 148, 144, 144, 144, 150, 144, 151, 144, 152, 153, 153, 153, 153, 153, 154,
+ 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155,
+ 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155, 155,
+ 155, 155, 155, 155, 155, 155, 155, 155, 156, 157, 158, 158, 158, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, 169, 169, 169, 170, 171, 171,
+ 172, 173, 174, 174, 174, 174, 174, 175, 174, 174, 176, 155, 155, 155, 155, 177,
+ 178, 179, 180, 180, 181, 182, 183, 184, 185, 185, 186, 185, 187, 188, 169, 169,
+ 189, 190, 191, 191, 191, 192, 191, 193, 194, 194, 195, 8, 196, 45, 45, 45,
+ 197, 197, 197, 197, 198, 197, 197, 199, 200, 200, 200, 200, 201, 201, 201, 202,
+ 203, 203, 203, 204, 205, 206, 206, 206, 207, 140, 140, 208, 209, 210, 211, 212,
+ 4, 4, 213, 4, 4, 214, 215, 216, 4, 4, 4, 217, 8, 8, 8, 218,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 11, 219, 11, 11, 219, 220, 11, 221, 11, 11, 11, 222, 222, 223, 11, 224,
+ 225, 0, 0, 0, 0, 0, 226, 227, 228, 229, 0, 0, 45, 8, 8, 196,
+ 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 234, 45, 235, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 237, 0, 238, 0, 0, 0, 0, 0, 0,
+ 239, 239, 240, 239, 239, 240, 4, 4, 241, 241, 241, 241, 241, 241, 241, 242,
+ 140, 140, 141, 243, 243, 243, 244, 245, 144, 246, 247, 247, 247, 247, 14, 14,
+ 0, 0, 0, 0, 0, 248, 45, 45, 249, 250, 249, 249, 249, 249, 249, 251,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 252, 45, 253,
+ 254, 0, 255, 256, 257, 258, 258, 258, 258, 259, 260, 261, 261, 261, 261, 262,
+ 263, 264, 264, 265, 143, 143, 143, 143, 266, 0, 264, 264, 0, 0, 267, 261,
+ 143, 266, 0, 0, 0, 0, 143, 268, 0, 0, 0, 0, 0, 261, 261, 269,
+ 261, 261, 261, 261, 261, 270, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 0, 0, 0, 0,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 271,
+ 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272,
+ 272, 272, 272, 272, 272, 272, 272, 272, 273, 272, 272, 272, 274, 275, 275, 275,
+ 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276,
+ 276, 276, 277, 45, 14, 14, 14, 14, 14, 14, 278, 278, 278, 278, 278, 279,
+ 0, 0, 280, 4, 4, 4, 4, 4, 281, 4, 4, 4, 282, 45, 45, 283,
+ 284, 284, 285, 286, 287, 287, 287, 288, 289, 289, 289, 289, 290, 291, 50, 50,
+ 292, 292, 293, 294, 294, 295, 143, 296, 297, 297, 297, 297, 298, 299, 139, 300,
+ 301, 301, 301, 302, 303, 304, 139, 139, 305, 305, 305, 305, 306, 307, 308, 309,
+ 310, 311, 247, 4, 4, 312, 313, 153, 153, 153, 153, 153, 308, 308, 314, 315,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143,
+ 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 316, 143, 317, 143, 143, 318,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 319, 249, 249, 249, 249, 249, 249, 320, 45, 45,
+ 321, 322, 21, 323, 324, 27, 27, 27, 27, 27, 27, 27, 325, 48, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 326, 45, 27, 27, 27, 27, 327, 27, 27, 47, 45, 45, 328,
+ 8, 286, 329, 0, 0, 330, 331, 46, 27, 27, 27, 27, 27, 27, 27, 332,
+ 333, 0, 1, 2, 1, 2, 334, 260, 261, 335, 143, 266, 336, 337, 338, 339,
+ 340, 341, 342, 343, 344, 344, 45, 45, 341, 341, 341, 341, 341, 341, 341, 345,
+ 346, 0, 0, 347, 11, 11, 11, 11, 348, 349, 350, 45, 45, 0, 0, 351,
+ 45, 45, 45, 45, 45, 45, 45, 45, 352, 353, 354, 354, 354, 355, 356, 253,
+ 357, 357, 358, 359, 360, 361, 361, 362, 363, 364, 365, 365, 366, 367, 45, 45,
+ 368, 368, 368, 368, 368, 369, 369, 369, 370, 371, 372, 373, 373, 374, 373, 375,
+ 376, 376, 377, 378, 378, 378, 379, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380, 380,
+ 380, 380, 380, 381, 380, 382, 383, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 384, 385, 385, 386, 387, 388, 389, 389, 390, 391, 392, 45, 45, 45, 393, 394,
+ 395, 396, 397, 398, 45, 45, 45, 45, 399, 399, 400, 401, 400, 402, 400, 400,
+ 403, 404, 405, 406, 407, 407, 408, 408, 409, 409, 45, 45, 410, 410, 411, 412,
+ 413, 413, 413, 414, 415, 416, 417, 418, 419, 420, 421, 45, 45, 45, 45, 45,
+ 422, 422, 422, 422, 423, 45, 45, 45, 424, 424, 424, 425, 424, 424, 424, 426,
+ 427, 427, 428, 429, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 27, 430, 431, 431, 432, 433, 45, 45, 45, 45,
+ 434, 434, 435, 436, 436, 437, 45, 45, 45, 45, 45, 438, 439, 45, 440, 441,
+ 442, 442, 442, 442, 443, 444, 442, 445, 446, 446, 446, 446, 447, 448, 449, 450,
+ 451, 451, 451, 452, 453, 454, 454, 455, 456, 456, 456, 456, 456, 456, 457, 458,
+ 459, 460, 459, 461, 45, 45, 45, 45, 462, 463, 464, 465, 465, 465, 466, 467,
+ 468, 469, 470, 471, 472, 473, 474, 475, 45, 45, 45, 45, 45, 45, 45, 45,
+ 476, 476, 476, 476, 476, 477, 478, 45, 479, 479, 479, 479, 480, 481, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 482, 482, 482, 483, 482, 484, 45, 45,
+ 485, 485, 485, 485, 486, 487, 488, 45, 489, 489, 489, 490, 491, 45, 45, 45,
+ 492, 493, 494, 492, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 495, 495, 495, 496, 45, 45, 45, 45, 45, 45, 497, 497, 497, 497, 497, 498,
+ 499, 500, 501, 502, 503, 504, 45, 45, 45, 45, 505, 506, 506, 505, 507, 45,
+ 508, 508, 508, 508, 509, 510, 510, 510, 510, 510, 511, 45, 512, 512, 512, 513,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 514, 515, 515, 516, 517, 515, 518, 519, 519, 520, 521, 522, 45, 45, 45, 45,
+ 523, 524, 524, 525, 526, 527, 528, 529, 530, 531, 532, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 533, 534,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 535, 536, 536, 536, 537,
+ 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538,
+ 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538,
+ 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538,
+ 538, 538, 538, 538, 538, 538, 538, 538, 538, 539, 45, 45, 45, 45, 45, 45,
+ 538, 538, 538, 538, 538, 538, 540, 541, 538, 538, 538, 538, 538, 538, 538, 538,
+ 538, 538, 538, 538, 542, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543,
+ 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543,
+ 543, 543, 544, 545, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546,
+ 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546,
+ 546, 546, 546, 546, 547, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
+ 278, 278, 278, 548, 549, 550, 551, 45, 45, 45, 45, 45, 45, 552, 553, 554,
+ 555, 555, 555, 555, 556, 557, 558, 559, 555, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 560, 560, 560, 560, 560, 561, 45, 45, 45, 45, 45, 45,
+ 562, 562, 562, 562, 563, 562, 562, 562, 564, 562, 45, 45, 45, 45, 565, 566,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 568,
+ 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567,
+ 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569,
+ 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 569, 570, 45, 45,
+ 571, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 572, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258,
+ 258, 573, 45, 45, 45, 574, 575, 576, 576, 576, 576, 576, 576, 576, 576, 576,
+ 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 577,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 578, 578, 578, 578, 578, 578, 579, 580, 581, 582, 267, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 583,
+ 0, 0, 584, 0, 0, 0, 585, 586, 587, 0, 588, 0, 0, 0, 589, 45,
+ 11, 11, 11, 11, 590, 45, 45, 45, 45, 45, 45, 45, 45, 45, 0, 267,
+ 0, 0, 0, 0, 0, 234, 0, 589, 45, 45, 45, 45, 45, 45, 45, 45,
+ 0, 0, 0, 0, 0, 226, 0, 0, 0, 591, 592, 593, 594, 0, 0, 0,
+ 595, 596, 0, 597, 598, 599, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 600, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 601, 0, 0, 0,
+ 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602,
+ 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, 604, 605, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 606, 607, 608, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 609, 609, 610, 611, 612, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 613, 613, 613, 614,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 616, 617, 45, 45,
+ 618, 618, 618, 618, 619, 620, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 333, 0, 0, 0, 621, 45, 45, 45, 45,
+ 333, 0, 0, 622, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 623, 27, 624, 625, 626, 627, 628, 629, 630, 631, 632, 631, 45, 45, 45, 325,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 0, 0, 253, 0, 0, 0, 0, 0, 0, 267, 228, 333, 333, 333, 0, 583,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 622, 45, 45, 45, 633, 0,
+ 634, 0, 0, 253, 589, 635, 583, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 636, 349, 349,
+ 0, 0, 0, 0, 0, 0, 0, 267, 0, 0, 0, 0, 0, 589, 253, 45,
+ 253, 0, 0, 0, 636, 286, 0, 0, 636, 0, 622, 635, 45, 45, 45, 45,
+ 0, 0, 0, 0, 0, 0, 0, 637, 0, 0, 0, 0, 638, 0, 0, 0,
+ 0, 0, 0, 0, 0, 267, 622, 639, 234, 0, 589, 234, 248, 234, 45, 45,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 330, 0, 0, 235, 45, 45, 286,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 319, 45, 45,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 640, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 319, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 566, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 641, 45,
+ 249, 319, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249,
+ 249, 249, 249, 249, 642, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 643, 45, 0, 0, 0, 0, 0, 0, 45, 45, 45, 45, 45, 45, 45, 45,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,1606,1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 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, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 2, 6, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0,
+ 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, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 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 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[2176+(((_hb_ucd_u16[((_hb_ucd_u8[u>>4>>5])<<5)+((u>>4)&31u)])<<4)+((u)&15u))]:2;
+}
+static inline uint_fast8_t
+_hb_ucd_ccc (unsigned u)
+{
+ return u<125259u?_hb_ucd_u8[15060+(((_hb_ucd_u8[13636+(((_hb_ucd_u8[12656+(u>>3>>4)])<<4)+((u>>3)&15u))])<<3)+((u)&7u))]: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[16372+(((_hb_ucd_b4(16244+_hb_ucd_u8,u>>2>>6))<<6)+((u>>2)&63u))])<<2)+((u)&3u)]:0;
+}
+static inline uint_fast8_t
+_hb_ucd_sc (unsigned u)
+{
+ return u<918000u?_hb_ucd_u8[19126+(((_hb_ucd_u16[3040+(((_hb_ucd_u8[17332+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:2;
+}
+static inline uint_fast16_t
+_hb_ucd_dm (unsigned u)
+{
+ return u<195102u?_hb_ucd_u16[6144+(((_hb_ucd_u8[29430+(u>>6)])<<6)+((u)&63u))]:0;
+}
+
+
+#elif !defined(HB_NO_UCD_UNASSIGNED)
+
+static const uint8_t
+_hb_ucd_u8[17508] =
+{
+ 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 9, 10, 11, 7, 7, 7, 7, 12, 13, 14, 14, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 21, 23, 21, 21, 21, 21, 24, 7, 7,
+ 25, 26, 21, 21, 21, 21, 27, 28, 21, 21, 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, 35, 7, 36, 37, 7, 38, 7, 7, 7, 39, 21, 40,
+ 7, 7, 41, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 42, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 43,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 44,
+ 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,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 91,
+ 92, 34, 34, 34, 34, 34, 34, 34, 34, 93, 34, 34, 94, 95, 96, 97,
+ 98, 99,100,101,102,103,104,105, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,106,
+ 107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,107,
+ 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,
+ 108,108, 34, 34,109,110,111,112, 34, 34,113,114,115,116,117,118,
+ 119,120,121,122,123,124,125,126,127,128,129,123, 34, 34,130,123,
+ 131,132,133,134,135,136,137,138,139,140,141,123,142,143,144,145,
+ 146,147,148,149,150,151,152,123,153,154,123,155,156,157,158,123,
+ 159,160,161,162,163,164,123,123,165,166,167,168,123,169,123,170,
+ 34, 34, 34, 34, 34, 34, 34,171,172, 34,173,123,123,123,123,123,
+ 123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,
+ 34, 34, 34, 34, 34, 34, 34, 34,174,123,123,123,123,123,123,123,
+ 123,123,123,123,123,123,123,123, 34, 34, 34, 34,175,123,123,123,
+ 34, 34, 34, 34,176,177,178,179,123,123,123,123,180,181,182,183,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,184,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34,185,186,123,123,123,123,123,
+ 34, 34,187, 34, 34,188,123,123,123,123,123,123,123,123,123,123,
+ 123,123,123,123,123,123,123,123,189,190,123,123,123,123,123,123,
+ 69,191,192,193,194,195,196,123,197,198,199,200,201,202,203,204,
+ 69, 69, 69, 69,205,206,123,123,123,123,123,123,123,123,123,123,
+ 207,123,208,123,123,209,123,123,123,123,123,123,123,123,123,123,
+ 34,210,211,123,123,123,123,123,212,213,214,123,215,216,123,123,
+ 217,218,219,220,221,123, 69,222, 69, 69, 69, 69, 69,223,224,225,
+ 226,227,228,229,230,231, 69,232,123,123,123,123,123,123,123,123,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,233, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,234, 34,
+ 235, 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,236, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34,237,123,123,123,123,123,123,123,123,
+ 34, 34, 34, 34,238,123,123,123,123,123,123,123,123,123,123,123,
+ 34, 34, 34, 34, 34, 34,239,123,123,123,123,123,123,123,123,123,
+ 240,123,241,242,123,123,123,123,123,123,123,123,123,123,123,123,
+ 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,243,
+ 108,108,108,108,108,108,108,108,108,108,108,108,108,108,108,244,
+ 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,
+ 44, 44, 44, 44, 44, 44, 44, 44, 36, 36, 61, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 44, 44, 44, 44, 44, 57, 43, 43, 43, 43, 43, 43,
+ 43, 82, 43, 43, 43, 43, 43, 43, 43, 83, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 83, 71, 84, 85, 43, 43, 43, 83, 84, 85, 84,
+ 70, 43, 43, 43, 36, 36, 36, 36, 36, 43, 2, 7, 7, 7, 7, 7,
+ 86, 36, 36, 36, 36, 36, 36, 36, 70, 84, 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, 84, 85, 43, 80, 87, 88, 87, 85, 61,
+ 44, 44, 44, 87, 44, 44, 36, 62, 36, 43, 44, 7, 7, 7, 7, 7,
+ 36, 20, 27, 27, 27, 56, 63, 80, 57, 83, 62, 36, 36, 61, 44, 62,
+ 61, 36, 62, 61, 36, 44, 80, 84, 85, 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, 83, 62, 36, 36, 36, 36, 62,
+ 36, 62, 36, 36, 36, 36, 36, 36, 61, 36, 62, 36, 36, 44, 71, 84,
+ 85, 43, 43, 57, 83, 87, 85, 44, 61, 44, 44, 44, 44, 44, 44, 44,
+ 66, 44, 44, 44, 62, 43, 43, 43, 57, 84, 62, 36, 36, 36, 61, 62,
+ 61, 36, 62, 36, 36, 44, 71, 85, 85, 43, 80, 87, 88, 87, 85, 44,
+ 44, 44, 57, 83, 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, 84,
+ 83, 88, 44, 84, 88, 84, 85, 44, 61, 44, 44, 87, 44, 44, 44, 44,
+ 27, 89, 67, 67, 56, 90, 44, 44, 83, 84, 71, 36, 36, 36, 61, 36,
+ 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 44, 62, 43,
+ 83, 84, 88, 43, 80, 43, 43, 44, 44, 44, 57, 80, 36, 61, 44, 44,
+ 44, 44, 44, 91, 27, 27, 27, 89, 70, 84, 72, 36, 36, 36, 61, 36,
+ 36, 36, 62, 36, 36, 44, 71, 85, 84, 84, 88, 83, 88, 84, 43, 44,
+ 44, 44, 87, 88, 44, 44, 44, 61, 62, 61, 44, 44, 44, 44, 44, 44,
+ 43, 84, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 70, 71, 84,
+ 85, 43, 80, 84, 88, 84, 85, 77, 44, 44, 36, 92, 27, 27, 27, 93,
+ 27, 27, 27, 27, 89, 36, 36, 36, 57, 84, 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, 87, 84, 43, 80, 80, 84, 84, 84, 84,
+ 44, 84, 64, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36, 36,
+ 70, 36, 43, 43, 43, 80, 44, 94, 36, 36, 36, 75, 43, 43, 43, 60,
+ 7, 7, 7, 7, 7, 2, 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, 44, 7, 7, 7, 7, 7, 44, 36, 36,
+ 77, 67, 2, 2, 2, 2, 2, 2, 2, 95, 95, 67, 43, 67, 67, 67,
+ 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 50, 50, 50, 4, 4, 84,
+ 36, 36, 36, 36, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 61, 44,
+ 57, 43, 43, 43, 43, 43, 43, 83, 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, 90, 67, 2, 2, 95, 67, 21, 64, 44, 44,
+ 36, 36, 36, 36, 36, 92, 85, 43, 83, 43, 43, 43, 85, 83, 85, 71,
+ 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 84, 43, 36, 36, 43,
+ 71, 84, 96, 92, 84, 84, 84, 36, 70, 43, 71, 36, 36, 36, 36, 36,
+ 36, 83, 85, 83, 84, 84, 85, 92, 7, 7, 7, 7, 7, 84, 85, 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, 97, 27, 27, 27, 27, 27, 27, 27, 27, 27, 98, 44,
+ 67, 67, 67, 67, 67, 44, 44, 44, 11, 11, 11, 44, 16, 16, 16, 44,
+ 99, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 77, 72,
+ 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,101,102, 44,
+ 36, 36, 36, 36, 36, 63, 2,103,104, 36, 36, 36, 61, 44, 44, 44,
+ 36, 36, 36, 36, 36, 36, 61, 36, 36, 43, 80, 44, 44, 44, 44, 44,
+ 36, 43, 60, 64, 44, 44, 44, 44, 36, 43, 44, 44, 44, 44, 44, 44,
+ 61, 43, 44, 44, 44, 44, 44, 44, 36, 36, 43, 85, 43, 43, 43, 84,
+ 84, 84, 84, 83, 85, 43, 43, 43, 43, 43, 2, 86, 2, 66, 70, 44,
+ 7, 7, 7, 7, 7, 44, 44, 44, 27, 27, 27, 27, 27, 44, 44, 44,
+ 2, 2, 2,105, 2, 59, 43, 68, 36,106, 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, 83, 84, 85, 83, 84, 44, 44,
+ 84, 83, 84, 84, 85, 43, 44, 44, 90, 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, 98, 44, 67, 67, 67, 67, 67, 67, 67, 67, 67,
+ 36, 36, 36, 70, 83, 85, 44, 2, 36, 36, 92, 83, 43, 43, 43, 80,
+ 83, 83, 85, 43, 43, 43, 83, 84, 84, 85, 43, 43, 43, 43, 80, 57,
+ 2, 2, 2, 86, 2, 2, 2, 44, 43, 43, 43, 43, 43, 43, 43,107,
+ 80, 44, 44, 44, 44, 44, 44, 44, 43, 43, 96, 36, 36, 36, 36, 36,
+ 36, 36, 83, 43, 43, 83, 83, 84, 84, 83, 96, 36, 36, 36, 44, 44,
+ 95, 67, 67, 67, 67, 50, 43, 43, 43, 43, 67, 67, 67, 67, 90, 44,
+ 43, 96, 36, 36, 36, 36, 36, 36, 92, 43, 43, 84, 43, 85, 43, 36,
+ 36, 36, 36, 83, 43, 84, 85, 85, 43, 84, 44, 44, 44, 44, 2, 2,
+ 36, 36, 84, 84, 84, 84, 43, 43, 43, 43, 84, 43, 44, 91, 2, 2,
+ 7, 7, 7, 7, 7, 44, 62, 36, 36, 36, 36, 36, 40, 40, 40, 2,
+ 16, 16, 16, 16,108, 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,
+ 83, 43, 43, 43, 71, 36, 70, 36, 36, 36, 71, 92, 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,109, 40, 40,
+ 43, 43, 43, 43, 43, 57, 43, 43, 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,110,110,110,110, 16, 16,108, 16, 11, 11,111,112,
+ 41, 16,108, 16, 11, 11,111, 41, 16, 16, 44, 16, 11, 11,113, 41,
+ 16, 16, 16, 16, 11, 11,114, 41, 44, 16,108, 16, 11, 11,111,115,
+ 116,116,116,116,116,117, 65, 65,118,118,118, 2,119,120,119,120,
+ 2, 2, 2, 2,121, 65, 65,122, 2, 2, 2, 2,123,124, 2,125,
+ 126, 2,127,128, 2, 2, 2, 2, 2, 9,126, 2, 2, 2, 2,129,
+ 65, 65, 68, 65, 65, 65, 65, 65,130, 44, 27, 27, 27, 8,127,131,
+ 27, 27, 27, 27, 27, 8,127,102, 40, 40, 40, 40, 40, 40, 81, 44,
+ 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,132, 51,
+ 107, 51,107, 43, 43, 43, 43, 43, 67,133, 67,134, 67, 34, 11, 16,
+ 11, 32,134, 67, 49, 11, 11, 67, 67, 67,133,133,133, 11, 11,135,
+ 11, 11, 35, 36, 39, 67, 16, 11, 8, 8, 49, 16, 16, 26, 67,136,
+ 27, 27, 27, 27, 27, 27, 27, 27,103,103,103,103,103,103,103,103,
+ 103,137,138,103,139, 67, 44, 44, 8, 8,140, 67, 67, 8, 67, 67,
+ 140, 26, 67,140, 67, 67, 67,140, 67, 67, 67, 67, 67, 67, 67, 8,
+ 67,140,140, 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,141,142, 67, 67, 67, 67, 67, 67, 67, 67,140, 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, 90, 44, 44, 44, 44, 67, 67, 67, 67, 67, 90, 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,127,143, 8, 8, 8, 8,
+ 8, 8, 8, 4, 4, 4, 4, 4, 8,127,144,144,144,144,144,144,
+ 144,144,144,144,143, 8, 8, 8, 8, 8, 8, 8, 4, 4, 8, 8,
+ 8, 8, 8, 8, 8, 8, 4, 8, 8, 8,140, 26, 8, 8,140, 67,
+ 67, 67, 44, 67, 67, 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 67,
+ 11, 11, 11, 11, 11, 11, 11, 47, 16, 16, 16, 16, 16, 16, 16,108,
+ 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 40, 11,
+ 32, 32,136, 67, 67,134, 34,145, 43, 32, 44, 44, 91, 2, 97, 2,
+ 16, 16, 16,146, 44, 44,146, 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,119,119, 2,123,124,119, 2,
+ 2, 2, 2, 6, 2,105,119, 2,119, 4, 4, 4, 4, 2, 2, 86,
+ 2, 2, 2, 2, 2,118, 2, 2,105,147, 2, 2, 2, 2, 2, 2,
+ 67, 64, 44, 44, 44, 44, 44, 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,
+ 67, 67, 67, 67, 67, 67, 44, 44, 1, 2,148,149, 4, 4, 4, 4,
+ 4, 67, 4, 4, 4, 4,150,151,152,103,103,103,103, 43, 43, 84,
+ 153, 40, 40, 67,103,154, 63, 67, 36, 36, 36, 61, 57,155,156, 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, 67, 67, 67, 67, 67, 90,
+ 27, 27, 27, 27, 27, 67, 67, 67, 67, 67, 67, 67, 27, 27, 27, 27,
+ 157, 27, 27, 27, 27, 27, 27, 27, 36, 36,106, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36,158, 2, 7, 7, 7, 7, 7, 36, 44, 44,
+ 32, 32, 32, 32, 32, 32, 32, 70, 51,159, 43, 43, 43, 43, 43, 86,
+ 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36,103,103,103,103,103,
+ 43, 2, 2, 2, 44, 44, 44, 44, 41, 41, 41,156, 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,160, 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, 44, 32, 11, 11, 34,108, 44, 44,
+ 44, 44, 48, 35, 40, 35, 36, 36, 36, 71, 36, 71, 36, 70, 36, 36,
+ 36, 92, 85, 83, 67, 67, 80, 44, 27, 27, 27, 67,161, 44, 44, 44,
+ 36, 36, 2, 2, 44, 44, 44, 44, 84, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 84, 84, 84, 84, 84, 84, 84, 84, 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, 84, 44, 44, 44, 44, 44, 91,
+ 36, 70, 84, 43, 43, 84, 43, 84,162, 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, 83,
+ 85, 83, 85, 80, 44, 44, 44, 44, 36, 70, 36, 36, 36, 36, 83, 44,
+ 7, 7, 7, 7, 7, 44, 2, 2, 69, 36, 36, 77, 67, 92, 83, 36,
+ 71, 43, 71, 70, 71, 36, 36, 43, 70, 61, 44, 44, 44, 44, 44, 44,
+ 44, 44, 44, 44, 44, 62,106, 2, 36, 36, 36, 36, 36, 92, 43, 84,
+ 2,106,163, 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,112, 40, 40,
+ 16, 16, 16, 16,109, 41, 44, 44, 36, 92, 85, 84, 83,162, 85, 44,
+ 36, 36, 44, 44, 44, 44, 44, 44, 36, 36, 36, 61, 44, 62, 36, 36,
+ 164,164,164,164,164,164,164,164,165,165,165,165,165,165,165,165,
+ 16, 16, 16,108, 44, 44, 44, 44, 44,146, 16, 16, 44, 44, 62, 71,
+ 36, 36, 36, 36,166, 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, 44, 44, 44, 44, 44, 44, 44, 44, 62, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36,144, 44, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36,161, 44, 2, 2, 2,167,128, 44, 44, 44,
+ 6,168,169,144,144,144,144,144,144,144,128,167,128, 2,125,170,
+ 2, 64, 2, 2,150,144,144,128, 2,171, 8,172, 66, 2, 44, 44,
+ 36, 36, 36, 36, 36, 36, 61, 79, 91, 2, 3, 2, 4, 5, 6, 2,
+ 16, 16, 16, 16, 16, 17, 18,127,128, 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,173, 56,174, 26, 8,140, 90, 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,175, 27, 27, 27, 27, 27, 27, 44, 55, 67, 67, 67, 67,
+ 103,103,139, 27, 89, 67, 67, 67, 67, 67, 67, 67, 67, 27, 67, 90,
+ 67, 67, 67, 67, 67, 67, 90, 44, 90, 44, 44, 44, 44, 44, 44, 44,
+ 67, 67, 67, 67, 67, 67, 50, 44,176, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 44, 44, 27, 27, 44, 44, 44, 44, 62, 36,
+ 149, 36, 36, 36, 36,177, 44, 44, 36, 36, 36, 43, 43, 80, 44, 44,
+ 36, 36, 36, 36, 36, 36, 36, 91, 36, 36, 44, 44, 36, 36, 36, 36,
+ 178,103,103, 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, 44, 44, 44, 44, 44, 91, 36, 36, 36, 44, 61, 36, 36, 36,
+ 36, 36, 36, 62, 61, 44, 61, 62, 36, 36, 36, 91, 27, 27, 27, 27,
+ 36, 36, 36, 77,157, 27, 27, 27, 44, 44, 44,175, 27, 27, 27, 27,
+ 36, 61, 36, 44, 44,175, 27, 27, 36, 36, 36, 27, 27, 27, 44, 91,
+ 36, 36, 36, 36, 36, 44, 44, 91, 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, 98, 44, 44, 44, 2, 2, 2, 2, 64, 44, 44, 44,
+ 36, 36, 36, 36, 36, 36,179, 30, 36, 36, 36, 36, 36, 36,179, 27,
+ 36, 36, 36, 36, 78, 36, 36, 36, 36, 36, 70, 80, 44,175, 27, 27,
+ 2, 2, 2, 64, 44, 44, 44, 44, 36, 36, 36, 44, 91, 2, 2, 2,
+ 36, 36, 36, 44, 27, 27, 27, 27, 36, 61, 44, 44, 27, 27, 27, 27,
+ 36, 44, 44, 44, 91, 2, 64, 44, 44, 44, 44, 44,175, 27, 27, 27,
+ 11, 47, 44, 44, 44, 44, 44, 44, 16,108, 44, 44, 44, 27, 27, 27,
+ 36, 36, 43, 43, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27, 98,
+ 36, 36, 36, 36, 36, 57,180, 44, 36, 44, 44, 44, 44, 44, 44, 44,
+ 27, 27, 27, 93, 44, 44, 44, 44,176, 27, 30, 2, 2, 44, 44, 44,
+ 36, 36,179, 27, 27, 27, 44, 44, 85, 96, 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, 44, 44, 44, 44, 44, 44, 44, 57,
+ 84, 85, 43, 83, 85, 60,181, 2, 2, 44, 44, 44, 44, 44, 79, 44,
+ 43, 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 43, 43, 85, 43,
+ 43, 43, 80, 7, 7, 7, 7, 7, 2, 2, 92, 96, 44, 44, 44, 44,
+ 36, 70, 2, 61, 44, 44, 44, 44, 36, 92, 84, 43, 43, 43, 43, 83,
+ 96, 36, 63, 2, 59, 43, 60, 85, 7, 7, 7, 7, 7, 63, 63, 2,
+ 175, 27, 27, 27, 27, 27, 27, 27, 27, 27, 98, 44, 44, 44, 44, 44,
+ 36, 36, 36, 36, 36, 36, 84, 85, 43, 84, 83, 43, 2, 2, 2, 80,
+ 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,
+ 84, 85, 43, 43, 43, 80, 44, 44, 43, 84, 62, 36, 36, 36, 61, 62,
+ 61, 36, 62, 36, 36, 57, 71, 84, 83, 84, 88, 87, 88, 87, 84, 44,
+ 61, 44, 44, 87, 44, 44, 62, 36, 36, 84, 44, 43, 43, 43, 80, 44,
+ 43, 43, 80, 44, 44, 44, 44, 44, 36, 36, 92, 84, 43, 43, 43, 43,
+ 84, 43, 83, 71, 36, 63, 2, 2, 7, 7, 7, 7, 7, 2, 91, 71,
+ 84, 85, 43, 43, 83, 83, 84, 85, 83, 43, 36, 72, 44, 44, 44, 44,
+ 36, 36, 36, 36, 36, 36, 36, 92, 84, 43, 43, 44, 84, 84, 43, 85,
+ 60, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 43, 44,
+ 84, 85, 43, 43, 43, 83, 85, 85, 60, 2, 61, 44, 44, 44, 44, 44,
+ 2, 2, 2, 2, 2, 2, 64, 44, 36, 36, 36, 36, 36, 70, 85, 84,
+ 43, 43, 43, 85, 61, 44, 44, 44, 84, 43, 43, 85, 43, 43, 44, 44,
+ 7, 7, 7, 7, 7, 27, 2, 95, 43, 43, 43, 43, 85, 60, 44, 44,
+ 27, 98, 44, 44, 44, 44, 44, 62, 36, 36, 36, 61, 62, 44, 36, 36,
+ 36, 36, 62, 61, 36, 36, 36, 36, 84, 84, 84, 87, 88, 57, 83, 71,
+ 96, 85, 2, 64, 44, 44, 44, 44, 36, 36, 36, 36, 44, 36, 36, 36,
+ 92, 84, 43, 43, 44, 43, 84, 84, 71, 72, 88, 44, 44, 44, 44, 44,
+ 70, 43, 43, 43, 43, 71, 36, 36, 36, 70, 43, 43, 83, 70, 43, 60,
+ 2, 2, 2, 59, 44, 44, 44, 44, 70, 43, 43, 83, 85, 43, 36, 36,
+ 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 83, 43, 2, 72, 2,
+ 2, 64, 44, 44, 44, 44, 44, 44, 43, 43, 43, 80, 43, 43, 43, 85,
+ 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, 87, 43, 43, 43,
+ 83, 43, 85, 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, 84, 84, 88,
+ 43, 87, 85, 85, 61, 44, 44, 44, 36, 70, 83,162, 64, 44, 44, 44,
+ 27, 27, 89, 67, 67, 67, 56, 20,161, 67, 67, 67, 67, 67, 67, 67,
+ 67, 44, 44, 44, 44, 44, 44, 91,103,103,103,103,103,103,103,177,
+ 2, 2, 64, 44, 44, 44, 44, 44, 65, 65, 65, 65, 68, 44, 44, 44,
+ 43, 43, 60, 44, 44, 44, 44, 44, 43, 43, 43, 60, 2, 2, 67, 67,
+ 40, 40, 95, 44, 44, 44, 44, 44, 7, 7, 7, 7, 7,175, 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,
+ 92, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84,
+ 84, 84, 84, 84, 44, 44, 44, 57, 43, 74, 40, 40, 40, 40, 40, 40,
+ 40, 86, 80, 44, 44, 44, 44, 44, 84, 44, 44, 44, 44, 44, 44, 44,
+ 36, 61, 44, 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,
+ 67, 67, 67, 90, 55, 67, 67, 67, 67, 67,182, 85, 43, 67,182, 84,
+ 84,183, 65, 65, 65, 82, 43, 43, 43, 76, 50, 43, 43, 43, 67, 67,
+ 67, 67, 67, 67, 67, 43, 43, 67, 67, 67, 67, 67, 90, 44, 44, 44,
+ 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,108, 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,146,146, 16,
+ 16, 16,146, 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, 43, 43, 43, 80, 43, 43, 43, 43,
+ 43, 43, 43, 43, 80, 57, 43, 43, 43, 57, 80, 43, 43, 80, 44, 44,
+ 43, 43, 43, 74, 40, 40, 40, 44, 7, 7, 7, 7, 7, 44, 44, 77,
+ 36, 36, 36, 36, 36, 36, 43, 43, 7, 7, 7, 7, 7, 44, 44, 94,
+ 36, 36, 61,175, 27, 27, 27, 27, 43, 43, 43, 80, 44, 44, 44, 44,
+ 16, 16, 43, 43, 43, 74, 44, 44, 27, 27, 27, 27, 27, 27,157, 27,
+ 184, 27, 98, 44, 44, 44, 44, 44, 27, 27, 27, 27, 27, 27, 27,157,
+ 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, 55, 67, 67, 67, 67, 67, 67, 67,
+ 27, 27, 27, 27, 27, 27, 89, 67, 67, 67, 67, 67, 67, 67, 67, 44,
+ 44, 44, 44, 67, 67, 67, 67, 67, 67, 90, 44, 44, 44, 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, 44, 44, 67, 67, 67, 67, 90, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 55, 67, 67, 67, 90, 44, 67, 90, 44, 44,
+ 67, 90, 67, 67, 67, 67, 67, 67, 79, 44, 44, 44, 44, 44, 44, 44,
+ 65, 65, 65, 65, 65, 65, 65, 65,165,165,165,165,165,165,165, 44,
+ 165,165,165,165,165,165,165, 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, 2, 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, 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, 17, 21, 7, 6, 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, 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, 2, 5, 22, 21,
+ 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, 23, 26, 10, 21, 6, 10,
+ 4, 4, 3, 3, 7, 25, 21, 22, 17, 16, 16, 22, 16, 16, 25, 17,
+ 25, 2, 25, 24, 23, 2, 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, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34,
+ 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 41, 0,
+ 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, 0, 0, 0, 0,
+ 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, 0,
+ 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, 0, 0, 0, 17,
+ 18, 19, 20, 0, 0, 0, 21, 22, 0, 23, 24, 0, 0, 23, 25, 26,
+ 0, 23, 25, 0, 0, 23, 25, 0, 0, 23, 25, 0, 0, 0, 25, 0,
+ 0, 0, 27, 0, 0, 23, 25, 0, 0, 28, 25, 0, 0, 0, 29, 0,
+ 0, 30, 31, 0, 0, 32, 33, 0, 34, 35, 0, 36, 37, 0, 38, 0,
+ 0, 39, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 42, 42, 0, 0, 0, 0, 43, 0,
+ 0, 0, 0, 0, 0, 44, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0,
+ 46, 0, 0, 47, 0, 48, 49, 0, 0, 50, 51, 52, 0, 53, 0, 54,
+ 0, 55, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 0, 58, 59,
+ 0, 0, 0, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 0, 0, 0, 64,
+ 0, 65, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 67, 68, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, 0,
+ 70, 71, 0, 0, 0, 0, 51, 72, 0, 73, 74, 0, 0, 75, 76, 0,
+ 0, 0, 0, 0, 0, 77, 78, 79, 0, 0, 0, 0, 0, 0, 0, 25,
+ 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0,
+ 0, 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82,
+ 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 83, 0, 0, 0, 0,
+ 84, 85, 0, 0, 0, 0, 0, 86, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 89, 0, 0, 0, 0, 0,
+ 0, 0, 70, 63, 0, 90, 0, 0, 91, 92, 0, 75, 0, 0, 93, 0,
+ 0, 94, 0, 0, 0, 0, 0, 95, 0, 96, 25, 97, 0, 0, 0, 0,
+ 0, 0, 98, 0, 0, 0, 99, 0, 0, 0, 0, 0, 0, 63,100, 0,
+ 0, 63, 0, 0, 0,101, 0, 0, 0,102, 0, 0, 0, 0, 0, 0,
+ 0, 90, 0, 0, 0, 0, 0, 0, 0,103,104, 0, 0, 0, 0, 76,
+ 0, 42,105, 0,106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 63, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,108, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,109, 0,110, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,111,
+ 0, 0, 0, 0,112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113,114,115, 0, 0,
+ 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 117,118, 0, 0, 0, 0, 0, 0, 0,110, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,120, 0, 0, 0,121, 0, 0, 0, 0, 0,
+ 0, 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, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, 27, 28,
+ 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 31, 0,
+ 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, 37, 0, 0, 0,
+ 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 0, 0, 0, 0,
+ 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 43, 36, 44, 45,
+ 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, 21, 0, 0, 47,
+ 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 52, 1, 1, 1,
+ 53, 21, 43, 54, 55, 21, 35, 1, 0, 0, 0, 0, 0, 0, 0, 56,
+ 0, 0, 0, 57, 58, 59, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 57, 0, 61, 0, 0,
+ 0, 0, 0, 0, 0, 0, 62, 63, 0, 0, 64, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 0, 0, 0, 70, 71, 0,
+ 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, 0, 0, 0, 0, 0, 0,
+ 0, 78, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 80, 0,
+ 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49,
+ 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 64, 0, 0, 81,
+ 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0,
+ 0, 0, 0, 0, 0, 19, 84, 0, 63, 0, 0, 0, 0, 49, 1, 85,
+ 0, 0, 0, 0, 1, 54, 15, 86, 84, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 56, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0,
+ 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0,
+ 0, 88, 0, 0, 87, 0, 0, 0, 0, 0, 0, 0, 0, 79, 0, 0,
+ 0, 0, 0, 0, 89, 9, 12, 4, 90, 8, 91, 47, 0, 59, 50, 0,
+ 21, 1, 21, 92, 93, 1, 1, 1, 1, 1, 1, 1, 1, 94, 95, 96,
+ 0, 0, 0, 0, 97, 1, 98, 59, 81, 99,100, 4, 59, 0, 0, 0,
+ 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,101,102, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,103, 0, 0, 0, 0, 19, 0, 1, 1, 50,
+ 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0,
+ 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0,
+ 1, 1, 1, 1, 50, 0, 0, 0, 0, 0, 52, 69, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 79, 0, 0, 0,
+ 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104,105, 59, 38,
+ 81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,106, 1, 14, 4, 12, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 38, 89, 0,
+ 0, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,108, 62,
+ 0,109, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 19, 59, 0, 0, 0, 0, 0,110, 14, 54, 84, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,111, 0, 89, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 62, 63, 0, 0, 63, 0, 88, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,111, 0, 0, 0, 0,112, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 79, 56, 0, 38, 1, 59, 1, 59, 0, 0,
+ 64, 88, 0, 0, 0, 0, 0, 60,113, 0, 0, 0, 0, 0, 0, 0,
+ 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113, 0, 0,
+ 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80,
+ 79, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 57, 0, 88,114, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 8, 91, 0, 0,
+ 0, 0, 0, 0, 1, 89, 0, 0, 0, 0, 0, 0,115, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0,116, 0,117,118,119,120, 0, 52, 4,
+ 121, 49, 23, 0, 0, 0, 0, 0, 0, 0, 38, 50, 0, 0, 0, 0,
+ 38, 59, 0, 0, 0, 0, 0, 0, 1, 89, 1, 1, 1, 1, 39, 1,
+ 48,104, 89, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 4,121, 0, 0, 0, 1,122, 0, 0, 0, 0, 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,
+ 0, 0, 0,220,230,230, 0,220,230,220,220,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, 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,
+ 220, 0, 0, 0,230, 0, 0,220,230,220, 0,220, 0, 0, 9, 9,
+ 0, 0, 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0,
+ 230,234,214,220,202,230,230,230,230,230,232,228,228,220, 0,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,
+ 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, 9, 7, 0,
+ 0, 7, 9, 0, 0, 0, 9, 7, 9, 9, 0, 0, 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,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, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 1, 2, 39, 40,
+ 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, 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, 9,
+ 10, 11, 12, 12, 12, 12, 13, 14, 14, 14, 14, 15, 16, 17, 18, 19,
+ 20, 14, 21, 14, 22, 14, 14, 14, 14, 23, 24, 24, 25, 26, 14, 14,
+ 14, 14, 27, 28, 14, 14, 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,
+ 35, 7, 36, 37, 7, 38, 7, 7, 7, 39, 14, 40, 7, 7, 41, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 42, 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, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 80, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 82, 83, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 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, 96, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 70, 70, 98, 99,100,101,102,102,103,104,105,106,107,108,109,110,
+ 111,112, 97,113,114,115,116,117,118, 97,119,119,120, 97,121,122,
+ 123,124,125,126,127,128,129,130,131, 97,132,133,134,135,136,137,
+ 138,139,140,141,142, 97,143,144, 97,145,146,147,148, 97,149,150,
+ 151,152,153,154, 97, 97,155,156,157,158, 97,159, 97,160,161,161,
+ 161,161,161,161,161,162,163,161,164, 97, 97, 97, 97, 97,165,165,
+ 165,165,165,165,165,165,166, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97,167,167,167,167,168, 97, 97, 97,169,169,
+ 169,169,170,171,172,173, 97, 97, 97, 97,174,175,176,177,178,178,
+ 178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,178,
+ 178,178,178,178,178,178,178,178,178,178,178,178,178,179,178,178,
+ 178,178,178,178,180,180,180,181,182, 97, 97, 97, 97, 97,183,184,
+ 185,186,186,187, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97,188,189, 97, 97, 97, 97, 97, 97, 59,190,
+ 191,192,193,194,195, 97,196,197,198, 59, 59,199, 59,200,201,201,
+ 201,201,201,202, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,203, 97,
+ 204, 97, 97,205, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,206,207,
+ 208, 97, 97, 97, 97, 97,209,210,211, 97,212,213, 97, 97,214,215,
+ 59,216,217, 97, 59, 59, 59, 59, 59, 59, 59,218,219,220,221,222,
+ 223,224,225,226, 59,227, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,228, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70,229, 70,230, 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,231, 70, 70, 70, 70,
+ 70, 70, 70, 70, 70,232, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70,
+ 70, 70,233, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 70, 70,
+ 70, 70, 70, 70,234, 97, 97, 97, 97, 97, 97, 97, 97, 97,235, 97,
+ 236,237, 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, 2, 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, 2, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 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, 2, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2,
+ 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 2, 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, 2, 2, 2, 2, 2, 2, 16, 2, 16, 16,
+ 16, 16, 2, 2, 16, 16, 2, 16, 16, 2, 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, 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, 2, 45, 45, 45, 45,
+ 45, 45, 45, 2, 2, 2, 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, 2, 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, 2,
+ 2, 2, 2, 2, 2, 2, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 2, 2, 2, 2, 62, 62, 62, 62, 62, 2, 2, 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, 1, 1,
+ 2, 1, 1, 1, 1, 1, 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, 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, 56, 56, 56, 56, 56, 56, 56, 2, 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, 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, 2, 2, 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, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17,
+ 17, 17, 17, 17, 17, 0, 13, 13, 13, 13, 13, 2, 2, 2, 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, 2, 2, 19, 19, 19, 19, 19, 19, 19, 19,
+ 19, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 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,
+ 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 0, 0, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1, 1,
+ 1, 1, 1, 1, 6, 6, 0, 0, 0, 2, 0, 0, 0, 0, 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,110,110,
+ 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110,
+ 110,110,110,110, 2, 2, 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, 3, 3,
+ 3, 3, 3, 3, 3, 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,147,147,
+ 147,147,147,147,147,147,148,148,148,148,148,148,148,148,148,148,
+ 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,
+ 2, 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,
+ 108,108,108,108,108, 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, 2, 2, 2, 2, 2, 2, 2,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,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,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, 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, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 80, 2, 80, 2, 2, 2, 2, 2, 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,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, 0, 0,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, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 2, 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, 0, 0,
+ 0, 0, 0, 0, 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, 0, 2, 2, 2, 2, 2, 2, 2, 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, 56, 2, 2, 56, 56, 56, 56, 56, 56, 56,
+ 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2,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,152,152,
+ 152,152,152,152,152,152,152,152, 2, 2, 2, 2, 2,152,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, 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[9080] =
+{
+ 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, 72, 72, 72, 48, 73, 74, 75, 76, 32,
+ 77, 48, 48, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ 91, 84, 85, 92, 93, 94, 95, 96, 97, 98, 85, 99, 100, 101, 89, 102,
+ 103, 84, 85, 104, 105, 106, 89, 107, 108, 109, 110, 111, 112, 113, 95, 114,
+ 115, 116, 85, 117, 118, 119, 89, 120, 121, 116, 85, 122, 123, 124, 89, 125,
+ 126, 116, 48, 127, 128, 129, 89, 130, 131, 132, 48, 133, 134, 135, 95, 136,
+ 137, 48, 48, 138, 139, 140, 72, 72, 141, 48, 142, 143, 144, 145, 72, 72,
+ 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 72, 72,
+ 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,
+ 184, 185, 48, 186, 48, 187, 184, 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, 72, 72, 72,
+ 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, 240,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13,
+ 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251,
+ 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 72, 263, 264, 216,
+ 265, 266, 267, 268, 269, 270, 271, 271, 272, 273, 274, 209, 275, 276, 209, 277,
+ 278, 278, 278, 278, 278, 278, 278, 278, 279, 209, 280, 209, 209, 209, 209, 281,
+ 209, 282, 278, 283, 209, 284, 285, 209, 209, 209, 286, 72, 287, 72, 270, 270,
+ 270, 288, 209, 209, 209, 209, 289, 270, 209, 209, 209, 209, 209, 209, 209, 209,
+ 209, 209, 209, 290, 291, 209, 209, 292, 209, 209, 209, 209, 209, 209, 293, 209,
+ 209, 209, 209, 209, 209, 209, 294, 295, 270, 296, 209, 209, 297, 278, 298, 278,
+ 299, 300, 278, 278, 278, 301, 278, 302, 209, 209, 209, 278, 303, 209, 209, 304,
+ 209, 305, 209, 209, 209, 209, 209, 209, 9, 9, 306, 11, 11, 307, 308, 309,
+ 13, 13, 13, 13, 13, 13, 310, 311, 11, 11, 312, 48, 48, 48, 313, 314,
+ 48, 315, 316, 316, 316, 316, 32, 32, 317, 318, 319, 320, 321, 322, 72, 72,
+ 209, 323, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 325, 72, 326,
+ 327, 328, 329, 330, 137, 48, 48, 48, 48, 331, 178, 48, 48, 48, 48, 332,
+ 333, 48, 48, 137, 48, 48, 48, 48, 200, 334, 48, 48, 209, 209, 324, 48,
+ 209, 335, 336, 209, 337, 338, 209, 209, 336, 209, 209, 338, 209, 209, 209, 209,
+ 48, 48, 48, 48, 209, 209, 209, 209, 48, 48, 48, 48, 48, 48, 48, 151,
+ 48, 339, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 286, 48, 48, 229,
+ 340, 48, 341, 72, 13, 13, 342, 343, 13, 344, 48, 48, 48, 48, 345, 346,
+ 31, 347, 348, 349, 13, 13, 13, 350, 351, 352, 353, 354, 355, 72, 72, 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, 77, 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, 316, 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, 72, 72, 392, 393, 394, 395, 396, 48, 48, 48,
+ 48, 48, 48, 397, 398, 399, 48, 48, 48, 48, 48, 400, 72, 48, 48, 48,
+ 48, 401, 48, 48, 74, 72, 72, 402, 32, 403, 32, 404, 405, 406, 407, 73,
+ 48, 48, 48, 48, 48, 48, 48, 408, 409, 2, 3, 4, 5, 410, 411, 412,
+ 48, 413, 48, 200, 414, 415, 416, 417, 418, 48, 172, 419, 204, 204, 72, 72,
+ 48, 48, 48, 48, 48, 48, 48, 71, 420, 270, 270, 421, 271, 271, 271, 422,
+ 423, 424, 425, 72, 72, 209, 209, 426, 72, 72, 72, 72, 72, 72, 72, 72,
+ 48, 151, 48, 48, 48, 101, 427, 428, 48, 48, 429, 48, 430, 48, 48, 431,
+ 48, 432, 48, 48, 433, 434, 72, 72, 9, 9, 435, 11, 11, 48, 48, 48,
+ 48, 204, 192, 9, 9, 436, 11, 437, 48, 48, 74, 48, 48, 48, 438, 72,
+ 48, 48, 48, 315, 48, 199, 74, 72, 439, 48, 48, 440, 48, 441, 48, 442,
+ 48, 200, 443, 72, 72, 72, 48, 444, 48, 445, 48, 446, 72, 72, 72, 72,
+ 48, 48, 48, 447, 270, 448, 270, 270, 449, 450, 48, 451, 452, 453, 48, 454,
+ 48, 455, 72, 72, 456, 48, 457, 458, 48, 48, 48, 459, 48, 460, 48, 461,
+ 48, 462, 463, 72, 72, 72, 72, 72, 48, 48, 48, 48, 196, 72, 72, 72,
+ 9, 9, 9, 464, 11, 11, 11, 465, 48, 48, 466, 192, 72, 72, 72, 72,
+ 72, 72, 72, 72, 72, 72, 270, 467, 48, 48, 468, 469, 72, 72, 72, 72,
+ 48, 455, 470, 48, 62, 471, 72, 72, 72, 72, 72, 48, 472, 72, 48, 315,
+ 473, 48, 48, 474, 475, 448, 476, 477, 222, 48, 48, 478, 479, 48, 196, 192,
+ 480, 48, 481, 482, 483, 48, 48, 484, 222, 48, 48, 485, 486, 487, 488, 489,
+ 48, 98, 490, 491, 72, 72, 72, 72, 492, 493, 494, 48, 48, 495, 496, 192,
+ 497, 84, 85, 498, 499, 500, 501, 502, 48, 48, 48, 503, 504, 505, 469, 72,
+ 48, 48, 48, 506, 507, 192, 72, 72, 48, 48, 508, 509, 510, 511, 72, 72,
+ 48, 48, 48, 512, 513, 192, 514, 72, 48, 48, 515, 516, 192, 72, 72, 72,
+ 48, 173, 517, 518, 72, 72, 72, 72, 48, 48, 490, 519, 72, 72, 72, 72,
+ 72, 72, 9, 9, 11, 11, 148, 520, 521, 522, 48, 523, 524, 192, 72, 72,
+ 72, 72, 525, 48, 48, 526, 527, 72, 528, 48, 48, 529, 530, 531, 48, 48,
+ 532, 533, 534, 72, 48, 48, 48, 196, 85, 48, 508, 535, 536, 148, 175, 537,
+ 48, 538, 539, 540, 72, 72, 72, 72, 541, 48, 48, 542, 543, 192, 544, 48,
+ 545, 546, 192, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 48, 547,
+ 72, 72, 72, 101, 270, 548, 549, 550, 48, 207, 72, 72, 72, 72, 72, 72,
+ 271, 271, 271, 271, 271, 271, 551, 552, 48, 48, 48, 48, 388, 72, 72, 72,
+ 48, 48, 200, 553, 72, 72, 72, 72, 48, 48, 48, 48, 315, 72, 72, 72,
+ 48, 48, 48, 196, 48, 200, 370, 72, 72, 72, 72, 72, 72, 48, 204, 554,
+ 48, 48, 48, 555, 556, 557, 558, 559, 48, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 72, 9, 9, 11, 11, 270, 560, 72, 72, 72, 72, 72, 72,
+ 48, 48, 48, 48, 561, 562, 563, 563, 564, 565, 72, 72, 72, 72, 566, 567,
+ 48, 48, 48, 48, 48, 48, 48, 74, 48, 48, 48, 48, 48, 199, 72, 72,
+ 196, 72, 72, 72, 72, 72, 72, 72, 48, 200, 72, 72, 72, 568, 569, 48,
+ 48, 48, 48, 48, 48, 48, 48, 206, 48, 48, 48, 48, 48, 48, 71, 151,
+ 196, 570, 571, 72, 72, 72, 72, 72, 209, 209, 209, 209, 209, 209, 209, 325,
+ 209, 209, 572, 209, 209, 209, 573, 574, 575, 209, 576, 209, 209, 209, 577, 72,
+ 209, 209, 209, 209, 578, 72, 72, 72, 72, 72, 72, 72, 72, 72, 270, 579,
+ 209, 209, 209, 209, 209, 286, 270, 452, 9, 580, 11, 581, 582, 583, 242, 9,
+ 584, 585, 586, 587, 588, 9, 580, 11, 589, 590, 11, 591, 592, 593, 594, 9,
+ 595, 11, 9, 580, 11, 581, 582, 11, 242, 9, 584, 594, 9, 595, 11, 9,
+ 580, 11, 596, 9, 597, 598, 599, 600, 11, 601, 9, 602, 603, 604, 605, 11,
+ 606, 9, 607, 11, 608, 609, 609, 609, 32, 32, 32, 610, 32, 32, 611, 612,
+ 613, 614, 45, 72, 72, 72, 72, 72, 615, 616, 617, 72, 72, 72, 72, 72,
+ 48, 48, 151, 618, 619, 72, 72, 72, 72, 72, 72, 72, 48, 48, 620, 621,
+ 48, 48, 48, 48, 622, 623, 72, 72, 9, 9, 584, 11, 624, 370, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 488, 270, 270, 625, 626, 72, 72, 72, 72,
+ 488, 270, 627, 628, 72, 72, 72, 72, 629, 48, 630, 631, 632, 633, 634, 635,
+ 636, 206, 637, 206, 72, 72, 72, 638, 209, 209, 326, 209, 209, 209, 209, 209,
+ 209, 324, 335, 639, 639, 639, 209, 325, 640, 209, 209, 209, 209, 209, 209, 209,
+ 209, 209, 641, 72, 72, 72, 642, 209, 643, 209, 209, 326, 577, 644, 325, 72,
+ 209, 209, 209, 209, 209, 209, 209, 645, 209, 209, 209, 209, 209, 646, 424, 424,
+ 209, 209, 209, 209, 209, 209, 209, 324, 209, 209, 209, 209, 209, 577, 326, 72,
+ 326, 209, 209, 209, 646, 176, 209, 209, 646, 209, 641, 644, 72, 72, 72, 72,
+ 209, 209, 209, 209, 209, 209, 209, 647, 209, 209, 209, 209, 648, 209, 209, 209,
+ 209, 209, 209, 209, 209, 324, 641, 649, 286, 209, 577, 286, 643, 286, 72, 72,
+ 209, 650, 209, 209, 287, 72, 72, 192, 48, 48, 48, 48, 48, 204, 72, 72,
+ 48, 48, 48, 205, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48,
+ 48, 48, 469, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 101, 72,
+ 48, 204, 72, 72, 72, 72, 72, 72, 48, 48, 48, 48, 71, 72, 72, 72,
+ 651, 72, 652, 652, 652, 652, 652, 652, 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 72, 391, 391, 391, 391, 391, 391, 391, 653,
+ 391, 391, 391, 391, 391, 391, 391, 654, 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, 26, 26, 26, 26, 26, 26, 31, 31, 50, 31,
+ 31, 26, 51, 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, 129, 130, 122, 131, 26, 132, 133, 134, 132, 132, 132, 132, 132,
+ 133, 134, 135, 132, 136, 132, 132, 132, 137, 138, 139, 140, 138, 138, 141, 142,
+ 139, 143, 144, 138, 145, 138, 146, 26, 147, 148, 148, 148, 148, 148, 148, 149,
+ 148, 148, 148, 150, 26, 26, 26, 26, 151, 152, 153, 153, 154, 153, 153, 155,
+ 156, 155, 153, 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, 185, 186, 26, 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, 199, 200, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 199, 201, 199, 199, 199, 199, 199, 202, 178, 178,
+ 178, 178, 178, 178, 178, 178, 203, 26, 204, 204, 204, 205, 204, 206, 204, 206,
+ 207, 204, 208, 208, 208, 209, 210, 26, 211, 211, 211, 211, 211, 212, 211, 211,
+ 211, 213, 211, 214, 194, 194, 194, 194, 215, 215, 215, 216, 217, 217, 217, 217,
+ 217, 217, 217, 218, 217, 217, 217, 219, 217, 220, 217, 220, 217, 221, 9, 9,
+ 222, 26, 26, 26, 26, 26, 26, 26, 223, 223, 223, 223, 223, 223, 223, 223,
+ 223, 224, 223, 223, 223, 223, 223, 225, 226, 226, 226, 226, 226, 226, 226, 226,
+ 227, 227, 227, 227, 227, 227, 228, 229, 230, 230, 230, 230, 230, 230, 230, 231,
+ 230, 232, 233, 233, 233, 233, 233, 233, 18, 234, 165, 165, 165, 165, 165, 235,
+ 226, 26, 236, 9, 237, 238, 239, 240, 2, 2, 2, 2, 241, 242, 2, 2,
+ 2, 2, 2, 243, 244, 245, 2, 246, 2, 2, 2, 2, 2, 2, 2, 247,
+ 9, 9, 9, 9, 9, 9, 9, 248, 14, 14, 249, 249, 14, 14, 14, 14,
+ 249, 249, 14, 250, 14, 14, 14, 249, 14, 14, 14, 14, 14, 14, 251, 14,
+ 251, 14, 252, 253, 14, 14, 254, 255, 0, 256, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 257, 0, 258, 259, 0, 260, 2, 261, 0, 0, 0, 0,
+ 26, 26, 9, 9, 9, 9, 222, 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, 260, 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, 271, 270, 270,
+ 270, 270, 270, 271, 2, 2, 2, 2, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 272, 273, 165, 165, 165, 165, 166, 167, 274, 274,
+ 274, 274, 274, 274, 274, 275, 276, 275, 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, 266, 26, 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, 281, 282, 0, 0, 0, 283, 284, 0, 285,
+ 286, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 289, 290, 291, 291, 291,
+ 291, 291, 291, 291, 291, 291, 291, 292, 293, 294, 294, 294, 294, 294, 295, 169,
+ 169, 169, 169, 169, 169, 169, 169, 169, 169, 296, 0, 0, 294, 294, 294, 294,
+ 0, 0, 0, 0, 281, 26, 291, 291, 169, 169, 169, 296, 0, 0, 0, 0,
+ 0, 0, 0, 0, 169, 169, 169, 297, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 291, 291, 291, 291, 291, 298, 291, 291, 291, 291, 291, 291, 291, 291,
+ 291, 291, 291, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277,
+ 0, 0, 0, 0, 0, 0, 0, 0, 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 299, 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,
+ 309, 310, 26, 26, 26, 26, 311, 2, 312, 312, 312, 312, 312, 313, 0, 314,
+ 315, 315, 315, 315, 315, 315, 315, 26, 316, 316, 316, 316, 316, 316, 316, 316,
+ 317, 318, 316, 319, 53, 53, 53, 53, 320, 320, 320, 320, 320, 321, 322, 322,
+ 322, 322, 323, 324, 169, 169, 169, 325, 326, 326, 326, 326, 326, 326, 326, 326,
+ 326, 327, 326, 328, 164, 164, 164, 329, 330, 330, 330, 330, 330, 330, 331, 26,
+ 330, 332, 330, 333, 164, 164, 164, 164, 334, 334, 334, 334, 334, 334, 334, 334,
+ 335, 26, 26, 336, 337, 337, 338, 26, 339, 339, 339, 26, 172, 172, 2, 2,
+ 2, 2, 2, 340, 341, 342, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176,
+ 337, 337, 337, 337, 337, 343, 337, 344, 169, 169, 169, 169, 345, 26, 169, 169,
+ 296, 346, 169, 169, 169, 169, 169, 345, 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, 347, 26, 26, 26, 26, 348, 26, 349, 350, 25, 25, 351, 352,
+ 353, 25, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 354, 26, 51, 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, 355,
+ 26, 26, 31, 31, 31, 31, 31, 31, 31, 31, 356, 31, 31, 31, 31, 31,
+ 31, 26, 26, 26, 26, 26, 31, 357, 9, 9, 0, 314, 9, 358, 0, 0,
+ 0, 0, 359, 0, 260, 281, 50, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 360, 361, 0, 0, 0, 1, 2, 2, 3,
+ 1, 2, 2, 3, 362, 291, 290, 291, 291, 291, 291, 363, 169, 169, 169, 296,
+ 364, 364, 364, 365, 260, 260, 26, 366, 367, 368, 367, 367, 369, 367, 367, 370,
+ 367, 371, 367, 371, 26, 26, 26, 26, 367, 367, 367, 367, 367, 367, 367, 367,
+ 367, 367, 367, 367, 367, 367, 367, 372, 373, 0, 0, 0, 0, 0, 374, 0,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 255, 0, 375, 376, 26, 26, 26,
+ 26, 26, 0, 0, 0, 0, 0, 377, 378, 378, 378, 379, 380, 380, 380, 380,
+ 380, 380, 381, 26, 382, 0, 0, 281, 383, 383, 383, 383, 384, 385, 386, 386,
+ 386, 387, 388, 388, 388, 388, 388, 389, 390, 390, 390, 391, 392, 392, 392, 392,
+ 393, 392, 394, 26, 26, 26, 26, 26, 395, 395, 395, 395, 395, 395, 395, 395,
+ 395, 395, 396, 396, 396, 396, 396, 396, 397, 397, 397, 398, 397, 399, 400, 400,
+ 400, 400, 401, 400, 400, 400, 400, 401, 402, 402, 402, 402, 402, 26, 403, 403,
+ 403, 403, 403, 403, 404, 405, 26, 26, 406, 406, 406, 406, 406, 406, 406, 406,
+ 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 406, 407, 26,
+ 406, 406, 408, 26, 406, 26, 26, 26, 409, 410, 411, 411, 411, 411, 412, 413,
+ 414, 414, 415, 414, 416, 416, 416, 416, 417, 417, 417, 418, 419, 417, 26, 26,
+ 26, 26, 26, 26, 420, 420, 421, 422, 423, 423, 423, 424, 425, 425, 425, 426,
+ 26, 26, 26, 26, 26, 26, 26, 26, 427, 427, 427, 427, 428, 428, 428, 429,
+ 428, 428, 430, 428, 428, 428, 428, 428, 431, 432, 433, 434, 435, 435, 436, 437,
+ 435, 438, 435, 438, 439, 439, 439, 439, 440, 440, 440, 440, 26, 26, 26, 26,
+ 441, 441, 441, 441, 442, 443, 442, 26, 444, 444, 444, 444, 444, 444, 445, 446,
+ 447, 447, 448, 447, 449, 449, 450, 449, 451, 451, 452, 453, 26, 454, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 455, 455, 455, 455, 455, 455, 455, 455,
+ 455, 456, 26, 26, 26, 26, 26, 26, 457, 457, 457, 457, 457, 457, 458, 26,
+ 457, 457, 457, 457, 457, 457, 458, 459, 460, 460, 460, 460, 460, 26, 460, 461,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 31, 31, 31, 462, 463, 463, 463, 463, 463, 464, 465, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 466, 466, 466, 466, 466, 26, 467, 467,
+ 467, 467, 467, 468, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 469, 469,
+ 469, 470, 26, 26, 471, 471, 472, 26, 473, 473, 473, 473, 473, 473, 473, 473,
+ 473, 474, 475, 473, 473, 473, 26, 476, 477, 477, 477, 477, 477, 477, 477, 477,
+ 478, 479, 480, 480, 480, 481, 480, 482, 483, 483, 483, 483, 483, 483, 484, 483,
+ 483, 26, 485, 485, 485, 485, 486, 26, 487, 487, 487, 487, 487, 487, 487, 487,
+ 487, 487, 487, 487, 488, 138, 489, 26, 490, 490, 491, 490, 490, 490, 490, 492,
+ 26, 26, 26, 26, 26, 26, 26, 26, 493, 494, 495, 496, 495, 497, 498, 498,
+ 498, 498, 498, 498, 498, 499, 498, 500, 501, 502, 503, 504, 504, 505, 506, 507,
+ 502, 508, 509, 510, 511, 512, 512, 26, 513, 513, 513, 513, 513, 513, 513, 513,
+ 513, 513, 513, 514, 515, 26, 26, 26, 516, 516, 516, 516, 516, 516, 516, 516,
+ 516, 26, 516, 517, 26, 26, 26, 26, 518, 518, 518, 518, 518, 518, 519, 518,
+ 518, 518, 518, 519, 26, 26, 26, 26, 520, 520, 520, 520, 520, 520, 520, 520,
+ 521, 26, 520, 522, 199, 523, 26, 26, 524, 524, 524, 524, 524, 524, 524, 525,
+ 524, 526, 26, 26, 26, 26, 26, 26, 527, 527, 527, 528, 527, 529, 527, 527,
+ 26, 26, 26, 26, 26, 26, 26, 26, 530, 530, 530, 530, 530, 530, 530, 531,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 532, 532, 532, 532,
+ 532, 532, 532, 532, 532, 532, 533, 534, 535, 536, 537, 538, 538, 538, 539, 540,
+ 535, 26, 538, 541, 26, 26, 26, 26, 26, 26, 26, 26, 542, 543, 542, 542,
+ 542, 542, 542, 543, 544, 26, 26, 26, 545, 545, 545, 545, 545, 545, 545, 545,
+ 545, 26, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 547, 26, 26, 26,
+ 548, 548, 548, 548, 548, 548, 548, 549, 550, 551, 550, 550, 550, 550, 552, 550,
+ 553, 26, 550, 550, 550, 554, 555, 555, 555, 555, 556, 555, 555, 557, 558, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 559, 560, 561, 561, 561, 561, 559, 562,
+ 561, 26, 561, 563, 564, 565, 566, 566, 566, 567, 568, 569, 566, 570, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 571, 571, 571, 572, 26, 26, 26, 26, 26, 26, 573, 26,
+ 108, 108, 108, 108, 108, 108, 574, 575, 576, 576, 576, 576, 576, 576, 576, 576,
+ 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 577, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 576, 576, 576, 576, 576, 576, 576, 576,
+ 576, 576, 576, 576, 576, 578, 579, 26, 576, 576, 576, 576, 576, 576, 576, 576,
+ 580, 26, 26, 26, 26, 26, 26, 26, 581, 581, 581, 581, 581, 581, 581, 581,
+ 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 582, 581, 583,
+ 26, 26, 26, 26, 26, 26, 26, 26, 584, 584, 584, 584, 584, 584, 584, 584,
+ 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584,
+ 585, 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, 586,
+ 587, 587, 587, 588, 587, 589, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 590, 590, 590, 591, 591, 26, 592, 592, 592, 592, 592, 592, 592, 592,
+ 593, 26, 592, 594, 594, 592, 592, 595, 592, 592, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 597, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 598, 598, 598, 598, 598, 598, 598, 598,
+ 598, 599, 598, 598, 598, 598, 598, 598, 598, 600, 598, 598, 26, 26, 26, 26,
+ 26, 26, 26, 26, 601, 26, 347, 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,
+ 602, 602, 602, 602, 602, 602, 602, 26, 603, 603, 603, 603, 603, 603, 603, 603,
+ 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603,
+ 603, 603, 604, 26, 26, 26, 26, 26, 602, 605, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 606, 287, 287, 287, 287, 287, 287, 287,
+ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287,
+ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, 288, 26, 26, 26, 26,
+ 26, 26, 607, 26, 608, 26, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609,
+ 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609,
+ 609, 609, 609, 609, 609, 609, 609, 610, 611, 611, 611, 611, 611, 611, 611, 611,
+ 611, 611, 611, 611, 611, 612, 611, 613, 611, 614, 611, 615, 281, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 616, 26, 0, 0, 0, 0, 260, 361, 0, 0,
+ 0, 0, 0, 0, 617, 618, 0, 619, 620, 621, 0, 0, 0, 622, 0, 0,
+ 0, 0, 0, 0, 0, 623, 26, 26, 14, 14, 14, 14, 14, 14, 14, 14,
+ 249, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 260, 26, 0, 0, 0, 623, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 257, 0, 0, 0, 0, 0, 0, 0, 0, 257, 624, 625, 0, 626,
+ 627, 0, 0, 0, 0, 0, 0, 0, 269, 628, 257, 257, 0, 0, 0, 629,
+ 630, 631, 632, 0, 0, 0, 0, 0, 0, 0, 0, 0, 616, 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, 633, 633, 633, 633, 633, 633, 633, 633,
+ 633, 633, 633, 633, 633, 633, 633, 633, 633, 634, 26, 635, 636, 633, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 271, 270, 270, 637, 638, 639, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 640, 640, 640, 640, 640, 641, 640, 642,
+ 640, 643, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 644, 644, 644, 644, 644, 644, 644, 645, 646, 646, 646, 646, 646, 646, 646, 646,
+ 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646, 646,
+ 647, 646, 648, 26, 26, 26, 26, 26, 649, 649, 649, 649, 649, 649, 649, 649,
+ 649, 650, 649, 651, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 361, 0, 0, 0, 0, 0, 0, 0, 375, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 361, 0, 0, 0, 0, 0, 0, 616,
+ 26, 26, 26, 26, 26, 26, 26, 26, 652, 31, 31, 31, 653, 654, 655, 656,
+ 657, 658, 653, 659, 653, 655, 655, 660, 31, 661, 31, 662, 663, 661, 31, 662,
+ 26, 26, 26, 26, 26, 26, 354, 26, 0, 0, 0, 0, 0, 281, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 281, 26, 0, 260, 361, 0,
+ 361, 0, 361, 0, 0, 0, 616, 26, 0, 0, 0, 0, 0, 616, 26, 26,
+ 26, 26, 26, 26, 664, 0, 0, 0, 665, 26, 0, 0, 0, 0, 0, 281,
+ 0, 623, 314, 26, 616, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 26, 0, 375, 0, 375, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 281, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 623, 0, 281, 26, 26, 0, 281, 0, 0, 0, 0, 0, 0,
+ 0, 26, 0, 314, 0, 0, 0, 0, 0, 26, 0, 0, 0, 616, 314, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 632, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 627, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 281, 26, 0, 616, 375, 266, 260, 26, 0, 0, 0, 623, 260, 26,
+ 266, 26, 260, 26, 26, 26, 26, 26, 0, 0, 359, 0, 0, 0, 0, 0,
+ 0, 266, 26, 26, 26, 26, 0, 314, 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 280, 26, 26, 26, 26, 277, 277, 277, 277, 277, 277, 299, 26,
+ 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, 347, 26, 277, 277,
+ 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277,
+ 277, 277, 277, 277, 666, 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, 667, 26, 26, 26, 26, 26, 26, 668, 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[6504+(((_hb_ucd_u8[1264+(((_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[8768+(((_hb_ucd_u8[7792+(((_hb_ucd_u8[7120+(((_hb_ucd_u8[6874+(u>>2>>3>>4)])<<4)+((u>>2>>3)&15u))])<<3)+((u>>2)&7u))])<<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[9508+(((_hb_ucd_u8[9388+(((_hb_ucd_b4(9260+_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[10974+(((_hb_ucd_u16[1960+(((_hb_ucd_u8[10286+(((_hb_ucd_u8[9836+(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[5768+(((_hb_ucd_u8[16708+(((_hb_ucd_u8[16326+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0;
+}
+
+
+#else
+
+static const uint8_t
+_hb_ucd_u8[13344] =
+{
+ 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 15, 18, 15, 19, 20, 21,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 22, 23,
+ 5, 24, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 25, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+ 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, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 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, 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,
+ 34, 34, 34, 34, 34, 34, 34, 34, 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,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 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,111, 34, 34, 34,111,
+ 118,119,120,121,122,123,124,125, 34,126,127,111,128,129,130,131,
+ 132,133,134,135,136,137,138,111,139,140,111,141,142,143,144,111,
+ 145,146,147,148,149,150,111,111,151,152,153,154,111,155,111,156,
+ 34, 34, 34, 34, 34, 34, 34, 34,157, 34, 34,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, 34, 34, 34,158,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,
+ 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,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,111,111,
+ 34, 34, 34, 34,159,160,161, 34,111,111,111,111,162,163,164,165,
+ 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,111,111,111,111,111,
+ 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,166,111,111,111,111,111,111,
+ 67, 67,167,168,169,128, 65,111,170,171,172,173,174,175,176,177,
+ 67, 67, 67, 67,178,179,111,111,111,111,111,111,111,111,111,111,
+ 180,111,181,111,111,182,111,111,111,111,111,111,111,111,111,111,
+ 34,183,184,111,111,111,111,111,128,185,186,111, 34,187,111,111,
+ 67, 67,188, 67, 67,111, 67,189, 67, 67, 67, 67, 67, 67, 67, 67,
+ 67, 67, 67, 67, 67, 67, 67,190,111,111,111,111,111,111,111,111,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 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,111,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,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,
+ 191,111,180,180,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,
+ 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, 59, 2, 62, 36, 36, 36, 36, 63, 43, 43,
+ 7, 7, 7, 7, 7, 2, 2, 36, 64, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 65, 43, 43, 43, 66, 47, 43, 43, 67, 68, 69, 43, 43, 36,
+ 7, 7, 7, 7, 7, 36, 70, 71, 2, 2, 2, 2, 2, 2, 2, 72,
+ 63, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 64, 36,
+ 36, 36, 36, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7, 36, 36, 36,
+ 36, 36, 36, 36, 36, 63, 43, 43, 43, 43, 40, 21, 2, 40, 68, 20,
+ 36, 36, 36, 43, 43, 68, 43, 43, 43, 43, 68, 43, 68, 43, 43, 43,
+ 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 63, 43, 43, 2,
+ 36, 63, 43, 43, 43, 43, 43, 43, 43, 73, 43, 43, 43, 43, 43, 43,
+ 43, 74, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 74, 64, 75,
+ 76, 43, 43, 43, 74, 75, 76, 75, 63, 43, 43, 43, 36, 36, 36, 36,
+ 36, 43, 2, 7, 7, 7, 7, 7, 77, 36, 36, 36, 36, 36, 36, 36,
+ 63, 75, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 75,
+ 76, 43, 43, 74, 75, 75, 76, 36, 36, 36, 36, 79, 75, 75, 36, 36,
+ 36, 43, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 53, 58, 43,
+ 43, 74, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 75,
+ 76, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 64, 36, 36, 36,
+ 36, 36, 36, 7, 7, 7, 7, 7, 43, 36, 63, 2, 2, 2, 2, 2,
+ 76, 43, 43, 43, 74, 75, 76, 43, 60, 20, 20, 20, 80, 43, 43, 43,
+ 43, 75, 78, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 76,
+ 76, 43, 43, 74, 75, 75, 76, 43, 43, 43, 43, 74, 75, 75, 36, 36,
+ 71, 27, 27, 27, 27, 27, 27, 27, 43, 64, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 75, 74, 75, 75, 75, 75, 75, 76, 43,
+ 36, 36, 36, 79, 75, 75, 75, 75, 75, 75, 75, 7, 7, 7, 7, 7,
+ 27, 81, 61, 61, 53, 61, 61, 61, 74, 75, 64, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 43, 74, 75, 75, 43, 43, 43, 43, 43,
+ 43, 43, 43, 43, 36, 36, 36, 36, 7, 7, 7, 82, 27, 27, 27, 81,
+ 63, 75, 65, 36, 36, 36, 36, 36, 75, 75, 75, 74, 75, 75, 43, 43,
+ 43, 43, 74, 75, 75, 75, 75, 36, 83, 36, 36, 36, 36, 36, 36, 36,
+ 43, 75, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 75,
+ 76, 43, 43, 75, 75, 75, 76, 70, 61, 61, 36, 79, 27, 27, 27, 84,
+ 27, 27, 27, 27, 81, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 74,
+ 75, 43, 43, 43, 75, 75, 75, 75, 7, 75, 2, 2, 2, 2, 2, 2,
+ 63, 36, 43, 43, 43, 43, 43, 85, 36, 36, 36, 68, 43, 43, 43, 57,
+ 7, 7, 7, 7, 7, 2, 2, 2, 63, 36, 43, 43, 43, 43, 64, 36,
+ 36, 36, 36, 40, 43, 43, 43, 43, 7, 7, 7, 7, 7, 7, 36, 36,
+ 70, 61, 2, 2, 2, 2, 2, 2, 2, 86, 86, 61, 43, 61, 61, 61,
+ 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 47, 47, 47, 4, 4, 75,
+ 63, 43, 43, 43, 43, 43, 43, 74, 43, 43, 57, 43, 36, 36, 63, 43,
+ 43, 43, 43, 43, 43, 43, 43, 61, 61, 61, 61, 69, 61, 61, 61, 61,
+ 2, 2, 86, 61, 21, 2, 2, 2, 36, 36, 36, 36, 36, 79, 76, 43,
+ 74, 43, 43, 43, 76, 74, 76, 64, 36, 36, 36, 75, 43, 36, 36, 43,
+ 64, 75, 78, 79, 75, 75, 75, 36, 63, 43, 64, 36, 36, 36, 36, 36,
+ 36, 74, 76, 74, 75, 75, 76, 79, 7, 7, 7, 7, 7, 75, 76, 61,
+ 16, 16, 16, 16, 16, 50, 44, 16, 36, 36, 36, 36, 36, 36, 63, 43,
+ 2, 2, 2, 2, 87, 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,
+ 88, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 65,
+ 89, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 90, 91, 91,
+ 36, 36, 36, 36, 36, 58, 2, 92, 93, 36, 36, 36, 36, 36, 36, 36,
+ 36, 43, 43, 43, 43, 43, 43, 43, 36, 43, 57, 2, 2, 2, 2, 2,
+ 36, 36, 43, 76, 43, 43, 43, 75, 75, 75, 75, 74, 76, 43, 43, 43,
+ 43, 43, 2, 77, 2, 60, 63, 43, 7, 7, 7, 7, 7, 7, 7, 7,
+ 2, 2, 2, 94, 2, 56, 43, 59, 36, 95, 36, 36, 36, 36, 36, 36,
+ 36, 36, 63, 64, 36, 36, 36, 36, 36, 36, 36, 36, 63, 36, 36, 36,
+ 43, 74, 75, 76, 74, 75, 75, 75, 75, 74, 75, 75, 76, 43, 43, 43,
+ 61, 61, 2, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 27, 27, 61,
+ 36, 36, 36, 63, 74, 76, 43, 2, 36, 36, 79, 74, 43, 43, 43, 43,
+ 74, 74, 76, 43, 43, 43, 74, 75, 75, 76, 43, 43, 43, 43, 43, 43,
+ 2, 2, 2, 77, 2, 2, 2, 2, 43, 43, 43, 43, 43, 43, 43, 96,
+ 43, 43, 78, 36, 36, 36, 36, 36, 36, 36, 74, 43, 43, 74, 74, 75,
+ 75, 74, 78, 36, 36, 36, 36, 36, 86, 61, 61, 61, 61, 47, 43, 43,
+ 43, 43, 61, 61, 61, 61, 61, 61, 43, 78, 36, 36, 36, 36, 36, 36,
+ 79, 43, 43, 75, 43, 76, 43, 36, 36, 36, 36, 74, 43, 75, 76, 76,
+ 43, 75, 75, 75, 75, 75, 2, 2, 36, 36, 75, 75, 75, 75, 43, 43,
+ 43, 43, 75, 43, 43, 57, 2, 2, 7, 7, 7, 7, 7, 7, 83, 36,
+ 36, 36, 36, 36, 40, 40, 40, 2, 43, 57, 43, 43, 43, 43, 43, 43,
+ 74, 43, 43, 43, 64, 36, 63, 36, 36, 36, 64, 79, 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, 97, 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, 98, 98, 98, 98,
+ 16, 16, 16, 16, 11, 11, 99,100, 41, 16, 16, 16, 11, 11, 99, 41,
+ 16, 16, 16, 16, 11, 11,101, 41,102,102,102,102,102,103, 59, 59,
+ 51, 51, 51, 2,104,105,104,105, 2, 2, 2, 2,106, 59, 59,107,
+ 2, 2, 2, 2,108,109, 2,110,111, 2,112,113, 2, 2, 2, 2,
+ 2, 9,111, 2, 2, 2, 2,114, 59, 59, 59, 59, 59, 59, 59, 59,
+ 115, 40, 27, 27, 27, 8,112,116, 27, 27, 27, 27, 27, 8,112, 91,
+ 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43,117, 48,
+ 96, 48, 96, 43, 43, 43, 43, 43, 61,118, 61,119, 61, 34, 11, 16,
+ 11, 32,119, 61, 46, 11, 11, 61, 61, 61,118,118,118, 11, 11,120,
+ 11, 11, 35, 36, 39, 61, 16, 11, 8, 8, 46, 16, 16, 26, 61,121,
+ 92, 92, 92, 92, 92, 92, 92, 92, 92,122,123, 92,124, 61, 61, 61,
+ 8, 8,125, 61, 61, 8, 61, 61,125, 26, 61,125, 61, 61, 61,125,
+ 61, 61, 61, 61, 61, 61, 61, 8, 61,125,125, 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,126,127, 61, 61,
+ 61, 61, 61, 61, 61, 61,125, 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,112,128, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4,
+ 8,112,129,129,129,129,129,129,129,129,129,129,128, 8, 8, 8,
+ 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8,
+ 8, 8,125, 26, 8, 8,125, 61, 32, 11, 32, 34, 34, 34, 34, 11,
+ 32, 32, 34, 16, 16, 16, 40, 11, 32, 32,121, 61, 61,119, 34,130,
+ 43, 32, 16, 16, 50, 2, 87, 2, 36, 36, 36, 36, 36, 36, 36, 95,
+ 2, 2, 2, 2, 2, 2, 2, 56, 2,104,104, 2,108,109,104, 2,
+ 2, 2, 2, 6, 2, 94,104, 2,104, 4, 4, 4, 4, 2, 2, 77,
+ 2, 2, 2, 2, 2, 51, 2, 2, 94,131, 2, 2, 2, 2, 2, 2,
+ 61, 2, 2, 2, 2, 2, 2, 2, 1, 2,132,133, 4, 4, 4, 4,
+ 4, 61, 4, 4, 4, 4,134, 91,135, 92, 92, 92, 92, 43, 43, 75,
+ 136, 40, 40, 61, 92,137, 58, 61, 71, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 63,138,139, 62, 36, 36, 36, 36, 36, 58, 40, 62,
+ 61, 27, 27, 61, 61, 61, 61, 61, 27, 27, 27, 27, 27, 61, 61, 61,
+ 61, 61, 61, 61, 27, 27, 27, 27,140, 27, 27, 27, 27, 27, 27, 27,
+ 36, 36, 95, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,141, 2,
+ 32, 32, 32, 32, 32, 32, 32, 63, 48,142, 43, 43, 43, 43, 43, 77,
+ 32, 32, 32, 32, 32, 32, 40, 43, 36, 36, 36, 92, 92, 92, 92, 92,
+ 43, 2, 2, 2, 2, 2, 2, 2, 41, 41, 41,139, 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,143, 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, 16, 32, 11, 11, 34, 16, 16, 16,
+ 16, 16, 34, 35, 40, 35, 36, 36, 36, 64, 36, 64, 36, 63, 36, 36,
+ 36, 79, 76, 74, 61, 61, 43, 43, 27, 27, 27, 61,144, 61, 61, 61,
+ 36, 36, 2, 2, 2, 2, 2, 2, 75, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 75, 75, 75, 75, 75, 75, 75, 75, 43, 43, 43, 43, 43, 2,
+ 43, 36, 36, 36, 2, 65, 65, 63, 36, 36, 36, 43, 43, 43, 43, 2,
+ 36, 36, 36, 63, 43, 43, 43, 43, 43, 75, 75, 75, 75, 75, 75,145,
+ 36, 63, 75, 43, 43, 75, 43, 75,145, 2, 2, 2, 2, 2, 2, 77,
+ 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 63, 62, 36, 36, 36, 36,
+ 36, 36, 36, 36, 63, 43, 43, 74, 76, 74, 76, 43, 43, 43, 43, 43,
+ 36, 63, 36, 36, 36, 36, 74, 75, 7, 7, 7, 7, 7, 7, 2, 2,
+ 62, 36, 36, 70, 61, 79, 74, 36, 64, 43, 64, 63, 64, 36, 36, 43,
+ 36, 36, 36, 36, 36, 36, 95, 2, 36, 36, 36, 36, 36, 79, 43, 75,
+ 2, 95,146, 43, 43, 43, 43, 43, 16, 16, 16, 16, 16,100, 40, 40,
+ 16, 16, 16, 16, 97, 41, 41, 41, 36, 79, 76, 75, 74,145, 76, 43,
+ 147,147,147,147,147,147,147,147,148,148,148,148,148,148,148,148,
+ 16, 16, 16, 16, 16, 16, 35, 64, 36, 36, 36, 36,149, 36, 36, 36,
+ 36, 41, 41, 41, 41, 41, 41, 41, 41,150, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36,129,151,151,151,151,151,151,151,151,
+ 36, 36, 36, 36, 36, 36,144, 61, 2, 2, 2,152,113, 2, 2, 2,
+ 6,153,154,129,129,129,129,129,129,129,113,152,113, 2,110,155,
+ 2, 2, 2, 2,134,129,129,113, 2,156, 8, 8, 60, 2, 2, 2,
+ 36, 36, 36, 36, 36, 36, 36,157, 2, 2, 3, 2, 4, 5, 6, 2,
+ 16, 16, 16, 16, 16, 17, 18,112,113, 4, 2, 36, 36, 36, 36, 36,
+ 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 40,
+ 20,158, 53, 20, 26, 8,125, 61, 61, 61, 61, 61,159, 59, 61, 61,
+ 2, 2, 2, 87, 27, 27, 27, 27, 27, 27, 27, 81, 61, 61, 61, 61,
+ 92, 92,124, 27, 81, 61, 61, 61, 61, 61, 61, 61, 61, 27, 61, 61,
+ 61, 61, 61, 61, 61, 61, 47, 43,160,160,160,160,160,160,160,160,
+ 161, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 84, 36,
+ 133, 36, 36, 36, 36, 92, 92, 92, 36, 36, 36, 36, 36, 36, 36, 58,
+ 162, 92, 92, 92, 92, 92, 92, 92, 36, 36, 36, 58, 27, 27, 27, 27,
+ 36, 36, 36, 70,140, 27, 27, 27, 36, 36, 36,163, 27, 27, 27, 27,
+ 36, 36, 36, 36, 36,163, 27, 27, 36, 36, 36, 27, 27, 27, 27, 30,
+ 36, 36, 36, 36, 36, 36, 27, 36, 63, 43, 43, 43, 43, 43, 43, 43,
+ 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 30,
+ 36, 36, 36, 36, 36, 36,163, 27, 36, 36, 36, 36, 71, 36, 36, 36,
+ 36, 36, 63, 43, 43,161, 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, 63,164, 51,
+ 27, 27, 27, 84, 36, 36, 36, 36,161, 27, 30, 2, 2, 2, 2, 2,
+ 36, 36,163, 27, 27, 27, 27, 27, 76, 78, 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, 7, 7, 7, 7, 7, 7, 7,165,
+ 75, 76, 43, 74, 76, 57, 72, 2, 2, 2, 2, 2, 2, 2, 72, 59,
+ 36, 36, 36, 63, 43, 43, 76, 43, 43, 43, 43, 7, 7, 7, 7, 7,
+ 2, 2, 79, 78, 36, 36, 36, 36, 36, 63, 2, 36, 36, 36, 36, 36,
+ 36, 79, 75, 43, 43, 43, 43, 74, 78, 36, 58, 2, 56, 43, 57, 76,
+ 7, 7, 7, 7, 7, 58, 58, 2, 87, 27, 27, 27, 27, 27, 27, 27,
+ 36, 36, 36, 36, 36, 36, 75, 76, 43, 75, 74, 43, 2, 2, 2, 43,
+ 36, 36, 36, 36, 36, 36, 36, 63, 74, 75, 75, 75, 75, 75, 75, 75,
+ 36, 36, 36, 79, 75, 75, 78, 36, 36, 75, 75, 43, 43, 43, 43, 43,
+ 36, 36, 79, 75, 43, 43, 43, 43, 75, 43, 74, 64, 36, 58, 2, 2,
+ 7, 7, 7, 7, 7, 2, 2, 64, 75, 76, 43, 43, 74, 74, 75, 76,
+ 74, 43, 36, 65, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 79,
+ 75, 43, 43, 43, 75, 75, 43, 76, 57, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 36, 36, 43, 43, 75, 76, 43, 43, 43, 74, 76, 76,
+ 57, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 76, 75,
+ 43, 43, 43, 76, 36, 36, 36, 36, 75, 43, 43, 76, 43, 43, 43, 43,
+ 7, 7, 7, 7, 7, 27, 2, 86, 43, 43, 43, 43, 76, 57, 2, 2,
+ 27, 27, 27, 27, 27, 27, 27, 84, 75, 75, 75, 75, 75, 76, 74, 64,
+ 78, 76, 2, 2, 2, 2, 2, 2, 79, 75, 43, 43, 43, 43, 75, 75,
+ 64, 65, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75,
+ 63, 43, 43, 43, 43, 64, 36, 36, 36, 63, 43, 43, 74, 63, 43, 57,
+ 2, 2, 2, 56, 43, 43, 43, 43, 63, 43, 43, 74, 76, 43, 36, 36,
+ 36, 36, 36, 36, 36, 43, 43, 43, 43, 43, 43, 74, 43, 2, 65, 2,
+ 43, 43, 43, 43, 43, 43, 43, 76, 58, 2, 2, 2, 2, 2, 2, 2,
+ 2, 36, 36, 36, 36, 36, 36, 36, 43, 43, 43, 43, 74, 43, 43, 43,
+ 74, 43, 76, 43, 43, 43, 43, 43, 43, 43, 43, 63, 43, 43, 43, 43,
+ 36, 36, 36, 36, 36, 75, 75, 75, 43, 74, 76, 76, 36, 36, 36, 36,
+ 36, 63, 74,145, 2, 2, 2, 2, 27, 27, 81, 61, 61, 61, 53, 20,
+ 144, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, 21,
+ 43, 43, 57, 2, 2, 2, 2, 2, 43, 43, 43, 57, 2, 2, 61, 61,
+ 40, 40, 86, 61, 61, 61, 61, 61, 7, 7, 7, 7, 7,166, 27, 27,
+ 27, 84, 36, 36, 36, 36, 36, 36, 27, 27, 27, 30, 2, 2, 2, 2,
+ 79, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 76,
+ 43, 67, 40, 40, 40, 40, 40, 40, 40, 77, 43, 43, 43, 43, 43, 43,
+ 36, 36, 36, 36, 36, 36, 47, 57, 61, 61,167, 76, 43, 61,167, 75,
+ 75,168, 59, 59, 59, 73, 43, 43, 43, 69, 47, 43, 43, 43, 61, 61,
+ 61, 61, 61, 61, 61, 43, 43, 61, 61, 43, 69, 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, 69, 61, 47, 43, 43, 43, 43, 43, 43, 43, 43, 69, 61,
+ 61, 61, 47, 61, 61, 61, 61, 61, 61, 61, 69, 21, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 56, 43, 43, 43, 43, 43, 67, 40, 40, 40, 40,
+ 7, 7, 7, 7, 7, 7, 7, 70, 36, 36, 36, 36, 36, 36, 43, 43,
+ 7, 7, 7, 7, 7, 7, 7,169, 16, 16, 43, 43, 43, 67, 40, 40,
+ 27, 27, 27, 27, 27, 27,140, 27,170, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27,140, 27, 27, 27, 27, 27, 27, 81, 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, 6, 7, 7, 12, 12, 7, 21, 7, 12, 1, 12, 6,
+ 6, 12, 12, 26, 7, 26, 26, 7, 21, 1, 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, 17, 21, 7, 6, 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,
+ 26, 6, 7, 14, 17, 22, 26, 14, 17, 6, 14, 6, 12, 24, 24, 6,
+ 26, 15, 6, 21, 11, 21, 24, 9, 23, 26, 10, 21, 6, 10, 4, 4,
+ 3, 3, 7, 25, 24, 7, 22, 22, 21, 22, 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, 12, 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, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 34, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, 0, 39, 40,
+ 0, 0, 41, 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, 0, 14, 15, 16, 15, 17, 15, 18, 15, 18,
+ 15, 18, 0, 18, 0, 19, 15, 18, 20, 18, 0, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 32, 0, 0, 0, 0, 0, 0, 33, 0, 0, 34, 0, 0, 35, 0,
+ 36, 0, 0, 0, 37, 38, 39, 40, 41, 42, 43, 44, 45, 0, 0, 46,
+ 0, 0, 0, 47, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 49,
+ 0, 50, 0, 51, 52, 0, 53, 0, 0, 0, 0, 0, 0, 54, 55, 56,
+ 0, 0, 0, 0, 57, 0, 0, 58, 59, 60, 61, 62, 0, 0, 63, 64,
+ 0, 0, 0, 65, 0, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 69,
+ 0, 70, 0, 0, 71, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0,
+ 73, 0, 0, 0, 0, 0, 74, 0, 0, 75, 0, 0, 0, 76, 77, 0,
+ 78, 61, 0, 79, 80, 0, 0, 81, 82, 83, 0, 0, 0, 84, 0, 85,
+ 0, 0, 50, 86, 50, 0, 87, 0, 88, 0, 0, 0, 77, 0, 0, 0,
+ 89, 90, 0, 91, 92, 93, 94, 0, 0, 0, 0, 0, 50, 0, 0, 0,
+ 0, 95, 96, 0, 0, 0, 0, 97, 98, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 99, 0, 0,100, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,101,102, 0, 0,103, 0, 0, 0, 0, 0, 0,104, 0, 0, 0,
+ 98, 0, 0, 0, 0, 0, 0,105, 0, 0, 0, 0, 0, 0, 0,106,
+ 0,107, 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, 28, 29,
+ 0, 0, 0, 30, 31, 32, 0, 0, 31, 0, 0, 33, 31, 0, 0, 0,
+ 31, 34, 0, 0, 0, 0, 0, 35, 36, 0, 0, 0, 0, 0, 0, 37,
+ 38, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 41, 0, 42,
+ 0, 0, 0, 43, 44, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 46,
+ 47, 0, 0, 0, 0, 48, 0, 0, 0, 49, 0, 49, 0, 50, 0, 0,
+ 0, 0, 51, 0, 0, 0, 0, 52, 0, 53, 0, 0, 0, 0, 54, 55,
+ 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 0, 58, 49, 0, 59, 60,
+ 0, 0, 61, 0, 0, 0, 62, 63, 0, 0, 0, 64, 0, 65, 66, 67,
+ 68, 69, 1, 70, 0, 71, 72, 73, 0, 0, 74, 75, 0, 0, 0, 76,
+ 0, 0, 1, 1, 0, 0, 77, 0, 0, 78, 0, 0, 0, 0, 74, 79,
+ 0, 80, 0, 0, 0, 0, 0, 75, 81, 0, 82, 0, 49, 0, 1, 75,
+ 0, 0, 83, 0, 0, 84, 0, 0, 0, 0, 0, 85, 54, 0, 0, 0,
+ 0, 0, 0, 86, 87, 0, 0, 81, 0, 0, 31, 0, 0, 88, 0, 0,
+ 0, 0, 89, 0, 0, 0, 0, 47, 0, 0, 57, 0, 0, 0, 0, 90,
+ 91, 0, 0, 92, 0, 0, 93, 0, 0, 0, 94, 0, 0, 0, 95, 0,
+ 96, 57, 0, 0, 81, 0, 0, 76, 0, 0, 0, 97, 98, 0, 0, 99,
+ 100, 0, 0, 0, 0, 0, 0,101, 0, 0,102, 0, 0, 0, 0,103,
+ 31, 0,104,105,106, 33, 0, 0,107, 0, 0, 0,108, 0, 0, 0,
+ 0, 0, 0,109, 0, 0,110, 0, 0, 0, 0,111, 85, 0, 0, 0,
+ 0, 0, 54, 0, 0, 0, 0, 49,112, 0, 0, 0, 0,113, 0, 0,
+ 114, 0, 0, 0, 0,112, 0, 0, 0, 0, 0,115, 0, 0, 0,116,
+ 0, 0, 0,117, 0,118, 0, 0, 0, 0,119,120,121, 0,122, 0,
+ 123, 0, 0, 0,124,125,126, 0, 0, 0,127, 0, 0,128, 0, 0,
+ 129, 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,
+ 52, 1, 1, 1, 53, 21, 43, 54, 55, 21, 35, 1, 0, 0, 0, 56,
+ 0, 0, 0, 57, 58, 59, 0, 0, 0, 0, 0, 60, 0, 61, 0, 0,
+ 0, 0, 62, 63, 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0,
+ 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 69, 0, 0, 70, 71, 0,
+ 72, 73, 74, 75, 76, 77, 0, 0, 0, 78, 0, 0, 0, 79, 80, 0,
+ 0, 0, 0, 47, 0, 0, 0, 49, 0, 63, 0, 0, 64, 0, 0, 81,
+ 0, 0, 82, 0, 0, 0, 83, 0, 0, 19, 84, 0, 63, 0, 0, 0,
+ 0, 49, 1, 85, 1, 54, 15, 86, 84, 0, 0, 0, 0, 56, 0, 0,
+ 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 87, 0, 0, 88, 0, 0,
+ 87, 0, 0, 0, 0, 79, 0, 0, 89, 9, 12, 4, 90, 8, 91, 47,
+ 0, 59, 50, 0, 21, 1, 21, 92, 93, 1, 1, 1, 1, 94, 95, 96,
+ 97, 1, 98, 59, 81, 99,100, 4, 59, 0, 0, 0, 0, 0, 0, 19,
+ 50, 0, 0, 0, 0, 0, 0, 62, 0, 0,101,102, 0, 0,103, 0,
+ 0, 1, 1, 50, 0, 0, 0, 38, 0, 64, 0, 0, 0, 0, 0, 63,
+ 0, 0, 52, 69, 62, 0, 0, 0, 79, 0, 0, 0,104,105, 59, 38,
+ 81, 0, 0, 0, 0, 0, 0,106, 1, 14, 4, 12, 0, 38, 89, 0,
+ 0, 0, 0,107, 0, 0,108, 62, 0,109, 0, 0, 0, 1, 0, 0,
+ 0, 0, 19, 59, 0,110, 14, 54, 0, 0,111, 0, 89, 0, 0, 0,
+ 62, 63, 0, 0, 63, 0, 88, 0, 0,111, 0, 0, 0, 0,112, 0,
+ 0, 0, 79, 56, 0, 38, 1, 59, 1, 59, 0, 0, 64, 88, 0, 0,
+ 113, 0, 0, 0, 56, 0, 0, 0, 0,113, 0, 0, 0, 0, 62, 0,
+ 0, 0, 0, 80, 0, 62, 0, 0, 0, 0, 57, 0, 88,114, 0, 0,
+ 8, 91, 0, 0, 1, 89, 0, 0,115, 0, 0, 0, 0, 0, 0,116,
+ 0,117,118,119,120, 0, 52, 4,121, 49, 23, 0, 0, 0, 38, 50,
+ 38, 59, 0, 0, 1, 89, 1, 1, 1, 1, 39, 1, 48,104, 89, 0,
+ 0, 0, 0, 1, 4,121, 0, 0, 0, 1,122, 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, 0, 0, 0,220,
+ 230,230, 0,220,230,220,220,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,
+ 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,
+ 230, 0, 0,220,230,220, 0,220, 0, 0, 9, 9, 0, 0, 7, 0,
+ 230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,
+ 202,230,230,230,230,230,232,228,228,220, 0,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,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, 9, 7, 0, 0, 7, 9, 0,
+ 0, 0, 9, 7, 9, 9, 0, 0, 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,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, 3, 3, 3, 3, 3, 3, 3, 24, 3, 3, 3, 3, 3, 3, 3,
+ 3, 25, 3, 3, 26, 27, 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, 19, 56, 31, 0, 0, 0, 0,
+ 0, 0, 0, 57, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3,
+ 0, 0, 0, 58, 59, 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, 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, 11, 12, 12, 12, 12, 13, 14, 15, 16, 17, 18, 12, 19, 12,
+ 20, 12, 12, 12, 12, 21, 22, 22, 22, 23, 12, 12, 12, 12, 24, 25,
+ 12, 12, 26, 27, 28, 29, 30, 31, 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, 32, 12, 33, 7, 7, 34, 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, 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, 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, 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, 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, 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, 35, 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, 33,
+ 33, 34, 35, 35, 35, 35, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 2, 2, 51, 51, 52, 53, 54, 55, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 56, 56, 56, 56,
+ 56, 56, 58, 59, 60, 61, 56, 62, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 56, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 71, 62, 62, 62, 62, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 73, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 87, 87, 87, 87, 87, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62,
+ 88, 89, 89, 89, 90, 89, 91, 92, 93, 94, 95, 95, 96, 97, 87, 98,
+ 99,100,101,102,103, 87,104,104,104, 87,105,106,107,108,109,110,
+ 111,112,113,114,115, 87, 89,116,117,118,119,120,121,122,123,124,
+ 125, 87,126,127, 87,128,129,130,131, 87,132,133,134,135,136,137,
+ 87, 87,138,139,140,141, 87,142, 87,143,144,144,144,144,144,144,
+ 144,144,144,144,144, 87, 87, 87, 87, 87,145,145,145,145,145,145,
+ 145,145,145, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87,146,146,146,146,146, 87, 87, 87,147,147,147,147,148,149,
+ 150,150, 87, 87, 87, 87,151,151,152,153,154,154,154,154,154,154,
+ 154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,
+ 155,155,155,155,154, 87, 87, 87, 87, 87,156,157,158,159,159,159,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,
+ 87, 87,160,161, 87, 87, 87, 87, 87, 87, 56, 56,162,163, 51, 56,
+ 56, 87, 56, 56, 56, 56, 56, 56, 56, 56,164,164,164,164,164,164,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,165, 87,166, 87, 87,167,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,168,168,169, 87, 87, 87,
+ 87, 87, 56, 56, 56, 87, 89, 89, 87, 87, 56, 56, 56, 56,170, 87,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 62,
+ 62, 62, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 87,
+ 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 62, 62, 62, 62, 62, 62,
+ 62, 87, 87, 87, 87, 87, 87, 87, 87, 87, 56, 87,171,171, 0, 1,
+ 2, 2, 0, 1, 2, 2, 2, 3, 4, 5, 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, 11, 11, 11, 11, 12, 11, 13, 13, 13, 13, 13, 13,
+ 13, 13, 14, 13, 13, 13, 13, 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, 26, 21, 21, 21, 21, 21, 21, 31, 21, 32, 32, 32, 32, 32, 33,
+ 34, 32, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36,
+ 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38,
+ 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40,
+ 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42,
+ 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 45, 44, 44,
+ 44, 44, 46, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 48, 47, 47, 49, 49, 49, 49, 49, 49,
+ 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 52, 52, 52, 52,
+ 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54,
+ 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 57, 57, 57, 57,
+ 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, 64, 64, 64, 64, 64,
+ 64, 64, 65, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 55, 55, 55,
+ 55, 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, 8, 8,
+ 8, 8, 72, 72, 72, 72, 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, 8, 8,
+ 8, 8, 11, 11, 11, 11, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 90, 90, 90, 90, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91,
+ 4, 4, 92, 92, 92, 92, 92, 92, 92, 92, 50, 50, 50, 93, 93, 93,
+ 93, 93, 53, 53, 53, 53, 53, 53, 13, 13, 94, 94, 94, 94, 94, 94,
+ 94, 94, 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, 52,
+ 52, 52, 52, 0,104,104, 0, 0, 0,102, 52, 52, 0, 0, 0, 0,
+ 52,106, 0, 0, 0, 0, 0,102,102,107,102,102,102,102,102,108,
+ 0, 0, 94, 94, 94, 94, 0, 0, 0, 0,109,109,109,109,109,109,
+ 109,109,109,109,109,109,109,110,110,110,111,111,111,111,111,111,
+ 111,111,111,111,111,111, 13, 13, 13, 13, 13, 13,112,112,112,112,
+ 112,112, 0, 0,113, 4, 4, 4, 4, 4,114, 4, 4, 4, 4, 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, 54, 54, 54, 54,125,125,
+ 125,125,128,128,128,128,128,128,128,128, 4,129, 18, 18, 18, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,130, 0, 21,
+ 21, 21, 8, 0,131, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 21,
+ 21,132, 0, 0, 1, 2, 1, 2,133,101,102,134, 52, 52, 52, 52,
+ 0, 0,135,135,135,135,135,135,135,135, 0, 0, 0, 0, 11, 11,
+ 11, 11, 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,150,151,151,151,151,151,151,
+ 151,151,152,152,152,152,153,153,154,154,155,155,155,155,155,155,
+ 156,156,157,157,158,158,158,158,158,158,159,159,160,160,160,160,
+ 160,160,161,161,161,161,161,161,162,162,163,163,163,163,164,164,
+ 164,164,165,165,165,165,166,166,167,167,168,168,168,168,168,168,
+ 168,168,169,169,169,169,169,169,169,169,170,170,170,170,170,170,
+ 170,170,171,171,171,171,171,171,171,171,172,172,172,172,172,172,
+ 172,172,173,173,173,174,174,174,174,174,175,175,175,175,175,175,
+ 176,176,177,177,177,177,177,177,177,177,178,178,178,178,178,179,
+ 179,179,180,180,180,180,180,181,181,181,182,182,182,182,182,182,
+ 183, 43,184,184,184,184,184,184,184,184,185,185,185,186,186,186,
+ 186,186,187,187,187,188,187,187,187,187,189,189,189,189,189,189,
+ 189,189,190,190,190,190,190,190,190,190,191,191,191,191,191,191,
+ 191,191,192,192,192,192,192,192, 66, 66,193,193,193,193,193,193,
+ 193,193,194,194,194,194,194,194,194,194,195,195,195,195,195,195,
+ 195,195,196,196,196,196,196,196,196,196,197,197,197,197,197,197,
+ 197,197,198,198,198,198,198,198,198,198,199,199,199,199,199,200,
+ 200,200,200,200,200,200,201,201,201,201,202,202,202,202,202,202,
+ 202,203,203,203,203,203,203,203,203,203,204,204,204,204,204,204,
+ 205,205,205,205,205,205,205,205,205,205,206,206,206,206,206,206,
+ 206,206,110,110,110,110, 39, 39, 39, 39,207,207,207,207,207,207,
+ 207,207,208,208,208,208,208,208,208,208,209,209,209,209,209,209,
+ 209,209,112,112,112,112,112,112,112,112,112,112,112,112,210,210,
+ 210,210,211,211,211,211,211,211,211,211,212,212,212,212,212,212,
+ 212,212,213,213,213,213,213,213,213,213,214,214,214,214,214,214,
+ 214,214,214,214,214,214,214,214,215, 94,216,216,216,216,216,216,
+ 216,216,217,217,217,217,217,217,217,217,218, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+ 219,220,220,220,220,220,220,220,220,220,221,221,221,221,221,221,
+ 221,221,221,221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 222,223,224, 0,225, 0, 0, 0, 0, 0,226,226,226,226,226,226,
+ 226,226, 91, 91, 91, 91, 91, 91, 91, 91,227,227,227,227,227,227,
+ 227,227,228,228,228,228,228,228,228,228,229,229,229,229,229,229,
+ 229,229,230,230,230,230,230,230,230,230,231, 0, 0, 0, 0, 0,
+ 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, 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,175,175,175,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, 45, 45, 45,185,185,185,185,186,186,
+ 186,186,187,187,187,187,188,188,188,188,188,188,189,188,190,190,
+ 190,190,191,191,191,191,192,192,192,192,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,217,217,217,218,218,218,218,217,217,
+ 217,217,219,106,106,106,106,109,109,109,220,220,220,220,221,221,
+ 221,221, 0,222, 86, 0, 0, 0,222, 7, 82,138, 7, 0, 0, 0,
+ 223, 86,224,224,224,224,225,225,225,225,226,226,226,226,227,227,
+ 227,227,228,228,228,228,229, 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,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,147,147,147,147,148,148,148,148,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, 63, 63, 63, 63, 80, 80,
+ 80, 80,127,127,127,127,115,115,115,115,103,103,103,103,119,119,
+ 119,119,146,146,146,146, 99, 99, 99, 99,136,139, 0, 0,155,155,
+ 155,155,136,136,136,136, 17, 15, 15, 15,139,139,139,139,105,105,
+ 105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131,151,151,
+ 151,151,152,152,152,152,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[4848] =
+{
+ 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, 47, 47, 47, 47, 47, 68, 69, 32,
+ 70, 47, 47, 71, 72, 73, 74, 75, 76, 47, 47, 77, 78, 79, 80, 81,
+ 82, 47, 47, 83, 84, 85, 86, 87, 82, 47, 47, 77, 88, 47, 80, 89,
+ 90, 47, 47, 91, 92, 93, 80, 94, 95, 47, 47, 96, 97, 98, 99, 100,
+ 101, 47, 47, 102, 103, 104, 80, 105, 106, 47, 47, 91, 107, 108, 80, 109,
+ 110, 47, 47, 111, 112, 113, 80, 114, 90, 47, 47, 47, 115, 116, 99, 117,
+ 47, 47, 47, 118, 119, 120, 66, 66, 47, 47, 47, 121, 122, 123, 47, 47,
+ 124, 125, 126, 127, 47, 47, 47, 128, 129, 32, 32, 130, 131, 132, 66, 66,
+ 47, 47, 133, 134, 120, 135, 136, 137, 138, 139, 9, 9, 9, 11, 11, 140,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 141, 142, 143,
+ 47, 144, 9, 9, 9, 9, 9, 145, 146, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 147, 47, 148, 149, 47, 47, 47, 47, 150, 151,
+ 47, 152, 47, 153, 47, 152, 47, 152, 47, 47, 47, 154, 155, 156, 157, 143,
+ 158, 157, 47, 47, 159, 47, 47, 47, 160, 47, 161, 47, 47, 47, 47, 47,
+ 47, 47, 162, 163, 164, 47, 47, 47, 47, 47, 47, 47, 47, 165, 144, 144,
+ 47, 166, 47, 47, 47, 167, 168, 169, 157, 157, 170, 171, 32, 32, 32, 32,
+ 172, 47, 47, 173, 174, 120, 175, 176, 177, 47, 178, 61, 47, 47, 179, 180,
+ 47, 47, 181, 182, 183, 61, 47, 184, 11, 9, 9, 9, 66, 185, 186, 187,
+ 11, 11, 188, 27, 27, 27, 189, 190, 11, 191, 27, 27, 32, 32, 32, 32,
+ 13, 13, 13, 13, 13, 13, 13, 13, 13, 192, 13, 13, 13, 13, 13, 13,
+ 193, 193, 193, 193, 193, 194, 193, 11, 195, 195, 195, 196, 197, 198, 198, 197,
+ 199, 200, 201, 202, 203, 204, 205, 206, 207, 27, 208, 208, 208, 209, 210, 32,
+ 211, 212, 213, 214, 215, 143, 216, 216, 217, 218, 219, 144, 220, 221, 144, 222,
+ 223, 223, 223, 223, 223, 223, 223, 223, 224, 144, 225, 144, 144, 144, 144, 226,
+ 144, 227, 223, 228, 144, 229, 230, 144, 144, 144, 144, 144, 144, 144, 143, 143,
+ 143, 231, 144, 144, 144, 144, 232, 143, 144, 144, 144, 144, 144, 144, 144, 144,
+ 144, 144, 144, 233, 234, 144, 144, 235, 144, 144, 144, 144, 144, 144, 236, 144,
+ 144, 144, 144, 144, 144, 144, 237, 238, 143, 239, 144, 144, 240, 223, 241, 223,
+ 242, 243, 223, 223, 223, 244, 223, 245, 144, 144, 144, 223, 246, 144, 144, 144,
+ 9, 9, 9, 11, 11, 11, 247, 248, 13, 13, 13, 13, 13, 13, 249, 250,
+ 11, 11, 11, 47, 47, 47, 251, 252, 47, 47, 47, 47, 47, 47, 32, 32,
+ 253, 254, 255, 256, 257, 258, 66, 66, 259, 260, 261, 262, 263, 47, 47, 47,
+ 47, 264, 146, 47, 47, 47, 47, 265, 47, 266, 47, 47, 144, 144, 144, 47,
+ 144, 144, 267, 144, 268, 269, 144, 144, 267, 144, 144, 269, 144, 144, 144, 144,
+ 47, 47, 47, 47, 144, 144, 144, 144, 47, 270, 47, 47, 47, 47, 47, 47,
+ 47, 144, 144, 144, 144, 47, 47, 184, 271, 47, 61, 47, 13, 13, 272, 273,
+ 13, 274, 47, 47, 47, 47, 275, 276, 31, 277, 278, 279, 13, 13, 13, 280,
+ 281, 282, 283, 284, 285, 11, 11, 286, 287, 47, 288, 289, 47, 47, 47, 290,
+ 291, 47, 47, 292, 293, 157, 32, 294, 61, 47, 295, 47, 296, 297, 47, 47,
+ 70, 47, 47, 298, 299, 300, 301, 61, 47, 47, 302, 303, 304, 305, 47, 306,
+ 47, 47, 47, 307, 58, 308, 309, 310, 47, 47, 47, 11, 11, 311, 312, 11,
+ 11, 11, 11, 11, 47, 47, 313, 157, 314, 314, 314, 314, 314, 314, 314, 314,
+ 315, 315, 315, 315, 315, 315, 315, 315, 11, 316, 317, 47, 47, 47, 47, 47,
+ 47, 47, 47, 318, 31, 319, 47, 47, 47, 47, 47, 320, 321, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 322, 32, 323, 32, 324, 325, 326, 327, 47,
+ 47, 47, 47, 47, 47, 47, 47, 328, 329, 2, 3, 4, 5, 330, 331, 332,
+ 47, 333, 47, 47, 47, 47, 334, 335, 336, 143, 143, 337, 216, 216, 216, 338,
+ 339, 144, 144, 144, 144, 144, 144, 340, 341, 341, 341, 341, 341, 341, 341, 341,
+ 47, 47, 47, 47, 47, 47, 342, 143, 47, 47, 343, 47, 344, 47, 47, 60,
+ 47, 345, 47, 47, 47, 346, 216, 216, 9, 9, 145, 11, 11, 47, 47, 47,
+ 47, 47, 157, 9, 9, 145, 11, 11, 47, 47, 47, 47, 47, 47, 345, 66,
+ 47, 47, 47, 47, 47, 347, 47, 348, 47, 47, 349, 143, 143, 143, 47, 350,
+ 47, 351, 47, 345, 66, 66, 66, 66, 47, 47, 47, 352, 143, 143, 143, 143,
+ 353, 47, 47, 354, 143, 66, 47, 355, 47, 356, 143, 143, 357, 47, 358, 66,
+ 47, 47, 47, 359, 47, 360, 47, 360, 47, 359, 142, 143, 143, 143, 143, 143,
+ 9, 9, 9, 9, 11, 11, 11, 361, 47, 47, 362, 157, 157, 157, 157, 157,
+ 143, 143, 143, 143, 143, 143, 143, 143, 47, 47, 363, 47, 47, 47, 47, 47,
+ 47, 356, 364, 47, 60, 365, 66, 66, 47, 47, 47, 47, 366, 143, 47, 47,
+ 367, 47, 47, 354, 368, 369, 370, 371, 177, 47, 47, 372, 373, 47, 47, 157,
+ 95, 47, 374, 375, 376, 47, 47, 377, 177, 47, 47, 378, 379, 380, 381, 143,
+ 47, 47, 382, 383, 32, 32, 32, 32, 47, 47, 359, 47, 47, 384, 169, 157,
+ 90, 47, 47, 111, 385, 386, 387, 32, 47, 47, 47, 388, 389, 390, 47, 47,
+ 47, 47, 47, 391, 392, 157, 157, 157, 47, 47, 393, 394, 395, 396, 32, 32,
+ 47, 47, 47, 397, 398, 157, 66, 66, 47, 47, 399, 400, 157, 157, 157, 157,
+ 47, 141, 401, 402, 144, 144, 144, 144, 47, 47, 382, 403, 66, 66, 66, 66,
+ 9, 9, 9, 9, 11, 11, 126, 404, 47, 47, 47, 405, 406, 157, 157, 157,
+ 47, 47, 47, 47, 47, 407, 408, 409, 410, 47, 47, 411, 412, 413, 47, 47,
+ 414, 415, 66, 66, 47, 47, 47, 47, 47, 47, 393, 416, 417, 126, 143, 418,
+ 47, 152, 419, 420, 32, 32, 32, 32, 47, 47, 47, 353, 421, 157, 47, 47,
+ 422, 423, 157, 157, 157, 157, 157, 157, 47, 47, 47, 47, 47, 47, 47, 424,
+ 47, 47, 47, 47, 143, 425, 426, 427, 216, 216, 216, 216, 216, 216, 216, 66,
+ 47, 47, 47, 205, 205, 205, 205, 205, 47, 47, 47, 47, 47, 47, 300, 66,
+ 47, 47, 47, 47, 47, 47, 47, 428, 47, 47, 47, 429, 430, 431, 432, 47,
+ 9, 9, 9, 9, 9, 9, 11, 11, 143, 433, 66, 66, 66, 66, 66, 66,
+ 47, 47, 47, 47, 384, 434, 409, 409, 435, 436, 27, 27, 27, 27, 437, 409,
+ 47, 438, 205, 205, 205, 205, 205, 205, 144, 144, 144, 144, 144, 144, 439, 440,
+ 441, 144, 442, 144, 144, 144, 144, 144, 144, 144, 144, 144, 443, 144, 144, 144,
+ 9, 444, 11, 445, 446, 11, 193, 9, 447, 448, 9, 449, 11, 9, 444, 11,
+ 445, 446, 11, 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, 445, 446, 11,
+ 193, 9, 447, 448, 9, 449, 11, 9, 444, 11, 193, 9, 450, 451, 452, 453,
+ 11, 454, 9, 455, 456, 457, 458, 11, 459, 9, 460, 11, 461, 157, 157, 157,
+ 32, 32, 32, 462, 32, 32, 463, 464, 465, 466, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 47, 467, 468, 144, 144, 144,
+ 47, 47, 47, 47, 47, 47, 469, 470, 47, 47, 47, 47, 349, 32, 32, 32,
+ 9, 9, 447, 11, 471, 300, 66, 66, 143, 143, 472, 473, 143, 143, 143, 143,
+ 143, 143, 474, 143, 143, 143, 143, 143, 47, 47, 47, 47, 47, 47, 47, 223,
+ 475, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 476,
+ 144, 144, 144, 144, 144, 144, 144, 157, 205, 205, 205, 205, 205, 205, 205, 205,
+ 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[4920+(((_hb_ucd_u8[1104+(((_hb_ucd_u16[((_hb_ucd_u8[272+(((_hb_ucd_u8[u>>1>>3>>3>>5])<<5)+((u>>1>>3>>3)&31u))])<<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[6796+(((_hb_ucd_u8[6276+(((_hb_ucd_u8[5844+(((_hb_ucd_u8[5508+(((_hb_ucd_u8[5262+(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[7672+(((_hb_ucd_u8[7448+(((_hb_ucd_u8[7352+(((_hb_ucd_b4(7288+_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[11242+(((_hb_ucd_u8[10314+(((_hb_ucd_u8[8938+(((_hb_ucd_u8[8362+(((_hb_ucd_u8[7912+(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[1536+(((_hb_ucd_u8[12544+(((_hb_ucd_u8[12162+(u>>4>>5)])<<5)+((u>>4)&31u))])<<4)+((u)&15u))]:0;
+}
+
+#endif
+
+
+#endif /* HB_UCD_TABLE_HH */
+
+/* == End of generated table == */
diff --git a/thirdparty/harfbuzz/src/hb-ucd.cc b/thirdparty/harfbuzz/src/hb-ucd.cc
new file mode 100644
index 0000000000..ad72a26c04
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-ucd.cc
@@ -0,0 +1,248 @@
+/*
+ * 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)
+{
+ if (_hb_ucd_compose_hangul (a, b, ab)) return true;
+
+ hb_codepoint_t u = 0;
+
+ if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u)
+ {
+ 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
+ {
+ 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 (likely (!i)) return false;
+ i--;
+
+ if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map))
+ {
+ if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map))
+ *a = _hb_ucd_dm1_p0_map[i];
+ else
+ {
+ 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);
+
+ if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map))
+ {
+ 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);
+
+ uint64_t v = _hb_ucd_dm2_u64_map[i];
+ *a = HB_CODEPOINT_DECODE3_1 (v);
+ *b = HB_CODEPOINT_DECODE3_2 (v);
+ return true;
+}
+
+
+#if HB_USE_ATEXIT
+static void free_static_ucd_funcs ();
+#endif
+
+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);
+
+#if HB_USE_ATEXIT
+ atexit (free_static_ucd_funcs);
+#endif
+
+ return funcs;
+ }
+} static_ucd_funcs;
+
+#if HB_USE_ATEXIT
+static
+void free_static_ucd_funcs ()
+{
+ static_ucd_funcs.free_instance ();
+}
+#endif
+
+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/thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh b/thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh
new file mode 100644
index 0000000000..eb7776eecb
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh
@@ -0,0 +1,78 @@
+/* == 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: 2020-01-28, 20:52:38 GMT
+ * # © 2020 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 http://www.unicode.org/terms_of_use.html
+ * #
+ * # Emoji Data for UTS #51
+ * # Version: 13.0
+ * #
+ * # For documentation and usage, see http://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[448] =
+{
+ 0, 0, 0, 0, 33, 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, 84,118,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 3,
+ 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 7, 9, 10, 11, 0,
+ 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0,
+ 7, 7, 7, 14, 15, 16, 17, 18, 19, 20, 7, 7, 7, 7, 7, 21,
+ 7, 7, 7, 7, 22, 23, 7, 7, 7, 24, 7, 14, 0, 25, 0, 26,
+ 27, 28, 29, 14, 30, 31, 7, 7, 7, 7, 7, 14, 0, 0, 0, 0,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240, 1, 0, 2, 0, 0,
+ 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 7, 3,
+ 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56,
+ 159,255,243,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 31, 0,255,255,255,255,255,255, 31,255, 3, 0, 0, 0, 8, 0,
+ 0, 0, 24, 0,120, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 16, 0, 96, 0, 0, 8, 0, 0, 0, 0,
+ 255,255,255,255,255,255,255,127, 0, 96, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,240, 1, 64, 0, 0,254, 3, 0,224,255,255,
+ 255,255,255,255, 31, 0, 0, 0,254,127, 0, 0, 0, 0,252,115,
+ 0,254,255,255,255,255,255,255,255,255,255,255,255,255,255, 3,
+ 255,255,255,255,255,255,255, 31,192,255,255,255,255,255,255,255,
+ 255,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,240,127,
+ 0, 0,224,255,255,255,255,127, 0,112, 0, 0, 0, 0, 0, 0,
+ 0,127, 0,124, 0, 0, 0, 0, 0,127, 0, 0, 0,192,255,255,
+ 0,240,255,255,255,255,255,243,159,255,255,255,255,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<131069u?_hb_emoji_b1(192+_hb_emoji_u8,((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>6>>4))<<4)+((u>>6)&15u))])<<6)+((u)&63u)):0;
+}
+
+
+#endif /* HB_UNICODE_EMOJI_TABLE_HH */
+
+/* == End of generated table == */
diff --git a/thirdparty/harfbuzz/src/hb-unicode.cc b/thirdparty/harfbuzz/src/hb-unicode.cc
new file mode 100644
index 0000000000..36070a7f18
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-unicode.cc
@@ -0,0 +1,586 @@
+/*
+ * 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.
+ * Client can pass its own Unicode functions to HarfBuzz, or access
+ * the built-in Unicode functions that come with HarfBuzz.
+ *
+ * With the Unicode functions, one can query variour Unicode character
+ * properties, such as General Category, Script, Combining Class, etc.
+ **/
+
+
+/*
+ * 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_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, check the code."
+#endif
+
+/**
+ * hb_unicode_funcs_create: (Xconstructor)
+ * @parent: (nullable):
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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:
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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: Unicode functions.
+ *
+ *
+ *
+ * Return value: (transfer full):
+ *
+ * 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: Unicode functions.
+ *
+ *
+ *
+ * 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);
+
+ free (ufuncs);
+}
+
+/**
+ * hb_unicode_funcs_set_user_data: (skip)
+ * @ufuncs: Unicode functions.
+ * @key:
+ * @data:
+ * @destroy:
+ * @replace:
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: Unicode functions.
+ * @key:
+ *
+ *
+ *
+ * Return value: (transfer none):
+ *
+ * Since: 0.9.2
+ **/
+void *
+hb_unicode_funcs_get_user_data (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: Unicode functions.
+ *
+ *
+ *
+ * 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: Unicode functions.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: Unicode functions.
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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)) \
+ return; \
+ \
+ if (ufuncs->destroy.name) \
+ ufuncs->destroy.name (ufuncs->user_data.name); \
+ \
+ if (func) { \
+ ufuncs->func.name = func; \
+ ufuncs->user_data.name = user_data; \
+ ufuncs->destroy.name = destroy; \
+ } else { \
+ ufuncs->func.name = ufuncs->parent->func.name; \
+ ufuncs->user_data.name = ufuncs->parent->user_data.name; \
+ ufuncs->destroy.name = nullptr; \
+ } \
+}
+
+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: Unicode functions.
+ * @a:
+ * @b:
+ * @ab: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: Unicode functions.
+ * @ab:
+ * @a: (out):
+ * @b: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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: Unicode functions.
+ * @u:
+ * @decomposed: (out):
+ *
+ *
+ *
+ * Return value:
+ *
+ * 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/thirdparty/harfbuzz/src/hb-unicode.h b/thirdparty/harfbuzz/src/hb-unicode.h
new file mode 100644
index 0000000000..61b1b0ba1f
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-unicode.h
@@ -0,0 +1,404 @@
+/*
+ * 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
+ */
+
+#ifndef HB_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_UNICODE_H
+#define HB_UNICODE_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+/**
+ * HB_UNICODE_MAX
+ *
+ * Since: 1.9.0
+ **/
+#define HB_UNICODE_MAX 0x10FFFFu
+
+
+/* hb_unicode_general_category_t */
+
+/* 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 */
+
+/* Note: newer versions of Unicode may add new values. Clients should be ready to handle
+ * any value in the 0..254 range being returned from hb_unicode_combining_class().
+ */
+
+/* Unicode Character Database property: Canonical_Combining_Class (ccc) */
+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_CCC133 = 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
+ */
+
+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 (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 */
+
+typedef hb_unicode_combining_class_t (*hb_unicode_combining_class_func_t) (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode,
+ void *user_data);
+typedef hb_unicode_general_category_t (*hb_unicode_general_category_func_t) (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode,
+ void *user_data);
+typedef hb_codepoint_t (*hb_unicode_mirroring_func_t) (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode,
+ void *user_data);
+typedef hb_script_t (*hb_unicode_script_func_t) (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode,
+ void *user_data);
+
+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);
+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);
+
+/* setters */
+
+/**
+ * hb_unicode_funcs_set_combining_class_func:
+ * @ufuncs: a Unicode function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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 function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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 function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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 function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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 function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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 function structure
+ * @func: (closure user_data) (destroy destroy) (scope notified):
+ * @user_data:
+ * @destroy:
+ *
+ *
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * 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:
+ *
+ * Since: 0.9.2
+ **/
+HB_EXTERN hb_codepoint_t
+hb_unicode_mirroring (hb_unicode_funcs_t *ufuncs,
+ hb_codepoint_t unicode);
+
+/**
+ * hb_unicode_script:
+ *
+ * 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/thirdparty/harfbuzz/src/hb-unicode.hh b/thirdparty/harfbuzz/src/hb-unicode.hh
new file mode 100644
index 0000000000..34d66d7aa3
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-unicode.hh
@@ -0,0 +1,398 @@
+/*
+ * 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)
+ {
+ /* XXX This hack belongs to the USE shaper (for Tai Tham):
+ * Reorder SAKOT to ensure it comes after any tone marks. */
+ if (unlikely (u == 0x1A60u)) return 254;
+
+ /* XXX This hack belongs to the Tibetan shaper:
+ * 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 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 7.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
+ * 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 */
+#define HB_MODIFIED_COMBINING_CLASS_CCC19 14 /* holam */
+#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)))
+
+
+/*
+ * 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/thirdparty/harfbuzz/src/hb-uniscribe.cc b/thirdparty/harfbuzz/src/hb-uniscribe.cc
new file mode 100644
index 0000000000..48a5dc50ad
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-uniscribe.cc
@@ -0,0 +1,1047 @@
+/*
+ * 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-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;
+ }
+ }
+};
+
+#if HB_USE_ATEXIT
+static void free_static_uniscribe_shaper_funcs ();
+#endif
+
+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 *) calloc (1, sizeof (hb_uniscribe_shaper_funcs_t));
+ if (unlikely (!funcs))
+ return nullptr;
+
+ funcs->init ();
+
+#if HB_USE_ATEXIT
+ atexit (free_static_uniscribe_shaper_funcs);
+#endif
+
+ return funcs;
+ }
+ static void destroy (hb_uniscribe_shaper_funcs_t *p)
+ {
+ free ((void *) p);
+ }
+ static hb_uniscribe_shaper_funcs_t *get_null ()
+ {
+ return nullptr;
+ }
+} static_uniscribe_shaper_funcs;
+
+#if HB_USE_ATEXIT
+static
+void free_static_uniscribe_shaper_funcs ()
+{
+ static_uniscribe_shaper_funcs.free_instance ();
+}
+#endif
+
+static hb_uniscribe_shaper_funcs_t *
+hb_uniscribe_shaper_get_funcs ()
+{
+ return static_uniscribe_shaper_funcs.get_unconst ();
+}
+
+
+struct active_feature_t {
+ OPENTYPE_FEATURE_RECORD 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.tagFeature < b->rec.tagFeature ? -1 : a->rec.tagFeature > b->rec.tagFeature ? 1 :
+ a->order < b->order ? -1 : a->order > b->order ? 1 :
+ a->rec.lParameter < b->rec.lParameter ? -1 : a->rec.lParameter > b->rec.lParameter ? 1 :
+ 0;
+ }
+ bool operator== (const active_feature_t *f)
+ { 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 {
+ TEXTRANGE_PROPERTIES props;
+ unsigned int index_first; /* == start */
+ unsigned int index_last; /* == end - 1 */
+};
+
+
+/*
+ * 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 = calloc (1, new_length);
+ if (!new_sfnt_data)
+ {
+ hb_blob_destroy (blob);
+ return nullptr;
+ }
+
+ 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. */
+ {
+ 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, 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 *) calloc (1, sizeof (hb_uniscribe_face_data_t));
+ if (unlikely (!data))
+ return nullptr;
+
+ data->funcs = hb_uniscribe_shaper_get_funcs ();
+ if (unlikely (!data->funcs))
+ {
+ 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))
+ {
+ 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");
+ free (data);
+ return nullptr;
+ }
+
+ return data;
+}
+
+void
+_hb_uniscribe_shaper_face_data_destroy (hb_uniscribe_face_data_t *data)
+{
+ RemoveFontMemResourceEx (data->fh);
+ 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)
+{
+ memset (lf, 0, sizeof (*lf));
+ lf->lfHeight = - (int) font_size;
+ lf->lfCharSet = DEFAULT_CHARSET;
+
+ 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 *) 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);
+ 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;
+
+ /*
+ * Set up features.
+ */
+ hb_vector_t<OPENTYPE_FEATURE_RECORD> feature_records;
+ hb_vector_t<range_record_t> range_records;
+ 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;
+ feature.rec.tagFeature = hb_uint32_swap (features[i].tag);
+ feature.rec.lParameter = features[i].value;
+ 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.tagFeature = 0;
+ feature.rec.lParameter = 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 ();
+
+ unsigned int offset = feature_records.length;
+
+ active_features.qsort ();
+ for (unsigned int j = 0; j < active_features.length; j++)
+ {
+ if (!j || active_features[j].rec.tagFeature != feature_records[feature_records.length - 1].tagFeature)
+ {
+ feature_records.push (active_features[j].rec);
+ }
+ else
+ {
+ /* Overrides value for existing feature. */
+ feature_records[feature_records.length - 1].lParameter = active_features[j].rec.lParameter;
+ }
+ }
+
+ /* Will convert to pointer after all is ready, since feature_records.array
+ * may move as we grow it. */
+ range->props.potfRecords = reinterpret_cast<OPENTYPE_FEATURE_RECORD *> (offset);
+ range->props.cotfRecords = 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
+ {
+ active_feature_t *feature = active_features.find (&event->feature);
+ if (feature)
+ active_features.remove (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++)
+ {
+ range_record_t *range = &range_records[i];
+ range->props.potfRecords = (OPENTYPE_FEATURE_RECORD *) feature_records + reinterpret_cast<uintptr_t> (range->props.potfRecords);
+ }
+ }
+
+#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", 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);
+ hb_vector_t<TEXTRANGE_PROPERTIES*> range_properties;
+ hb_vector_t<int> 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 (num_features)
+ {
+ range_properties.shrink (0);
+ range_char_counts.shrink (0);
+
+ range_record_t *last_range = &range_records[0];
+
+ for (unsigned int k = chars_offset; k < chars_offset + item_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_properties.length ||
+ &range->props != range_properties[range_properties.length - 1])
+ {
+ TEXTRANGE_PROPERTIES **props = range_properties.push ();
+ int *c = range_char_counts.push ();
+ if (unlikely (!props || !c))
+ {
+ range_properties.shrink (0);
+ range_char_counts.shrink (0);
+ break;
+ }
+ *props = &range->props;
+ *c = 1;
+ }
+ else
+ {
+ range_char_counts[range_char_counts.length - 1]++;
+ }
+
+ last_range = range;
+ }
+ }
+
+ /* 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,
+ range_char_counts.arrayZ,
+ 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", 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,
+ range_char_counts.arrayZ,
+ 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", 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->unsafe_to_break_all ();
+
+ /* Wow, done! */
+ return true;
+}
+
+
+#endif
diff --git a/thirdparty/harfbuzz/src/hb-uniscribe.h b/thirdparty/harfbuzz/src/hb-uniscribe.h
new file mode 100644
index 0000000000..4e4ef9986a
--- /dev/null
+++ b/thirdparty/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/thirdparty/harfbuzz/src/hb-utf.hh b/thirdparty/harfbuzz/src/hb-utf.hh
new file mode 100644
index 0000000000..ff5712d16d
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-utf.hh
@@ -0,0 +1,453 @@
+/*
+ * 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 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 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 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 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 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++;
+ 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;
+ }
+};
+
+#endif /* HB_UTF_HH */
diff --git a/thirdparty/harfbuzz/src/hb-vector.hh b/thirdparty/harfbuzz/src/hb-vector.hh
new file mode 100644
index 0000000000..079b94a6b4
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-vector.hh
@@ -0,0 +1,313 @@
+/*
+ * 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-null.hh"
+
+
+template <typename Type>
+struct hb_vector_t
+{
+ typedef Type item_t;
+ static constexpr unsigned item_size = hb_static_size (Type);
+
+ hb_vector_t () { init (); }
+ hb_vector_t (const hb_vector_t &o)
+ {
+ init ();
+ alloc (o.length);
+ hb_copy (o, *this);
+ }
+ hb_vector_t (hb_vector_t &&o)
+ {
+ allocated = o.allocated;
+ length = o.length;
+ arrayZ = o.arrayZ;
+ o.init ();
+ }
+ ~hb_vector_t () { fini (); }
+
+ private:
+ int allocated; /* == -1 means allocation failed. */
+ public:
+ unsigned int length;
+ public:
+ Type *arrayZ;
+
+ void init ()
+ {
+ allocated = length = 0;
+ arrayZ = nullptr;
+ }
+
+ void fini ()
+ {
+ free (arrayZ);
+ init ();
+ }
+ void fini_deep ()
+ {
+ unsigned int count = length;
+ for (unsigned int i = 0; i < count; i++)
+ arrayZ[i].fini ();
+ fini ();
+ }
+
+ void reset () { resize (0); }
+
+ hb_vector_t& operator = (const hb_vector_t &o)
+ {
+ reset ();
+ alloc (o.length);
+ hb_copy (o, *this);
+ return *this;
+ }
+ hb_vector_t& operator = (hb_vector_t &&o)
+ {
+ fini ();
+ allocated = o.allocated;
+ length = o.length;
+ arrayZ = o.arrayZ;
+ o.init ();
+ return *this;
+ }
+
+ hb_bytes_t as_bytes () const
+ { return hb_bytes_t ((const char *) arrayZ, length * item_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 (hb_forward<T> (v)); return *this; }
+
+ hb_array_t< Type> as_array () { return hb_array (arrayZ, length); }
+ hb_array_t<const Type> as_array () const { return hb_array (arrayZ, 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 (); }
+
+ hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int count) const
+ { return as_array ().sub_array (start_offset, count); }
+ hb_array_t<const Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */) const
+ { return as_array ().sub_array (start_offset, count); }
+ hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int count)
+ { return as_array ().sub_array (start_offset, count); }
+ hb_array_t<Type> sub_array (unsigned int start_offset, unsigned int *count = nullptr /* IN/OUT */)
+ { return as_array ().sub_array (start_offset, count); }
+
+ 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 &Crap (Type);
+ return &arrayZ[length - 1];
+ }
+ template <typename T>
+ Type *push (T&& v)
+ {
+ Type *p = push ();
+ *p = hb_forward<T> (v);
+ return p;
+ }
+
+ bool in_error () const { return allocated < 0; }
+
+ /* Allocate for size but don't adjust length. */
+ bool alloc (unsigned int size)
+ {
+ if (unlikely (allocated < 0))
+ return false;
+
+ if (likely (size <= (unsigned) allocated))
+ return true;
+
+ /* Reallocate */
+
+ unsigned int new_allocated = allocated;
+ while (size >= new_allocated)
+ new_allocated += (new_allocated >> 1) + 8;
+
+ Type *new_array = nullptr;
+ bool overflows =
+ (int) new_allocated < 0 ||
+ (new_allocated < (unsigned) allocated) ||
+ hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
+ if (likely (!overflows))
+ new_array = (Type *) realloc (arrayZ, new_allocated * sizeof (Type));
+
+ if (unlikely (!new_array))
+ {
+ allocated = -1;
+ return false;
+ }
+
+ arrayZ = new_array;
+ allocated = new_allocated;
+
+ return true;
+ }
+
+ bool resize (int size_)
+ {
+ unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
+ if (!alloc (size))
+ return false;
+
+ if (size > length)
+ memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
+
+ length = size;
+ return true;
+ }
+
+ Type pop ()
+ {
+ if (!length) return Null (Type);
+ return hb_move (arrayZ[--length]); /* Does this move actually work? */
+ }
+
+ void remove (unsigned int i)
+ {
+ if (unlikely (i >= length))
+ return;
+ memmove (static_cast<void *> (&arrayZ[i]),
+ static_cast<void *> (&arrayZ[i + 1]),
+ (length - i - 1) * sizeof (Type));
+ length--;
+ }
+
+ void shrink (int size_)
+ {
+ unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
+ if (size < length)
+ length = size;
+ }
+
+ template <typename T>
+ Type *find (T v)
+ {
+ for (unsigned int i = 0; i < length; i++)
+ if (arrayZ[i] == v)
+ return &arrayZ[i];
+ return nullptr;
+ }
+ template <typename T>
+ const Type *find (T v) const
+ {
+ for (unsigned int i = 0; i < length; i++)
+ if (arrayZ[i] == v)
+ return &arrayZ[i];
+ return nullptr;
+ }
+
+ void qsort (int (*cmp)(const void*, const void*))
+ { as_array ().qsort (cmp); }
+ void qsort (unsigned int start = 0, unsigned int end = (unsigned int) -1)
+ { as_array ().qsort (start, end); }
+
+ 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); }
+};
+
+template <typename Type>
+struct hb_sorted_vector_t : hb_vector_t<Type>
+{
+ hb_sorted_array_t< Type> as_array () { return hb_sorted_array (this->arrayZ, this->length); }
+ hb_sorted_array_t<const Type> as_array () const { return hb_sorted_array (this->arrayZ, this->length); }
+
+ /* Iterator. */
+ typedef hb_sorted_array_t<const Type> const_iter_t;
+ typedef hb_sorted_array_t< Type> iter_t;
+ const_iter_t iter () const { return as_array (); }
+ const_iter_t citer () const { return as_array (); }
+ iter_t iter () { return as_array (); }
+ operator iter_t () { return iter (); }
+ operator const_iter_t () const { return iter (); }
+
+ template <typename T>
+ Type *bsearch (const T &x, Type *not_found = nullptr)
+ { return as_array ().bsearch (x, not_found); }
+ template <typename T>
+ const Type *bsearch (const T &x, const Type *not_found = nullptr) const
+ { return as_array ().bsearch (x, not_found); }
+ template <typename T>
+ bool bfind (const T &x, unsigned int *i = nullptr,
+ hb_bfind_not_found_t not_found = HB_BFIND_NOT_FOUND_DONT_STORE,
+ unsigned int to_store = (unsigned int) -1) const
+ { return as_array ().bfind (x, i, not_found, to_store); }
+};
+
+#endif /* HB_VECTOR_HH */
diff --git a/thirdparty/harfbuzz/src/hb-version.h b/thirdparty/harfbuzz/src/hb-version.h
new file mode 100644
index 0000000000..92d61b8cdb
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb-version.h
@@ -0,0 +1,66 @@
+/*
+ * 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_H_IN
+#error "Include <hb.h> instead."
+#endif
+
+#ifndef HB_VERSION_H
+#define HB_VERSION_H
+
+#include "hb-common.h"
+
+HB_BEGIN_DECLS
+
+
+#define HB_VERSION_MAJOR 2
+#define HB_VERSION_MINOR 7
+#define HB_VERSION_MICRO 2
+
+#define HB_VERSION_STRING "2.7.2"
+
+#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/thirdparty/harfbuzz/src/hb.h b/thirdparty/harfbuzz/src/hb.h
new file mode 100644
index 0000000000..360686ca68
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb.h
@@ -0,0 +1,50 @@
+/*
+ * 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-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/thirdparty/harfbuzz/src/hb.hh b/thirdparty/harfbuzz/src/hb.hh
new file mode 100644
index 0000000000..274a0e98db
--- /dev/null
+++ b/thirdparty/harfbuzz/src/hb.hh
@@ -0,0 +1,634 @@
+/*
+ * 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 "-Wc++11-narrowing"
+#pragma GCC diagnostic error "-Wcast-align"
+#pragma GCC diagnostic error "-Wcast-function-type"
+#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 "-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 "-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 "-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 "-Wformat-signedness" // TODO fix
+#pragma GCC diagnostic ignored "-Wshadow" // TODO fix
+#pragma GCC diagnostic ignored "-Wunsafe-loop-optimizations" // TODO fix
+#pragma GCC diagnostic ignored "-Wunused-parameter" // TODO fix
+#endif
+
+/* Ignored intentionally. */
+#ifndef HB_NO_PRAGMA_GCC_DIAGNOSTIC_IGNORED
+#pragma GCC diagnostic ignored "-Wclass-memaccess"
+#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 "-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"
+
+
+/*
+ * 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 <limits.h>
+#include <math.h>
+#include <float.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#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 malloc hb_malloc_impl
+#define calloc hb_calloc_impl
+#define realloc hb_realloc_impl
+#define free hb_free_impl
+#endif
+
+
+/*
+ * Compiler attributes
+ */
+
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__)
+#define likely(expr) (__builtin_expect (!!(expr), 1))
+#define unlikely(expr) (__builtin_expect (!!(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_PURE_FUNC __attribute__((pure))
+#define HB_CONST_FUNC __attribute__((const))
+#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#else
+#define HB_PURE_FUNC
+#define HB_CONST_FUNC
+#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
+
+/*
+ * 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
+#define hb_success_t HB_NODISCARD bool
+
+/* 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 <errno.h>
+#else
+static int HB_UNUSED _hb_errno = 0;
+# undef errno
+# define errno _hb_errno
+#endif
+
+#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
+#ifdef HB_NO_ATEXIT
+# undef HB_USE_ATEXIT
+#endif
+#ifndef HB_USE_ATEXIT
+# define HB_USE_ATEXIT 0
+#endif
+
+#define HB_STMT_START do
+#define HB_STMT_END while (0)
+
+/* Static-assert as expression. */
+template <unsigned int cond> class hb_assert_constant_t;
+template <> class hb_assert_constant_t<1> {};
+#define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * (unsigned int) sizeof (hb_assert_constant_t<_cond>))
+
+/* Lets assert int types. Saves trouble down the road. */
+static_assert ((sizeof (int8_t) == 1), "");
+static_assert ((sizeof (uint8_t) == 1), "");
+static_assert ((sizeof (int16_t) == 2), "");
+static_assert ((sizeof (uint16_t) == 2), "");
+static_assert ((sizeof (int32_t) == 4), "");
+static_assert ((sizeof (uint32_t) == 4), "");
+static_assert ((sizeof (int64_t) == 8), "");
+static_assert ((sizeof (uint64_t) == 8), "");
+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), "");
+
+#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
+
+
+/* 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 T operator | (T l, T r) { return T ((unsigned) l | (unsigned) r); } \
+ static inline T operator & (T l, T r) { return T ((unsigned) l & (unsigned) r); } \
+ static inline T operator ^ (T l, T r) { return T ((unsigned) l ^ (unsigned) r); } \
+ static inline T operator ~ (T r) { return T (~(unsigned int) 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) (ASSERT_STATIC_EXPR_ZERO ((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) (ASSERT_STATIC_EXPR_ZERO ((x) < (y)) + FLAG(y+1) - FLAG(x))
+#define FLAG64(x) (ASSERT_STATIC_EXPR_ZERO ((unsigned)(x) < 64) + (((uint64_t) 1ULL) << (unsigned)(x)))
+#define FLAG64_UNSAFE(x) ((unsigned)(x) < 64 ? (((uint64_t) 1ULL) << (unsigned)(x)) : 0)
+
+
+/* Size signifying variable-sized array */
+#ifndef HB_VAR_ARRAY
+#define HB_VAR_ARRAY 1
+#endif
+
+static inline float
+_hb_roundf (float x) { return floorf (x + .5f); }
+#define roundf(x) _hb_roundf(x)
+
+/* Endian swap, used in Windows related backends */
+static inline uint16_t hb_uint16_swap (const uint16_t v)
+{ return (v >> 8) | (v << 8); }
+static inline uint32_t hb_uint32_swap (const uint32_t v)
+{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
+
+/*
+ * Big-endian integers. Here because fundamental.
+ */
+
+template <typename Type, int Bytes> struct BEInt;
+
+template <typename Type>
+struct BEInt<Type, 1>
+{
+ public:
+ BEInt<Type, 1>& operator = (Type V)
+ {
+ v = V;
+ return *this;
+ }
+ operator Type () const { return v; }
+ private: uint8_t v;
+};
+template <typename Type>
+struct BEInt<Type, 2>
+{
+ public:
+ BEInt<Type, 2>& operator = (Type V)
+ {
+ v[0] = (V >> 8) & 0xFF;
+ v[1] = (V ) & 0xFF;
+ return *this;
+ }
+ operator Type () const
+ {
+#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \
+ defined(__BYTE_ORDER) && \
+ (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
+ /* Spoon-feed the compiler a big-endian integer with alignment 1.
+ * https://github.com/harfbuzz/harfbuzz/pull/1398 */
+ struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ return __builtin_bswap16 (((packed_uint16_t *) this)->v);
+#else /* __BYTE_ORDER == __BIG_ENDIAN */
+ return ((packed_uint16_t *) this)->v;
+#endif
+#endif
+ return (v[0] << 8)
+ + (v[1] );
+ }
+ private: uint8_t v[2];
+};
+template <typename Type>
+struct BEInt<Type, 3>
+{
+ public:
+ BEInt<Type, 3>& operator = (Type V)
+ {
+ v[0] = (V >> 16) & 0xFF;
+ v[1] = (V >> 8) & 0xFF;
+ v[2] = (V ) & 0xFF;
+ return *this;
+ }
+ operator Type () const
+ {
+ return (v[0] << 16)
+ + (v[1] << 8)
+ + (v[2] );
+ }
+ private: uint8_t v[3];
+};
+template <typename Type>
+struct BEInt<Type, 4>
+{
+ public:
+ BEInt<Type, 4>& operator = (Type V)
+ {
+ v[0] = (V >> 24) & 0xFF;
+ v[1] = (V >> 16) & 0xFF;
+ v[2] = (V >> 8) & 0xFF;
+ v[3] = (V ) & 0xFF;
+ return *this;
+ }
+ operator Type () const
+ {
+ return (v[0] << 24)
+ + (v[1] << 16)
+ + (v[2] << 8)
+ + (v[3] );
+ }
+ private: uint8_t v[4];
+};
+
+
+/*
+ * For lack of a better place, put Zawgyi script hack here.
+ * https://github.com/harfbuzz/harfbuzz/issues/1162
+ */
+
+#define HB_SCRIPT_MYANMAR_ZAWGYI ((hb_script_t) HB_TAG ('Q','a','a','g'))
+
+
+/* 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-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 */