Просмотр исходного кода

[Complex Text Layouts] Add third-party TextServer dependencies (ICU, HarfBuzz, Graphite).

bruvzg 5 лет назад
Родитель
Сommit
b9f441e81e
100 измененных файлов с 27035 добавлено и 4 удалено
  1. 102 0
      COPYRIGHT.txt
  2. 3 0
      SConstruct
  3. 419 0
      modules/text_server_adv/SCsub
  4. 5 0
      modules/text_server_adv/config.py
  5. 63 0
      modules/text_server_adv/icu_data/icudata_stub.cpp
  6. 1 1
      platform/android/detect.py
  7. 2 0
      platform/javascript/detect.py
  8. 18 1
      platform/linuxbsd/detect.py
  9. 29 2
      platform/server/detect.py
  10. 33 0
      thirdparty/README.md
  11. 26 0
      thirdparty/graphite/COPYING
  12. 238 0
      thirdparty/graphite/ChangeLog
  13. 389 0
      thirdparty/graphite/include/graphite2/Font.h
  14. 85 0
      thirdparty/graphite/include/graphite2/Log.h
  15. 461 0
      thirdparty/graphite/include/graphite2/Segment.h
  16. 79 0
      thirdparty/graphite/include/graphite2/Types.h
  17. 155 0
      thirdparty/graphite/src/CmapCache.cpp
  18. 782 0
      thirdparty/graphite/src/Code.cpp
  19. 1115 0
      thirdparty/graphite/src/Collider.cpp
  20. 125 0
      thirdparty/graphite/src/Decompressor.cpp
  21. 366 0
      thirdparty/graphite/src/Face.cpp
  22. 293 0
      thirdparty/graphite/src/FeatureMap.cpp
  23. 115 0
      thirdparty/graphite/src/FileFace.cpp
  24. 58 0
      thirdparty/graphite/src/Font.cpp
  25. 492 0
      thirdparty/graphite/src/GlyphCache.cpp
  26. 48 0
      thirdparty/graphite/src/GlyphFace.cpp
  27. 298 0
      thirdparty/graphite/src/Intervals.cpp
  28. 282 0
      thirdparty/graphite/src/Justifier.cpp
  29. 254 0
      thirdparty/graphite/src/NameTable.cpp
  30. 1107 0
      thirdparty/graphite/src/Pass.cpp
  31. 97 0
      thirdparty/graphite/src/Position.cpp
  32. 423 0
      thirdparty/graphite/src/Segment.cpp
  33. 439 0
      thirdparty/graphite/src/Silf.cpp
  34. 529 0
      thirdparty/graphite/src/Slot.cpp
  35. 62 0
      thirdparty/graphite/src/Sparse.cpp
  36. 2053 0
      thirdparty/graphite/src/TtfUtil.cpp
  37. 45 0
      thirdparty/graphite/src/UtfCodec.cpp
  38. 138 0
      thirdparty/graphite/src/call_machine.cpp
  39. 140 0
      thirdparty/graphite/src/direct_machine.cpp
  40. 65 0
      thirdparty/graphite/src/gr_char_info.cpp
  41. 267 0
      thirdparty/graphite/src/gr_face.cpp
  42. 138 0
      thirdparty/graphite/src/gr_features.cpp
  43. 74 0
      thirdparty/graphite/src/gr_font.cpp
  44. 267 0
      thirdparty/graphite/src/gr_logging.cpp
  45. 175 0
      thirdparty/graphite/src/gr_segment.cpp
  46. 173 0
      thirdparty/graphite/src/gr_slot.cpp
  47. 66 0
      thirdparty/graphite/src/inc/CharInfo.h
  48. 82 0
      thirdparty/graphite/src/inc/CmapCache.h
  49. 171 0
      thirdparty/graphite/src/inc/Code.h
  50. 245 0
      thirdparty/graphite/src/inc/Collider.h
  51. 104 0
      thirdparty/graphite/src/inc/Compression.h
  52. 54 0
      thirdparty/graphite/src/inc/Decompressor.h
  53. 111 0
      thirdparty/graphite/src/inc/Endian.h
  54. 134 0
      thirdparty/graphite/src/inc/Error.h
  55. 225 0
      thirdparty/graphite/src/inc/Face.h
  56. 198 0
      thirdparty/graphite/src/inc/FeatureMap.h
  57. 68 0
      thirdparty/graphite/src/inc/FeatureVal.h
  58. 80 0
      thirdparty/graphite/src/inc/FileFace.h
  59. 90 0
      thirdparty/graphite/src/inc/Font.h
  60. 223 0
      thirdparty/graphite/src/inc/GlyphCache.h
  61. 83 0
      thirdparty/graphite/src/inc/GlyphFace.h
  62. 234 0
      thirdparty/graphite/src/inc/Intervals.h
  63. 168 0
      thirdparty/graphite/src/inc/List.h
  64. 207 0
      thirdparty/graphite/src/inc/Machine.h
  65. 199 0
      thirdparty/graphite/src/inc/Main.h
  66. 65 0
      thirdparty/graphite/src/inc/NameTable.h
  67. 118 0
      thirdparty/graphite/src/inc/Pass.h
  68. 68 0
      thirdparty/graphite/src/inc/Position.h
  69. 305 0
      thirdparty/graphite/src/inc/Rule.h
  70. 236 0
      thirdparty/graphite/src/inc/Segment.h
  71. 128 0
      thirdparty/graphite/src/inc/Silf.h
  72. 170 0
      thirdparty/graphite/src/inc/Slot.h
  73. 168 0
      thirdparty/graphite/src/inc/Sparse.h
  74. 419 0
      thirdparty/graphite/src/inc/TtfTypes.h
  75. 208 0
      thirdparty/graphite/src/inc/TtfUtil.h
  76. 251 0
      thirdparty/graphite/src/inc/UtfCodec.h
  77. 150 0
      thirdparty/graphite/src/inc/bits.h
  78. 89 0
      thirdparty/graphite/src/inc/debug.h
  79. 178 0
      thirdparty/graphite/src/inc/json.h
  80. 450 0
      thirdparty/graphite/src/inc/locale2lcid.h
  81. 124 0
      thirdparty/graphite/src/inc/opcode_table.h
  82. 691 0
      thirdparty/graphite/src/inc/opcodes.h
  83. 147 0
      thirdparty/graphite/src/json.cpp
  84. 14 0
      thirdparty/harfbuzz/AUTHORS
  85. 38 0
      thirdparty/harfbuzz/COPYING
  86. 2412 0
      thirdparty/harfbuzz/NEWS
  87. 7 0
      thirdparty/harfbuzz/THANKS
  88. 98 0
      thirdparty/harfbuzz/src/hb-aat-layout-ankr-table.hh
  89. 158 0
      thirdparty/harfbuzz/src/hb-aat-layout-bsln-table.hh
  90. 840 0
      thirdparty/harfbuzz/src/hb-aat-layout-common.hh
  91. 222 0
      thirdparty/harfbuzz/src/hb-aat-layout-feat-table.hh
  92. 417 0
      thirdparty/harfbuzz/src/hb-aat-layout-just-table.hh
  93. 999 0
      thirdparty/harfbuzz/src/hb-aat-layout-kerx-table.hh
  94. 1157 0
      thirdparty/harfbuzz/src/hb-aat-layout-morx-table.hh
  95. 173 0
      thirdparty/harfbuzz/src/hb-aat-layout-opbd-table.hh
  96. 230 0
      thirdparty/harfbuzz/src/hb-aat-layout-trak-table.hh
  97. 382 0
      thirdparty/harfbuzz/src/hb-aat-layout.cc
  98. 486 0
      thirdparty/harfbuzz/src/hb-aat-layout.h
  99. 75 0
      thirdparty/harfbuzz/src/hb-aat-layout.hh
  100. 92 0
      thirdparty/harfbuzz/src/hb-aat-ltag-table.hh

+ 102 - 0
COPYRIGHT.txt

@@ -174,6 +174,33 @@ Copyright: 2015-2020 Google, Inc.
   2002, NVIDIA Corporation.
 License: glslang
 
+Files: ./thirdparty/graphite/
+Comment: Graphite engine
+Copyright: 2010, SIL International
+License: MPL-2.0
+
+Files: ./thirdparty/harfbuzz/
+Comment: HarfBuzz text shaping library
+Copyright: 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc.
+ 2018,2019,2020 Ebrahim Byagowi
+ 2019,2020 Facebook, Inc.
+ 2012 Mozilla Foundation
+ 2011 Codethink Limited
+ 2008,2010 Nokia Corporation and/or its subsidiary(-ies)
+ 2009 Keith Stribley
+ 2009 Martin Hosken and SIL International
+ 2007 Chris Wilson
+ 2006 Behdad Esfahbod
+ 2005 David Turner
+ 2004,2007,2008,2009,2010 Red Hat, Inc.
+ 1998-2004 David Turner and Werner Lemberg
+License: HarfBuzz
+
+Files: ./thirdparty/icu4c/
+Comment: International Components for Unicode
+Copyright: 1991-2020, Unicode
+License: Unicode
+
 Files: ./thirdparty/jpeg_compressor/
 Comment: jpeg-compressor
 Copyright: 2012, Rich Geldreich
@@ -1180,6 +1207,46 @@ License: FTL
    Robert Wilhelm    <[email protected]>
    Werner Lemberg    <[email protected]>
 
+License: HarfBuzz
+ HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
+ For parts of HarfBuzz that are licensed under different licenses see individual
+ files names COPYING in subdirectories where applicable.
+ .
+ Copyright (C) 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020  Google, Inc.
+ Copyright (C) 2018,2019,2020  Ebrahim Byagowi
+ Copyright (C) 2019,2020  Facebook, Inc.
+ Copyright (C) 2012  Mozilla Foundation
+ Copyright (C) 2011  Codethink Limited
+ Copyright (C) 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
+ Copyright (C) 2009  Keith Stribley
+ Copyright (C) 2009  Martin Hosken and SIL International
+ Copyright (C) 2007  Chris Wilson
+ Copyright (C) 2006  Behdad Esfahbod
+ Copyright (C) 2005  David Turner
+ Copyright (C) 2004,2007,2008,2009,2010  Red Hat, Inc.
+ Copyright (C) 1998-2004  David Turner and Werner Lemberg
+ .
+ For full copyright notices consult the individual files in the package.
+ .
+ .
+ Permission is hereby granted, without written agreement and without
+ license or royalty fees, to use, copy, modify, and distribute this
+ software and its documentation for any purpose, provided that the
+ above copyright notice and the following two paragraphs appear in
+ all copies of this software.
+ .
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
+ .
+ THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
 License: MPL-2.0
  Mozilla Public License Version 2.0
  ==================================
@@ -1650,6 +1717,41 @@ License: Tamsyn
  In no event will the author be held liable for damages arising from the use
  of this font.
 
+License: Unicode
+ COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later)
+ .
+ Copyright (C) 1991-2020 Unicode, Inc. All rights reserved.
+ Distributed under the Terms of Use in https://www.unicode.org/copyright.html.
+ .
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of the Unicode data files and any associated documentation
+ (the "Data Files") or Unicode software and any associated documentation
+ (the "Software") to deal in the Data Files or Software
+ without restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, and/or sell copies of
+ the Data Files or Software, and to permit persons to whom the Data Files
+ or Software are furnished to do so, provided that either
+ (a) this copyright and permission notice appear with all copies
+ of the Data Files or Software, or
+ (b) this copyright and permission notice appear in associated
+ Documentation.
+ .
+ THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
+ NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 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 THE DATA FILES OR SOFTWARE.
+ .
+ Except as contained in this notice, the name of a copyright holder
+ shall not be used in advertising or otherwise to promote the sale,
+ use or other dealings in these Data Files or Software without prior
+ written authorization of the copyright holder.
+
 License: Zlib
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages

+ 3 - 0
SConstruct

@@ -144,6 +144,9 @@ opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundle
 opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True))
 opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType library", True))
 opts.Add(BoolVariable("builtin_glslang", "Use the built-in glslang library", True))
+opts.Add(BoolVariable("builtin_graphite", "Use the built-in Graphite library", True))
+opts.Add(BoolVariable("builtin_harfbuzz", "Use the built-in HarfBuzz library", True))
+opts.Add(BoolVariable("builtin_icu", "Use the built-in ICU library", True))
 opts.Add(BoolVariable("builtin_libogg", "Use the built-in libogg library", True))
 opts.Add(BoolVariable("builtin_libpng", "Use the built-in libpng library", True))
 opts.Add(BoolVariable("builtin_libtheora", "Use the built-in libtheora library", True))

+ 419 - 0
modules/text_server_adv/SCsub

@@ -0,0 +1,419 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+if env["builtin_harfbuzz"]:
+    env_harfbuzz = env_modules.Clone()
+
+    # Thirdparty source files
+    thirdparty_dir = "#thirdparty/harfbuzz/"
+    thirdparty_sources = [
+        "src/hb-aat-layout.cc",
+        "src/hb-aat-map.cc",
+        "src/hb-blob.cc",
+        "src/hb-buffer-serialize.cc",
+        "src/hb-buffer.cc",
+        "src/hb-common.cc",
+        #'src/hb-coretext.cc',
+        #'src/hb-directwrite.cc',
+        "src/hb-draw.cc",
+        "src/hb-face.cc",
+        "src/hb-fallback-shape.cc",
+        "src/hb-font.cc",
+        "src/hb-ft.cc",
+        #'src/hb-gdi.cc',
+        #'src/hb-glib.cc',
+        #'src/hb-gobject-structs.cc',
+        "src/hb-graphite2.cc",
+        "src/hb-icu.cc",
+        "src/hb-map.cc",
+        "src/hb-number.cc",
+        "src/hb-ot-cff1-table.cc",
+        "src/hb-ot-cff2-table.cc",
+        "src/hb-ot-color.cc",
+        "src/hb-ot-face.cc",
+        "src/hb-ot-font.cc",
+        "src/hb-ot-layout.cc",
+        "src/hb-ot-map.cc",
+        "src/hb-ot-math.cc",
+        "src/hb-ot-meta.cc",
+        "src/hb-ot-metrics.cc",
+        "src/hb-ot-name.cc",
+        "src/hb-ot-shape-complex-arabic.cc",
+        "src/hb-ot-shape-complex-default.cc",
+        "src/hb-ot-shape-complex-hangul.cc",
+        "src/hb-ot-shape-complex-hebrew.cc",
+        "src/hb-ot-shape-complex-indic-table.cc",
+        "src/hb-ot-shape-complex-indic.cc",
+        "src/hb-ot-shape-complex-khmer.cc",
+        "src/hb-ot-shape-complex-myanmar.cc",
+        "src/hb-ot-shape-complex-thai.cc",
+        "src/hb-ot-shape-complex-use-table.cc",
+        "src/hb-ot-shape-complex-use.cc",
+        "src/hb-ot-shape-complex-vowel-constraints.cc",
+        "src/hb-ot-shape-fallback.cc",
+        "src/hb-ot-shape-normalize.cc",
+        "src/hb-ot-shape.cc",
+        "src/hb-ot-tag.cc",
+        "src/hb-ot-var.cc",
+        "src/hb-set.cc",
+        "src/hb-shape-plan.cc",
+        "src/hb-shape.cc",
+        "src/hb-shaper.cc",
+        "src/hb-static.cc",
+        "src/hb-style.cc",
+        "src/hb-subset-cff-common.cc",
+        "src/hb-subset-cff1.cc",
+        "src/hb-subset-cff2.cc",
+        "src/hb-subset-input.cc",
+        "src/hb-subset-plan.cc",
+        "src/hb-subset.cc",
+        "src/hb-ucd.cc",
+        "src/hb-unicode.cc",
+        #'src/hb-uniscribe.cc'
+    ]
+    thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+    if env["platform"] == "android" or env["platform"] == "linuxbsd" or env["platform"] == "server":
+        env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
+
+    if env["platform"] == "javascript":
+        if env["threads_enabled"]:
+            env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
+        else:
+            env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"])
+
+    env_harfbuzz.Append(
+        CPPPATH=[
+            "#thirdparty/harfbuzz/src",
+            "#thirdparty/freetype/include",
+            "#thirdparty/graphite/include",
+            "#thirdparty/icu4c/common/",
+        ]
+    )
+    env_harfbuzz.Append(
+        CCFLAGS=["-DHAVE_ICU_BUILTIN", "-DHAVE_ICU", "-DHAVE_FREETYPE", "-DHAVE_GRAPHITE2", "-DGRAPHITE2_STATIC",]
+    )
+    env_harfbuzz.disable_warnings()
+    env_thirdparty = env_harfbuzz.Clone()
+    env_thirdparty.disable_warnings()
+    lib = env_thirdparty.add_library("harfbuzz_builtin", thirdparty_sources)
+
+    # Needs to be appended to arrive after libscene in the linker call,
+    # but we don't want it to arrive *after* system libs, so manual hack
+    # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+    # and then plain strings for system library. We insert between the two.
+    inserted = False
+    for idx, linklib in enumerate(env["LIBS"]):
+        if isinstance(linklib, (str, bytes)):  # first system lib such as "X11", otherwise SCons lib object
+            env["LIBS"].insert(idx, lib)
+            inserted = True
+            break
+    if not inserted:
+        env.Append(LIBS=[lib])
+
+if env["builtin_graphite"]:
+    env_graphite = env_modules.Clone()
+
+    # Thirdparty source files
+    thirdparty_dir = "#thirdparty/graphite/"
+    thirdparty_sources = [
+        "src/gr_char_info.cpp",
+        "src/gr_face.cpp",
+        "src/gr_features.cpp",
+        "src/gr_font.cpp",
+        "src/gr_logging.cpp",
+        "src/gr_segment.cpp",
+        "src/gr_slot.cpp",
+        "src/CmapCache.cpp",
+        "src/Code.cpp",
+        "src/Collider.cpp",
+        "src/Decompressor.cpp",
+        "src/Face.cpp",
+        #'src/FileFace.cpp',
+        "src/FeatureMap.cpp",
+        "src/Font.cpp",
+        "src/GlyphCache.cpp",
+        "src/GlyphFace.cpp",
+        "src/Intervals.cpp",
+        "src/Justifier.cpp",
+        "src/NameTable.cpp",
+        "src/Pass.cpp",
+        "src/Position.cpp",
+        "src/Segment.cpp",
+        "src/Silf.cpp",
+        "src/Slot.cpp",
+        "src/Sparse.cpp",
+        "src/TtfUtil.cpp",
+        "src/UtfCodec.cpp",
+        "src/FileFace.cpp",
+        "src/json.cpp",
+    ]
+    if not env_graphite.msvc:
+        thirdparty_sources += ["src/direct_machine.cpp"]
+    else:
+        thirdparty_sources += ["src/call_machine.cpp"]
+
+    thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+    env_graphite.Append(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"])
+    env_graphite.Append(CCFLAGS=["-DGRAPHITE2_STATIC", "-DGRAPHITE2_NTRACING", "-DGRAPHITE2_NFILEFACE"])
+    env_graphite.disable_warnings()
+    env_thirdparty = env_graphite.Clone()
+    env_thirdparty.disable_warnings()
+    lib = env_thirdparty.add_library("graphite_builtin", thirdparty_sources)
+
+    # Needs to be appended to arrive after libscene in the linker call,
+    # but we don't want it to arrive *after* system libs, so manual hack
+    # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+    # and then plain strings for system library. We insert between the two.
+    inserted = False
+    for idx, linklib in enumerate(env["LIBS"]):
+        if isinstance(linklib, (str, bytes)):  # first system lib such as "X11", otherwise SCons lib object
+            env["LIBS"].insert(idx, lib)
+            inserted = True
+            break
+    if not inserted:
+        env.Append(LIBS=[lib])
+
+if env["builtin_icu"]:
+    env_icu = env_modules.Clone()
+
+    # Thirdparty source files
+    thirdparty_dir = "#thirdparty/icu4c/"
+    # Thirdparty source files
+    thirdparty_sources = [
+        "common/appendable.cpp",
+        "common/bmpset.cpp",
+        "common/brkeng.cpp",
+        "common/brkiter.cpp",
+        "common/bytesinkutil.cpp",
+        "common/bytestream.cpp",
+        "common/bytestrie.cpp",
+        "common/bytestriebuilder.cpp",
+        "common/bytestrieiterator.cpp",
+        "common/caniter.cpp",
+        "common/characterproperties.cpp",
+        "common/chariter.cpp",
+        "common/charstr.cpp",
+        "common/cmemory.cpp",
+        "common/cstr.cpp",
+        "common/cstring.cpp",
+        "common/cwchar.cpp",
+        "common/dictbe.cpp",
+        "common/dictionarydata.cpp",
+        "common/dtintrv.cpp",
+        "common/edits.cpp",
+        "common/errorcode.cpp",
+        "common/filteredbrk.cpp",
+        "common/filterednormalizer2.cpp",
+        "common/icudataver.cpp",
+        "common/icuplug.cpp",
+        "common/loadednormalizer2impl.cpp",
+        "common/localebuilder.cpp",
+        "common/localematcher.cpp",
+        "common/localeprioritylist.cpp",
+        "common/locavailable.cpp",
+        "common/locbased.cpp",
+        "common/locdispnames.cpp",
+        "common/locdistance.cpp",
+        "common/locdspnm.cpp",
+        "common/locid.cpp",
+        "common/loclikely.cpp",
+        "common/loclikelysubtags.cpp",
+        "common/locmap.cpp",
+        "common/locresdata.cpp",
+        "common/locutil.cpp",
+        "common/lsr.cpp",
+        "common/messagepattern.cpp",
+        "common/normalizer2.cpp",
+        "common/normalizer2impl.cpp",
+        "common/normlzr.cpp",
+        "common/parsepos.cpp",
+        "common/patternprops.cpp",
+        "common/pluralmap.cpp",
+        "common/propname.cpp",
+        "common/propsvec.cpp",
+        "common/punycode.cpp",
+        "common/putil.cpp",
+        "common/rbbi.cpp",
+        "common/rbbi_cache.cpp",
+        "common/rbbidata.cpp",
+        "common/rbbinode.cpp",
+        "common/rbbirb.cpp",
+        "common/rbbiscan.cpp",
+        "common/rbbisetb.cpp",
+        "common/rbbistbl.cpp",
+        "common/rbbitblb.cpp",
+        "common/resbund.cpp",
+        "common/resbund_cnv.cpp",
+        "common/resource.cpp",
+        "common/restrace.cpp",
+        "common/ruleiter.cpp",
+        "common/schriter.cpp",
+        "common/serv.cpp",
+        "common/servlk.cpp",
+        "common/servlkf.cpp",
+        "common/servls.cpp",
+        "common/servnotf.cpp",
+        "common/servrbf.cpp",
+        "common/servslkf.cpp",
+        "common/sharedobject.cpp",
+        "common/simpleformatter.cpp",
+        "common/static_unicode_sets.cpp",
+        "common/stringpiece.cpp",
+        "common/stringtriebuilder.cpp",
+        "common/uarrsort.cpp",
+        "common/ubidi.cpp",
+        "common/ubidi_props.cpp",
+        "common/ubidiln.cpp",
+        "common/ubiditransform.cpp",
+        "common/ubidiwrt.cpp",
+        "common/ubrk.cpp",
+        "common/ucase.cpp",
+        "common/ucasemap.cpp",
+        "common/ucasemap_titlecase_brkiter.cpp",
+        "common/ucat.cpp",
+        "common/uchar.cpp",
+        "common/ucharstrie.cpp",
+        "common/ucharstriebuilder.cpp",
+        "common/ucharstrieiterator.cpp",
+        "common/uchriter.cpp",
+        "common/ucln_cmn.cpp",
+        "common/ucmndata.cpp",
+        "common/ucnv.cpp",
+        "common/ucnv2022.cpp",
+        "common/ucnv_bld.cpp",
+        "common/ucnv_cb.cpp",
+        "common/ucnv_cnv.cpp",
+        "common/ucnv_ct.cpp",
+        "common/ucnv_err.cpp",
+        "common/ucnv_ext.cpp",
+        "common/ucnv_io.cpp",
+        "common/ucnv_lmb.cpp",
+        "common/ucnv_set.cpp",
+        "common/ucnv_u16.cpp",
+        "common/ucnv_u32.cpp",
+        "common/ucnv_u7.cpp",
+        "common/ucnv_u8.cpp",
+        "common/ucnvbocu.cpp",
+        "common/ucnvdisp.cpp",
+        "common/ucnvhz.cpp",
+        "common/ucnvisci.cpp",
+        "common/ucnvlat1.cpp",
+        "common/ucnvmbcs.cpp",
+        "common/ucnvscsu.cpp",
+        "common/ucnvsel.cpp",
+        "common/ucol_swp.cpp",
+        "common/ucptrie.cpp",
+        "common/ucurr.cpp",
+        "common/udata.cpp",
+        "common/udatamem.cpp",
+        "common/udataswp.cpp",
+        "common/uenum.cpp",
+        "common/uhash.cpp",
+        "common/uhash_us.cpp",
+        "common/uidna.cpp",
+        "common/uinit.cpp",
+        "common/uinvchar.cpp",
+        "common/uiter.cpp",
+        "common/ulist.cpp",
+        "common/uloc.cpp",
+        "common/uloc_keytype.cpp",
+        "common/uloc_tag.cpp",
+        "common/umapfile.cpp",
+        "common/umath.cpp",
+        "common/umutablecptrie.cpp",
+        "common/umutex.cpp",
+        "common/unames.cpp",
+        "common/unifiedcache.cpp",
+        "common/unifilt.cpp",
+        "common/unifunct.cpp",
+        "common/uniset.cpp",
+        "common/uniset_closure.cpp",
+        "common/uniset_props.cpp",
+        "common/unisetspan.cpp",
+        "common/unistr.cpp",
+        "common/unistr_case.cpp",
+        "common/unistr_case_locale.cpp",
+        "common/unistr_cnv.cpp",
+        "common/unistr_props.cpp",
+        "common/unistr_titlecase_brkiter.cpp",
+        "common/unorm.cpp",
+        "common/unormcmp.cpp",
+        "common/uobject.cpp",
+        "common/uprops.cpp",
+        "common/ures_cnv.cpp",
+        "common/uresbund.cpp",
+        "common/uresdata.cpp",
+        "common/usc_impl.cpp",
+        "common/uscript.cpp",
+        "common/uscript_props.cpp",
+        "common/uset.cpp",
+        "common/uset_props.cpp",
+        "common/usetiter.cpp",
+        "common/ushape.cpp",
+        "common/usprep.cpp",
+        "common/ustack.cpp",
+        "common/ustr_cnv.cpp",
+        "common/ustr_titlecase_brkiter.cpp",
+        "common/ustr_wcs.cpp",
+        "common/ustrcase.cpp",
+        "common/ustrcase_locale.cpp",
+        "common/ustrenum.cpp",
+        "common/ustrfmt.cpp",
+        "common/ustring.cpp",
+        "common/ustrtrns.cpp",
+        "common/utext.cpp",
+        "common/utf_impl.cpp",
+        "common/util.cpp",
+        "common/util_props.cpp",
+        "common/utrace.cpp",
+        "common/utrie.cpp",
+        "common/utrie2.cpp",
+        "common/utrie2_builder.cpp",
+        "common/utrie_swap.cpp",
+        "common/uts46.cpp",
+        "common/utypes.cpp",
+        "common/uvector.cpp",
+        "common/uvectr32.cpp",
+        "common/uvectr64.cpp",
+        "common/wintz.cpp",
+    ]
+    thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+    thirdparty_sources += ["icu_data/icudata_stub.cpp"]
+
+    env_icu.Append(CPPPATH=["#thirdparty/icu4c/common/"])
+    env_icu.Append(
+        CXXFLAGS=["-DU_STATIC_IMPLEMENTATION", "-DU_COMMON_IMPLEMENTATION", "-DPKGDATA_MODE=static",]
+    )
+
+    env_icu.disable_warnings()
+    env_thirdparty = env_icu.Clone()
+    env_thirdparty.disable_warnings()
+    lib = env_thirdparty.add_library("icu_builtin", thirdparty_sources)
+
+    # Needs to be appended to arrive after libscene in the linker call,
+    # but we don't want it to arrive *after* system libs, so manual hack
+    # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+    # and then plain strings for system library. We insert between the two.
+    inserted = False
+    for idx, linklib in enumerate(env["LIBS"]):
+        if isinstance(linklib, (str, bytes)):  # first system lib such as "X11", otherwise SCons lib object
+            env["LIBS"].insert(idx, lib)
+            inserted = True
+            break
+    if not inserted:
+        env.Append(LIBS=[lib])
+
+env_text_server_adv = env_modules.Clone()
+env_text_server_adv.Append(
+    CPPPATH=[
+        "#thirdparty/harfbuzz/src",
+        "#thirdparty/freetype/include",
+        "#thirdparty/graphite/include",
+        "#thirdparty/icu4c/common/",
+    ]
+)
+env_text_server_adv.add_source_files(env.modules_sources, "*.cpp")

+ 5 - 0
modules/text_server_adv/config.py

@@ -0,0 +1,5 @@
+def can_build(env, platform):
+    return True
+
+def configure(env):
+    pass

+ 63 - 0
modules/text_server_adv/icu_data/icudata_stub.cpp

@@ -0,0 +1,63 @@
+/*************************************************************************/
+/*  icudata_stub.cpp                                                     */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "unicode/udata.h"
+#include "unicode/utypes.h"
+#include "unicode/uversion.h"
+
+typedef struct {
+	uint16_t header_size;
+	uint8_t magic_1, magic_2;
+	UDataInfo info;
+	char padding[8];
+	uint32_t count, reserved;
+	int fake_name_and_data[4];
+} ICU_data_header;
+
+extern "C" U_EXPORT const ICU_data_header U_ICUDATA_ENTRY_POINT = {
+	32,
+	0xDA, 0x27,
+	{ sizeof(UDataInfo),
+			0,
+#if U_IS_BIG_ENDIAN
+			1,
+#else
+			0,
+#endif
+			U_CHARSET_FAMILY,
+			sizeof(UChar),
+			0,
+			{ 0x54, 0x6F, 0x43, 0x50 },
+			{ 1, 0, 0, 0 },
+			{ 0, 0, 0, 0 } },
+	{ 0, 0, 0, 0, 0, 0, 0, 0 },
+	0, 0,
+	{ 0, 0, 0, 0 }
+};

+ 1 - 1
platform/android/detect.py

@@ -215,7 +215,7 @@ def configure(env):
     env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"])
 
     # Disable exceptions and rtti on non-tools (template) builds
-    if env["tools"]:
+    if env["tools"] or env["builtin_icu"]:
         env.Append(CXXFLAGS=["-frtti"])
     else:
         env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"])

+ 2 - 0
platform/javascript/detect.py

@@ -71,6 +71,8 @@ def configure(env):
             )
         # Tools need more memory. Initial stack memory in bytes. See `src/settings.js` in emscripten repository (will be renamed to INITIAL_MEMORY).
         env.Append(LINKFLAGS=["-s", "TOTAL_MEMORY=33554432"])
+    elif env["builtin_icu"]:
+        env.Append(CCFLAGS=["-frtti"])
     else:
         # Disable exceptions and rtti on non-tools (template) builds
         # These flags help keep the file size down.

+ 18 - 1
platform/linuxbsd/detect.py

@@ -205,14 +205,31 @@ def configure(env):
 
     # freetype depends on libpng and zlib, so bundling one of them while keeping others
     # as shared libraries leads to weird issues
-    if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]:
+    if (
+        env["builtin_freetype"]
+        or env["builtin_libpng"]
+        or env["builtin_zlib"]
+        or env["builtin_graphite"]
+        or env["builtin_harfbuzz"]
+    ):
         env["builtin_freetype"] = True
         env["builtin_libpng"] = True
         env["builtin_zlib"] = True
+        env["builtin_graphite"] = True
+        env["builtin_harfbuzz"] = True
 
     if not env["builtin_freetype"]:
         env.ParseConfig("pkg-config freetype2 --cflags --libs")
 
+    if not env["builtin_graphite"]:
+        env.ParseConfig("pkg-config graphite2 --cflags --libs")
+
+    if not env["builtin_icu"]:
+        env.ParseConfig("pkg-config icu-uc --cflags --libs")
+
+    if not env["builtin_harfbuzz"]:
+        env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs")
+
     if not env["builtin_libpng"]:
         env.ParseConfig("pkg-config libpng16 --cflags --libs")
 

+ 29 - 2
platform/server/detect.py

@@ -138,14 +138,31 @@ def configure(env):
 
     # freetype depends on libpng and zlib, so bundling one of them while keeping others
     # as shared libraries leads to weird issues
-    if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]:
+    if (
+        env["builtin_freetype"]
+        or env["builtin_libpng"]
+        or env["builtin_zlib"]
+        or env["builtin_graphite"]
+        or env["builtin_harfbuzz"]
+    ):
         env["builtin_freetype"] = True
         env["builtin_libpng"] = True
         env["builtin_zlib"] = True
+        env["builtin_graphite"] = True
+        env["builtin_harfbuzz"] = True
 
     if not env["builtin_freetype"]:
         env.ParseConfig("pkg-config freetype2 --cflags --libs")
 
+    if not env["builtin_graphite"]:
+        env.ParseConfig("pkg-config graphite2 --cflags --libs")
+
+    if not env["builtin_icu"]:
+        env.ParseConfig("pkg-config icu-uc --cflags --libs")
+
+    if not env["builtin_harfbuzz"]:
+        env.ParseConfig("pkg-config harfbuzz harfbuzz-icu --cflags --libs")
+
     if not env["builtin_libpng"]:
         env.ParseConfig("pkg-config libpng16 --cflags --libs")
 
@@ -233,7 +250,17 @@ def configure(env):
     env.Append(CPPDEFINES=["SERVER_ENABLED", "UNIX_ENABLED"])
 
     if platform.system() == "Darwin":
-        env.Append(LINKFLAGS=["-framework", "Cocoa", "-framework", "Carbon", "-lz", "-framework", "IOKit"])
+        env.Append(
+            LINKFLAGS=[
+                "-framework",
+                "Cocoa",
+                "-framework",
+                "Carbon",
+                "-lz",
+                "-framework",
+                "IOKit",
+            ]
+        )
 
     env.Append(LIBS=["pthread"])
 

+ 33 - 0
thirdparty/README.md

@@ -184,6 +184,39 @@ Files extracted from upstream source:
 
 Patches in the `patches` directory should be re-applied after updates.
 
+## Graphite engine
+
+- Upstream: https://github.com/silnrsi/graphite
+- Version: 1.3.14
+- License: MPL-2.0
+
+Files extracted from upstream source:
+- the `include` folder
+- the `src` folder
+- `COPYING`, `ChangeLog`
+
+## HarfBuzz
+
+- Upstream: https://github.com/harfbuzz/harfbuzz
+- Version: 2.7.2
+- License: HarfBuzz
+
+Files extracted from upstream source:
+- the `src` folder
+- `AUTHORS`, `COPYING`, `NEWS`, `THANKS`
+
+## International Components for Unicode
+
+- Upstream: https://github.com/unicode-org/icu
+- Version: 68.1
+- License: Unicode
+
+Files extracted from upstream source:
+- the `common` folder
+- `APIChangeReport.md`, `LICENSE`
+
+Files generated from upstream source:
+- the `icudt68l.dat` built with the provided `godot_data.json` config file (see https://github.com/unicode-org/icu/blob/master/docs/userguide/icu_data/buildtool.md for instructions)
 
 ## jpeg-compressor
 

+ 26 - 0
thirdparty/graphite/COPYING

@@ -0,0 +1,26 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+    Alternatively, you may use this library under the terms of the Mozilla
+    Public License (http://mozilla.org/MPL) or under the GNU General Public
+    License, as published by the Free Sofware Foundation; either version
+    2 of the license or (at your option) any later version.
+*/

+ 238 - 0
thirdparty/graphite/ChangeLog

@@ -0,0 +1,238 @@
+1.3.14
+    . Bug fixes
+    . Allow features to be hidden (for aliases)
+    . Move to python3
+    . Rename doc files from .txt to .asc
+
+1.3.13
+    . Resolve minor spacing issue in rtl non-overlap kerning
+    . python3 for graphite.py
+    . Better fuzzing
+    . Better building on windows
+
+1.3.12
+    . Graphite no longer does dumb rendering for fonts with no smarts
+    . Segment caching code removed. Anything attempting to use the segment cache gets given a regular face instead
+    . Add libfuzzer support
+    . Builds now require C++11
+    . Improvements to Windows 64 bit builds
+    . Support different versions of python including 32 bit and python 3
+    . Various minor bug fixes
+
+1.3.11
+    . Fixes due to security review
+    . Minor collision avoidance fixes
+    . Fix LZ4 decompressor against high compression
+
+1.3.10
+    . Address floating point build parameters to give consistent positioning results across platforms
+    . Various bug fixes
+
+1.3.9
+    . Add Collision COLL_ISSPACE to allow for visible spaces in collision avoidance
+    . Add segment and pass direction information to tracing output
+    . Bug fix rule length testing in 32-bit
+    . Increase slanted margin distances for collision avoidance
+    . Change kerning algorithm to simple outline expansion. Seems to make no visible difference.
+    . Add trace2svg to test tools
+
+1.3.8
+    . Various bug fixes arising from fuzzing
+    . Fix regression that stopped piglatin from working
+    . Make collision avoidance kerning give more regular results
+    . Minor modification to clustering algorithm to handle variable width chars
+
+1.3.7
+    . Bug fixes
+    . Start to deprecate SegCache. This will be going away in a later release.
+
+1.3.6
+    . Bug fixes
+
+1.3.5
+    . Bug fixes
+        . Security bug fix
+        . Fix ARM misalignment problem
+        . Track latest cmake
+
+1.3.4
+    . Transition from Mercurial to Git
+    . Bug fixes
+        . Fix Collision Kerning ignoring some diacritics
+        . Handle pass bits 16-31 to speed up fonts with > 16 passes
+        . Various minor fuzz bug fixes
+        . Make Coverity happy
+        . Add GR_FALLTHROUGH macro for clang c++11
+
+1.3.3
+    . Slight speed up in Collision Avoidance
+    . Remove dead bidi code
+    . Bug fixes
+        . Between pass bidi reorderings and at the end
+        . Decompressor fuzz bugs
+        . Other fuzz bugs
+
+1.3.2
+    . Remove full bidi. All segments are assumed to be single directioned.
+    . Bug fixes:
+        . Decompressor corner cases
+        . Various fuzz bugs
+
+1.3.1
+    . Deprecation warning: Full bidi support is about to be deprecated. Make contact
+      if this impacts you.
+    . Change compression block format slightly to conform to LZ4
+    . Bug fixes:
+        . Handle mono direction text with diacritics consistently. Fonts
+          now see the direction they expect consistently and bidi now
+          gives expected results.
+        . Fixed lots of fuzz bugs
+        . Coverity cleanups
+        . Build now works for clang and/or asan and/or afl etc.
+
+1.3.0
+    . Add collision avoidance
+        . Shift Collider
+        . Kern Collider
+        . Octabox outlines and subboxes
+    . Add compressed Silf and Glat table support
+    . Bug fixes:
+        . Stop loops forming in the child, sibling tree
+        . Handle bidi mirroring correctly if no bidi occurring
+
+1.2.4
+    . Face failure now has error code reporting via debug logging
+        . can now call gr_start_logging(NULL, fname)
+        . gr2fonttest --alltrace added
+    . Format 14 table support
+        . Not done. To be handled entirely in the compiler
+    . Bidi support for Unicode 6.3 Isolating direction controls
+    . Fonts no longer require a glyf/loca table. In such cases the bounding box is always 0.
+    . Clang ASAN build support added for testing.
+    . Handle out of memory sanely.
+    . Documentation improvements
+    . Bug fixes:
+        . Enforce fonts having to store glyph attributes by monotonically increasing attribute number
+        . zeropadding was not getting called on feature tags
+        . automatic associations for unassociated characters
+        . use direct engine on Mac
+        . various extreme case reading 1 past the end errors fixed
+        . remove tabs from sources so that it becomes readable again
+
+1.2.3
+    . Bug fixes only:
+        . fix byte swapping when testing cmap subtable lengths
+        . work around armel compilation problems with conditional operators
+        . fix pseudoglyph support for advance and bbox
+
+1.2.2
+    . Add support for passKeySlot (makes Charis 2x faster) up to 32 passes
+    . Add telemetry output to json if enabled in build GRAPHITE2_TELEMETRY
+    . Shrink font memory footprint particularly in the fsm
+    . Add -S to comparerenderer
+    . Bug fixes:
+        . Fix shift.x being reversed for rtl text
+        . Fix faulty fallback justification
+        . Fix bad cmap handling
+        . Support compiling on old Solaris where bidi attributes clash with register names
+        . Follow the crowd in using Windows.h
+
+1.2.1
+    . Bug fixes:
+        . Allow glyph reattachment
+        . Allow signed glyph attributes
+        . Various build problems with MacOS, old gcc versions, etc.
+        . Various overrun read errors fixed
+
+1.2.0
+    . API Changes:
+        . Added Windows friendly gr_start_logging and gr_stop_logging, now per face
+        . Added gr_make_face_with_ops, gr_make_face_with_seg_cache_and_ops
+        . Added gr_make_font_with_ops
+        . Added gr_face_is_char_supported
+        . Added gr_face_info to give info to apps about face capabilities
+        . Deprecated gr_make_face, gr_make_face_with_seg_cache, gr_make_font_with_advance_fn
+        . Deprecated graphite_start_logging and graphite_stop_logging
+            . These functions are stubbed now and do nothing, but do compile and link.
+        . Bump API version to 3
+    . Add C# wrapper to contrib
+    . Handle justification information in a font and do something useful with it
+    . Builds clang clean (has done for a while)
+    . Bug fixes
+    . Windows build and bug fixes
+    . Add extra information to json debug output
+    . Added windows build documentation
+    . Added freetype sample code and test
+
+1.1.3
+    . Default build has GRAPHITE2_COMPARE_RENDERER to OFF to reduce dependencies
+    . Builds on Mac with clang
+    . Debug output improvements
+    . Tidy up perl wrappers
+    . Fuzz tester improvements
+    . Various bug fixes for bad font handling
+
+1.1.2
+    . Support feature ids < 4 chars when space padded for inclusion in FF 14.
+    . More fuzztesting and removal of causes of valgrind bad reads and sigabrts
+    . Remove contrib/android into its own repo (http://hg.palaso.org/grandroid)
+    . Update comparerenderer to latest harfbuzzng api
+
+1.1.1
+    . Missing Log.h included
+    . perl wrappers updated
+
+1.1.0
+    . Refactored debug output to use json
+    . Renamed VM_MACHINE_TYPE to GRAPHITE2_VM_TYPE
+    . Renamed DISABLE_SEGCACHE to GRAPHITE2_NSEGCACE
+    . Renamed DISBALE_FILE_FACE to GRAPHITE2_NFILEFACE
+    . Renamed ENABLE_COMPARE_RENDERER to GRAPHTIE2_COMPARE_RENDERER
+    . Renamed DOXYGEN_CONFIG to GRAPHITE2_DOXYGEN_CONFIG
+    . Renamed GR2_CUSTOM_HEADER to GRAPHITE2_CUSTOM_HEADER
+    . Renamed GR2_EXPORTING to GRAPHITE2_EXPORTING
+    . Added GRAPHITE2_STATIC for static only builds
+    . Added GRAPHITE2_NTRACING to compile out tracing code
+    . Documented GRAPHITE2_{EXPORTING,STATIC,NTRACING} in hacking.txt
+    . Bump libtool version to 2.1.0
+    . dumb font rendering works
+    . slot user attributes are now signed rather than unsigned
+    . add support for long class maps
+    . Rename perl library to avoid nameclash on Windows
+    . Various robustness fixes
+    . Moved internal .h files into src/inc
+    . Parallelise fuzztest
+    . General build improvements, particularly on Windows
+
+1.0.3
+    . Fix UTF16 surrogate support
+    . script and lang tags may be space padded or null padded
+    . Remove need for WORDS_BIGENDIAN, do it all automatically
+    . Remove all #include <new>. Use CLASS_NEW_DELETE instead.
+    . Fix comparerenderer to work with current hbng
+    . Add valgrind to fuzztest to ensure good memory use at all times
+    . Fix new fuzztest exposed bugs.
+    . Fix bugs exposed by Mozilla security review
+    . Add continuous integration build on Windows support
+
+1.0.2
+    . Fix Windows build
+    . Comparerenderer uses hbng enforcing ot rendering
+    . Add Bidi .hasChar support and refactor mirroring code
+    . Make cmake default Release rather than debug
+    . Don't compile in a boat load of TtfUtil that isn't used, saving 15% of binary
+    . Chase the FSF around its latest office moves
+    . WORDS_BIGENDIAN is set at the top so tests now pass on ppc, etc.
+    . More words in the manual
+
+1.0.1
+    . Release is the default build in cmake now.
+    . Refactor cmake build to not rebuild things so much.
+    . Include a missing file
+    . Remove -nostdlibs, making gcc happy everywhere
+    . Update comparerenderer to latest hbng interface
+    . Add changelog
+
+1.0.0
+    . First major release of perfect code!
+

+ 389 - 0
thirdparty/graphite/include/graphite2/Font.h

@@ -0,0 +1,389 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+    Alternatively, the contents of this file may be used under the terms
+    of the Mozilla Public License (http://mozilla.org/MPL) or the GNU
+    General Public License, as published by the Free Software Foundation,
+    either version 2 of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "graphite2/Types.h"
+
+#define GR2_VERSION_MAJOR   1
+#define GR2_VERSION_MINOR   3
+#define GR2_VERSION_BUGFIX  14
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct gr_face          gr_face;
+typedef struct gr_font          gr_font;
+typedef struct gr_feature_ref   gr_feature_ref;
+typedef struct gr_feature_val   gr_feature_val;
+
+/**
+* Returns version information on this engine
+*/
+GR2_API void gr_engine_version(int *nMajor, int *nMinor, int *nBugFix);
+
+/**
+* The Face Options allow the application to require that certain tables are
+* read during face construction. This may be of concern if the appFaceHandle
+* used in the gr_get_table_fn may change.
+* The values can be combined
+*/
+enum gr_face_options {
+    /** No preload, no cmap caching, fail if the graphite tables are invalid */
+    gr_face_default = 0,
+    /** Dumb rendering will be enabled if the graphite tables are invalid. @deprecated Since 1.311 */
+    gr_face_dumbRendering = 1,
+    /** preload glyphs at construction time */
+    gr_face_preloadGlyphs = 2,
+    /** Cache the lookup from code point to glyph ID at construction time */
+    gr_face_cacheCmap = 4,
+    /** Preload everything */
+    gr_face_preloadAll = gr_face_preloadGlyphs | gr_face_cacheCmap
+};
+
+/** Holds information about a particular Graphite silf table that has been loaded */
+struct gr_faceinfo {
+    gr_uint16 extra_ascent;     /**< The extra_ascent in the GDL, in design units */
+    gr_uint16 extra_descent;    /**< The extra_descent in the GDL, in design units */
+    gr_uint16 upem;             /**< The design units for the font */
+    enum gr_space_contextuals {
+        gr_space_unknown = 0,       /**< no information is known. */
+        gr_space_none = 1,          /**< the space character never occurs in any rules. */
+        gr_space_left_only = 2,     /**< the space character only occurs as the first element in a rule. */
+        gr_space_right_only = 3,    /**< the space character only occurs as the last element in a rule. */
+        gr_space_either_only = 4,   /**< the space character only occurs as the only element in a rule. */
+        gr_space_both = 5,          /**< the space character may occur as the first or last element of a rule. */
+        gr_space_cross = 6          /**< the space character occurs in a rule not as a first or last element. */
+    } space_contextuals;
+    unsigned int has_bidi_pass : 1; /**< the table specifies that a bidirectional pass should run */
+    unsigned int line_ends : 1;     /**< there are line end contextuals somewhere */
+    unsigned int justifies : 1;     /**< there are .justify properties set somewhere on some glyphs */
+};
+
+typedef struct gr_faceinfo gr_faceinfo;
+
+/** type describing function to retrieve font table information
+  *
+  * @return a pointer to the table in memory. The pointed to memory must exist as
+  *          long as the gr_face which makes the call.
+  * @param appFaceHandle is the unique information passed to gr_make_face()
+  * @param name is a 32bit tag to the table name.
+  * @param len returned by this function to say how long the table is in memory.
+  */
+typedef const void *(*gr_get_table_fn)(const void* appFaceHandle, unsigned int name, size_t *len);
+
+/** type describing function to release any resources allocated by the above get_table table function
+  *
+  * @param appFaceHandle is the unique information passed to gr_make_face()
+  * @param pointer to table memory returned by get_table.
+  */
+typedef void (*gr_release_table_fn)(const void* appFaceHandle, const void *table_buffer);
+
+/** struct housing function pointers to manage font table buffers for the graphite engine. */
+struct gr_face_ops
+{
+        /** size in bytes of this structure */
+    size_t              size;
+        /** a pointer to a function to request a table from the client. */
+	gr_get_table_fn 	get_table;
+        /** is a pointer to a function to notify the client the a table can be released.
+          * This can be NULL to signify that the client does not wish to do any release handling. */
+	gr_release_table_fn	release_table;
+};
+typedef struct gr_face_ops	gr_face_ops;
+
+/** Create a gr_face object given application information and a table functions.
+  *
+  * @return gr_face or NULL if the font fails to load for some reason.
+  * @param appFaceHandle This is application specific information that is passed
+  *                      to the getTable function. The appFaceHandle must stay
+  *                      alive as long as the gr_face is alive.
+  * @param face_ops      Pointer to face specific callback structure for table
+  *                      management. Must stay alive for the duration of the
+  *                      call only.
+  * @param faceOptions   Bitfield describing various options. See enum gr_face_options for details.
+  */
+GR2_API gr_face* gr_make_face_with_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *face_ops, unsigned int faceOptions);
+
+/** @deprecated Since v1.2.0 in favour of gr_make_face_with_ops.
+  * Create a gr_face object given application information and a getTable function.
+  *
+  * @return gr_face or NULL if the font fails to load for some reason.
+  * @param appFaceHandle This is application specific information that is passed
+  *                      to the getTable function. The appFaceHandle must stay
+  *                      alive as long as the gr_face is alive.
+  * @param getTable      Callback function to get table data.
+  * @param faceOptions   Bitfield describing various options. See enum gr_face_options for details.
+  */
+GR2_DEPRECATED_API gr_face* gr_make_face(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn getTable, unsigned int faceOptions);
+
+/** @deprecated   Since 1.3.7 this function is now an alias for gr_make_face_with_ops().
+  *
+  * Create a gr_face object given application information, with subsegmental caching support
+  *
+  * @return gr_face or NULL if the font fails to load.
+  * @param appFaceHandle is a pointer to application specific information that is passed to getTable.
+  *                      This may not be NULL and must stay alive as long as the gr_face is alive.
+  * @param face_ops      Pointer to face specific callback structure for table management. Must stay
+  *                      alive for the duration of the call only.
+  * @param segCacheMaxSize Unused.
+  * @param faceOptions   Bitfield of values from enum gr_face_options
+  */
+GR2_DEPRECATED_API gr_face* gr_make_face_with_seg_cache_and_ops(const void* appFaceHandle, const gr_face_ops *face_ops, unsigned int segCacheMaxSize, unsigned int faceOptions);
+
+/** @deprecated   Since 1.3.7 this function is now an alias for gr_make_face().
+  *
+  * Create a gr_face object given application information, with subsegmental caching support.
+  * This function is deprecated as of v1.2.0 in favour of gr_make_face_with_seg_cache_and_ops.
+  *
+  * @return gr_face or NULL if the font fails to load.
+  * @param appFaceHandle is a pointer to application specific information that is passed to getTable.
+  *                      This may not be NULL and must stay alive as long as the gr_face is alive.
+  * @param getTable      The function graphite calls to access font table data
+  * @param segCacheMaxSize   How large the segment cache is.
+  * @param faceOptions   Bitfield of values from enum gr_face_options
+  */
+GR2_DEPRECATED_API gr_face* gr_make_face_with_seg_cache(const void* appFaceHandle, gr_get_table_fn getTable, unsigned int segCacheMaxSize, unsigned int faceOptions);
+
+/** Convert a tag in a string into a gr_uint32
+  *
+  * @return gr_uint32 tag, zero padded
+  * @param str a nul terminated string of which at most the first 4 characters are read
+  */
+GR2_API gr_uint32 gr_str_to_tag(const char *str);
+
+/** Convert a gr_uint32 tag into a string
+  *
+  * @param tag contains the tag to convert
+  * @param str is a pointer to a char array of at least size 4 bytes. The first 4 bytes of this array
+  *            will be overwritten by this function. No nul is appended.
+  */
+GR2_API void gr_tag_to_str(gr_uint32 tag, char *str);
+
+/** Get feature values for a given language or default
+  *
+  * @return a copy of the default feature values for a given language. The application must call
+  *          gr_featureval_destroy() to free this object when done.
+  * @param pFace The font face to get feature values from
+  * @param langname The language tag to get feature values for. If there is no such language or
+  *                  langname is 0, the default feature values for the font are returned.
+  *                  langname is right 0 padded and assumes lowercase. Thus the en langauge
+  *                  would be 0x656E0000. Langname may also be space padded, thus 0x656E2020.
+  */
+GR2_API gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, gr_uint32 langname);
+
+/** Get feature reference for a given feature id from a face
+  *
+  * @return a feature reference corresponding to the given id. This data is part of the gr_face and
+  *          will be freed when the face is destroyed.
+  * @param pFace Font face to get information on.
+  * @param featId    Feature id tag to get reference to.
+  */
+GR2_API const gr_feature_ref* gr_face_find_fref(const gr_face* pFace, gr_uint32 featId);
+
+/** Returns number of feature references in a face **/
+GR2_API gr_uint16 gr_face_n_fref(const gr_face* pFace);
+
+/** Returns feature reference at given index in face **/
+GR2_API const gr_feature_ref* gr_face_fref(const gr_face* pFace, gr_uint16 i);
+
+/** Return number of languages the face knows about **/
+GR2_API unsigned short gr_face_n_languages(const gr_face* pFace);
+
+/** Returns a language id corresponding to a language of given index in the face **/
+GR2_API gr_uint32 gr_face_lang_by_index(const gr_face* pFace, gr_uint16 i);
+
+/** Destroy the given face and free its memory **/
+GR2_API void gr_face_destroy(gr_face *face);
+
+/** Returns the number of glyphs in the face **/
+GR2_API unsigned short gr_face_n_glyphs(const gr_face* pFace);
+
+/** Returns a faceinfo for the face and script **/
+GR2_API const gr_faceinfo *gr_face_info(const gr_face *pFace, gr_uint32 script);
+
+/** Returns whether the font supports a given Unicode character
+  *
+  * @return true if the character is supported.
+  * @param pFace    face to test within
+  * @param usv      Unicode Scalar Value of character to test
+  * @param script   Tag of script for selecting which set of pseudo glyphs to test. May be NULL.
+  */
+GR2_API int gr_face_is_char_supported(const gr_face *pFace, gr_uint32 usv, gr_uint32 script);
+
+#ifndef GRAPHITE2_NFILEFACE
+/** Create gr_face from a font file
+  *
+  * @return gr_face that accesses a font file directly. Returns NULL on failure.
+  * @param filename Full path and filename to font file
+  * @param faceOptions Bitfile from enum gr_face_options to control face options.
+  */
+GR2_API gr_face* gr_make_file_face(const char *filename, unsigned int faceOptions);
+
+/** @deprecated   Since 1.3.7. This function is now an alias for gr_make_file_face().
+  *
+  * Create gr_face from a font file, with subsegment caching support.
+  *
+  * @return gr_face that accesses a font file directly. Returns NULL on failure.
+  * @param filename Full path and filename to font file
+  * @param segCacheMaxSize Specifies how big to make the cache in segments.
+  * @param faceOptions   Bitfield from enum gr_face_options to control face options.
+  */
+GR2_DEPRECATED_API gr_face* gr_make_file_face_with_seg_cache(const char *filename, unsigned int segCacheMaxSize, unsigned int faceOptions);
+#endif      // !GRAPHITE2_NFILEFACE
+
+/** Create a font from a face
+  *
+  * @return gr_font Call font_destroy to free this font
+  * @param ppm Resolution of the font in pixels per em
+  * @param face Face this font corresponds to. This must stay alive as long as the font is alive.
+  */
+GR2_API gr_font* gr_make_font(float ppm, const gr_face *face);
+
+/** query function to find the hinted advance of a glyph
+  *
+  * @param appFontHandle is the unique information passed to gr_make_font_with_advance()
+  * @param glyphid is the glyph to retireve the hinted advance for.
+ */
+typedef float (*gr_advance_fn)(const void* appFontHandle, gr_uint16 glyphid);
+
+/** struct housing function pointers to manage font hinted metrics for the
+  * graphite engine. */
+struct gr_font_ops
+{
+        /** size of the structure in bytes to allow for future extensibility */
+    size_t              size;
+        /** a pointer to a function to retrieve the hinted
+          * advance width of a glyph which the font cannot
+          * provide without client assistance.  This can be
+          * NULL to signify no horizontal hinted metrics are necessary. */
+    gr_advance_fn       glyph_advance_x;
+        /** a pointer to a function to retrieve the hinted
+          * advance height of a glyph which the font cannot
+          * provide without client assistance.  This can be
+          * NULL to signify no horizontal hinted metrics are necessary. */
+    gr_advance_fn       glyph_advance_y;
+};
+typedef struct gr_font_ops  gr_font_ops;
+
+/** Creates a font with hinted advance width query functions
+  *
+  * @return gr_font to be destroyed via font_destroy
+  * @param ppm size of font in pixels per em
+  * @param appFontHandle font specific information that must stay alive as long
+  *        as the font does
+  * @param font_ops pointer font specific callback structure for hinted metrics.
+  *        Need only stay alive for the duration of the call.
+  * @param face the face this font corresponds to. Must stay alive as long as
+  *        the font does.
+  */
+GR2_API gr_font* gr_make_font_with_ops(float ppm, const void* appFontHandle, const gr_font_ops * font_ops, const gr_face *face);
+
+/** Creates a font with hinted advance width query function.
+  * This function is deprecated. Use gr_make_font_with_ops instead.
+  *
+  * @return gr_font to be destroyed via font_destroy
+  * @param ppm size of font in pixels per em
+  * @param appFontHandle font specific information that must stay alive as long
+  *        as the font does
+  * @param getAdvance callback function reference that returns horizontal advance in pixels for a glyph.
+  * @param face the face this font corresponds to. Must stay alive as long as
+  *        the font does.
+  */
+GR2_API gr_font* gr_make_font_with_advance_fn(float ppm, const void* appFontHandle, gr_advance_fn getAdvance, const gr_face *face);
+
+/** Free a font **/
+GR2_API void gr_font_destroy(gr_font *font);
+
+/** get a feature value
+  *
+  * @return value of specific feature or 0 if any problems.
+  * @param pfeatureref   gr_feature_ref to the feature
+  * @param feats gr_feature_val containing all the values
+  */
+GR2_API gr_uint16 gr_fref_feature_value(const gr_feature_ref* pfeatureref, const gr_feature_val* feats);
+
+/** set a feature value
+  *
+  * @return false if there were any problems (value out of range, etc.)
+  * @param pfeatureref   gr_feature_ref to the feature
+  * @param val   value to set the feature to
+  * @param pDest the gr_feature_val containing all the values for all the features
+  */
+GR2_API int gr_fref_set_feature_value(const gr_feature_ref* pfeatureref, gr_uint16 val, gr_feature_val* pDest);
+
+/** Returns the id tag for a gr_feature_ref **/
+GR2_API gr_uint32 gr_fref_id(const gr_feature_ref* pfeatureref);
+
+/** Returns number of values a feature may take, given a gr_feature_ref **/
+GR2_API gr_uint16 gr_fref_n_values(const gr_feature_ref* pfeatureref);
+
+/** Returns the value associated with a particular value in a feature
+  *
+  * @return value
+  * @param pfeatureref gr_feature_ref of the feature of interest
+  * @param settingno   Index up to the return value of gr_fref_n_values() of the value
+  */
+GR2_API gr_int16 gr_fref_value(const gr_feature_ref* pfeatureref, gr_uint16 settingno);
+
+/** Returns a string of the UI name of a feature
+  *
+  * @return string of the UI name, in the encoding form requested. Call gr_label_destroy() after use.
+  * @param pfeatureref   gr_feature_ref of the feature
+  * @param langId    This is a pointer since the face may not support a string in the requested
+  *                  language. The actual language of the string is returned in langId
+  * @param utf   Encoding form for the string
+  * @param length    Used to return the length of the string returned in bytes.
+  */
+GR2_API void* gr_fref_label(const gr_feature_ref* pfeatureref, gr_uint16 *langId, enum gr_encform utf, gr_uint32 *length);
+
+/** Return a UI string for a possible value of a feature
+  *
+  * @return string of the UI name, in the encoding form requested. nul terminated. Call gr_label_destroy()
+  *          after use.
+  * @param pfeatureref   gr_feature_ref of the feature
+  * @param settingno     Value setting index
+  * @param langId        This is a pointer to the requested language. The requested language id is
+  *                      replaced by the actual language id of the string returned.
+  * @param utf   Encoding form for the string
+  * @param length    Returns the length of the string returned in bytes.
+  */
+GR2_API void* gr_fref_value_label(const gr_feature_ref* pfeatureref, gr_uint16 settingno/*rather than a value*/, gr_uint16 *langId, enum gr_encform utf, gr_uint32 *length);
+
+/** Destroy a previously returned label string **/
+GR2_API void gr_label_destroy(void * label);
+
+/** Copies a gr_feature_val **/
+GR2_API gr_feature_val* gr_featureval_clone(const gr_feature_val* pfeatures);
+
+/** Destroys a gr_feature_val **/
+GR2_API void gr_featureval_destroy(gr_feature_val *pfeatures);
+
+#ifdef __cplusplus
+}
+#endif

+ 85 - 0
thirdparty/graphite/include/graphite2/Log.h

@@ -0,0 +1,85 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+    Alternatively, the contents of this file may be used under the terms
+    of the Mozilla Public License (http://mozilla.org/MPL) or the GNU
+    General Public License, as published by the Free Software Foundation,
+    either version 2 of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <graphite2/Types.h>
+#include <graphite2/Font.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** deprecated mechanism that doesn't do anything now. */
+typedef enum {
+    GRLOG_NONE = 0x0,
+    GRLOG_FACE = 0x01,
+    GRLOG_SEGMENT = 0x02,
+    GRLOG_PASS = 0x04,
+    GRLOG_CACHE = 0x08,
+
+    GRLOG_OPCODE = 0x80,
+    GRLOG_ALL = 0xFF
+} GrLogMask;
+
+/** Start logging all segment creation and updates on the provided face.  This
+  * is logged to a JSON file, see "Segment JSON Schema.txt" for a precise
+  * definition of the file
+  *
+  * @return true    if the file was successfully created and logging is correctly
+  * 			    initialised.
+  * @param face     the gr_face whose segments you want to log to the given file
+  * @param log_path a utf8 encoded file name and path to log to.
+  */
+GR2_API bool gr_start_logging(gr_face * face, const char *log_path);
+
+
+/** Stop logging on the given face.  This will close the log file created by
+  * gr_start_logging.
+  *
+  * @param face the gr_face whose segments you want to stop logging
+  */
+GR2_API void gr_stop_logging(gr_face * face);
+
+/** Start logging to a FILE object.
+  * This function is deprecated as of 1.2.0, use the _face versions instead.
+  *
+  * @return        True on success
+  * @param logfile FILE reference to output logging to
+  * @param mask    What aspects of logging to report (ignored)
+  */
+GR2_API bool graphite_start_logging(FILE * logFile, GrLogMask mask);    //may not do anthing if disabled in the implementation of the engine.
+
+/** Stop logging to a FILE object.
+  * This function is deprecated as of 1.2.0, use the _face versions instead.
+  */
+GR2_API void graphite_stop_logging();
+
+#ifdef __cplusplus
+}
+#endif

+ 461 - 0
thirdparty/graphite/include/graphite2/Segment.h

@@ -0,0 +1,461 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+    Alternatively, the contents of this file may be used under the terms
+    of the Mozilla Public License (http://mozilla.org/MPL) or the GNU
+    General Public License, as published by the Free Software Foundation,
+    either version 2 of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "graphite2/Types.h"
+#include "graphite2/Font.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+enum gr_break_weight {
+    gr_breakNone = 0,
+    /* after break weights */
+    gr_breakWhitespace = 10,
+    gr_breakWord = 15,
+    gr_breakIntra = 20,
+    gr_breakLetter = 30,
+    gr_breakClip = 40,
+    /* before break weights */
+    gr_breakBeforeWhitespace = -10,
+    gr_breakBeforeWord = -15,
+    gr_breakBeforeIntra = -20,
+    gr_breakBeforeLetter = -30,
+    gr_breakBeforeClip = -40
+};
+
+enum gr_justFlags {
+    /// Indicates that this segment is a complete line
+    gr_justCompleteLine = 0,
+    /// Indicates that the start of the slot list is not at the start of a line
+    gr_justStartInline = 1,
+    /// Indicates that the end of the slot list is not at the end of a line
+    gr_justEndInline = 2
+};
+
+/** Used for looking up slot attributes. Most are already available in other functions **/
+enum gr_attrCode {
+    /// adjusted glyph advance in x direction in design units
+    gr_slatAdvX = 0,
+    /// adjusted glyph advance in y direction (usually 0) in design units
+    gr_slatAdvY,
+    /// returns 0. Deprecated.
+    gr_slatAttTo,
+    /// This slot attaches to its parent at the given design units in the x direction
+    gr_slatAttX,
+    /// This slot attaches to its parent at the given design units in the y direction
+    gr_slatAttY,
+    /// This slot attaches to its parent at the given glyph point (not implemented)
+    gr_slatAttGpt,
+    /// x-direction adjustment from the given glyph point (not implemented)
+    gr_slatAttXOff,
+    /// y-direction adjustment from the given glyph point (not implemented)
+    gr_slatAttYOff,
+    /// Where on this glyph should align with the attachment point on the parent glyph in the x-direction.
+    gr_slatAttWithX,
+    /// Where on this glyph should align with the attachment point on the parent glyph in the y-direction
+    gr_slatAttWithY,
+    /// Which glyph point on this glyph should align with the attachment point on the parent glyph (not implemented).
+    gr_slatWithGpt,
+    /// Adjustment to gr_slatWithGpt in x-direction (not implemented)
+    gr_slatAttWithXOff,
+    /// Adjustment to gr_slatWithGpt in y-direction (not implemented)
+    gr_slatAttWithYOff,
+    /// Attach at given nesting level (not implemented)
+    gr_slatAttLevel,
+    /// Line break breakweight for this glyph
+    gr_slatBreak,
+    /// Ligature component reference (not implemented)
+    gr_slatCompRef,
+    /// bidi directionality of this glyph (not implemented)
+    gr_slatDir,
+    /// Whether insertion is allowed before this glyph
+    gr_slatInsert,
+    /// Final positioned position of this glyph relative to its parent in x-direction in pixels
+    gr_slatPosX,
+    /// Final positioned position of this glyph relative to its parent in y-direction in pixels
+    gr_slatPosY,
+    /// Amount to shift glyph by in x-direction design units
+    gr_slatShiftX,
+    /// Amount to shift glyph by in y-direction design units
+    gr_slatShiftY,
+    /// attribute user1
+    gr_slatUserDefnV1,
+    /// not implemented
+    gr_slatMeasureSol,
+    /// not implemented
+    gr_slatMeasureEol,
+    /// Amount this slot can stretch (not implemented)
+    gr_slatJStretch,
+    /// Amount this slot can shrink (not implemented)
+    gr_slatJShrink,
+    /// Granularity by which this slot can stretch or shrink (not implemented)
+    gr_slatJStep,
+    /// Justification weight for this glyph (not implemented)
+    gr_slatJWeight,
+    /// Amount this slot mush shrink or stretch in design units
+    gr_slatJWidth = 29,
+    /// SubSegment split point
+    gr_slatSegSplit = gr_slatJStretch + 29,
+    /// User defined attribute, see subattr for user attr number
+    gr_slatUserDefn,
+    /// Bidi level
+    gr_slatBidiLevel = 56,
+    /// Collision flags
+    gr_slatColFlags,
+    /// Collision constraint rectangle left (bl.x)
+    gr_slatColLimitblx,
+    /// Collision constraint rectangle lower (bl.y)
+    gr_slatColLimitbly,
+    /// Collision constraint rectangle right (tr.x)
+    gr_slatColLimittrx,
+    /// Collision constraint rectangle upper (tr.y)
+    gr_slatColLimittry,
+    /// Collision shift x
+    gr_slatColShiftx,
+    /// Collision shift y
+    gr_slatColShifty,
+    /// Collision margin
+    gr_slatColMargin,
+    /// Margin cost weight
+    gr_slatColMarginWt,
+    // Additional glyph that excludes movement near this one:
+    gr_slatColExclGlyph,
+    gr_slatColExclOffx,
+    gr_slatColExclOffy,
+    // Collision sequence enforcing attributes:
+    gr_slatSeqClass,
+    gr_slatSeqProxClass,
+    gr_slatSeqOrder,
+    gr_slatSeqAboveXoff,
+    gr_slatSeqAboveWt,
+    gr_slatSeqBelowXlim,
+    gr_slatSeqBelowWt,
+    gr_slatSeqValignHt,
+    gr_slatSeqValignWt,
+
+    /// not implemented
+    gr_slatMax,
+    /// not implemented
+    gr_slatNoEffect = gr_slatMax + 1
+};
+
+enum gr_bidirtl {
+    /// Underlying paragraph direction is RTL
+    gr_rtl = 1,
+    /// Set this to not run the bidi pass internally, even if the font asks for it.
+    /// This presumes that the segment is in a single direction. Most of the time
+    /// this bit should be set unless you know you are passing full paragraphs of text.
+    gr_nobidi = 2,
+    /// Disable auto mirroring for rtl text
+    gr_nomirror = 4
+};
+
+typedef struct gr_char_info     gr_char_info;
+typedef struct gr_segment       gr_segment;
+typedef struct gr_slot          gr_slot;
+
+/** Returns Unicode character for a charinfo.
+  *
+  * @param p Pointer to charinfo to return information on.
+  */
+GR2_API unsigned int gr_cinfo_unicode_char(const gr_char_info* p/*not NULL*/);
+
+/** Returns breakweight for a charinfo.
+  *
+  * @return Breakweight is a number between -50 and 50 indicating the cost of a
+  * break before or after this character. If the value < 0, the absolute value
+  * is this character's contribution to the overall breakweight before it. If the value
+  * > 0, then the value is this character's contribution to the overall breakweight after it.
+  * The overall breakweight between two characters is the maximum of the breakweight
+  * contributions from the characters either side of it. If a character makes no
+  * contribution to the breakweight on one side of it, the contribution is considered
+  * to be 0.
+  * @param p Pointer to charinfo to return information on.
+  */
+GR2_API int gr_cinfo_break_weight(const gr_char_info* p/*not NULL*/);
+
+/** Returns the slot index that after this character is after in the slot stream
+  *
+  * In effect each character is associated with a set of slots and this returns
+  * the index of the last slot in the segment this character is associated with.
+  *
+  * @return after slot index between 0 and gr_seg_n_slots()
+  * @param p Pointer to charinfo to return information on.
+  */
+GR2_API int gr_cinfo_after(const gr_char_info* p/*not NULL*/);
+
+/** Returns the slot index that before this character is before in the slot stream
+  *
+  * In effect each character is associated with a set of slots and this returns
+  * the index of the first slot in the segment this character is associated with.
+  *
+  * @return before slot index between 0 and gr_seg_n_slots()
+  * @param p Pointer to charinfo to return information on.
+  */
+GR2_API int gr_cinfo_before(const gr_char_info* p/*not NULL*/);
+
+/** Returns the code unit index of this character in the input string
+  *
+  * @return code unit index between 0 and the end of the string
+  * @param p Pointer to charinfo to return information on.
+  */
+GR2_API size_t gr_cinfo_base(const gr_char_info* p/*not NULL*/);
+
+/** Returns the number of unicode characters in a string.
+  *
+  * @return number of characters in the string
+  * @param enc Specifies the type of data in the string: utf8, utf16, utf32
+  * @param buffer_begin The start of the string
+  * @param buffer_end Measure up to the first nul or when end is reached, whichever is earliest.
+  *            This parameter may be NULL.
+  * @param pError If there is a structural fault in the string, the location is returned
+  *               in this variable. If no error occurs, pError will contain NULL. NULL
+  *               may be passed for pError if no such information is required.
+  */
+GR2_API size_t gr_count_unicode_characters(enum gr_encform enc, const void* buffer_begin, const void* buffer_end, const void** pError);
+
+/** Creates and returns a segment.
+  *
+  * @return a segment that needs seg_destroy called on it. May return NULL if bad problems
+  *     in segment processing.
+  * @param font Gives the size of the font in pixels per em for final positioning. If
+  *             NULL, positions are returned in design units, i.e. at a ppm of the upem
+  *             of the face.
+  * @param face The face containing all the non-size dependent information.
+  * @param script This is a tag containing a script identifier that is used to choose
+  *               which graphite table within the font to use. Maybe 0. Tag may be 4 chars
+  *               NULL padded in LSBs or space padded in LSBs.
+  * @param pFeats Pointer to a feature values to be used for the segment. Only one
+  *               feature values may be used for a segment. If NULL the default features
+  *               for the font will be used.
+  * @param enc Specifies what encoding form the string is in (utf8, utf16, utf32)
+  * @param pStart Start of the string
+  * @param nChars Number of unicode characters to process in the string. The string will
+  *               be processed either up to the first NULL or until nChars have been
+  *               processed. nChars is also used to initialise the internal memory
+  *               allocations of the segment. So it is wise not to make nChars too much
+  *               greater than the actual number of characters being processed.
+  * @param dir Specifies whether the segment is processed right to left (1) or left to
+  *            right (0) and whether to run the internal bidi pass, if a font requests it.
+  *            See enum gr_bidirtl for details.
+  */
+GR2_API gr_segment* gr_make_seg(const gr_font* font, const gr_face* face, gr_uint32 script, const gr_feature_val* pFeats, enum gr_encform enc, const void* pStart, size_t nChars, int dir);
+
+/** Destroys a segment, freeing the memory.
+  *
+  * @param p The segment to destroy
+  */
+GR2_API void gr_seg_destroy(gr_segment* p);
+
+/** Returns the advance for the whole segment.
+  *
+  * Returns the width of the segment up to the next glyph origin after the segment
+  */
+GR2_API float gr_seg_advance_X(const gr_segment* pSeg/*not NULL*/);
+
+/** Returns the height advance for the segment. **/
+GR2_API float gr_seg_advance_Y(const gr_segment* pSeg/*not NULL*/);
+
+/** Returns the number of gr_char_infos in the segment. **/
+GR2_API unsigned int gr_seg_n_cinfo(const gr_segment* pSeg/*not NULL*/);
+
+/** Returns a gr_char_info at a given index in the segment. **/
+GR2_API const gr_char_info* gr_seg_cinfo(const gr_segment* pSeg/*not NULL*/, unsigned int index/*must be <number_of_CharInfo*/);
+
+/** Returns the number of glyph gr_slots in the segment. **/
+GR2_API unsigned int gr_seg_n_slots(const gr_segment* pSeg/*not NULL*/);      //one slot per glyph
+
+/** Returns the first gr_slot in the segment.
+  *
+  * The first slot in a segment has a gr_slot_prev_in_segment() of NULL. Slots are owned
+  * by their segment and are destroyed along with the segment.
+  */
+GR2_API const gr_slot* gr_seg_first_slot(gr_segment* pSeg/*not NULL*/);    //may give a base slot or a slot which is attached to another
+
+/** Returns the last gr_slot in the segment.
+  *
+  * The last slot in a segment has a gr_slot_next_in_segment() of NULL
+  */
+GR2_API const gr_slot* gr_seg_last_slot(gr_segment* pSeg/*not NULL*/);    //may give a base slot or a slot which is attached to another
+
+/** Justifies a linked list of slots for a line to a given width
+  *
+  * Passed a pointer to the start of a linked list of slots corresponding to a line, as
+  * set up by gr_slot_linebreak_before, this function will position the glyphs in the line
+  * to take up the given width. It is possible to specify a subrange within the line to process.
+  * This allows skipping of line initial or final whitespace, for example. While this will ensure
+  * that the subrange fits width, the line will still be positioned with the first glyph of the
+  * line at 0. So the resulting positions may be beyond width.
+  *
+  * @return float   The resulting width of the range of slots justified.
+  * @param pSeg     Pointer to the segment
+  * @param pStart   Pointer to the start of the line linked list (including skipped characters)
+  * @param pFont    Font to use for positioning
+  * @param width    Width in pixels in which to fit the line. If < 0. don't adjust natural width, just run justification passes
+  *                 to handle line end contextuals, if there are any.
+  * @param flags    Indicates line ending types. Default is linked list is a full line
+  * @param pFirst   If not NULL, the first slot in the list to be considered part of the line (so can skip)
+  * @param pLast    If not NULL, the last slot to process in the line (allow say trailing whitespace to be skipped)
+  */
+GR2_API float gr_seg_justify(gr_segment* pSeg/*not NULL*/, const gr_slot* pStart/*not NULL*/, const gr_font *pFont, double width, enum gr_justFlags flags, const gr_slot* pFirst, const gr_slot* pLast);
+
+/** Returns the next slot along in the segment.
+  *
+  * Slots are held in a linked list. This returns the next in the linked list. The slot
+  * may or may not be attached to another slot. Returns NULL at the end of the segment.
+  */
+GR2_API const gr_slot* gr_slot_next_in_segment(const gr_slot* p);
+
+/** Returns the previous slot along in the segment.
+  *
+  * Slots are held in a doubly linked list. This returns the previos slot in the linked
+  * list. This slot may or may not be attached to it. Returns NULL at the start of the
+  * segment.
+  */
+GR2_API const gr_slot* gr_slot_prev_in_segment(const gr_slot* p);
+
+/** Returns the attachment parent slot of this slot.
+  *
+  * Attached slots form a tree. This returns the parent of this slot in that tree. A
+  * base glyph which is not attached to another glyph, always returns NULL.
+  */
+GR2_API const gr_slot* gr_slot_attached_to(const gr_slot* p);
+
+/** Returns the first slot attached to this slot.
+  *
+  * Attached slots form a singly linked list from the parent. This returns the first
+  * slot in that list. Note that this is a reference to another slot that is also in
+  * the main segment doubly linked list.
+  *
+  * if gr_slot_first_attachment(p) != NULL then gr_slot_attached_to(gr_slot_first_attachment(p)) == p.
+  */
+GR2_API const gr_slot* gr_slot_first_attachment(const gr_slot* p);
+
+/** Returns the next slot attached to our attachment parent.
+  *
+  * This returns the next slot in the singly linked list of slots attached to this
+  * slot's parent. If there are no more such slots, NULL is returned. If there is
+  * no parent, i.e. the passed slot is a cluster base, then the next cluster base
+  * in graphical order (ltr, even for rtl text) is returned.
+  *
+  * if gr_slot_next_sibling_attachment(p) != NULL then gr_slot_attached_to(gr_slot_next_sibling_attachment(p)) == gr_slot_attached_to(p).
+  */
+GR2_API const gr_slot* gr_slot_next_sibling_attachment(const gr_slot* p);
+
+
+/** Returns glyph id of the slot
+  *
+  * Each slot has a glyphid which is rendered at the position given by the slot. This
+  * glyphid is the real glyph to be rendered and never a pseudo glyph.
+  */
+GR2_API unsigned short gr_slot_gid(const gr_slot* p);
+
+/** Returns X offset of glyph from start of segment **/
+GR2_API float gr_slot_origin_X(const gr_slot* p);
+
+/** Returns Y offset of glyph from start of segment **/
+GR2_API float gr_slot_origin_Y(const gr_slot* p);
+
+/** Returns the glyph advance for this glyph as adjusted for kerning
+  *
+  * @param p    Slot to give results for
+  * @param face gr_face of the glyphs. May be NULL if unhinted advances used
+  * @param font gr_font to scale for pixel results. If NULL returns design
+  *             units advance. If not NULL then returns pixel advance based
+  *             on hinted or scaled glyph advances in the font. face must be
+  *             passed for hinted advances to be used.
+  */
+GR2_API float gr_slot_advance_X(const gr_slot* p, const gr_face* face, const gr_font *font);
+
+/** Returns the vertical advance for the glyph in the slot adjusted for kerning
+  *
+  * Returns design units unless font is not NULL in which case the pixel value
+  * is returned scaled for the given font
+  */
+GR2_API float gr_slot_advance_Y(const gr_slot* p, const gr_face* face, const gr_font *font);
+
+/** Returns the gr_char_info index before us
+  *
+  * Returns the index of the gr_char_info that a cursor before this slot, would put
+  * an underlying cursor before. This may also be interpretted as each slot holding
+  * a set of char_infos that it is associated with and this function returning the
+  * index of the char_info with lowest index, from this set.
+  */
+GR2_API int gr_slot_before(const gr_slot* p/*not NULL*/);
+
+/** Returns the gr_char_info index after us
+  *
+  * Returns the index of the gr_char_info that a cursor after this slot would put an
+  * underlying cursor after. This may also be interpretted as each slot holding a set
+  * of char_infos that it is associated with and this function returning the index of
+  * the char_info with the highest index, from this set.
+  */
+GR2_API int gr_slot_after(const gr_slot* p/*not NULL*/);
+
+/** Returns the index of this slot in the segment
+  *
+  * Returns the index given to this slot during final positioning. This corresponds
+  * to the value returned br gr_cinfo_before() and gr_cinfo_after()
+  */
+GR2_API unsigned int gr_slot_index(const gr_slot* p/*not NULL*/);
+
+/** Return a slot attribute value
+  *
+  * Given a slot and an attribute along with a possible subattribute, return the
+  * corresponding value in the slot. See enum gr_attrCode for details of each attribute.
+  */
+GR2_API int gr_slot_attr(const gr_slot* p/*not NULL*/, const gr_segment* pSeg/*not NULL*/, enum gr_attrCode index, gr_uint8 subindex); //tbd - do we need to expose this?
+
+/** Returns whether text may be inserted before this glyph.
+  *
+  * This indicates whether a cursor can be put before this slot. It applies to
+  * base glyphs that have no parent as well as attached glyphs that have the
+  * .insert attribute explicitly set to true. This is the primary mechanism
+  * for identifying contiguous sequences of base plus diacritics.
+  */
+GR2_API int gr_slot_can_insert_before(const gr_slot* p);
+
+/** Returns the original gr_char_info index this slot refers to.
+  *
+  * Each Slot has a gr_char_info that it originates from. This is that gr_char_info.
+  * The index is passed to gr_seg_cinfo(). This information is useful for testing.
+  */
+GR2_API int gr_slot_original(const gr_slot* p/*not NULL*/);
+
+/** Breaks a segment into lines.
+  *
+  * Breaks the slot linked list at the given point in the linked list. It is up
+  * to the application to keep track of the first slot on each line.
+  */
+GR2_API void gr_slot_linebreak_before(gr_slot *p/*not NULL*/);
+
+#ifdef __cplusplus
+}
+#endif

+ 79 - 0
thirdparty/graphite/include/graphite2/Types.h

@@ -0,0 +1,79 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+    Alternatively, the contents of this file may be used under the terms
+    of the Mozilla Public License (http://mozilla.org/MPL) or the GNU
+    General Public License, as published by the Free Software Foundation,
+    either version 2 of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <stddef.h>
+
+typedef unsigned char   gr_uint8;
+typedef gr_uint8        gr_byte;
+typedef signed char     gr_int8;
+typedef unsigned short  gr_uint16;
+typedef short           gr_int16;
+typedef unsigned int    gr_uint32;
+typedef int             gr_int32;
+
+enum gr_encform {
+  gr_utf8 = 1/*sizeof(uint8)*/, gr_utf16 = 2/*sizeof(uint16)*/, gr_utf32 = 4/*sizeof(uint32)*/
+};
+
+
+// Define API function declspec/attributes and how each supported compiler or OS
+// allows us to specify them.
+#if defined __GNUC__
+  #define _gr2_and ,
+  #define _gr2_tag_fn(a)        __attribute__((a))
+  #define _gr2_deprecated_flag  deprecated
+  #define _gr2_export_flag      visibility("default")
+  #define _gr2_import_flag      visibility("default")
+  #define _gr2_static_flag      visibility("hidden")
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__
+  #if defined __GNUC__  // These three will be redefined for Windows
+    #undef _gr2_export_flag
+    #undef _gr2_import_flag
+    #undef _gr2_static_flag
+  #else  // How MSVC sepcifies function level attributes adn deprecation
+    #define _gr2_and
+    #define _gr2_tag_fn(a)       __declspec(a)
+    #define _gr2_deprecated_flag deprecated
+  #endif
+  #define _gr2_export_flag     dllexport
+  #define _gr2_import_flag     dllimport
+  #define _gr2_static_flag
+#endif
+
+#if defined GRAPHITE2_STATIC
+  #define GR2_API             _gr2_tag_fn(_gr2_static_flag)
+  #define GR2_DEPRECATED_API  _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_static_flag)
+#elif defined GRAPHITE2_EXPORTING
+  #define GR2_API             _gr2_tag_fn(_gr2_export_flag)
+  #define GR2_DEPRECATED_API  _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_export_flag)
+#else
+  #define GR2_API             _gr2_tag_fn(_gr2_import_flag)
+  #define GR2_DEPRECATED_API  _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_import_flag)
+#endif

+ 155 - 0
thirdparty/graphite/src/CmapCache.cpp

@@ -0,0 +1,155 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+#include "inc/Main.h"
+#include "inc/CmapCache.h"
+#include "inc/Face.h"
+#include "inc/TtfTypes.h"
+#include "inc/TtfUtil.h"
+
+
+using namespace graphite2;
+
+const void * bmp_subtable(const Face::Table & cmap)
+{
+    const void * stbl;
+    if (!cmap.size()) return 0;
+    if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()), cmap + cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()), cmap + cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()), cmap + cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()), cmap + cmap.size())
+     || TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size()), cmap + cmap.size()))
+        return stbl;
+    return 0;
+}
+
+const void * smp_subtable(const Face::Table & cmap)
+{
+    const void * stbl;
+    if (!cmap.size()) return 0;
+    if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()), cmap + cmap.size())
+     || TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size()), cmap + cmap.size()))
+        return stbl;
+    return 0;
+}
+
+template <unsigned int (*NextCodePoint)(const void *, unsigned int, int *),
+          uint16 (*LookupCodePoint)(const void *, unsigned int, int)>
+bool cache_subtable(uint16 * blocks[], const void * cst, const unsigned int limit)
+{
+    int rangeKey = 0;
+    uint32          codePoint = NextCodePoint(cst, 0, &rangeKey),
+                    prevCodePoint = 0;
+    while (codePoint < limit)
+    {
+        unsigned int block = codePoint >> 8;
+        if (!blocks[block])
+        {
+            blocks[block] = grzeroalloc<uint16>(0x100);
+            if (!blocks[block])
+                return false;
+        }
+        blocks[block][codePoint & 0xFF] = LookupCodePoint(cst, codePoint, rangeKey);
+        // prevent infinite loop
+        if (codePoint <= prevCodePoint)
+            codePoint = prevCodePoint + 1;
+        prevCodePoint = codePoint;
+        codePoint =  NextCodePoint(cst, codePoint, &rangeKey);
+    }
+    return true;
+}
+
+
+CachedCmap::CachedCmap(const Face & face)
+: m_isBmpOnly(true),
+  m_blocks(0)
+{
+    const Face::Table cmap(face, Tag::cmap);
+    if (!cmap)  return;
+
+    const void * bmp_cmap = bmp_subtable(cmap);
+    const void * smp_cmap = smp_subtable(cmap);
+    m_isBmpOnly = !smp_cmap;
+
+    m_blocks = grzeroalloc<uint16 *>(m_isBmpOnly ? 0x100 : 0x1100);
+    if (m_blocks && smp_cmap)
+    {
+        if (!cache_subtable<TtfUtil::CmapSubtable12NextCodepoint, TtfUtil::CmapSubtable12Lookup>(m_blocks, smp_cmap, 0x10FFFF))
+            return;
+    }
+
+    if (m_blocks && bmp_cmap)
+    {
+        if (!cache_subtable<TtfUtil::CmapSubtable4NextCodepoint, TtfUtil::CmapSubtable4Lookup>(m_blocks, bmp_cmap, 0xFFFF))
+            return;
+    }
+}
+
+CachedCmap::~CachedCmap() throw()
+{
+    if (!m_blocks) return;
+    unsigned int numBlocks = (m_isBmpOnly)? 0x100 : 0x1100;
+    for (unsigned int i = 0; i < numBlocks; i++)
+        free(m_blocks[i]);
+    free(m_blocks);
+}
+
+uint16 CachedCmap::operator [] (const uint32 usv) const throw()
+{
+    if ((m_isBmpOnly && usv > 0xFFFF) || (usv > 0x10FFFF))
+        return 0;
+    const uint32 block = 0xFFFF & (usv >> 8);
+    if (m_blocks[block])
+        return m_blocks[block][usv & 0xFF];
+    return 0;
+};
+
+CachedCmap::operator bool() const throw()
+{
+    return m_blocks != 0;
+}
+
+
+DirectCmap::DirectCmap(const Face & face)
+: _cmap(face, Tag::cmap),
+  _smp(smp_subtable(_cmap)),
+  _bmp(bmp_subtable(_cmap))
+{
+}
+
+uint16 DirectCmap::operator [] (const uint32 usv) const throw()
+{
+    return usv > 0xFFFF
+            ? (_smp ? TtfUtil::CmapSubtable12Lookup(_smp, usv, 0) : 0)
+            : TtfUtil::CmapSubtable4Lookup(_bmp, usv, 0);
+}
+
+DirectCmap::operator bool () const throw()
+{
+    return _cmap && _bmp;
+}
+

+ 782 - 0
thirdparty/graphite/src/Code.cpp

@@ -0,0 +1,782 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// This class represents loaded graphite stack machine code.  It performs
+// basic sanity checks, on the incoming code to prevent more obvious problems
+// from crashing graphite.
+// Author: Tim Eves
+
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include "graphite2/Segment.h"
+#include "inc/Code.h"
+#include "inc/Face.h"
+#include "inc/GlyphFace.h"
+#include "inc/GlyphCache.h"
+#include "inc/Machine.h"
+#include "inc/Rule.h"
+#include "inc/Silf.h"
+
+#include <cstdio>
+
+#ifdef NDEBUG
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+#endif
+
+
+using namespace graphite2;
+using namespace vm;
+
+namespace {
+
+inline bool is_return(const instr i) {
+    const opcode_t * opmap = Machine::getOpcodeTable();
+    const instr pop_ret  = *opmap[POP_RET].impl,
+                ret_zero = *opmap[RET_ZERO].impl,
+                ret_true = *opmap[RET_TRUE].impl;
+    return i == pop_ret || i == ret_zero || i == ret_true;
+}
+
+struct context
+{
+    context(uint8 ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false;}
+    struct {
+        uint8   changed:1,
+                referenced:1;
+    } flags;
+    uint8       codeRef;
+};
+
+} // end namespace
+
+
+class Machine::Code::decoder
+{
+public:
+    struct limits;
+    static const int NUMCONTEXTS = 256;
+
+    decoder(limits & lims, Code &code, enum passtype pt) throw();
+
+    bool        load(const byte * bc_begin, const byte * bc_end);
+    void        apply_analysis(instr * const code, instr * code_end);
+    byte        max_ref() { return _max_ref; }
+    int         out_index() const { return _out_index; }
+
+private:
+    void        set_ref(int index) throw();
+    void        set_noref(int index) throw();
+    void        set_changed(int index) throw();
+    opcode      fetch_opcode(const byte * bc);
+    void        analyse_opcode(const opcode, const int8 * const dp) throw();
+    bool        emit_opcode(opcode opc, const byte * & bc);
+    bool        validate_opcode(const byte opc, const byte * const bc);
+    bool        valid_upto(const uint16 limit, const uint16 x) const throw();
+    bool        test_context() const throw();
+    bool        test_ref(int8 index) const throw();
+    bool        test_attr(attrCode attr) const throw();
+    void        failure(const status_t s) const throw() { _code.failure(s); }
+
+    Code              & _code;
+    int                 _out_index;
+    uint16              _out_length;
+    instr             * _instr;
+    byte              * _data;
+    limits            & _max;
+    enum passtype       _passtype;
+    int                 _stack_depth;
+    bool                _in_ctxt_item;
+    int16               _slotref;
+    context             _contexts[NUMCONTEXTS];
+    byte                _max_ref;
+};
+
+
+struct Machine::Code::decoder::limits
+{
+  const byte       * bytecode;
+  const uint8        pre_context;
+  const uint16       rule_length,
+                     classes,
+                     glyf_attrs,
+                     features;
+  const byte         attrid[gr_slatMax];
+};
+
+inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw()
+: _code(code),
+  _out_index(code._constraint ? 0 : lims.pre_context),
+  _out_length(code._constraint ? 1 : lims.rule_length),
+  _instr(code._code), _data(code._data), _max(lims), _passtype(pt),
+  _stack_depth(0),
+  _in_ctxt_item(false),
+  _slotref(0),
+  _max_ref(0)
+{ }
+
+
+
+Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
+           uint8 pre_context, uint16 rule_length, const Silf & silf, const Face & face,
+           enum passtype pt, byte * * const _out)
+ :  _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
+    _constraint(is_constraint), _modify(false), _delete(false), _own(_out==0)
+{
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _code_cat(face.tele.code);
+#endif
+    assert(bytecode_begin != 0);
+    if (bytecode_begin == bytecode_end)
+    {
+      // ::new (this) Code();
+      return;
+    }
+    assert(bytecode_end > bytecode_begin);
+    const opcode_t *    op_to_fn = Machine::getOpcodeTable();
+
+    // Allocate code and data target buffers, these sizes are a worst case
+    // estimate.  Once we know their real sizes the we'll shrink them.
+    if (_out)   _code = reinterpret_cast<instr *>(*_out);
+    else        _code = static_cast<instr *>(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin, 1, is_constraint ? 0 : rule_length)));
+    _data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));
+
+    if (!_code || !_data) {
+        failure(alloc_failed);
+        return;
+    }
+
+    decoder::limits lims = {
+        bytecode_end,
+        pre_context,
+        rule_length,
+        silf.numClasses(),
+        face.glyphs().numAttrs(),
+        face.numFeatures(),
+        {1,1,1,1,1,1,1,1,
+         1,1,1,1,1,1,1,255,
+         1,1,1,1,1,1,1,1,
+         1,1,1,1,1,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, silf.numUser()}
+    };
+
+    decoder dec(lims, *this, pt);
+    if(!dec.load(bytecode_begin, bytecode_end))
+       return;
+
+    // Is this an empty program?
+    if (_instr_count == 0)
+    {
+      release_buffers();
+      ::new (this) Code();
+      return;
+    }
+
+    // When we reach the end check we've terminated it correctly
+    if (!is_return(_code[_instr_count-1])) {
+        failure(missing_return);
+        return;
+    }
+
+    assert((_constraint && immutable()) || !_constraint);
+    dec.apply_analysis(_code, _code + _instr_count);
+    _max_ref = dec.max_ref();
+
+    // Now we know exactly how much code and data the program really needs
+    // realloc the buffers to exactly the right size so we don't waste any
+    // memory.
+    assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_instr_count));
+    assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_data_size));
+    memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte));
+    size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr);
+    if (_out)
+        *_out += total_sz;
+    else
+    {
+      instr * const old_code = _code;
+      _code = static_cast<instr *>(realloc(_code, total_sz));
+      if (!_code) free(old_code);
+    }
+   _data = reinterpret_cast<byte *>(_code + (_instr_count+1));
+
+    if (!_code)
+    {
+        failure(alloc_failed);
+        return;
+    }
+
+    // Make this RET_ZERO, we should never reach this but just in case ...
+    _code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint];
+
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::count_bytes(_data_size + (_instr_count+1)*sizeof(instr));
+#endif
+}
+
+Machine::Code::~Code() throw ()
+{
+    if (_own)
+        release_buffers();
+}
+
+
+bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end)
+{
+    _max.bytecode = bc_end;
+    while (bc < bc_end)
+    {
+        const opcode opc = fetch_opcode(bc++);
+        if (opc == vm::MAX_OPCODE)
+            return false;
+
+        analyse_opcode(opc, reinterpret_cast<const int8 *>(bc));
+
+        if (!emit_opcode(opc, bc))
+            return false;
+    }
+
+    return bool(_code);
+}
+
+// Validation check and fixups.
+//
+
+opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
+{
+    const byte opc = *bc++;
+
+    // Do some basic sanity checks based on what we know about the opcode
+    if (!validate_opcode(opc, bc))  return MAX_OPCODE;
+
+    // And check its arguments as far as possible
+    switch (opcode(opc))
+    {
+        case NOP :
+            break;
+        case PUSH_BYTE :
+        case PUSH_BYTEU :
+        case PUSH_SHORT :
+        case PUSH_SHORTU :
+        case PUSH_LONG :
+            ++_stack_depth;
+            break;
+        case ADD :
+        case SUB :
+        case MUL :
+        case DIV :
+        case MIN_ :
+        case MAX_ :
+        case AND :
+        case OR :
+        case EQUAL :
+        case NOT_EQ :
+        case LESS :
+        case GTR :
+        case LESS_EQ :
+        case GTR_EQ :
+        case BITOR :
+        case BITAND :
+            if (--_stack_depth <= 0)
+                failure(underfull_stack);
+            break;
+        case NEG :
+        case TRUNC8 :
+        case TRUNC16 :
+        case NOT :
+        case BITNOT :
+        case BITSET :
+            if (_stack_depth <= 0)
+                failure(underfull_stack);
+            break;
+        case COND :
+            _stack_depth -= 2;
+            if (_stack_depth <= 0)
+                failure(underfull_stack);
+            break;
+        case NEXT_N :           // runtime checked
+            break;
+        case NEXT :
+        case COPY_NEXT :
+            ++_out_index;
+            if (_out_index < -1 || _out_index > _out_length || _slotref > _max.rule_length)
+                failure(out_of_range_data);
+            break;
+        case PUT_GLYPH_8BIT_OBS :
+            valid_upto(_max.classes, bc[0]);
+            test_context();
+            break;
+        case PUT_SUBS_8BIT_OBS :
+            test_ref(int8(bc[0]));
+            valid_upto(_max.classes, bc[1]);
+            valid_upto(_max.classes, bc[2]);
+            test_context();
+            break;
+        case PUT_COPY :
+            test_ref(int8(bc[0]));
+            test_context();
+            break;
+        case INSERT :
+            if (_passtype >= PASS_TYPE_POSITIONING)
+                failure(invalid_opcode);
+            ++_out_length;
+            if (_out_index < 0) ++_out_index;
+            if (_out_index < -1 || _out_index >= _out_length)
+                failure(out_of_range_data);
+            break;
+        case DELETE :
+            if (_passtype >= PASS_TYPE_POSITIONING)
+                failure(invalid_opcode);
+            if (_out_index < _max.pre_context)
+                failure(out_of_range_data);
+            --_out_index;
+            --_out_length;
+            if (_out_index < -1 || _out_index > _out_length)
+                failure(out_of_range_data);
+            break;
+        case ASSOC :
+            if (bc[0] == 0)
+                failure(out_of_range_data);
+            for (uint8 num = bc[0]; num; --num)
+                test_ref(int8(bc[num]));
+            test_context();
+            break;
+        case CNTXT_ITEM :
+            valid_upto(_max.rule_length, _max.pre_context + int8(bc[0]));
+            if (bc + 2 + bc[1] >= _max.bytecode)    failure(jump_past_end);
+            if (_in_ctxt_item)                      failure(nested_context_item);
+            break;
+        case ATTR_SET :
+        case ATTR_ADD :
+        case ATTR_SUB :
+        case ATTR_SET_SLOT :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
+            valid_upto(gr_slatMax, bc[0]);
+            if (attrCode(bc[0]) == gr_slatUserDefn)     // use IATTR for user attributes
+                failure(out_of_range_data);
+            test_attr(attrCode(bc[0]));
+            test_context();
+            break;
+        case IATTR_SET_SLOT :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
+            if (valid_upto(gr_slatMax, bc[0]))
+                valid_upto(_max.attrid[bc[0]], bc[1]);
+            test_attr(attrCode(bc[0]));
+            test_context();
+            break;
+        case PUSH_SLOT_ATTR :
+            ++_stack_depth;
+            valid_upto(gr_slatMax, bc[0]);
+            test_ref(int8(bc[1]));
+            if (attrCode(bc[0]) == gr_slatUserDefn)     // use IATTR for user attributes
+                failure(out_of_range_data);
+            test_attr(attrCode(bc[0]));
+            break;
+        case PUSH_GLYPH_ATTR_OBS :
+        case PUSH_ATT_TO_GATTR_OBS :
+            ++_stack_depth;
+            valid_upto(_max.glyf_attrs, bc[0]);
+            test_ref(int8(bc[1]));
+            break;
+        case PUSH_ATT_TO_GLYPH_METRIC :
+        case PUSH_GLYPH_METRIC :
+            ++_stack_depth;
+            valid_upto(kgmetDescent, bc[0]);
+            test_ref(int8(bc[1]));
+            // level: dp[2] no check necessary
+            break;
+        case PUSH_FEAT :
+            ++_stack_depth;
+            valid_upto(_max.features, bc[0]);
+            test_ref(int8(bc[1]));
+            break;
+        case PUSH_ISLOT_ATTR :
+            ++_stack_depth;
+            if (valid_upto(gr_slatMax, bc[0]))
+            {
+                test_ref(int8(bc[1]));
+                valid_upto(_max.attrid[bc[0]], bc[2]);
+            }
+            test_attr(attrCode(bc[0]));
+            break;
+        case PUSH_IGLYPH_ATTR :// not implemented
+            ++_stack_depth;
+            break;
+        case POP_RET :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
+            GR_FALLTHROUGH;
+            // no break
+        case RET_ZERO :
+        case RET_TRUE :
+            break;
+        case IATTR_SET :
+        case IATTR_ADD :
+        case IATTR_SUB :
+            if (--_stack_depth < 0)
+                failure(underfull_stack);
+            if (valid_upto(gr_slatMax, bc[0]))
+                valid_upto(_max.attrid[bc[0]], bc[1]);
+            test_attr(attrCode(bc[0]));
+            test_context();
+            break;
+        case PUSH_PROC_STATE :  // dummy: dp[0] no check necessary
+        case PUSH_VERSION :
+            ++_stack_depth;
+            break;
+        case PUT_SUBS :
+            test_ref(int8(bc[0]));
+            valid_upto(_max.classes, uint16(bc[1]<< 8) | bc[2]);
+            valid_upto(_max.classes, uint16(bc[3]<< 8) | bc[4]);
+            test_context();
+            break;
+        case PUT_SUBS2 :        // not implemented
+        case PUT_SUBS3 :        // not implemented
+            break;
+        case PUT_GLYPH :
+            valid_upto(_max.classes, uint16(bc[0]<< 8) | bc[1]);
+            test_context();
+            break;
+        case PUSH_GLYPH_ATTR :
+        case PUSH_ATT_TO_GLYPH_ATTR :
+            ++_stack_depth;
+            valid_upto(_max.glyf_attrs, uint16(bc[0]<< 8) | bc[1]);
+            test_ref(int8(bc[2]));
+            break;
+        case SET_FEAT :
+            valid_upto(_max.features, bc[0]);
+            test_ref(int8(bc[1]));
+            break;
+        default:
+            failure(invalid_opcode);
+            break;
+    }
+
+    return bool(_code) ? opcode(opc) : MAX_OPCODE;
+}
+
+
+void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8  * arg) throw()
+{
+  switch (opc)
+  {
+    case DELETE :
+      _code._delete = true;
+      break;
+    case ASSOC :
+      set_changed(0);
+//      for (uint8 num = arg[0]; num; --num)
+//        _analysis.set_noref(num);
+      break;
+    case PUT_GLYPH_8BIT_OBS :
+    case PUT_GLYPH :
+      _code._modify = true;
+      set_changed(0);
+      break;
+    case ATTR_SET :
+    case ATTR_ADD :
+    case ATTR_SUB :
+    case ATTR_SET_SLOT :
+    case IATTR_SET_SLOT :
+    case IATTR_SET :
+    case IATTR_ADD :
+    case IATTR_SUB :
+      set_noref(0);
+      break;
+    case NEXT :
+    case COPY_NEXT :
+      ++_slotref;
+      _contexts[_slotref] = context(uint8(_code._instr_count+1));
+      // if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
+      break;
+    case INSERT :
+      if (_slotref >= 0) --_slotref;
+      _code._modify = true;
+      break;
+    case PUT_SUBS_8BIT_OBS :    // slotref on 1st parameter
+    case PUT_SUBS :
+      _code._modify = true;
+      set_changed(0);
+      GR_FALLTHROUGH;
+      // no break
+    case PUT_COPY :
+      if (arg[0] != 0) { set_changed(0); _code._modify = true; }
+      set_ref(arg[0]);
+      break;
+    case PUSH_GLYPH_ATTR_OBS :
+    case PUSH_SLOT_ATTR :
+    case PUSH_GLYPH_METRIC :
+    case PUSH_ATT_TO_GATTR_OBS :
+    case PUSH_ATT_TO_GLYPH_METRIC :
+    case PUSH_ISLOT_ATTR :
+    case PUSH_FEAT :
+    case SET_FEAT :
+      set_ref(arg[1]);
+      break;
+    case PUSH_ATT_TO_GLYPH_ATTR :
+    case PUSH_GLYPH_ATTR :
+      set_ref(arg[2]);
+      break;
+    default:
+        break;
+  }
+}
+
+
+bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc)
+{
+    const opcode_t * op_to_fn = Machine::getOpcodeTable();
+    const opcode_t & op       = op_to_fn[opc];
+    if (op.impl[_code._constraint] == 0)
+    {
+        failure(unimplemented_opcode_used);
+        return false;
+    }
+
+    const size_t     param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
+
+    // Add this instruction
+    *_instr++ = op.impl[_code._constraint];
+    ++_code._instr_count;
+
+    // Grab the parameters
+    if (param_sz) {
+        memcpy(_data, bc, param_sz * sizeof(byte));
+        bc               += param_sz;
+        _data            += param_sz;
+        _code._data_size += param_sz;
+    }
+
+    // recursively decode a context item so we can split the skip into
+    // instruction and data portions.
+    if (opc == CNTXT_ITEM)
+    {
+        assert(_out_index == 0);
+        _in_ctxt_item = true;
+        _out_index = _max.pre_context + int8(_data[-2]);
+        _slotref = int8(_data[-2]);
+        _out_length = _max.rule_length;
+
+        const size_t ctxt_start = _code._instr_count;
+        byte & instr_skip = _data[-1];
+        byte & data_skip  = *_data++;
+        ++_code._data_size;
+        const byte *curr_end = _max.bytecode;
+
+        if (load(bc, bc + instr_skip))
+        {
+            bc += instr_skip;
+            data_skip  = instr_skip - byte(_code._instr_count - ctxt_start);
+            instr_skip =  byte(_code._instr_count - ctxt_start);
+            _max.bytecode = curr_end;
+
+            _out_length = 1;
+            _out_index = 0;
+            _slotref = 0;
+            _in_ctxt_item = false;
+        }
+        else
+        {
+            _out_index = 0;
+            _slotref = 0;
+            return false;
+        }
+    }
+
+    return bool(_code);
+}
+
+
+void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end)
+{
+    // insert TEMP_COPY commands for slots that need them (that change and are referenced later)
+    int tempcount = 0;
+    if (_code._constraint) return;
+
+    const instr temp_copy = Machine::getOpcodeTable()[TEMP_COPY].impl[0];
+    for (const context * c = _contexts, * const ce = c + _slotref; c < ce; ++c)
+    {
+        if (!c->flags.referenced || !c->flags.changed) continue;
+
+        instr * const tip = code + c->codeRef + tempcount;
+        memmove(tip+1, tip, (code_end - tip) * sizeof(instr));
+        *tip = temp_copy;
+        ++code_end;
+        ++tempcount;
+        _code._delete = true;
+    }
+
+    _code._instr_count = code_end - code;
+}
+
+
+inline
+bool Machine::Code::decoder::validate_opcode(const byte opc, const byte * const bc)
+{
+    if (opc >= MAX_OPCODE)
+    {
+        failure(invalid_opcode);
+        return false;
+    }
+    const opcode_t & op = Machine::getOpcodeTable()[opc];
+    if (op.impl[_code._constraint] == 0)
+    {
+        failure(unimplemented_opcode_used);
+        return false;
+    }
+    if (op.param_sz == VARARGS && bc >= _max.bytecode)
+    {
+        failure(arguments_exhausted);
+        return false;
+    }
+    const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
+    if (bc - 1 + param_sz >= _max.bytecode)
+    {
+        failure(arguments_exhausted);
+        return false;
+    }
+    return true;
+}
+
+
+bool Machine::Code::decoder::valid_upto(const uint16 limit, const uint16 x) const throw()
+{
+    const bool t = (limit != 0) && (x < limit);
+    if (!t) failure(out_of_range_data);
+    return t;
+}
+
+inline
+bool Machine::Code::decoder::test_ref(int8 index) const throw()
+{
+    if (_code._constraint && !_in_ctxt_item)
+    {
+        if (index > 0 || -index > _max.pre_context)
+        {
+            failure(out_of_range_data);
+            return false;
+        }
+    }
+    else
+    {
+      if (_max.rule_length == 0
+          || (_slotref + _max.pre_context + index >= _max.rule_length)
+          || (_slotref + _max.pre_context + index < 0))
+      {
+        failure(out_of_range_data);
+        return false;
+      }
+    }
+    return true;
+}
+
+bool Machine::Code::decoder::test_context() const throw()
+{
+    if (_out_index >= _out_length || _out_index < 0 || _slotref >= NUMCONTEXTS - 1)
+    {
+        failure(out_of_range_data);
+        return false;
+    }
+    return true;
+}
+
+bool Machine::Code::decoder::test_attr(attrCode) const throw()
+{
+#if 0   // This code is coming but causes backward compatibility problems.
+    if (_passtype < PASS_TYPE_POSITIONING)
+    {
+        if (attr != gr_slatBreak && attr != gr_slatDir && attr != gr_slatUserDefn
+                                 && attr != gr_slatCompRef)
+        {
+            failure(out_of_range_data);
+            return false;
+        }
+    }
+#endif
+    return true;
+}
+
+inline
+void Machine::Code::failure(const status_t s) throw() {
+    release_buffers();
+    _status = s;
+}
+
+
+inline
+void Machine::Code::decoder::set_ref(int index) throw() {
+    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
+    _contexts[index + _slotref].flags.referenced = true;
+    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
+}
+
+
+inline
+void Machine::Code::decoder::set_noref(int index) throw() {
+    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
+    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
+}
+
+
+inline
+void Machine::Code::decoder::set_changed(int index) throw() {
+    if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
+    _contexts[index + _slotref].flags.changed= true;
+    if (index + _slotref > _max_ref) _max_ref = index + _slotref;
+}
+
+
+void Machine::Code::release_buffers() throw()
+{
+    if (_own)
+        free(_code);
+    _code = 0;
+    _data = 0;
+    _own  = false;
+}
+
+
+int32 Machine::Code::run(Machine & m, slotref * & map) const
+{
+//    assert(_own);
+    assert(*this);          // Check we are actually runnable
+
+    if (m.slotMap().size() <= size_t(_max_ref + m.slotMap().context())
+        || m.slotMap()[_max_ref + m.slotMap().context()] == 0)
+    {
+        m._status = Machine::slot_offset_out_bounds;
+        return 1;
+//        return m.run(_code, _data, map);
+    }
+
+    return  m.run(_code, _data, map);
+}

+ 1115 - 0
thirdparty/graphite/src/Collider.cpp

@@ -0,0 +1,1115 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <algorithm>
+#include <limits>
+#include <cmath>
+#include <string>
+#include <functional>
+#include "inc/Collider.h"
+#include "inc/Segment.h"
+#include "inc/Slot.h"
+#include "inc/GlyphCache.h"
+#include "inc/Sparse.h"
+
+#define ISQRT2 0.707106781f
+
+// Possible rounding error for subbox boundaries: 0.016 = 1/64 = 1/256 * 4
+// (values in font range from 0..256)
+// #define SUBBOX_RND_ERR 0.016
+
+using namespace graphite2;
+
+////    SHIFT-COLLIDER    ////
+
+// Initialize the Collider to hold the basic movement limits for the
+// target slot, the one we are focusing on fixing.
+bool ShiftCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin, float marginWeight,
+    const Position &currShift, const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout)
+{
+    int i;
+    float mx, mn;
+    float a, shift;
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    unsigned short gid = aSlot->gid();
+    if (!gc.check(gid))
+        return false;
+    const BBox &bb = gc.getBoundingBBox(gid);
+    const SlantBox &sb = gc.getBoundingSlantBox(gid);
+    //float sx = aSlot->origin().x + currShift.x;
+    //float sy = aSlot->origin().y + currShift.y;
+    if (currOffset.x != 0.f || currOffset.y != 0.f)
+        _limit = Rect(limit.bl - currOffset, limit.tr - currOffset);
+    else
+        _limit = limit;
+    // For a ShiftCollider, these indices indicate which vector we are moving by:
+    // each _ranges represents absolute space with respect to the origin of the slot. Thus take into account true origins but subtract the vmin for the slot
+    for (i = 0; i < 4; ++i)
+    {
+        switch (i) {
+            case 0 :	// x direction
+                mn = _limit.bl.x + currOffset.x;
+                mx = _limit.tr.x + currOffset.x;
+                _len[i] = bb.xa - bb.xi;
+                a = currOffset.y + currShift.y;
+                _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
+                break;
+            case 1 :	// y direction
+                mn = _limit.bl.y + currOffset.y;
+                mx = _limit.tr.y + currOffset.y;
+                _len[i] = bb.ya - bb.yi;
+                a = currOffset.x + currShift.x;
+                _ranges[i].initialise<XY>(mn, mx, margin, marginWeight, a);
+                break;
+            case 2 :	// sum (negatively sloped diagonal boundaries)
+                // pick closest x,y limit boundaries in s direction
+                shift = currOffset.x + currOffset.y + currShift.x + currShift.y;
+                mn = -2 * min(currShift.x - _limit.bl.x, currShift.y - _limit.bl.y) + shift;
+                mx = 2 * min(_limit.tr.x - currShift.x, _limit.tr.y - currShift.y) + shift;
+                _len[i] = sb.sa - sb.si;
+                a = currOffset.x - currOffset.y + currShift.x - currShift.y;
+                _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
+                break;
+            case 3 :	// diff (positively sloped diagonal boundaries)
+                // pick closest x,y limit boundaries in d direction
+                shift = currOffset.x - currOffset.y + currShift.x - currShift.y;
+                mn = -2 * min(currShift.x - _limit.bl.x, _limit.tr.y - currShift.y) + shift;
+                mx = 2 * min(_limit.tr.x - currShift.x, currShift.y - _limit.bl.y) + shift;
+                _len[i] = sb.da - sb.di;
+                a = currOffset.x + currOffset.y + currShift.x + currShift.y;
+                _ranges[i].initialise<SD>(mn, mx, margin / ISQRT2, marginWeight, a);
+                break;
+        }
+    }
+
+	_target = aSlot;
+    if ((dir & 1) == 0)
+    {
+        // For LTR, switch and negate x limits.
+        _limit.bl.x = -1 * limit.tr.x;
+        //_limit.tr.x = -1 * limit.bl.x;
+    }
+    _currOffset = currOffset;
+    _currShift = currShift;
+    _origin = aSlot->origin() - currOffset;     // the original anchor position of the glyph
+
+	_margin = margin;
+	_marginWt = marginWeight;
+
+    SlotCollision *c = seg->collisionInfo(aSlot);
+    _seqClass = c->seqClass();
+	_seqProxClass = c->seqProxClass();
+    _seqOrder = c->seqOrder();
+    return true;
+}
+
+template <class O>
+float sdm(float vi, float va, float mx, float my, O op)
+{
+    float res = 2 * mx - vi;
+    if (op(res, vi + 2 * my))
+    {
+        res = va + 2 * my;
+        if (op(res, 2 * mx - va))
+            res = mx + my;
+    }
+    return res;
+}
+
+// Mark an area with a cost that can vary along the x or y axis. The region is expressed in terms of the centre of the target glyph in each axis
+void ShiftCollider::addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int axis)
+{
+    float a, c;
+    switch (axis) {
+        case 0 :
+             if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
+            {
+                a = org.y + 0.5f * (bb.yi + bb.ya);
+                c = 0.5f * (bb.xi + bb.xa);
+                if (isx)
+                    _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, m,
+                                                (minright ? box.tr.x : box.bl.x) - c, a, 0, false);
+                else
+                    _ranges[axis].weighted<XY>(box.bl.x - c, box.tr.x - c, weight, a, 0, 0, org.y,
+                                                m * (a * a + sqr((minright ? box.tr.y : box.bl.y) - 0.5f * (bb.yi + bb.ya))), false);
+            }
+            break;
+        case 1 :
+            if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
+            {
+                a = org.x + 0.5f * (bb.xi + bb.xa);
+                c = 0.5f * (bb.yi + bb.ya);
+                if (isx)
+                    _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, 0, 0, org.x,
+                                                m * (a * a + sqr((minright ? box.tr.x : box.bl.x) - 0.5f * (bb.xi + bb.xa))), false);
+                else
+                    _ranges[axis].weighted<XY>(box.bl.y - c, box.tr.y - c, weight, a, m,
+                                                (minright ? box.tr.y : box.bl.y) - c, a, 0, false);
+            }
+            break;
+        case 2 :
+            if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di)
+            {
+                float d = org.x - org.y + 0.5f * (sb.di + sb.da);
+                c = 0.5f * (sb.si + sb.sa);
+                float smax = min(2 * box.tr.x - d, 2 * box.tr.y + d);
+                float smin = max(2 * box.bl.x - d, 2 * box.bl.y + d);
+                if (smin > smax) return;
+                float si;
+                a = d;
+                if (isx)
+                    si = 2 * (minright ? box.tr.x : box.bl.x) - a;
+                else
+                    si = 2 * (minright ? box.tr.y : box.bl.y) + a;
+                _ranges[axis].weighted<SD>(smin - c, smax - c, weight / 2, a, m / 2, si, 0, 0, isx);
+            }
+            break;
+        case 3 :
+            if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si)
+            {
+                float s = org.x + org.y + 0.5f * (sb.si + sb.sa);
+                c = 0.5f * (sb.di + sb.da);
+                float dmax = min(2 * box.tr.x - s, s - 2 * box.bl.y);
+                float dmin = max(2 * box.bl.x - s, s - 2 * box.tr.y);
+                if (dmin > dmax) return;
+                float di;
+                a = s;
+                if (isx)
+                    di = 2 * (minright ? box.tr.x : box.bl.x) - a;
+                else
+                    di = 2 * (minright ? box.tr.y : box.bl.y) + a;
+                _ranges[axis].weighted<SD>(dmin - c, dmax - c, weight / 2, a, m / 2, di, 0, 0, !isx);
+            }
+            break;
+        default :
+            break;
+    }
+    return;
+}
+
+// Mark an area with an absolute cost, making it completely inaccessible.
+inline void ShiftCollider::removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int axis)
+{
+    float c;
+    switch (axis) {
+        case 0 :
+            if (box.bl.y < org.y + bb.ya && box.tr.y > org.y + bb.yi && box.width() > 0)
+            {
+                c = 0.5f * (bb.xi + bb.xa);
+                _ranges[axis].exclude(box.bl.x - c, box.tr.x - c);
+            }
+            break;
+        case 1 :
+            if (box.bl.x < org.x + bb.xa && box.tr.x > org.x + bb.xi && box.height() > 0)
+            {
+                c = 0.5f * (bb.yi + bb.ya);
+                _ranges[axis].exclude(box.bl.y - c, box.tr.y - c);
+            }
+            break;
+        case 2 :
+            if (box.bl.x - box.tr.y < org.x - org.y + sb.da && box.tr.x - box.bl.y > org.x - org.y + sb.di
+                && box.width() > 0 && box.height() > 0)
+            {
+                float di = org.x - org.y + sb.di;
+                float da = org.x - org.y + sb.da;
+                float smax = sdm(di, da, box.tr.x, box.tr.y, std::greater<float>());
+                float smin = sdm(da, di, box.bl.x, box.bl.y, std::less<float>());
+                c = 0.5f * (sb.si + sb.sa);
+                _ranges[axis].exclude(smin - c, smax - c);
+            }
+            break;
+        case 3 :
+            if (box.bl.x + box.bl.y < org.x + org.y + sb.sa && box.tr.x + box.tr.y > org.x + org.y + sb.si
+                && box.width() > 0 && box.height() > 0)
+            {
+                float si = org.x + org.y + sb.si;
+                float sa = org.x + org.y + sb.sa;
+                float dmax = sdm(si, sa, box.tr.x, -box.bl.y, std::greater<float>());
+                float dmin = sdm(sa, si, box.bl.x, -box.tr.y, std::less<float>());
+                c = 0.5f * (sb.di + sb.da);
+                _ranges[axis].exclude(dmin - c, dmax - c);
+            }
+            break;
+        default :
+            break;
+    }
+    return;
+}
+
+// Adjust the movement limits for the target to avoid having it collide
+// with the given neighbor slot. Also determine if there is in fact a collision
+// between the target and the given slot.
+bool ShiftCollider::mergeSlot(Segment *seg, Slot *slot, const SlotCollision *cslot, const Position &currShift,
+		bool isAfter,  // slot is logically after _target
+		bool sameCluster, bool &hasCol, bool isExclusion,
+        GR_MAYBE_UNUSED json * const dbgout )
+{
+    bool isCol = false;
+    const float sx = slot->origin().x - _origin.x + currShift.x;
+    const float sy = slot->origin().y - _origin.y + currShift.y;
+    const float sd = sx - sy;
+    const float ss = sx + sy;
+    float vmin, vmax;
+    float omin, omax, otmin, otmax;
+    float cmin, cmax;   // target limits
+    float torg;
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    const unsigned short gid = slot->gid();
+    if (!gc.check(gid))
+        return false;
+    const BBox &bb = gc.getBoundingBBox(gid);
+
+    // SlotCollision * cslot = seg->collisionInfo(slot);
+    int orderFlags = 0;
+    bool sameClass = _seqProxClass == 0 && cslot->seqClass() == _seqClass;
+    if (sameCluster && _seqClass
+        && (sameClass || (_seqProxClass != 0 && cslot->seqClass() == _seqProxClass)))
+		// Force the target glyph to be in the specified direction from the slot we're testing.
+        orderFlags = _seqOrder;
+
+    // short circuit if only interested in direct collision and we are out of range
+    if (orderFlags || (sx + bb.xa + _margin >= _limit.bl.x && sx + bb.xi - _margin <= _limit.tr.x)
+                    || (sy + bb.ya + _margin >= _limit.bl.y && sy + bb.yi - _margin <= _limit.tr.y))
+
+    {
+        const float tx = _currOffset.x + _currShift.x;
+        const float ty = _currOffset.y + _currShift.y;
+        const float td = tx - ty;
+        const float ts = tx + ty;
+        const SlantBox &sb = gc.getBoundingSlantBox(gid);
+        const unsigned short tgid = _target->gid();
+        const BBox &tbb = gc.getBoundingBBox(tgid);
+        const SlantBox &tsb = gc.getBoundingSlantBox(tgid);
+        float seq_above_wt = cslot->seqAboveWt();
+        float seq_below_wt = cslot->seqBelowWt();
+        float seq_valign_wt = cslot->seqValignWt();
+        float lmargin;
+        // if isAfter, invert orderFlags for diagonal orders.
+        if (isAfter)
+        {
+            // invert appropriate bits
+            orderFlags ^= (sameClass ? 0x3F : 0x3);
+            // consider 2 bits at a time, non overlapping. If both bits set, clear them
+            orderFlags = orderFlags ^ ((((orderFlags >> 1) & orderFlags) & 0x15) * 3);
+        }
+
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+            dbgout->setenv(0, slot);
+#endif
+
+        // Process main bounding octabox.
+        for (int i = 0; i < 4; ++i)
+        {
+            switch (i) {
+                case 0 :	// x direction
+                    vmin = max(max(bb.xi - tbb.xa + sx, sb.di - tsb.da + ty + sd), sb.si - tsb.sa - ty + ss);
+                    vmax = min(min(bb.xa - tbb.xi + sx, sb.da - tsb.di + ty + sd), sb.sa - tsb.si - ty + ss);
+                    otmin = tbb.yi + ty;
+                    otmax = tbb.ya + ty;
+                    omin = bb.yi + sy;
+                    omax = bb.ya + sy;
+                    torg = _currOffset.x;
+                    cmin = _limit.bl.x + torg;
+                    cmax = _limit.tr.x - tbb.xi + tbb.xa + torg;
+                    lmargin = _margin;
+                    break;
+                case 1 :	// y direction
+                    vmin = max(max(bb.yi - tbb.ya + sy, tsb.di - sb.da + tx - sd), sb.si - tsb.sa - tx + ss);
+                    vmax = min(min(bb.ya - tbb.yi + sy, tsb.da - sb.di + tx - sd), sb.sa - tsb.si - tx + ss);
+                    otmin = tbb.xi + tx;
+                    otmax = tbb.xa + tx;
+                    omin = bb.xi + sx;
+                    omax = bb.xa + sx;
+                    torg = _currOffset.y;
+                    cmin = _limit.bl.y + torg;
+                    cmax = _limit.tr.y - tbb.yi + tbb.ya + torg;
+                    lmargin = _margin;
+                    break;
+                case 2 :    // sum - moving along the positively-sloped vector, so the boundaries are the
+                            // negatively-sloped boundaries.
+                    vmin = max(max(sb.si - tsb.sa + ss, 2 * (bb.yi - tbb.ya + sy) + td), 2 * (bb.xi - tbb.xa + sx) - td);
+                    vmax = min(min(sb.sa - tsb.si + ss, 2 * (bb.ya - tbb.yi + sy) + td), 2 * (bb.xa - tbb.xi + sx) - td);
+                    otmin = tsb.di + td;
+                    otmax = tsb.da + td;
+                    omin = sb.di + sd;
+                    omax = sb.da + sd;
+                    torg = _currOffset.x + _currOffset.y;
+                    cmin = _limit.bl.x + _limit.bl.y + torg;
+                    cmax = _limit.tr.x + _limit.tr.y - tsb.si + tsb.sa + torg;
+                    lmargin = _margin / ISQRT2;
+                    break;
+                case 3 :    // diff - moving along the negatively-sloped vector, so the boundaries are the
+                            // positively-sloped boundaries.
+                    vmin = max(max(sb.di - tsb.da + sd, 2 * (bb.xi - tbb.xa + sx) - ts), -2 * (bb.ya - tbb.yi + sy) + ts);
+                    vmax = min(min(sb.da - tsb.di + sd, 2 * (bb.xa - tbb.xi + sx) - ts), -2 * (bb.yi - tbb.ya + sy) + ts);
+                    otmin = tsb.si + ts;
+                    otmax = tsb.sa + ts;
+                    omin = sb.si + ss;
+                    omax = sb.sa + ss;
+                    torg = _currOffset.x - _currOffset.y;
+                    cmin = _limit.bl.x - _limit.tr.y + torg;
+                    cmax = _limit.tr.x - _limit.bl.y - tsb.di + tsb.da + torg;
+                    lmargin = _margin / ISQRT2;
+                    break;
+                default :
+                    continue;
+            }
+
+#if !defined GRAPHITE2_NTRACING
+            if (dbgout)
+                dbgout->setenv(1, reinterpret_cast<void *>(-1));
+#define DBGTAG(x) if (dbgout) dbgout->setenv(1, reinterpret_cast<void *>(-x));
+#else
+#define DBGTAG(x)
+#endif
+
+            if (orderFlags)
+            {
+                Position org(tx, ty);
+                float xminf = _limit.bl.x + _currOffset.x + tbb.xi;
+                float xpinf = _limit.tr.x + _currOffset.x + tbb.xa;
+                float ypinf = _limit.tr.y + _currOffset.y + tbb.ya;
+                float yminf = _limit.bl.y + _currOffset.y + tbb.yi;
+                switch (orderFlags) {
+                    case SlotCollision::SEQ_ORDER_RIGHTUP :
+                    {
+                        float r1Xedge = cslot->seqAboveXoff() + 0.5f * (bb.xi + bb.xa) + sx;
+                        float r3Xedge = cslot->seqBelowXlim() + bb.xa + sx + 0.5f * (tbb.xa - tbb.xi);
+                        float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
+
+                        // DBGTAG(1x) means the regions are up and right
+                        // region 1
+                        DBGTAG(11)
+                        addBox_slope(true, Rect(Position(xminf, r2Yedge), Position(r1Xedge, ypinf)),
+                                        tbb, tsb, org, 0, seq_above_wt, true, i);
+                        // region 2
+                        DBGTAG(12)
+                        removeBox(Rect(Position(xminf, yminf), Position(r3Xedge, r2Yedge)), tbb, tsb, org, i);
+                        // region 3, which end is zero is irrelevant since m weight is 0
+                        DBGTAG(13)
+                        addBox_slope(true, Rect(Position(r3Xedge, yminf), Position(xpinf, r2Yedge - cslot->seqValignHt())),
+                                        tbb, tsb, org, seq_below_wt, 0, true, i);
+                        // region 4
+                        DBGTAG(14)
+                        addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge), Position(xpinf, r2Yedge + cslot->seqValignHt())),
+                                        tbb, tsb, org, 0, seq_valign_wt, true, i);
+                        // region 5
+                        DBGTAG(15)
+                        addBox_slope(false, Rect(Position(sx + bb.xi, r2Yedge - cslot->seqValignHt()), Position(xpinf, r2Yedge)),
+                                        tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
+                        break;
+                    }
+                    case SlotCollision::SEQ_ORDER_LEFTDOWN :
+                    {
+                        float r1Xedge = 0.5f * (bb.xi + bb.xa) + cslot->seqAboveXoff() + sx;
+                        float r3Xedge = bb.xi - cslot->seqBelowXlim() + sx - 0.5f * (tbb.xa - tbb.xi);
+                        float r2Yedge = 0.5f * (bb.yi + bb.ya) + sy;
+                        // DBGTAG(2x) means the regions are up and right
+                        // region 1
+                        DBGTAG(21)
+                        addBox_slope(true, Rect(Position(r1Xedge, yminf), Position(xpinf, r2Yedge)),
+                                        tbb, tsb, org, 0, seq_above_wt, false, i);
+                        // region 2
+                        DBGTAG(22)
+                        removeBox(Rect(Position(r3Xedge, r2Yedge), Position(xpinf, ypinf)), tbb, tsb, org, i);
+                        // region 3
+                        DBGTAG(23)
+                        addBox_slope(true, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()), Position(r3Xedge, ypinf)),
+                                        tbb, tsb, org, seq_below_wt, 0, false, i);
+                        // region 4
+                        DBGTAG(24)
+                        addBox_slope(false, Rect(Position(xminf, r2Yedge), Position(sx + bb.xa, r2Yedge + cslot->seqValignHt())),
+                                        tbb, tsb, org, 0, seq_valign_wt, true, i);
+                        // region 5
+                        DBGTAG(25)
+                        addBox_slope(false, Rect(Position(xminf, r2Yedge - cslot->seqValignHt()),
+                                        Position(sx + bb.xa, r2Yedge)), tbb, tsb, org, seq_below_wt, seq_valign_wt, false, i);
+                        break;
+                    }
+                    case SlotCollision::SEQ_ORDER_NOABOVE : // enforce neighboring glyph being above
+                        DBGTAG(31);
+                        removeBox(Rect(Position(bb.xi - tbb.xa + sx, sy + bb.ya),
+                                        Position(bb.xa - tbb.xi + sx, ypinf)), tbb, tsb, org, i);
+                        break;
+                    case SlotCollision::SEQ_ORDER_NOBELOW :	// enforce neighboring glyph being below
+                        DBGTAG(32);
+                        removeBox(Rect(Position(bb.xi - tbb.xa + sx, yminf),
+                                        Position(bb.xa - tbb.xi + sx, sy + bb.yi)), tbb, tsb, org, i);
+                        break;
+                    case SlotCollision::SEQ_ORDER_NOLEFT :  // enforce neighboring glyph being to the left
+                        DBGTAG(33)
+                        removeBox(Rect(Position(xminf, bb.yi - tbb.ya + sy),
+                                        Position(bb.xi - tbb.xa + sx, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
+                        break;
+                    case SlotCollision::SEQ_ORDER_NORIGHT : // enforce neighboring glyph being to the right
+                        DBGTAG(34)
+                        removeBox(Rect(Position(bb.xa - tbb.xi + sx, bb.yi - tbb.ya + sy),
+                                        Position(xpinf, bb.ya - tbb.yi + sy)), tbb, tsb, org, i);
+                        break;
+                    default :
+                        break;
+                }
+            }
+
+            if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
+                continue;
+
+            // Process sub-boxes that are defined for this glyph.
+            // We only need to do this if there was in fact a collision with the main octabox.
+            uint8 numsub = gc.numSubBounds(gid);
+            if (numsub > 0)
+            {
+                bool anyhits = false;
+                for (int j = 0; j < numsub; ++j)
+                {
+                    const BBox &sbb = gc.getSubBoundingBBox(gid, j);
+                    const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, j);
+                    switch (i) {
+                        case 0 :    // x
+                            vmin = max(max(sbb.xi-tbb.xa+sx, ssb.di-tsb.da+sd+ty), ssb.si-tsb.sa+ss-ty);
+                            vmax = min(min(sbb.xa-tbb.xi+sx, ssb.da-tsb.di+sd+ty), ssb.sa-tsb.si+ss-ty);
+                            omin = sbb.yi + sy;
+                            omax = sbb.ya + sy;
+                            break;
+                        case 1 :    // y
+                            vmin = max(max(sbb.yi-tbb.ya+sy, tsb.di-ssb.da-sd+tx), ssb.si-tsb.sa+ss-tx);
+                            vmax = min(min(sbb.ya-tbb.yi+sy, tsb.da-ssb.di-sd+tx), ssb.sa-tsb.si+ss-tx);
+                            omin = sbb.xi + sx;
+                            omax = sbb.xa + sx;
+                            break;
+                        case 2 :    // sum
+                            vmin = max(max(ssb.si-tsb.sa+ss, 2*(sbb.yi-tbb.ya+sy)+td), 2*(sbb.xi-tbb.xa+sx)-td);
+                            vmax = min(min(ssb.sa-tsb.si+ss, 2*(sbb.ya-tbb.yi+sy)+td), 2*(sbb.xa-tbb.xi+sx)-td);
+                            omin = ssb.di + sd;
+                            omax = ssb.da + sd;
+                            break;
+                        case 3 :    // diff
+                            vmin = max(max(ssb.di-tsb.da+sd, 2*(sbb.xi-tbb.xa+sx)-ts), -2*(sbb.ya-tbb.yi+sy)+ts);
+                            vmax = min(min(ssb.da-tsb.di+sd, 2*(sbb.xa-tbb.xi+sx)-ts), -2*(sbb.yi-tbb.ya+sy)+ts);
+                            omin = ssb.si + ss;
+                            omax = ssb.sa + ss;
+                            break;
+                    }
+                    if (vmax < cmin - lmargin || vmin > cmax + lmargin || omax < otmin - lmargin || omin > otmax + lmargin)
+                        continue;
+
+#if !defined GRAPHITE2_NTRACING
+                    if (dbgout)
+                        dbgout->setenv(1, reinterpret_cast<void *>(j));
+#endif
+                    if (omin > otmax)
+                        _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
+                                                sqr(lmargin - omin + otmax) * _marginWt, false);
+                    else if (omax < otmin)
+                        _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
+                                                sqr(lmargin - otmin + omax) * _marginWt, false);
+                    else
+                        _ranges[i].exclude_with_margins(vmin, vmax, i);
+                    anyhits = true;
+                }
+                if (anyhits)
+                    isCol = true;
+            }
+            else // no sub-boxes
+            {
+#if !defined GRAPHITE2_NTRACING
+                    if (dbgout)
+                        dbgout->setenv(1, reinterpret_cast<void *>(-1));
+#endif
+                isCol = true;
+                if (omin > otmax)
+                    _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
+                                            sqr(lmargin - omin + otmax) * _marginWt, false);
+                else if (omax < otmin)
+                    _ranges[i].weightedAxis(i, vmin - lmargin, vmax + lmargin, 0, 0, 0, 0, 0,
+                                            sqr(lmargin - otmin + omax) * _marginWt, false);
+                else
+                    _ranges[i].exclude_with_margins(vmin, vmax, i);
+
+            }
+        }
+    }
+    bool res = true;
+    if (cslot->exclGlyph() > 0 && gc.check(cslot->exclGlyph()) && !isExclusion)
+    {
+        // Set up the bogus slot representing the exclusion glyph.
+        Slot *exclSlot = seg->newSlot();
+        if (!exclSlot)
+            return res;
+        exclSlot->setGlyph(seg, cslot->exclGlyph());
+        Position exclOrigin(slot->origin() + cslot->exclOffset());
+        exclSlot->origin(exclOrigin);
+        SlotCollision exclInfo(seg, exclSlot);
+        res &= mergeSlot(seg, exclSlot, &exclInfo, currShift, isAfter, sameCluster, isCol, true, dbgout );
+        seg->freeSlot(exclSlot);
+    }
+    hasCol |= isCol;
+    return res;
+
+}   // end of ShiftCollider::mergeSlot
+
+
+// Figure out where to move the target glyph to, and return the amount to shift by.
+Position ShiftCollider::resolve(GR_MAYBE_UNUSED Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout)
+{
+    float tbase;
+    float totalCost = (float)(std::numeric_limits<float>::max() / 2);
+    Position resultPos = Position(0, 0);
+#if !defined GRAPHITE2_NTRACING
+	int bestAxis = -1;
+    if (dbgout)
+    {
+		outputJsonDbgStartSlot(dbgout, seg);
+        *dbgout << "vectors" << json::array;
+    }
+#endif
+    isCol = true;
+    for (int i = 0; i < 4; ++i)
+    {
+        float bestCost = -1;
+        float bestPos;
+        // Calculate the margin depending on whether we are moving diagonally or not:
+        switch (i) {
+            case 0 :	// x direction
+                tbase = _currOffset.x;
+                break;
+            case 1 :	// y direction
+                tbase = _currOffset.y;
+                break;
+            case 2 :	// sum (negatively-sloped diagonals)
+                tbase = _currOffset.x + _currOffset.y;
+                break;
+            case 3 :	// diff (positively-sloped diagonals)
+                tbase = _currOffset.x - _currOffset.y;
+                break;
+        }
+        Position testp;
+        bestPos = _ranges[i].closest(0, bestCost) - tbase;     // Get the best relative position
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+            outputJsonDbgOneVector(dbgout, seg, i, tbase, bestCost, bestPos) ;
+#endif
+        if (bestCost >= 0.0f)
+        {
+            isCol = false;
+            switch (i) {
+                case 0 : testp = Position(bestPos, _currShift.y); break;
+                case 1 : testp = Position(_currShift.x, bestPos); break;
+                case 2 : testp = Position(0.5f * (_currShift.x - _currShift.y + bestPos), 0.5f * (_currShift.y - _currShift.x + bestPos)); break;
+                case 3 : testp = Position(0.5f * (_currShift.x + _currShift.y + bestPos), 0.5f * (_currShift.x + _currShift.y - bestPos)); break;
+            }
+            if (bestCost < totalCost - 0.01f)
+            {
+                totalCost = bestCost;
+                resultPos = testp;
+#if !defined GRAPHITE2_NTRACING
+                bestAxis = i;
+#endif
+            }
+        }
+    }  // end of loop over 4 directions
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        outputJsonDbgEndSlot(dbgout, resultPos, bestAxis, isCol);
+#endif
+
+    return resultPos;
+
+}   // end of ShiftCollider::resolve
+
+
+#if !defined GRAPHITE2_NTRACING
+
+void ShiftCollider::outputJsonDbg(json * const dbgout, Segment *seg, int axis)
+{
+    int axisMax = axis;
+    if (axis < 0) // output all axes
+    {
+        *dbgout << "gid" << _target->gid()
+            << "limit" << _limit
+            << "target" << json::object
+                << "origin" << _target->origin()
+                << "margin" << _margin
+                << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
+                << "slantbox" << seg->getFace()->glyphs().slant(_target->gid())
+                << json::close; // target object
+        *dbgout << "ranges" << json::array;
+        axis = 0;
+        axisMax = 3;
+    }
+    for (int iAxis = axis; iAxis <= axisMax; ++iAxis)
+    {
+        *dbgout << json::flat << json::array << _ranges[iAxis].position();
+        for (Zones::const_iterator s = _ranges[iAxis].begin(), e = _ranges[iAxis].end(); s != e; ++s)
+            *dbgout << json::flat << json::array
+                        << Position(s->x, s->xm) << s->sm << s->smx << s->c
+                    << json::close;
+        *dbgout << json::close;
+    }
+    if (axis < axisMax) // looped through the _ranges array for all axes
+        *dbgout << json::close; // ranges array
+}
+
+void ShiftCollider::outputJsonDbgStartSlot(json * const dbgout, Segment *seg)
+{
+        *dbgout << json::object // slot - not closed till the end of the caller method
+                << "slot" << objectid(dslot(seg, _target))
+				<< "gid" << _target->gid()
+                << "limit" << _limit
+                << "target" << json::object
+                    << "origin" << _origin
+                    << "currShift" << _currShift
+                    << "currOffset" << seg->collisionInfo(_target)->offset()
+                    << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
+                    << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
+                    << "fix" << "shift";
+        *dbgout     << json::close; // target object
+}
+
+void ShiftCollider::outputJsonDbgEndSlot(GR_MAYBE_UNUSED json * const dbgout,
+	 Position resultPos, int bestAxis, bool isCol)
+{
+    *dbgout << json::close // vectors array
+    << "result" << resultPos
+	//<< "scraping" << _scraping[bestAxis]
+	<< "bestAxis" << bestAxis
+    << "stillBad" << isCol
+    << json::close; // slot object
+}
+
+void ShiftCollider::outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis,
+	float tleft, float bestCost, float bestVal)
+{
+	const char * label;
+	switch (axis)
+	{
+		case 0:	label = "x";			break;
+		case 1:	label = "y";			break;
+		case 2:	label = "sum (NE-SW)";	break;
+		case 3:	label = "diff (NW-SE)";	break;
+		default: label = "???";			break;
+	}
+
+	*dbgout << json::object // vector
+		<< "direction" << label
+		<< "targetMin" << tleft;
+
+	outputJsonDbgRemovals(dbgout, axis, seg);
+
+    *dbgout << "ranges";
+    outputJsonDbg(dbgout, seg, axis);
+
+    *dbgout << "bestCost" << bestCost
+        << "bestVal" << bestVal + tleft
+        << json::close; // vectors object
+}
+
+void ShiftCollider::outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg)
+{
+    *dbgout << "removals" << json::array;
+    _ranges[axis].jsonDbgOut(seg);
+    *dbgout << json::close; // removals array
+}
+
+#endif // !defined GRAPHITE2_NTRACING
+
+
+////    KERN-COLLIDER    ////
+
+inline
+static float localmax (float al, float au, float bl, float bu, float x)
+{
+    if (al < bl)
+    { if (au < bu) return au < x ? au : x; }
+    else if (au > bu) return bl < x ? bl : x;
+    return x;
+}
+
+inline
+static float localmin(float al, float au, float bl, float bu, float x)
+{
+    if (bl > al)
+    { if (bu > au) return bl > x ? bl : x; }
+    else if (au > bu) return al > x ? al : x;
+    return x;
+}
+
+// Return the given edge of the glyph at height y, taking any slant box into account.
+static float get_edge(Segment *seg, const Slot *s, const Position &shift, float y, float width, float margin, bool isRight)
+{
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    unsigned short gid = s->gid();
+    float sx = s->origin().x + shift.x;
+    float sy = s->origin().y + shift.y;
+    uint8 numsub = gc.numSubBounds(gid);
+    float res = isRight ? (float)-1e38 : (float)1e38;
+
+    if (numsub > 0)
+    {
+        for (int i = 0; i < numsub; ++i)
+        {
+            const BBox &sbb = gc.getSubBoundingBBox(gid, i);
+            const SlantBox &ssb = gc.getSubBoundingSlantBox(gid, i);
+            if (sy + sbb.yi - margin > y + width / 2 || sy + sbb.ya + margin < y - width / 2)
+                continue;
+            if (isRight)
+            {
+                float x = sx + sbb.xa + margin;
+                if (x > res)
+                {
+                    float td = sx - sy + ssb.da + margin + y;
+                    float ts = sx + sy + ssb.sa + margin - y;
+                    x = localmax(td - width / 2, td + width / 2,  ts - width / 2, ts + width / 2, x);
+                    if (x > res)
+                        res = x;
+                }
+            }
+            else
+            {
+                float x = sx + sbb.xi - margin;
+                if (x < res)
+                {
+                    float td = sx - sy + ssb.di - margin + y;
+                    float ts = sx + sy + ssb.si - margin - y;
+                    x = localmin(td - width / 2, td + width / 2, ts - width / 2, ts + width / 2, x);
+                    if (x < res)
+                        res = x;
+                }
+            }
+        }
+    }
+    else
+    {
+        const BBox &bb = gc.getBoundingBBox(gid);
+        const SlantBox &sb = gc.getBoundingSlantBox(gid);
+        if (sy + bb.yi - margin > y + width / 2 || sy + bb.ya + margin < y - width / 2)
+            return res;
+        float td = sx - sy + y;
+        float ts = sx + sy - y;
+        if (isRight)
+            res = localmax(td + sb.da - width / 2, td + sb.da + width / 2, ts + sb.sa - width / 2, ts + sb.sa + width / 2, sx + bb.xa) + margin;
+        else
+            res = localmin(td + sb.di - width / 2, td + sb.di + width / 2, ts + sb.si - width / 2, ts + sb.si + width / 2, sx + bb.xi) - margin;
+    }
+    return res;
+}
+
+
+bool KernCollider::initSlot(Segment *seg, Slot *aSlot, const Rect &limit, float margin,
+    const Position &currShift, const Position &offsetPrev, int dir,
+    float ymin, float ymax, GR_MAYBE_UNUSED json * const dbgout)
+{
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    const Slot *base = aSlot;
+    // const Slot *last = aSlot;
+    const Slot *s;
+    int numSlices;
+    while (base->attachedTo())
+        base = base->attachedTo();
+    if (margin < 10) margin = 10;
+
+    _limit = limit;
+    _offsetPrev = offsetPrev; // kern from a previous pass
+
+    // Calculate the height of the glyph and how many horizontal slices to use.
+    if (_maxy >= 1e37f)
+    {
+        _sliceWidth = margin / 1.5f;
+        _maxy = ymax + margin;
+        _miny = ymin - margin;
+        numSlices = int((_maxy - _miny + 2) / (_sliceWidth / 1.5f) + 1.f);  // +2 helps with rounding errors
+        _edges.clear();
+        _edges.insert(_edges.begin(), numSlices, (dir & 1) ? 1e38f : -1e38f);
+        _xbound = (dir & 1) ? (float)1e38f : (float)-1e38f;
+    }
+    else if (_maxy != ymax || _miny != ymin)
+    {
+        if (_miny != ymin)
+        {
+            numSlices = int((ymin - margin - _miny) / _sliceWidth - 1);
+            _miny += numSlices * _sliceWidth;
+            if (numSlices < 0)
+                _edges.insert(_edges.begin(), -numSlices, (dir & 1) ? 1e38f : -1e38f);
+            else if ((unsigned)numSlices < _edges.size())    // this shouldn't fire since we always grow the range
+            {
+                Vector<float>::iterator e = _edges.begin();
+                while (numSlices--)
+                    ++e;
+                _edges.erase(_edges.begin(), e);
+            }
+        }
+        if (_maxy != ymax)
+        {
+            numSlices = int((ymax + margin - _miny) / _sliceWidth + 1);
+            _maxy = numSlices * _sliceWidth + _miny;
+            if (numSlices > (int)_edges.size())
+                _edges.insert(_edges.end(), numSlices - _edges.size(), (dir & 1) ? 1e38f : -1e38f);
+            else if (numSlices < (int)_edges.size())   // this shouldn't fire since we always grow the range
+            {
+                while ((int)_edges.size() > numSlices)
+                    _edges.pop_back();
+            }
+        }
+        goto done;
+    }
+    numSlices = int(_edges.size());
+
+#if !defined GRAPHITE2_NTRACING
+    // Debugging
+    _seg = seg;
+    _slotNear.clear();
+    _slotNear.insert(_slotNear.begin(), numSlices, NULL);
+    _nearEdges.clear();
+    _nearEdges.insert(_nearEdges.begin(), numSlices, (dir & 1) ? -1e38f : +1e38f);
+#endif
+
+    // Determine the trailing edge of each slice (ie, left edge for a RTL glyph).
+    for (s = base; s; s = s->nextInCluster(s))
+    {
+        SlotCollision *c = seg->collisionInfo(s);
+        if (!gc.check(s->gid()))
+            return false;
+        const BBox &bs = gc.getBoundingBBox(s->gid());
+        float x = s->origin().x + c->shift().x + ((dir & 1) ? bs.xi : bs.xa);
+        // Loop over slices.
+        // Note smin might not be zero if glyph s is not at the bottom of the cluster; similarly for smax.
+        float toffset = c->shift().y - _miny + 1 + s->origin().y;
+        int smin = max(0, int((bs.yi + toffset) / _sliceWidth));
+        int smax = min(numSlices - 1, int((bs.ya + toffset) / _sliceWidth + 1));
+        for (int i = smin; i <= smax; ++i)
+        {
+            float t;
+            float y = _miny - 1 + (i + .5f) * _sliceWidth; // vertical center of slice
+            if ((dir & 1) && x < _edges[i])
+            {
+                t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, false);
+                if (t < _edges[i])
+                {
+                    _edges[i] = t;
+                    if (t < _xbound)
+                        _xbound = t;
+                }
+            }
+            else if (!(dir & 1) && x > _edges[i])
+            {
+                t = get_edge(seg, s, c->shift(), y, _sliceWidth, margin, true);
+                if (t > _edges[i])
+                {
+                    _edges[i] = t;
+                    if (t > _xbound)
+                        _xbound = t;
+                }
+            }
+        }
+    }
+    done:
+    _mingap = (float)1e37;      // less than 1e38 s.t. 1e38-_mingap is really big
+    _target = aSlot;
+    _margin = margin;
+    _currShift = currShift;
+    return true;
+}   // end of KernCollider::initSlot
+
+
+// Determine how much the target slot needs to kern away from the given slot.
+// In other words, merge information from given slot's position with what the target slot knows
+// about how it can kern.
+// Return false if we know there is no collision, true if we think there might be one.
+bool KernCollider::mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, GR_MAYBE_UNUSED json * const dbgout)
+{
+    int rtl = (dir & 1) * 2 - 1;
+    if (!seg->getFace()->glyphs().check(slot->gid()))
+        return false;
+    const Rect &bb = seg->theGlyphBBoxTemporary(slot->gid());
+    const float sx = slot->origin().x + currShift.x;
+    float x = (sx + (rtl > 0 ? bb.tr.x : bb.bl.x)) * rtl;
+    // this isn't going to reduce _mingap so skip
+    if (_hit && x < rtl * (_xbound - _mingap - currSpace))
+        return false;
+
+    const float sy = slot->origin().y + currShift.y;
+    int smin = max(1, int((bb.bl.y + (1 - _miny + sy)) / _sliceWidth + 1)) - 1;
+    int smax = min((int)_edges.size() - 2, int((bb.tr.y + (1 - _miny + sy)) / _sliceWidth + 1)) + 1;
+    if (smin > smax)
+        return false;
+    bool collides = false;
+    bool nooverlap = true;
+
+    for (int i = smin; i <= smax; ++i)
+    {
+        float here = _edges[i] * rtl;
+        if (here > (float)9e37)
+            continue;
+        if (!_hit || x > here - _mingap - currSpace)
+        {
+            float y = (float)(_miny - 1 + (i + .5f) * _sliceWidth);  // vertical center of slice
+            // 2 * currSpace to account for the space that is already separating them and the space we want to add
+            float m = get_edge(seg, slot, currShift, y, _sliceWidth, 0., rtl > 0) * rtl + 2 * currSpace;
+            if (m < (float)-8e37)       // only true if the glyph has a gap in it
+                continue;
+            nooverlap = false;
+            float t = here - m;
+            // _mingap is positive to shrink
+            if (t < _mingap || (!_hit && !collides))
+            {
+                _mingap = t;
+                collides = true;
+            }
+#if !defined GRAPHITE2_NTRACING
+            // Debugging - remember the closest neighboring edge for this slice.
+            if (m > rtl * _nearEdges[i])
+            {
+                _slotNear[i] = slot;
+                _nearEdges[i] = m * rtl;
+            }
+#endif
+        }
+        else
+            nooverlap = false;
+    }
+    if (nooverlap)
+        _mingap = max(_mingap, _xbound - rtl * (currSpace + _margin + x));
+    if (collides && !nooverlap)
+        _hit = true;
+    return collides | nooverlap;   // note that true is not a necessarily reliable value
+
+}   // end of KernCollider::mergeSlot
+
+
+// Return the amount to kern by.
+Position KernCollider::resolve(GR_MAYBE_UNUSED Segment *seg, GR_MAYBE_UNUSED Slot *slot,
+        int dir, GR_MAYBE_UNUSED json * const dbgout)
+{
+    float resultNeeded = (1 - 2 * (dir & 1)) * _mingap;
+    // float resultNeeded = (1 - 2 * (dir & 1)) * (_mingap - margin);
+    float result = min(_limit.tr.x - _offsetPrev.x, max(resultNeeded, _limit.bl.x - _offsetPrev.x));
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+    {
+        *dbgout << json::object // slot
+                << "slot" << objectid(dslot(seg, _target))
+				<< "gid" << _target->gid()
+                << "limit" << _limit
+                << "miny" << _miny
+                << "maxy" << _maxy
+                << "slicewidth" << _sliceWidth
+                << "target" << json::object
+                    << "origin" << _target->origin()
+                    //<< "currShift" << _currShift
+                    << "offsetPrev" << _offsetPrev
+                    << "bbox" << seg->theGlyphBBoxTemporary(_target->gid())
+                    << "slantBox" << seg->getFace()->glyphs().slant(_target->gid())
+                    << "fix" << "kern"
+                    << json::close; // target object
+
+        *dbgout << "slices" << json::array;
+        for (int is = 0; is < (int)_edges.size(); is++)
+        {
+            *dbgout << json::flat << json::object
+                << "i" << is
+                << "targetEdge" << _edges[is]
+                << "neighbor" << objectid(dslot(seg, _slotNear[is]))
+                << "nearEdge" << _nearEdges[is]
+                << json::close;
+        }
+        *dbgout << json::close; // slices array
+
+        *dbgout
+            << "xbound" << _xbound
+            << "minGap" << _mingap
+            << "needed" << resultNeeded
+            << "result" << result
+            << "stillBad" << (result != resultNeeded)
+            << json::close; // slot object
+    }
+#endif
+
+    return Position(result, 0.);
+
+}   // end of KernCollider::resolve
+
+void KernCollider::shift(const Position &mv, int dir)
+{
+    for (Vector<float>::iterator e = _edges.begin(); e != _edges.end(); ++e)
+        *e += mv.x;
+    _xbound += (1 - 2 * (dir & 1)) * mv.x;
+}
+
+////    SLOT-COLLISION    ////
+
+// Initialize the collision attributes for the given slot.
+SlotCollision::SlotCollision(Segment *seg, Slot *slot)
+{
+    initFromSlot(seg, slot);
+}
+
+void SlotCollision::initFromSlot(Segment *seg, Slot *slot)
+{
+    // Initialize slot attributes from glyph attributes.
+	// The order here must match the order in the grcompiler code,
+	// GrcSymbolTable::AssignInternalGlyphAttrIDs.
+    uint16 gid = slot->gid();
+    uint16 aCol = seg->silf()->aCollision(); // flags attr ID
+    const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(gid);
+    if (!glyphFace)
+        return;
+    const sparse &p = glyphFace->attrs();
+    _flags = p[aCol];
+    _limit = Rect(Position(int16(p[aCol+1]), int16(p[aCol+2])),
+                  Position(int16(p[aCol+3]), int16(p[aCol+4])));
+    _margin = p[aCol+5];
+    _marginWt = p[aCol+6];
+
+    _seqClass = p[aCol+7];
+	_seqProxClass = p[aCol+8];
+    _seqOrder = p[aCol+9];
+	_seqAboveXoff = p[aCol+10];
+	_seqAboveWt = p[aCol+11];
+	_seqBelowXlim = p[aCol+12];
+	_seqBelowWt = p[aCol+13];
+	_seqValignHt = p[aCol+14];
+	_seqValignWt = p[aCol+15];
+
+    // These attributes do not have corresponding glyph attribute:
+    _exclGlyph = 0;
+    _exclOffset = Position(0, 0);
+}
+
+float SlotCollision::getKern(int dir) const
+{
+    if ((_flags & SlotCollision::COLL_KERN) != 0)
+        return float(_shift.x * ((dir & 1) ? -1 : 1));
+    else
+    	return 0;
+}
+
+bool SlotCollision::ignore() const
+{
+	return ((flags() & SlotCollision::COLL_IGNORE) || (flags() & SlotCollision::COLL_ISSPACE));
+}

+ 125 - 0
thirdparty/graphite/src/Decompressor.cpp

@@ -0,0 +1,125 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2015, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cassert>
+
+#include "inc/Decompressor.h"
+#include "inc/Compression.h"
+
+using namespace lz4;
+
+namespace {
+
+inline
+u32 read_literal(u8 const * &s, u8 const * const e, u32 l) {
+    if (l == 15 && s != e)
+    {
+        u8 b = 0;
+        do { l += b = *s++; } while(b==0xff && s != e);
+    }
+    return l;
+}
+
+bool read_sequence(u8 const * &src, u8 const * const end, u8 const * &literal,
+                    u32 & literal_len, u32 & match_len, u32 & match_dist)
+{
+    u8 const token = *src++;
+
+    literal_len = read_literal(src, end, token >> 4);
+    literal = src;
+    src += literal_len;
+
+    // Normal exit for end of stream, wrap arround check and parital match check.
+    if (src > end - sizeof(u16) || src < literal)
+        return false;
+
+    match_dist  = *src++;
+    match_dist |= *src++ << 8;
+    match_len = read_literal(src, end, token & 0xf) + MINMATCH;
+
+    // Malformed stream check.
+    return src <= end-MINCODA;
+}
+
+}
+
+int lz4::decompress(void const *in, size_t in_size, void *out, size_t out_size)
+{
+    if (out_size <= in_size || in_size < MINSRCSIZE)
+        return -1;
+
+    u8 const *       src     = static_cast<u8 const *>(in),
+             *       literal = 0,
+             * const src_end = src + in_size;
+
+    u8 *       dst     = static_cast<u8*>(out),
+       * const dst_end = dst + out_size;
+
+    // Check the in and out size hasn't wrapped around.
+    if (src >= src_end || dst >= dst_end)
+        return -1;
+
+    u32 literal_len = 0,
+        match_len = 0,
+        match_dist = 0;
+
+    while (read_sequence(src, src_end, literal, literal_len, match_len,
+                         match_dist))
+    {
+        if (literal_len != 0)
+        {
+            // Copy in literal. At this point the a minimal literal + minminal
+            // match plus the coda (1 + 2 + 5) must be 8 bytes or more allowing
+            // us to remain within the src buffer for an overrun_copy on
+            // machines upto 64 bits.
+            if (align(literal_len) > out_size)
+                return -1;
+            dst = overrun_copy(dst, literal, literal_len);
+            out_size -= literal_len;
+        }
+
+        // Copy, possibly repeating, match from earlier in the
+        //  decoded output.
+        u8 const * const pcpy = dst - match_dist;
+        if (pcpy < static_cast<u8*>(out)
+              || match_len > unsigned(out_size - LASTLITERALS)
+              // Wrap around checks:
+              || out_size < LASTLITERALS || pcpy >= dst)
+            return -1;
+        if (dst > pcpy+sizeof(unsigned long)
+            && align(match_len) <= out_size)
+            dst = overrun_copy(dst, pcpy, match_len);
+        else
+            dst = safe_copy(dst, pcpy, match_len);
+        out_size -= match_len;
+    }
+
+    if (literal > src_end - literal_len || literal_len > out_size)
+        return -1;
+    dst = fast_copy(dst, literal, literal_len);
+
+    return int(dst - (u8*)out);
+}

+ 366 - 0
thirdparty/graphite/src/Face.cpp

@@ -0,0 +1,366 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cstring>
+#include "graphite2/Segment.h"
+#include "inc/CmapCache.h"
+#include "inc/debug.h"
+#include "inc/Decompressor.h"
+#include "inc/Endian.h"
+#include "inc/Face.h"
+#include "inc/FileFace.h"
+#include "inc/GlyphFace.h"
+#include "inc/json.h"
+#include "inc/Segment.h"
+#include "inc/NameTable.h"
+#include "inc/Error.h"
+
+using namespace graphite2;
+
+namespace
+{
+enum compression
+{
+    NONE,
+    LZ4
+};
+
+}
+
+Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
+: m_appFaceHandle(appFaceHandle),
+  m_pFileFace(NULL),
+  m_pGlyphFaceCache(NULL),
+  m_cmap(NULL),
+  m_pNames(NULL),
+  m_logger(NULL),
+  m_error(0), m_errcntxt(0),
+  m_silfs(NULL),
+  m_numSilf(0),
+  m_ascent(0),
+  m_descent(0)
+{
+    memset(&m_ops, 0, sizeof m_ops);
+    memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size));
+}
+
+
+Face::~Face()
+{
+    setLogger(0);
+    delete m_pGlyphFaceCache;
+    delete m_cmap;
+    delete[] m_silfs;
+#ifndef GRAPHITE2_NFILEFACE
+    delete m_pFileFace;
+#endif
+    delete m_pNames;
+}
+
+float Face::default_glyph_advance(const void* font_ptr, gr_uint16 glyphid)
+{
+    const Font & font = *reinterpret_cast<const Font *>(font_ptr);
+
+    return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale();
+}
+
+bool Face::readGlyphs(uint32 faceOptions)
+{
+    Error e;
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _glyph_cat(tele.glyph);
+#endif
+    error_context(EC_READGLYPHS);
+    m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
+
+    if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
+        || e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
+        || e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM))
+    {
+        return error(e);
+    }
+
+    if (faceOptions & gr_face_cacheCmap)
+        m_cmap = new CachedCmap(*this);
+    else
+        m_cmap = new DirectCmap(*this);
+    if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
+        return error(e);
+
+    if (faceOptions & gr_face_preloadGlyphs)
+        nameTable();        // preload the name table along with the glyphs.
+
+    return true;
+}
+
+bool Face::readGraphite(const Table & silf)
+{
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _silf_cat(tele.silf);
+#endif
+    Error e;
+    error_context(EC_READSILF);
+    const byte * p = silf;
+    if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e);
+
+    const uint32 version = be::read<uint32>(p);
+    if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
+    if (version >= 0x00030000)
+        be::skip<uint32>(p);        // compilerVersion
+    m_numSilf = be::read<uint16>(p);
+
+    be::skip<uint16>(p);            // reserved
+
+    bool havePasses = false;
+    m_silfs = new Silf[m_numSilf];
+    if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
+    for (int i = 0; i < m_numSilf; i++)
+    {
+        error_context(EC_ASILF + (i << 8));
+        const uint32 offset = be::read<uint32>(p),
+                     next   = i == m_numSilf - 1 ? uint32(silf.size()) : be::peek<uint32>(p);
+        if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
+            return error(e);
+
+        if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version))
+            return false;
+
+        if (m_silfs[i].numPasses())
+            havePasses = true;
+    }
+
+    return havePasses;
+}
+
+bool Face::readFeatures()
+{
+    return m_Sill.readFace(*this);
+}
+
+bool Face::runGraphite(Segment *seg, const Silf *aSilf) const
+{
+#if !defined GRAPHITE2_NTRACING
+    json * dbgout = logger();
+    if (dbgout)
+    {
+        *dbgout << json::object
+                    << "id"         << objectid(seg)
+                    << "passes"     << json::array;
+    }
+#endif
+
+//    if ((seg->dir() & 1) != aSilf->dir())
+//        seg->reverseSlots();
+    if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
+        seg->doMirror(aSilf->aMirror());
+    bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
+    if (res)
+    {
+        seg->associateChars(0, seg->charInfoCount());
+        if (aSilf->flags() & 0x20)
+            res &= seg->initCollisions();
+        if (res)
+            res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
+    }
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+{
+        seg->positionSlots(0, 0, 0, seg->currdir());
+        *dbgout             << json::item
+                            << json::close // Close up the passes array
+                << "outputdir" << (seg->currdir() ? "rtl" : "ltr")
+                << "output" << json::array;
+        for(Slot * s = seg->first(); s; s = s->next())
+            *dbgout     << dslot(seg, s);
+        *dbgout         << json::close
+                << "advance" << seg->advance()
+                << "chars"   << json::array;
+        for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i)
+            *dbgout     << json::flat << *seg->charinfo(int(i));
+        *dbgout         << json::close  // Close up the chars array
+                    << json::close;     // Close up the segment object
+    }
+#endif
+
+    return res;
+}
+
+void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED)
+{
+#if !defined GRAPHITE2_NTRACING
+    delete m_logger;
+    m_logger = log_file ? new json(log_file) : 0;
+#endif
+}
+
+const Silf *Face::chooseSilf(uint32 script) const
+{
+    if (m_numSilf == 0)
+        return NULL;
+    else if (m_numSilf == 1 || script == 0)
+        return m_silfs;
+    else // do more work here
+        return m_silfs;
+}
+
+uint16 Face::findPseudo(uint32 uid) const
+{
+    return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0;
+}
+
+int32 Face::getGlyphMetric(uint16 gid, uint8 metric) const
+{
+    switch (metrics(metric))
+    {
+        case kgmetAscent : return m_ascent;
+        case kgmetDescent : return m_descent;
+        default:
+            if (gid >= glyphs().numGlyphs()) return 0;
+            return glyphs().glyph(gid)->getMetric(metric);
+    }
+}
+
+void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/)
+{
+#ifndef GRAPHITE2_NFILEFACE
+    if (m_pFileFace==pFileFace)
+      return;
+
+    delete m_pFileFace;
+    m_pFileFace = pFileFace;
+#endif
+}
+
+NameTable * Face::nameTable() const
+{
+    if (m_pNames) return m_pNames;
+    const Table name(*this, Tag::name);
+    if (name)
+        m_pNames = new NameTable(name, name.size());
+    return m_pNames;
+}
+
+uint16 Face::languageForLocale(const char * locale) const
+{
+    nameTable();
+    if (m_pNames)
+        return m_pNames->getLanguageId(locale);
+    return 0;
+}
+
+
+
+Face::Table::Table(const Face & face, const Tag n, uint32 version) throw()
+: _f(&face), _sz(0), _compressed(false)
+{
+    _p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &_sz));
+
+    if (!TtfUtil::CheckTable(n, _p, _sz))
+    {
+        release();     // Make sure we release the table buffer even if the table failed its checks
+        return;
+    }
+
+    if (be::peek<uint32>(_p) >= version)
+        decompress();
+}
+
+void Face::Table::release()
+{
+    if (_compressed)
+        free(const_cast<byte *>(_p));
+    else if (_p && _f->m_ops.release_table)
+        (*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
+    _p = 0; _sz = 0;
+}
+
+Face::Table & Face::Table::operator = (const Table && rhs) throw()
+{
+    if (this == &rhs)   return *this;
+    release();
+    new (this) Table(std::move(rhs));
+    return *this;
+}
+
+Error Face::Table::decompress()
+{
+    Error e;
+    if (e.test(_sz < 5 * sizeof(uint32), E_BADSIZE))
+        return e;
+    byte * uncompressed_table = 0;
+    size_t uncompressed_size = 0;
+
+    const byte * p = _p;
+    const uint32 version = be::read<uint32>(p);    // Table version number.
+
+    // The scheme is in the top 5 bits of the 1st uint32.
+    const uint32 hdr = be::read<uint32>(p);
+    switch(compression(hdr >> 27))
+    {
+    case NONE: return e;
+
+    case LZ4:
+    {
+        uncompressed_size  = hdr & 0x07ffffff;
+        uncompressed_table = gralloc<byte>(uncompressed_size);
+        if (!e.test(!uncompressed_table || uncompressed_size < 4, E_OUTOFMEM))
+        {
+            memset(uncompressed_table, 0, 4);   // make sure version number is initialised
+            // coverity[forward_null : FALSE] - uncompressed_table has been checked so can't be null
+            // coverity[checked_return : FALSE] - we test e later
+            e.test(lz4::decompress(p, _sz - 2*sizeof(uint32), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
+        }
+        break;
+    }
+
+    default:
+        e.error(E_BADSCHEME);
+    };
+
+    // Check the uncompressed version number against the original.
+    if (!e)
+        // coverity[forward_null : FALSE] - uncompressed_table has already been tested so can't be null
+        // coverity[checked_return : FALSE] - we test e later
+        e.test(be::peek<uint32>(uncompressed_table) != version, E_SHRINKERFAILED);
+
+    // Tell the provider to release the compressed form since were replacing
+    //   it anyway.
+    release();
+
+    if (e)
+    {
+        free(uncompressed_table);
+        uncompressed_table = 0;
+        uncompressed_size  = 0;
+    }
+
+    _p = uncompressed_table;
+    _sz = uncompressed_size;
+    _compressed = true;
+
+    return e;
+}

+ 293 - 0
thirdparty/graphite/src/FeatureMap.cpp

@@ -0,0 +1,293 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cstring>
+
+#include "inc/Main.h"
+#include "inc/bits.h"
+#include "inc/Endian.h"
+#include "inc/FeatureMap.h"
+#include "inc/FeatureVal.h"
+#include "graphite2/Font.h"
+#include "inc/TtfUtil.h"
+#include <cstdlib>
+#include "inc/Face.h"
+
+
+using namespace graphite2;
+
+namespace
+{
+    static int cmpNameAndFeatures(const void *ap, const void *bp)
+    {
+        const NameAndFeatureRef & a = *static_cast<const NameAndFeatureRef *>(ap),
+                                & b = *static_cast<const NameAndFeatureRef *>(bp);
+        return (a < b ? -1 : (b < a ? 1 : 0));
+    }
+
+    const size_t    FEAT_HEADER     = sizeof(uint32) + 2*sizeof(uint16) + sizeof(uint32),
+                    FEATURE_SIZE    = sizeof(uint32)
+                                    + 2*sizeof(uint16)
+                                    + sizeof(uint32)
+                                    + 2*sizeof(uint16),
+                    FEATURE_SETTING_SIZE = sizeof(int16) + sizeof(uint16);
+
+    uint16 readFeatureSettings(const byte * p, FeatureSetting * s, size_t num_settings)
+    {
+        uint16 max_val = 0;
+        for (FeatureSetting * const end = s + num_settings; s != end; ++s)
+        {
+            const int16 value = be::read<int16>(p);
+            ::new (s) FeatureSetting(value, be::read<uint16>(p));
+            if (uint16(value) > max_val)    max_val = value;
+        }
+
+        return max_val;
+    }
+}
+
+FeatureRef::FeatureRef(const Face & face,
+    unsigned short & bits_offset, uint32 max_val,
+    uint32 name, uint16 uiName, flags_t flags,
+    FeatureSetting *settings, uint16 num_set) throw()
+: m_face(&face),
+  m_nameValues(settings),
+  m_mask(mask_over_val(max_val)),
+  m_max(max_val),
+  m_id(name),
+  m_nameid(uiName),
+  m_numSet(num_set),
+  m_flags(flags)
+{
+    const uint8 need_bits = bit_set_count(m_mask);
+    m_index = (bits_offset + need_bits) / SIZEOF_CHUNK;
+    if (m_index > bits_offset / SIZEOF_CHUNK)
+        bits_offset = m_index*SIZEOF_CHUNK;
+    m_bits = bits_offset % SIZEOF_CHUNK;
+    bits_offset += need_bits;
+    m_mask <<= m_bits;
+}
+
+FeatureRef::~FeatureRef() throw()
+{
+    free(m_nameValues);
+}
+
+bool FeatureMap::readFeats(const Face & face)
+{
+    const Face::Table feat(face, TtfUtil::Tag::Feat);
+    const byte * p = feat;
+    if (!p) return true;
+    if (feat.size() < FEAT_HEADER) return false;
+
+    const byte *const feat_start = p,
+               *const feat_end = p + feat.size();
+
+    const uint32 version = be::read<uint32>(p);
+    m_numFeats = be::read<uint16>(p);
+    be::skip<uint16>(p);
+    be::skip<uint32>(p);
+
+    // Sanity checks
+    if (m_numFeats == 0)    return true;
+    if (version < 0x00010000 ||
+        p + m_numFeats*FEATURE_SIZE > feat_end)
+    {   //defensive
+        m_numFeats = 0;
+        return false;
+    }
+
+    m_feats = new FeatureRef [m_numFeats];
+    uint16 * const  defVals = gralloc<uint16>(m_numFeats);
+    if (!defVals || !m_feats) return false;
+    unsigned short bits = 0;     //to cause overflow on first Feature
+
+    for (int i = 0, ie = m_numFeats; i != ie; i++)
+    {
+        const uint32    label   = version < 0x00020000 ? be::read<uint16>(p) : be::read<uint32>(p);
+        const uint16    num_settings = be::read<uint16>(p);
+        if (version >= 0x00020000)
+            be::skip<uint16>(p);
+        const uint32    settings_offset = be::read<uint32>(p);
+        const uint16    flags  = be::read<uint16>(p),
+                        uiName = be::read<uint16>(p);
+
+        if (settings_offset > size_t(feat_end - feat_start)
+            || settings_offset + num_settings * FEATURE_SETTING_SIZE > size_t(feat_end - feat_start))
+        {
+            free(defVals);
+            return false;
+        }
+
+        FeatureSetting *uiSet;
+        uint32 maxVal;
+        if (num_settings != 0)
+        {
+            uiSet = gralloc<FeatureSetting>(num_settings);
+            if (!uiSet)
+            {
+                free(defVals);
+                return false;
+            }
+            maxVal = readFeatureSettings(feat_start + settings_offset, uiSet, num_settings);
+            defVals[i] = uiSet[0].value();
+        }
+        else
+        {
+            uiSet = 0;
+            maxVal = 0xffffffff;
+            defVals[i] = 0;
+        }
+
+        ::new (m_feats + i) FeatureRef (face, bits, maxVal,
+                                       label, uiName, 
+                                       FeatureRef::flags_t(flags),
+                                       uiSet, num_settings);
+    }
+    new (&m_defaultFeatures) Features(bits/(sizeof(uint32)*8) + 1, *this);
+    m_pNamedFeats = new NameAndFeatureRef[m_numFeats];
+    if (!m_pNamedFeats)
+    {
+        free(defVals);
+        return false;
+    }
+    for (int i = 0; i < m_numFeats; ++i)
+    {
+        m_feats[i].applyValToFeature(defVals[i], m_defaultFeatures);
+        m_pNamedFeats[i] = m_feats[i];
+    }
+
+    free(defVals);
+
+    qsort(m_pNamedFeats, m_numFeats, sizeof(NameAndFeatureRef), &cmpNameAndFeatures);
+
+    return true;
+}
+
+bool SillMap::readFace(const Face & face)
+{
+    if (!m_FeatureMap.readFeats(face)) return false;
+    if (!readSill(face)) return false;
+    return true;
+}
+
+
+bool SillMap::readSill(const Face & face)
+{
+    const Face::Table sill(face, TtfUtil::Tag::Sill);
+    const byte *p = sill;
+
+    if (!p) return true;
+    if (sill.size() < 12) return false;
+    if (be::read<uint32>(p) != 0x00010000UL) return false;
+    m_numLanguages = be::read<uint16>(p);
+    m_langFeats = new LangFeaturePair[m_numLanguages];
+    if (!m_langFeats || !m_FeatureMap.m_numFeats) { m_numLanguages = 0; return true; }        //defensive
+
+    p += 6;     // skip the fast search
+    if (sill.size() < m_numLanguages * 8U + 12) return false;
+
+    for (int i = 0; i < m_numLanguages; i++)
+    {
+        uint32 langid = be::read<uint32>(p);
+        uint16 numSettings = be::read<uint16>(p);
+        uint16 offset = be::read<uint16>(p);
+        if (offset + 8U * numSettings > sill.size() && numSettings > 0) return false;
+        Features* feats = new Features(m_FeatureMap.m_defaultFeatures);
+        if (!feats) return false;
+        const byte *pLSet = sill + offset;
+
+        // Apply langauge specific settings
+        for (int j = 0; j < numSettings; j++)
+        {
+            uint32 name = be::read<uint32>(pLSet);
+            uint16 val = be::read<uint16>(pLSet);
+            pLSet += 2;
+            const FeatureRef* pRef = m_FeatureMap.findFeatureRef(name);
+            if (pRef)   pRef->applyValToFeature(val, *feats);
+        }
+        // Add the language id feature which is always feature id 1
+        const FeatureRef* pRef = m_FeatureMap.findFeatureRef(1);
+        if (pRef)   pRef->applyValToFeature(langid, *feats);
+
+        m_langFeats[i].m_lang = langid;
+        m_langFeats[i].m_pFeatures = feats;
+    }
+    return true;
+}
+
+
+Features* SillMap::cloneFeatures(uint32 langname/*0 means default*/) const
+{
+    if (langname)
+    {
+        // the number of languages in a font is usually small e.g. 8 in Doulos
+        // so this loop is not very expensive
+        for (uint16 i = 0; i < m_numLanguages; i++)
+        {
+            if (m_langFeats[i].m_lang == langname)
+                return new Features(*m_langFeats[i].m_pFeatures);
+        }
+    }
+    return new Features (m_FeatureMap.m_defaultFeatures);
+}
+
+
+
+const FeatureRef *FeatureMap::findFeatureRef(uint32 name) const
+{
+    NameAndFeatureRef *it;
+
+    for (it = m_pNamedFeats; it < m_pNamedFeats + m_numFeats; ++it)
+        if (it->m_name == name)
+            return it->m_pFRef;
+    return NULL;
+}
+
+bool FeatureRef::applyValToFeature(uint32 val, Features & pDest) const
+{
+    if (val>maxVal() || !m_face)
+      return false;
+    if (pDest.m_pMap==NULL)
+      pDest.m_pMap = &m_face->theSill().theFeatureMap();
+    else
+      if (pDest.m_pMap!=&m_face->theSill().theFeatureMap())
+        return false;       //incompatible
+    if (m_index >= pDest.size())
+        pDest.resize(m_index+1);
+    pDest[m_index] &= ~m_mask;
+    pDest[m_index] |= (uint32(val) << m_bits);
+    return true;
+}
+
+uint32 FeatureRef::getFeatureVal(const Features& feats) const
+{
+  if (m_index < feats.size() && m_face
+      && &m_face->theSill().theFeatureMap()==feats.m_pMap)
+    return (feats[m_index] & m_mask) >> m_bits;
+  else
+    return 0;
+}

+ 115 - 0
thirdparty/graphite/src/FileFace.cpp

@@ -0,0 +1,115 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2012, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cstring>
+#include "inc/FileFace.h"
+
+
+#ifndef GRAPHITE2_NFILEFACE
+
+using namespace graphite2;
+
+FileFace::FileFace(const char *filename)
+: _file(fopen(filename, "rb")),
+  _file_len(0),
+  _header_tbl(NULL),
+  _table_dir(NULL)
+{
+    if (!_file) return;
+
+    if (fseek(_file, 0, SEEK_END)) return;
+    _file_len = ftell(_file);
+    if (fseek(_file, 0, SEEK_SET)) return;
+
+    size_t tbl_offset, tbl_len;
+
+    // Get the header.
+    if (!TtfUtil::GetHeaderInfo(tbl_offset, tbl_len)) return;
+    if (fseek(_file, long(tbl_offset), SEEK_SET)) return;
+    _header_tbl = (TtfUtil::Sfnt::OffsetSubTable*)gralloc<char>(tbl_len);
+    if (_header_tbl)
+    {
+        if (fread(_header_tbl, 1, tbl_len, _file) != tbl_len) return;
+        if (!TtfUtil::CheckHeader(_header_tbl)) return;
+    }
+
+    // Get the table directory
+    if (!TtfUtil::GetTableDirInfo(_header_tbl, tbl_offset, tbl_len)) return;
+    _table_dir = (TtfUtil::Sfnt::OffsetSubTable::Entry*)gralloc<char>(tbl_len);
+    if (fseek(_file, long(tbl_offset), SEEK_SET)) return;
+    if (_table_dir && fread(_table_dir, 1, tbl_len, _file) != tbl_len)
+    {
+        free(_table_dir);
+        _table_dir = NULL;
+    }
+    return;
+}
+
+FileFace::~FileFace()
+{
+    free(_table_dir);
+    free(_header_tbl);
+    if (_file)
+        fclose(_file);
+}
+
+
+const void *FileFace::get_table_fn(const void* appFaceHandle, unsigned int name, size_t *len)
+{
+    if (appFaceHandle == 0)     return 0;
+    const FileFace & file_face = *static_cast<const FileFace *>(appFaceHandle);
+
+    void *tbl;
+    size_t tbl_offset, tbl_len;
+    if (!TtfUtil::GetTableInfo(name, file_face._header_tbl, file_face._table_dir, tbl_offset, tbl_len))
+        return 0;
+
+    if (tbl_offset > file_face._file_len || tbl_len > file_face._file_len - tbl_offset
+            || fseek(file_face._file, long(tbl_offset), SEEK_SET) != 0)
+        return 0;
+
+    tbl = malloc(tbl_len);
+    if (!tbl || fread(tbl, 1, tbl_len, file_face._file) != tbl_len)
+    {
+        free(tbl);
+        return 0;
+    }
+
+    if (len) *len = tbl_len;
+    return tbl;
+}
+
+void FileFace::rel_table_fn(const void* appFaceHandle, const void *table_buffer)
+{
+    if (appFaceHandle == 0)     return;
+
+    free(const_cast<void *>(table_buffer));
+}
+
+const gr_face_ops FileFace::ops = { sizeof FileFace::ops, &FileFace::get_table_fn, &FileFace::rel_table_fn };
+
+
+#endif                  //!GRAPHITE2_NFILEFACE

+ 58 - 0
thirdparty/graphite/src/Font.cpp

@@ -0,0 +1,58 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/Face.h"
+#include "inc/Font.h"
+#include "inc/GlyphCache.h"
+
+using namespace graphite2;
+
+Font::Font(float ppm, const Face & f, const void * appFontHandle, const gr_font_ops * ops)
+: m_appFontHandle(appFontHandle ? appFontHandle : this),
+  m_face(f),
+  m_scale(ppm / f.glyphs().unitsPerEm()),
+  m_hinted(appFontHandle && ops && (ops->glyph_advance_x || ops->glyph_advance_y))
+{
+    memset(&m_ops, 0, sizeof m_ops);
+    if (m_hinted && ops)
+        memcpy(&m_ops, ops, min(sizeof m_ops, ops->size));
+    else
+        m_ops.glyph_advance_x = &Face::default_glyph_advance;
+
+    size_t nGlyphs = f.glyphs().numGlyphs();
+    m_advances = gralloc<float>(nGlyphs);
+    if (m_advances)
+    {
+        for (float *advp = m_advances; nGlyphs; --nGlyphs, ++advp)
+            *advp = INVALID_ADVANCE;
+    }
+}
+
+
+/*virtual*/ Font::~Font()
+{
+    free(m_advances);
+}

+ 492 - 0
thirdparty/graphite/src/GlyphCache.cpp

@@ -0,0 +1,492 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2012, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "graphite2/Font.h"
+
+#include "inc/Main.h"
+#include "inc/Face.h"     //for the tags
+#include "inc/GlyphCache.h"
+#include "inc/GlyphFace.h"
+#include "inc/Endian.h"
+#include "inc/bits.h"
+
+using namespace graphite2;
+
+namespace
+{
+    // Iterator over version 1 or 2 glat entries which consist of a series of
+    //    +-+-+-+-+-+-+-+-+-+-+                +-+-+-+-+-+-+-+-+-+-+-+-+
+    // v1 |k|n|v1 |v2 |...|vN |     or    v2   | k | n |v1 |v2 |...|vN |
+    //    +-+-+-+-+-+-+-+-+-+-+                +-+-+-+-+-+-+-+-+-+-+-+-+
+    // variable length structures.
+
+    template<typename W>
+    class _glat_iterator : public std::iterator<std::input_iterator_tag, std::pair<sparse::key_type, sparse::mapped_type> >
+    {
+        unsigned short  key() const             { return uint16(be::peek<W>(_e) + _n); }
+        unsigned int    run() const             { return be::peek<W>(_e+sizeof(W)); }
+        void            advance_entry()         { _n = 0; _e = _v; be::skip<W>(_v,2); }
+    public:
+        _glat_iterator(const void * glat=0) : _e(reinterpret_cast<const byte *>(glat)), _v(_e+2*sizeof(W)), _n(0) {}
+
+        _glat_iterator<W> & operator ++ () {
+            ++_n; be::skip<uint16>(_v);
+            if (_n == run()) advance_entry();
+            return *this;
+        }
+        _glat_iterator<W>   operator ++ (int)   { _glat_iterator<W> tmp(*this); operator++(); return tmp; }
+
+        // This is strictly a >= operator. A true == operator could be
+        // implemented that test for overlap but it would be more expensive a
+        // test.
+        bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e - 1; }
+        bool operator != (const _glat_iterator<W> & rhs) { return !operator==(rhs); }
+
+        value_type          operator * () const {
+            return value_type(key(), be::peek<uint16>(_v));
+        }
+
+    protected:
+        const byte     * _e, * _v;
+        size_t        _n;
+    };
+
+    typedef _glat_iterator<uint8>   glat_iterator;
+    typedef _glat_iterator<uint16>  glat2_iterator;
+}
+
+const SlantBox SlantBox::empty = {0,0,0,0};
+
+
+class GlyphCache::Loader
+{
+public:
+    Loader(const Face & face);    //return result indicates success. Do not use if failed.
+
+    operator bool () const throw();
+    unsigned short int units_per_em() const throw();
+    unsigned short int num_glyphs() const throw();
+    unsigned short int num_attrs() const throw();
+    bool has_boxes() const throw();
+
+    const GlyphFace * read_glyph(unsigned short gid, GlyphFace &, int *numsubs) const throw();
+    GlyphBox * read_box(uint16 gid, GlyphBox *curr, const GlyphFace & face) const throw();
+
+    CLASS_NEW_DELETE;
+private:
+    Face::Table _head,
+                _hhea,
+                _hmtx,
+                _glyf,
+                _loca,
+                m_pGlat,
+                m_pGloc;
+
+    bool            _long_fmt;
+    bool            _has_boxes;
+    unsigned short  _num_glyphs_graphics,        //i.e. boundary box and advance
+                    _num_glyphs_attributes,
+                    _num_attrs;                    // number of glyph attributes per glyph
+};
+
+
+
+GlyphCache::GlyphCache(const Face & face, const uint32 face_options)
+: _glyph_loader(new Loader(face)),
+  _glyphs(_glyph_loader && *_glyph_loader && _glyph_loader->num_glyphs()
+        ? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),
+  _boxes(_glyph_loader && _glyph_loader->has_boxes() && _glyph_loader->num_glyphs()
+        ? grzeroalloc<GlyphBox *>(_glyph_loader->num_glyphs()) : 0),
+  _num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0),
+  _num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0),
+  _upem(_glyphs ? _glyph_loader->units_per_em() : 0)
+{
+    if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs)
+    {
+        int numsubs = 0;
+        GlyphFace * const glyphs = new GlyphFace [_num_glyphs];
+        if (!glyphs)
+            return;
+
+        // The 0 glyph is definately required.
+        _glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0], &numsubs);
+
+        // glyphs[0] has the same address as the glyphs array just allocated,
+        //  thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points
+        //  to the entire array.
+        const GlyphFace * loaded = _glyphs[0];
+        for (uint16 gid = 1; loaded && gid != _num_glyphs; ++gid)
+            _glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs);
+
+        if (!loaded)
+        {
+            _glyphs[0] = 0;
+            delete [] glyphs;
+        }
+        else if (numsubs > 0 && _boxes)
+        {
+            GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + numsubs * 8 * sizeof(float));
+            GlyphBox * currbox = boxes;
+
+            for (uint16 gid = 0; currbox && gid != _num_glyphs; ++gid)
+            {
+                _boxes[gid] = currbox;
+                currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]);
+            }
+            if (!currbox)
+            {
+                free(boxes);
+                _boxes[0] = 0;
+            }
+        }
+        delete _glyph_loader;
+        _glyph_loader = 0;
+	// coverity[leaked_storage : FALSE] - calling read_glyph on index 0 saved
+	// glyphs as _glyphs[0]. Setting _glyph_loader to nullptr here flags that
+	// the dtor needs to call delete[] on _glyphs[0] to release what was allocated
+	// as glyphs
+    }
+
+    if (_glyphs && glyph(0) == 0)
+    {
+        free(_glyphs);
+        _glyphs = 0;
+        if (_boxes)
+        {
+            free(_boxes);
+            _boxes = 0;
+        }
+        _num_glyphs = _num_attrs = _upem = 0;
+    }
+}
+
+
+GlyphCache::~GlyphCache()
+{
+    if (_glyphs)
+    {
+        if (_glyph_loader)
+        {
+            const GlyphFace *  * g = _glyphs;
+            for(unsigned short n = _num_glyphs; n; --n, ++g)
+                delete *g;
+        }
+        else
+            delete [] _glyphs[0];
+        free(_glyphs);
+    }
+    if (_boxes)
+    {
+        if (_glyph_loader)
+        {
+            GlyphBox *  * g = _boxes;
+            for (uint16 n = _num_glyphs; n; --n, ++g)
+                free(*g);
+        }
+        else
+            free(_boxes[0]);
+        free(_boxes);
+    }
+    delete _glyph_loader;
+}
+
+const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const      //result may be changed by subsequent call with a different glyphid
+{
+    if (glyphid >= numGlyphs())
+        return _glyphs[0];
+    const GlyphFace * & p = _glyphs[glyphid];
+    if (p == 0 && _glyph_loader)
+    {
+        int numsubs = 0;
+        GlyphFace * g = new GlyphFace();
+        if (g)  p = _glyph_loader->read_glyph(glyphid, *g, &numsubs);
+        if (!p)
+        {
+            delete g;
+            return *_glyphs;
+        }
+        if (_boxes)
+        {
+            _boxes[glyphid] = (GlyphBox *)gralloc<char>(sizeof(GlyphBox) + 8 * numsubs * sizeof(float));
+            if (!_glyph_loader->read_box(glyphid, _boxes[glyphid], *_glyphs[glyphid]))
+            {
+                free(_boxes[glyphid]);
+                _boxes[glyphid] = 0;
+            }
+        }
+    }
+    return p;
+}
+
+
+
+GlyphCache::Loader::Loader(const Face & face)
+: _head(face, Tag::head),
+  _hhea(face, Tag::hhea),
+  _hmtx(face, Tag::hmtx),
+  _glyf(face, Tag::glyf),
+  _loca(face, Tag::loca),
+  _long_fmt(false),
+  _has_boxes(false),
+  _num_glyphs_graphics(0),
+  _num_glyphs_attributes(0),
+  _num_attrs(0)
+{
+    if (!operator bool())
+        return;
+
+    const Face::Table maxp = Face::Table(face, Tag::maxp);
+    if (!maxp) { _head = Face::Table(); return; }
+
+    _num_glyphs_graphics = static_cast<unsigned short>(TtfUtil::GlyphCount(maxp));
+    // This will fail if the number of glyphs is wildly out of range.
+    if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-2))
+    {
+        _head = Face::Table();
+        return;
+    }
+
+    if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL
+        || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL
+        || m_pGloc.size() < 8)
+    {
+        _head = Face::Table();
+        return;
+    }
+    const byte    * p = m_pGloc;
+    int       version = be::read<uint32>(p);
+    const uint16    flags = be::read<uint16>(p);
+    _num_attrs = be::read<uint16>(p);
+    // We can accurately calculate the number of attributed glyphs by
+    //  subtracting the length of the attribids array (numAttribs long if present)
+    //  and dividing by either 2 or 4 depending on shor or lonf format
+    _long_fmt              = flags & 1;
+    ptrdiff_t tmpnumgattrs       = (m_pGloc.size()
+                               - (p - m_pGloc)
+                               - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
+                                   / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
+
+    if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535
+        || _num_attrs == 0 || _num_attrs > 0x3000  // is this hard limit appropriate?
+        || _num_glyphs_graphics > tmpnumgattrs
+        || m_pGlat.size() < 4)
+    {
+        _head = Face::Table();
+        return;
+    }
+
+    _num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);
+    p = m_pGlat;
+    version = be::read<uint32>(p);
+    if (version >= 0x00040000 || (version >= 0x00030000 && m_pGlat.size() < 8))       // reject Glat tables that are too new
+    {
+        _head = Face::Table();
+        return;
+    }
+    else if (version >= 0x00030000)
+    {
+        unsigned int glatflags = be::read<uint32>(p);
+        _has_boxes = glatflags & 1;
+        // delete this once the compiler is fixed
+        _has_boxes = true;
+    }
+}
+
+inline
+GlyphCache::Loader::operator bool () const throw()
+{
+    return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca));
+}
+
+inline
+unsigned short int GlyphCache::Loader::units_per_em() const throw()
+{
+    return _head ? TtfUtil::DesignUnits(_head) : 0;
+}
+
+inline
+unsigned short int GlyphCache::Loader::num_glyphs() const throw()
+{
+    return max(_num_glyphs_graphics, _num_glyphs_attributes);
+}
+
+inline
+unsigned short int GlyphCache::Loader::num_attrs() const throw()
+{
+    return _num_attrs;
+}
+
+inline
+bool GlyphCache::Loader::has_boxes () const throw()
+{
+    return _has_boxes;
+}
+
+const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph, int *numsubs) const throw()
+{
+    Rect        bbox;
+    Position    advance;
+
+    if (glyphid < _num_glyphs_graphics)
+    {
+        int nLsb;
+        unsigned int nAdvWid;
+        if (_glyf)
+        {
+            int xMin, yMin, xMax, yMax;
+            size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head);
+            void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size());
+
+            if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax))
+            {
+                if ((xMin > xMax) || (yMin > yMax))
+                    return 0;
+                bbox = Rect(Position(static_cast<float>(xMin), static_cast<float>(yMin)),
+                    Position(static_cast<float>(xMax), static_cast<float>(yMax)));
+            }
+        }
+        if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid))
+            advance = Position(static_cast<float>(nAdvWid), 0);
+    }
+
+    if (glyphid < _num_glyphs_attributes)
+    {
+        const byte * gloc = m_pGloc;
+        size_t      glocs = 0, gloce = 0;
+
+        be::skip<uint32>(gloc);
+        be::skip<uint16>(gloc,2);
+        if (_long_fmt)
+        {
+            if (8 + glyphid * sizeof(uint32) > m_pGloc.size())
+                return 0;
+            be::skip<uint32>(gloc, glyphid);
+            glocs = be::read<uint32>(gloc);
+            gloce = be::peek<uint32>(gloc);
+        }
+        else
+        {
+            if (8 + glyphid * sizeof(uint16) > m_pGloc.size())
+                return 0;
+            be::skip<uint16>(gloc, glyphid);
+            glocs = be::read<uint16>(gloc);
+            gloce = be::peek<uint16>(gloc);
+        }
+
+        if (glocs >= m_pGlat.size() - 1 || gloce > m_pGlat.size())
+            return 0;
+
+        const uint32 glat_version = be::peek<uint32>(m_pGlat);
+        if (glat_version >= 0x00030000)
+        {
+            if (glocs >= gloce)
+                return 0;
+            const byte * p = m_pGlat + glocs;
+            uint16 bmap = be::read<uint16>(p);
+            int num = bit_set_count((uint32)bmap);
+            if (numsubs) *numsubs += num;
+            glocs += 6 + 8 * num;
+            if (glocs > gloce)
+                return 0;
+        }
+        if (glat_version < 0x00020000)
+        {
+            if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16)
+                || gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16)))
+                    return 0;
+            new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce));
+        }
+        else
+        {
+            if (gloce - glocs < 3*sizeof(uint16)        // can a glyph have no attributes? why not?
+                || gloce - glocs > _num_attrs*3*sizeof(uint16)
+                || glocs > m_pGlat.size() - 2*sizeof(uint16))
+                    return 0;
+            new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce));
+        }
+        if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs)
+            return 0;
+    }
+    return &glyph;
+}
+
+inline float scale_to(uint8 t, float zmin, float zmax)
+{
+    return (zmin + t * (zmax - zmin) / 255);
+}
+
+Rect readbox(Rect &b, uint8 zxmin, uint8 zymin, uint8 zxmax, uint8 zymax)
+{
+    return Rect(Position(scale_to(zxmin, b.bl.x, b.tr.x), scale_to(zymin, b.bl.y, b.tr.y)),
+                Position(scale_to(zxmax, b.bl.x, b.tr.x), scale_to(zymax, b.bl.y, b.tr.y)));
+}
+
+GlyphBox * GlyphCache::Loader::read_box(uint16 gid, GlyphBox *curr, const GlyphFace & glyph) const throw()
+{
+    if (gid >= _num_glyphs_attributes) return 0;
+
+    const byte * gloc = m_pGloc;
+    size_t      glocs = 0, gloce = 0;
+
+    be::skip<uint32>(gloc);
+    be::skip<uint16>(gloc,2);
+    if (_long_fmt)
+    {
+        be::skip<uint32>(gloc, gid);
+        glocs = be::read<uint32>(gloc);
+        gloce = be::peek<uint32>(gloc);
+    }
+    else
+    {
+        be::skip<uint16>(gloc, gid);
+        glocs = be::read<uint16>(gloc);
+        gloce = be::peek<uint16>(gloc);
+    }
+
+    if (gloce > m_pGlat.size() || glocs + 6 >= gloce)
+        return 0;
+
+    const byte * p = m_pGlat + glocs;
+    uint16 bmap = be::read<uint16>(p);
+    int num = bit_set_count((uint32)bmap);
+
+    Rect bbox = glyph.theBBox();
+    Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y),
+                Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y));
+    Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]);
+    ::new (curr) GlyphBox(num, bmap, &diabound);
+    be::skip<uint8>(p, 4);
+    if (glocs + 6 + num * 8 >= gloce)
+        return 0;
+
+    for (int i = 0; i < num * 2; ++i)
+    {
+        Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]);
+        curr->addSubBox(i >> 1, i & 1, &box);
+        be::skip<uint8>(p, 4);
+    }
+    return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect));
+}

+ 48 - 0
thirdparty/graphite/src/GlyphFace.cpp

@@ -0,0 +1,48 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/GlyphFace.h"
+
+
+using namespace graphite2;
+
+int32 GlyphFace::getMetric(uint8 metric) const
+{
+    switch (metrics(metric))
+    {
+        case kgmetLsb       : return int32(m_bbox.bl.x);
+        case kgmetRsb       : return int32(m_advance.x - m_bbox.tr.x);
+        case kgmetBbTop     : return int32(m_bbox.tr.y);
+        case kgmetBbBottom  : return int32(m_bbox.bl.y);
+        case kgmetBbLeft    : return int32(m_bbox.bl.x);
+        case kgmetBbRight   : return int32(m_bbox.tr.x);
+        case kgmetBbHeight  : return int32(m_bbox.tr.y - m_bbox.bl.y);
+        case kgmetBbWidth   : return int32(m_bbox.tr.x - m_bbox.bl.x);
+        case kgmetAdvWidth  : return int32(m_advance.x);
+        case kgmetAdvHeight : return int32(m_advance.y);
+        default : return 0;
+    }
+}

+ 298 - 0
thirdparty/graphite/src/Intervals.cpp

@@ -0,0 +1,298 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "inc/Intervals.h"
+#include "inc/Segment.h"
+#include "inc/Slot.h"
+#include "inc/debug.h"
+#include "inc/bits.h"
+
+using namespace graphite2;
+
+#include <cmath>
+
+inline
+Zones::Exclusion  Zones::Exclusion::split_at(float p) {
+    Exclusion r(*this);
+    r.xm = x = p;
+    return r;
+}
+
+inline
+void Zones::Exclusion::left_trim(float p) {
+    x = p;
+}
+
+inline
+Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
+    c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
+    return *this;
+}
+
+inline
+uint8 Zones::Exclusion::outcode(float val) const {
+    float p = val;
+    //float d = std::numeric_limits<float>::epsilon();
+    float d = 0.;
+    return ((p - xm >= d) << 1) | (x - p > d);
+}
+
+void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
+    remove(xmin, xmax);
+    weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
+    weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
+}
+
+namespace
+{
+
+inline
+bool separated(float a, float b) {
+    return a != b;
+    //int exp;
+    //float res = frexpf(fabs(a - b), &exp);
+    //return (*(unsigned int *)(&res) > 4);
+    //return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
+    //return std::fabs(a-b) > 0.5f;
+}
+
+}
+
+void Zones::insert(Exclusion e)
+{
+#if !defined GRAPHITE2_NTRACING
+    addDebug(&e);
+#endif
+    e.x = max(e.x, _pos);
+    e.xm = min(e.xm, _posm);
+    if (e.x >= e.xm) return;
+
+    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
+    {
+        const uint8 oca = e.outcode(i->x),
+                    ocb = e.outcode(i->xm);
+        if ((oca & ocb) != 0) continue;
+
+        switch (oca ^ ocb)  // What kind of overlap?
+        {
+        case 0:     // e completely covers i
+            // split e at i.x into e1,e2
+            // split e2 at i.mx into e2,e3
+            // drop e1 ,i+e2, e=e3
+            *i += e;
+            e.left_trim(i->xm);
+            break;
+        case 1:     // e overlaps on the rhs of i
+            // split i at e->x into i1,i2
+            // split e at i.mx into e1,e2
+            // trim i1, insert i2+e1, e=e2
+            if (!separated(i->xm, e.x)) break;
+            if (separated(i->x,e.x))   { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
+            *i += e;
+            e.left_trim(i->xm);
+            break;
+        case 2:     // e overlaps on the lhs of i
+            // split e at i->x into e1,e2
+            // split i at e.mx into i1,i2
+            // drop e1, insert e2+i1, trim i2
+            if (!separated(e.xm, i->x)) return;
+            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
+            *i += e;
+            return;
+        case 3:     // i completely covers e
+            // split i at e.x into i1,i2
+            // split i2 at e.mx into i2,i3
+            // insert i1, insert e+i2
+            if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
+            i = _exclusions.insert(i, i->split_at(e.x));
+            *++i += e;
+            return;
+        }
+
+        ie = _exclusions.end();
+    }
+}
+
+
+void Zones::remove(float x, float xm)
+{
+#if !defined GRAPHITE2_NTRACING
+    removeDebug(x, xm);
+#endif
+    x = max(x, _pos);
+    xm = min(xm, _posm);
+    if (x >= xm) return;
+
+    for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
+    {
+        const uint8 oca = i->outcode(x),
+                    ocb = i->outcode(xm);
+        if ((oca & ocb) != 0)   continue;
+
+        switch (oca ^ ocb)  // What kind of overlap?
+        {
+        case 0:     // i completely covers e
+            if (separated(i->x, x))  { i = _exclusions.insert(i,i->split_at(x)); ++i; }
+            GR_FALLTHROUGH;
+            // no break
+        case 1:     // i overlaps on the rhs of e
+            i->left_trim(xm);
+            return;
+        case 2:     // i overlaps on the lhs of e
+            i->xm = x;
+            if (separated(i->x, i->xm)) break;
+            GR_FALLTHROUGH;
+            // no break
+        case 3:     // e completely covers i
+            i = _exclusions.erase(i);
+            --i;
+            break;
+        }
+
+        ie = _exclusions.end();
+    }
+}
+
+
+Zones::const_iterator Zones::find_exclusion_under(float x) const
+{
+    size_t l = 0, h = _exclusions.size();
+
+    while (l < h)
+    {
+        size_t const p = (l+h) >> 1;
+        switch (_exclusions[p].outcode(x))
+        {
+        case 0 : return _exclusions.begin()+p;
+        case 1 : h = p; break;
+        case 2 :
+        case 3 : l = p+1; break;
+        }
+    }
+
+    return _exclusions.begin()+l;
+}
+
+
+float Zones::closest(float origin, float & cost) const
+{
+    float best_c = std::numeric_limits<float>::max(),
+          best_x = 0;
+
+    const const_iterator start = find_exclusion_under(origin);
+
+    // Forward scan looking for lowest cost
+    for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
+        if (i->track_cost(best_c, best_x, origin)) break;
+
+    // Backward scan looking for lowest cost
+    //  We start from the exclusion to the immediate left of start since we've
+    //  already tested start with the right most scan above.
+    for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
+        if (i->track_cost(best_c, best_x, origin)) break;
+
+    cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
+    return best_x;
+}
+
+
+// Cost and test position functions
+
+bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
+    const float p = test_position(origin),
+                localc = cost(p - origin);
+    if (open && localc > best_cost) return true;
+
+    if (localc < best_cost)
+    {
+        best_cost = localc;
+        best_pos = p;
+    }
+    return false;
+}
+
+inline
+float Zones::Exclusion::cost(float p) const {
+    return (sm * p - 2 * smx) * p + c;
+}
+
+
+float Zones::Exclusion::test_position(float origin) const {
+    if (sm < 0)
+    {
+        // sigh, test both ends and perhaps the middle too!
+        float res = x;
+        float cl = cost(x);
+        if (x < origin && xm > origin)
+        {
+            float co = cost(origin);
+            if (co < cl)
+            {
+                cl = co;
+                res = origin;
+            }
+        }
+        float cr = cost(xm);
+        return cl > cr ? xm : res;
+    }
+    else
+    {
+        float zerox = smx / sm + origin;
+        if (zerox < x) return x;
+        else if (zerox > xm) return xm;
+        else return zerox;
+    }
+}
+
+
+#if !defined GRAPHITE2_NTRACING
+
+void Zones::jsonDbgOut(Segment *seg) const {
+
+    if (_dbg)
+    {
+        for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
+        {
+            *_dbg << json::flat << json::array
+                << objectid(dslot(seg, (Slot *)(s->_env[0])))
+                << reinterpret_cast<ptrdiff_t>(s->_env[1]);
+            if (s->_isdel)
+                *_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
+            else
+                *_dbg << "exclude" << json::flat << json::array
+                    << s->_excl.x << s->_excl.xm
+                    << s->_excl.sm << s->_excl.smx << s->_excl.c
+                    << json::close;
+            *_dbg << json::close;
+        }
+    }
+}
+
+#endif

+ 282 - 0
thirdparty/graphite/src/Justifier.cpp

@@ -0,0 +1,282 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2012, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+#include "inc/Segment.h"
+#include "graphite2/Font.h"
+#include "inc/debug.h"
+#include "inc/CharInfo.h"
+#include "inc/Slot.h"
+#include "inc/Main.h"
+#include <cmath>
+
+using namespace graphite2;
+
+class JustifyTotal {
+public:
+    JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
+    void accumulate(Slot *s, Segment *seg, int level);
+    int weight() const { return m_tWeight; }
+
+    CLASS_NEW_DELETE
+
+private:
+    int m_numGlyphs;
+    int m_tStretch;
+    int m_tShrink;
+    int m_tStep;
+    int m_tWeight;
+};
+
+void JustifyTotal::accumulate(Slot *s, Segment *seg, int level)
+{
+    ++m_numGlyphs;
+    m_tStretch += s->getJustify(seg, level, 0);
+    m_tShrink += s->getJustify(seg, level, 1);
+    m_tStep += s->getJustify(seg, level, 2);
+    m_tWeight += s->getJustify(seg, level, 3);
+}
+
+float Segment::justify(Slot *pSlot, const Font *font, float width, GR_MAYBE_UNUSED justFlags jflags, Slot *pFirst, Slot *pLast)
+{
+    Slot *end = last();
+    float currWidth = 0.0;
+    const float scale = font ? font->scale() : 1.0f;
+    Position res;
+
+    if (width < 0 && !(silf()->flags()))
+        return width;
+
+    if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
+    {
+        reverseSlots();
+        std::swap(pFirst, pLast);
+    }
+    if (!pFirst) pFirst = pSlot;
+    while (!pFirst->isBase()) pFirst = pFirst->attachedTo();
+    if (!pLast) pLast = last();
+    while (!pLast->isBase()) pLast = pLast->attachedTo();
+    const float base = pFirst->origin().x / scale;
+    width = width / scale;
+    if ((jflags & gr_justEndInline) == 0)
+    {
+        while (pLast != pFirst && pLast)
+        {
+            Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
+            if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)
+                break;
+            pLast = pLast->prev();
+        }
+    }
+
+    if (pLast)
+        end = pLast->nextSibling();
+    if (pFirst)
+        pFirst = pFirst->nextSibling();
+
+    int icount = 0;
+    int numLevels = silf()->numJustLevels();
+    if (!numLevels)
+    {
+        for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
+        {
+            CharInfo *c = charinfo(s->before());
+            if (isWhitespace(c->unicodeChar()))
+            {
+                s->setJustify(this, 0, 3, 1);
+                s->setJustify(this, 0, 2, 1);
+                s->setJustify(this, 0, 0, -1);
+                ++icount;
+            }
+        }
+        if (!icount)
+        {
+            for (Slot *s = pSlot; s && s != end; s = s->nextSibling())
+            {
+                s->setJustify(this, 0, 3, 1);
+                s->setJustify(this, 0, 2, 1);
+                s->setJustify(this, 0, 0, -1);
+            }
+        }
+        ++numLevels;
+    }
+
+    Vector<JustifyTotal> stats(numLevels);
+    for (Slot *s = pFirst; s && s != end; s = s->nextSibling())
+    {
+        float w = s->origin().x / scale + s->advance() - base;
+        if (w > currWidth) currWidth = w;
+        for (int j = 0; j < numLevels; ++j)
+            stats[j].accumulate(s, this, j);
+        s->just(0);
+    }
+
+    for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)
+    {
+        float diff;
+        float error = 0.;
+        float diffpw;
+        int tWeight = stats[i].weight();
+        if (tWeight == 0) continue;
+
+        do {
+            error = 0.;
+            diff = width - currWidth;
+            diffpw = diff / tWeight;
+            tWeight = 0;
+            for (Slot *s = pFirst; s && s != end; s = s->nextSibling()) // don't include final glyph
+            {
+                int w = s->getJustify(this, i, 3);
+                float pref = diffpw * w + error;
+                int step = s->getJustify(this, i, 2);
+                if (!step) step = 1;        // handle lazy font developers
+                if (pref > 0)
+                {
+                    float max = uint16(s->getJustify(this, i, 0));
+                    if (i == 0) max -= s->just();
+                    if (pref > max) pref = max;
+                    else tWeight += w;
+                }
+                else
+                {
+                    float max = uint16(s->getJustify(this, i, 1));
+                    if (i == 0) max += s->just();
+                    if (-pref > max) pref = -max;
+                    else tWeight += w;
+                }
+                int actual = int(pref / step) * step;
+
+                if (actual)
+                {
+                    error += diffpw * w - actual;
+                    if (i == 0)
+                        s->just(s->just() + actual);
+                    else
+                        s->setJustify(this, i, 4, actual);
+                }
+            }
+            currWidth += diff - error;
+        } while (i == 0 && int(std::abs(error)) > 0 && tWeight);
+    }
+
+    Slot *oldFirst = m_first;
+    Slot *oldLast = m_last;
+    if (silf()->flags() & 1)
+    {
+        m_first = pSlot = addLineEnd(pSlot);
+        m_last = pLast = addLineEnd(end);
+        if (!m_first || !m_last) return -1.0;
+    }
+    else
+    {
+        m_first = pSlot;
+        m_last = pLast;
+    }
+
+    // run justification passes here
+#if !defined GRAPHITE2_NTRACING
+    json * const dbgout = m_face->logger();
+    if (dbgout)
+        *dbgout << json::object
+                    << "justifies"  << objectid(this)
+                    << "passes"     << json::array;
+#endif
+
+    if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))
+        m_silf->runGraphite(this, m_silf->justificationPass(), m_silf->positionPass());
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+    {
+        *dbgout     << json::item << json::close; // Close up the passes array
+        positionSlots(NULL, pSlot, pLast, m_dir);
+        Slot *lEnd = pLast->nextSibling();
+        *dbgout << "output" << json::array;
+        for(Slot * t = pSlot; t != lEnd; t = t->next())
+            *dbgout     << dslot(this, t);
+        *dbgout         << json::close << json::close;
+    }
+#endif
+
+    res = positionSlots(font, pSlot, pLast, m_dir);
+
+    if (silf()->flags() & 1)
+    {
+        if (m_first)
+            delLineEnd(m_first);
+        if (m_last)
+            delLineEnd(m_last);
+    }
+    m_first = oldFirst;
+    m_last = oldLast;
+
+    if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
+        reverseSlots();
+    return res.x;
+}
+
+Slot *Segment::addLineEnd(Slot *nSlot)
+{
+    Slot *eSlot = newSlot();
+    if (!eSlot) return NULL;
+    const uint16 gid = silf()->endLineGlyphid();
+    const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
+    eSlot->setGlyph(this, gid, theGlyph);
+    if (nSlot)
+    {
+        eSlot->next(nSlot);
+        eSlot->prev(nSlot->prev());
+        nSlot->prev(eSlot);
+        eSlot->before(nSlot->before());
+        if (eSlot->prev())
+            eSlot->after(eSlot->prev()->after());
+        else
+            eSlot->after(nSlot->before());
+    }
+    else
+    {
+        nSlot = m_last;
+        eSlot->prev(nSlot);
+        nSlot->next(eSlot);
+        eSlot->after(eSlot->prev()->after());
+        eSlot->before(nSlot->after());
+    }
+    return eSlot;
+}
+
+void Segment::delLineEnd(Slot *s)
+{
+    Slot *nSlot = s->next();
+    if (nSlot)
+    {
+        nSlot->prev(s->prev());
+        if (s->prev())
+            s->prev()->next(nSlot);
+    }
+    else
+        s->prev()->next(NULL);
+    freeSlot(s);
+}

+ 254 - 0
thirdparty/graphite/src/NameTable.cpp

@@ -0,0 +1,254 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/Main.h"
+#include "inc/Endian.h"
+
+#include "inc/NameTable.h"
+#include "inc/UtfCodec.h"
+
+using namespace graphite2;
+
+NameTable::NameTable(const void* data, size_t length, uint16 platformId, uint16 encodingID)
+ : m_platformId(0), m_encodingId(0), m_languageCount(0),
+   m_platformOffset(0), m_platformLastRecord(0), m_nameDataLength(0),
+   m_table(0), m_nameData(NULL)
+{
+    void *pdata = gralloc<byte>(length);
+    if (!pdata) return;
+    memcpy(pdata, data, length);
+    m_table = reinterpret_cast<const TtfUtil::Sfnt::FontNames*>(pdata);
+
+    if ((length > sizeof(TtfUtil::Sfnt::FontNames)) &&
+        (length > sizeof(TtfUtil::Sfnt::FontNames) +
+         sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16>(m_table->count) - 1)))
+    {
+        uint16 offset = be::swap<uint16>(m_table->string_offset);
+        if (offset < length)
+        {
+            m_nameData = reinterpret_cast<const uint8*>(pdata) + offset;
+            setPlatformEncoding(platformId, encodingID);
+            m_nameDataLength = uint16(length - offset);
+            return;
+        }
+    }
+    free(const_cast<TtfUtil::Sfnt::FontNames*>(m_table));
+    m_table = NULL;
+}
+
+uint16 NameTable::setPlatformEncoding(uint16 platformId, uint16 encodingID)
+{
+    if (!m_nameData) return 0;
+    uint16 i = 0;
+    uint16 count = be::swap<uint16>(m_table->count);
+    for (; i < count; i++)
+    {
+        if (be::swap<uint16>(m_table->name_record[i].platform_id) == platformId &&
+            be::swap<uint16>(m_table->name_record[i].platform_specific_id) == encodingID)
+        {
+            m_platformOffset = i;
+            break;
+        }
+    }
+    while ((++i < count) &&
+           (be::swap<uint16>(m_table->name_record[i].platform_id) == platformId) &&
+           (be::swap<uint16>(m_table->name_record[i].platform_specific_id) == encodingID))
+    {
+        m_platformLastRecord = i;
+    }
+    m_encodingId = encodingID;
+    m_platformId = platformId;
+    return 0;
+}
+
+void* NameTable::getName(uint16& languageId, uint16 nameId, gr_encform enc, uint32& length)
+{
+    uint16 anyLang = 0;
+    uint16 enUSLang = 0;
+    uint16 bestLang = 0;
+    if (!m_table)
+    {
+        languageId = 0;
+        length = 0;
+        return NULL;
+    }
+    for (uint16 i = m_platformOffset; i <= m_platformLastRecord; i++)
+    {
+        if (be::swap<uint16>(m_table->name_record[i].name_id) == nameId)
+        {
+            uint16 langId = be::swap<uint16>(m_table->name_record[i].language_id);
+            if (langId == languageId)
+            {
+                bestLang = i;
+                break;
+            }
+            // MS language tags have the language in the lower byte, region in the higher
+            else if ((langId & 0xFF) == (languageId & 0xFF))
+            {
+                bestLang = i;
+            }
+            else if (langId == 0x409)
+            {
+                enUSLang = i;
+            }
+            else
+            {
+                anyLang = i;
+            }
+        }
+    }
+    if (!bestLang)
+    {
+        if (enUSLang) bestLang = enUSLang;
+        else
+        {
+            bestLang = anyLang;
+            if (!anyLang)
+            {
+                languageId = 0;
+                length = 0;
+                return NULL;
+            }
+        }
+    }
+    const TtfUtil::Sfnt::NameRecord & nameRecord = m_table->name_record[bestLang];
+    languageId = be::swap<uint16>(nameRecord.language_id);
+    uint16 utf16Length = be::swap<uint16>(nameRecord.length);
+    uint16 offset = be::swap<uint16>(nameRecord.offset);
+    if(offset + utf16Length > m_nameDataLength)
+    {
+        languageId = 0;
+        length = 0;
+        return NULL;
+    }
+    utf16Length >>= 1; // in utf16 units
+    utf16::codeunit_t * utf16Name = gralloc<utf16::codeunit_t>(utf16Length + 1);
+    if (!utf16Name)
+    {
+        languageId = 0;
+        length = 0;
+        return NULL;
+    }
+    const uint8* pName = m_nameData + offset;
+    for (size_t i = 0; i < utf16Length; i++)
+    {
+        utf16Name[i] = be::read<uint16>(pName);
+    }
+    utf16Name[utf16Length] = 0;
+    if (!utf16::validate(utf16Name, utf16Name + utf16Length))
+    {
+        free(utf16Name);
+        languageId = 0;
+        length = 0;
+        return NULL;
+    }
+    switch (enc)
+    {
+    case gr_utf8:
+    {
+        utf8::codeunit_t* uniBuffer = gralloc<utf8::codeunit_t>(3 * utf16Length + 1);
+        if (!uniBuffer)
+        {
+            free(utf16Name);
+            languageId = 0;
+            length = 0;
+            return NULL;
+        }
+        utf8::iterator d = uniBuffer;
+        for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
+            *d = *s;
+        length = uint32(d - uniBuffer);
+        uniBuffer[length] = 0;
+        free(utf16Name);
+        return uniBuffer;
+    }
+    case gr_utf16:
+        length = utf16Length;
+        return utf16Name;
+    case gr_utf32:
+    {
+        utf32::codeunit_t * uniBuffer = gralloc<utf32::codeunit_t>(utf16Length  + 1);
+        if (!uniBuffer)
+        {
+            free(utf16Name);
+            languageId = 0;
+            length = 0;
+            return NULL;
+        }
+        utf32::iterator d = uniBuffer;
+        for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
+            *d = *s;
+        length = uint32(d - uniBuffer);
+        uniBuffer[length] = 0;
+        free(utf16Name);
+        return uniBuffer;
+    }
+    }
+    free(utf16Name);
+    languageId = 0;
+    length = 0;
+    return NULL;
+}
+
+uint16 NameTable::getLanguageId(const char * bcp47Locale)
+{
+    size_t localeLength = strlen(bcp47Locale);
+    uint16 localeId = m_locale2Lang.getMsId(bcp47Locale);
+    if (m_table && (be::swap<uint16>(m_table->format) == 1))
+    {
+        const uint8 * pLangEntries = reinterpret_cast<const uint8*>(m_table) +
+            sizeof(TtfUtil::Sfnt::FontNames)
+            + sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16>(m_table->count) - 1);
+        uint16 numLangEntries = be::read<uint16>(pLangEntries);
+        const TtfUtil::Sfnt::LangTagRecord * langTag =
+            reinterpret_cast<const TtfUtil::Sfnt::LangTagRecord*>(pLangEntries);
+        if (pLangEntries + numLangEntries * sizeof(TtfUtil::Sfnt::LangTagRecord) <= m_nameData)
+        {
+            for (uint16 i = 0; i < numLangEntries; i++)
+            {
+                uint16 offset = be::swap<uint16>(langTag[i].offset);
+                uint16 length = be::swap<uint16>(langTag[i].length);
+                if ((offset + length <= m_nameDataLength) && (length == 2 * localeLength))
+                {
+                    const uint8* pName = m_nameData + offset;
+                    bool match = true;
+                    for (size_t j = 0; j < localeLength; j++)
+                    {
+                        uint16 code = be::read<uint16>(pName);
+                        if ((code > 0x7F) || (code != bcp47Locale[j]))
+                        {
+                            match = false;
+                            break;
+                        }
+                    }
+                    if (match)
+                        return 0x8000 + i;
+                }
+            }
+        }
+    }
+    return localeId;
+}

+ 1107 - 0
thirdparty/graphite/src/Pass.cpp

@@ -0,0 +1,1107 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/Main.h"
+#include "inc/debug.h"
+#include "inc/Endian.h"
+#include "inc/Pass.h"
+#include <cstring>
+#include <cstdlib>
+#include <cassert>
+#include <cmath>
+#include "inc/Segment.h"
+#include "inc/Code.h"
+#include "inc/Rule.h"
+#include "inc/Error.h"
+#include "inc/Collider.h"
+
+using namespace graphite2;
+using vm::Machine;
+typedef Machine::Code  Code;
+
+enum KernCollison
+{
+    None       = 0,
+    CrossSpace = 1,
+    InWord     = 2,
+    reserved   = 3
+};
+
+Pass::Pass()
+: m_silf(0),
+  m_cols(0),
+  m_rules(0),
+  m_ruleMap(0),
+  m_startStates(0),
+  m_transitions(0),
+  m_states(0),
+  m_codes(0),
+  m_progs(0),
+  m_numCollRuns(0),
+  m_kernColls(0),
+  m_iMaxLoop(0),
+  m_numGlyphs(0),
+  m_numRules(0),
+  m_numStates(0),
+  m_numTransition(0),
+  m_numSuccess(0),
+  m_successStart(0),
+  m_numColumns(0),
+  m_minPreCtxt(0),
+  m_maxPreCtxt(0),
+  m_colThreshold(0),
+  m_isReverseDir(false)
+{
+}
+
+Pass::~Pass()
+{
+    free(m_cols);
+    free(m_startStates);
+    free(m_transitions);
+    free(m_states);
+    free(m_ruleMap);
+
+    if (m_rules) delete [] m_rules;
+    if (m_codes) delete [] m_codes;
+    free(m_progs);
+}
+
+bool Pass::readPass(const byte * const pass_start, size_t pass_length, size_t subtable_base,
+        GR_MAYBE_UNUSED Face & face, passtype pt, GR_MAYBE_UNUSED uint32 version, Error &e)
+{
+    const byte * p              = pass_start,
+               * const pass_end = p + pass_length;
+    size_t numRanges;
+
+    if (e.test(pass_length < 40, E_BADPASSLENGTH)) return face.error(e);
+    // Read in basic values
+    const byte flags = be::read<byte>(p);
+    if (e.test((flags & 0x1f) &&
+            (pt < PASS_TYPE_POSITIONING || !m_silf->aCollision() || !face.glyphs().hasBoxes() || !(m_silf->flags() & 0x20)),
+            E_BADCOLLISIONPASS))
+        return face.error(e);
+    m_numCollRuns = flags & 0x7;
+    m_kernColls   = (flags >> 3) & 0x3;
+    m_isReverseDir = (flags >> 5) & 0x1;
+    m_iMaxLoop = be::read<byte>(p);
+    if (m_iMaxLoop < 1) m_iMaxLoop = 1;
+    be::skip<byte>(p,2); // skip maxContext & maxBackup
+    m_numRules = be::read<uint16>(p);
+    if (e.test(!m_numRules && m_numCollRuns == 0, E_BADEMPTYPASS)) return face.error(e);
+    be::skip<uint16>(p);   // fsmOffset - not sure why we would want this
+    const byte * const pcCode = pass_start + be::read<uint32>(p) - subtable_base,
+               * const rcCode = pass_start + be::read<uint32>(p) - subtable_base,
+               * const aCode  = pass_start + be::read<uint32>(p) - subtable_base;
+    be::skip<uint32>(p);
+    m_numStates = be::read<uint16>(p);
+    m_numTransition = be::read<uint16>(p);
+    m_numSuccess = be::read<uint16>(p);
+    m_numColumns = be::read<uint16>(p);
+    numRanges = be::read<uint16>(p);
+    be::skip<uint16>(p, 3); // skip searchRange, entrySelector & rangeShift.
+    assert(p - pass_start == 40);
+    // Perform some sanity checks.
+    if ( e.test(m_numTransition > m_numStates, E_BADNUMTRANS)
+            || e.test(m_numSuccess > m_numStates, E_BADNUMSUCCESS)
+            || e.test(m_numSuccess + m_numTransition < m_numStates, E_BADNUMSTATES)
+            || e.test(m_numRules && numRanges == 0, E_NORANGES)
+            || e.test(m_numColumns > 0x7FFF, E_BADNUMCOLUMNS))
+        return face.error(e);
+
+    m_successStart = m_numStates - m_numSuccess;
+    // test for beyond end - 1 to account for reading uint16
+    if (e.test(p + numRanges * 6 - 2 > pass_end, E_BADPASSLENGTH)) return face.error(e);
+    m_numGlyphs = be::peek<uint16>(p + numRanges * 6 - 4) + 1;
+    // Calculate the start of various arrays.
+    const byte * const ranges = p;
+    be::skip<uint16>(p, numRanges*3);
+    const byte * const o_rule_map = p;
+    be::skip<uint16>(p, m_numSuccess + 1);
+
+    // More sanity checks
+    if (e.test(reinterpret_cast<const byte *>(o_rule_map + m_numSuccess*sizeof(uint16)) > pass_end
+            || p > pass_end, E_BADRULEMAPLEN))
+        return face.error(e);
+    const size_t numEntries = be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
+    const byte * const   rule_map = p;
+    be::skip<uint16>(p, numEntries);
+
+    if (e.test(p + 2*sizeof(uint8) > pass_end, E_BADPASSLENGTH)) return face.error(e);
+    m_minPreCtxt = be::read<uint8>(p);
+    m_maxPreCtxt = be::read<uint8>(p);
+    if (e.test(m_minPreCtxt > m_maxPreCtxt, E_BADCTXTLENBOUNDS)) return face.error(e);
+    const byte * const start_states = p;
+    be::skip<int16>(p, m_maxPreCtxt - m_minPreCtxt + 1);
+    const uint16 * const sort_keys = reinterpret_cast<const uint16 *>(p);
+    be::skip<uint16>(p, m_numRules);
+    const byte * const precontext = p;
+    be::skip<byte>(p, m_numRules);
+
+    if (e.test(p + sizeof(uint16) + sizeof(uint8) > pass_end, E_BADCTXTLENS)) return face.error(e);
+    m_colThreshold = be::read<uint8>(p);
+    if (m_colThreshold == 0) m_colThreshold = 10;       // A default
+    const size_t pass_constraint_len = be::read<uint16>(p);
+
+    const uint16 * const o_constraint = reinterpret_cast<const uint16 *>(p);
+    be::skip<uint16>(p, m_numRules + 1);
+    const uint16 * const o_actions = reinterpret_cast<const uint16 *>(p);
+    be::skip<uint16>(p, m_numRules + 1);
+    const byte * const states = p;
+    if (e.test(2u*m_numTransition*m_numColumns >= (unsigned)(pass_end - p), E_BADPASSLENGTH)
+            || e.test(p >= pass_end, E_BADPASSLENGTH))
+        return face.error(e);
+    be::skip<int16>(p, m_numTransition*m_numColumns);
+    be::skip<uint8>(p);
+    if (e.test(p != pcCode, E_BADPASSCCODEPTR)) return face.error(e);
+    be::skip<byte>(p, pass_constraint_len);
+    if (e.test(p != rcCode, E_BADRULECCODEPTR)
+        || e.test(size_t(rcCode - pcCode) != pass_constraint_len, E_BADCCODELEN)) return face.error(e);
+    be::skip<byte>(p, be::peek<uint16>(o_constraint + m_numRules));
+    if (e.test(p != aCode, E_BADACTIONCODEPTR)) return face.error(e);
+    be::skip<byte>(p, be::peek<uint16>(o_actions + m_numRules));
+
+    // We should be at the end or within the pass
+    if (e.test(p > pass_end, E_BADPASSLENGTH)) return face.error(e);
+
+    // Load the pass constraint if there is one.
+    if (pass_constraint_len)
+    {
+        face.error_context(face.error_context() + 1);
+        m_cPConstraint = vm::Machine::Code(true, pcCode, pcCode + pass_constraint_len,
+                                  precontext[0], be::peek<uint16>(sort_keys), *m_silf, face, PASS_TYPE_UNKNOWN);
+        if (e.test(!m_cPConstraint, E_OUTOFMEM)
+                || e.test(m_cPConstraint.status() != Code::loaded, m_cPConstraint.status() + E_CODEFAILURE))
+            return face.error(e);
+        face.error_context(face.error_context() - 1);
+    }
+    if (m_numRules)
+    {
+        if (!readRanges(ranges, numRanges, e)) return face.error(e);
+        if (!readRules(rule_map, numEntries,  precontext, sort_keys,
+                   o_constraint, rcCode, o_actions, aCode, face, pt, e)) return false;
+    }
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _states_cat(face.tele.states);
+#endif
+    return m_numRules ? readStates(start_states, states, o_rule_map, face, e) : true;
+}
+
+
+bool Pass::readRules(const byte * rule_map, const size_t num_entries,
+                     const byte *precontext, const uint16 * sort_key,
+                     const uint16 * o_constraint, const byte *rc_data,
+                     const uint16 * o_action,     const byte * ac_data,
+                     Face & face, passtype pt, Error &e)
+{
+    const byte * const ac_data_end = ac_data + be::peek<uint16>(o_action + m_numRules);
+    const byte * const rc_data_end = rc_data + be::peek<uint16>(o_constraint + m_numRules);
+
+    precontext += m_numRules;
+    sort_key   += m_numRules;
+    o_constraint += m_numRules;
+    o_action += m_numRules;
+
+    // Load rules.
+    const byte * ac_begin = 0, * rc_begin = 0,
+               * ac_end = ac_data + be::peek<uint16>(o_action),
+               * rc_end = rc_data + be::peek<uint16>(o_constraint);
+
+    // Allocate pools
+    m_rules = new Rule [m_numRules];
+    m_codes = new Code [m_numRules*2];
+    int totalSlots = 0;
+    const uint16 *tsort = sort_key;
+    for (int i = 0; i < m_numRules; ++i)
+        totalSlots += be::peek<uint16>(--tsort);
+    const size_t prog_pool_sz = vm::Machine::Code::estimateCodeDataOut(ac_end - ac_data + rc_end - rc_data, 2 * m_numRules, totalSlots);
+    m_progs = gralloc<byte>(prog_pool_sz);
+    byte * prog_pool_free = m_progs,
+         * prog_pool_end  = m_progs + prog_pool_sz;
+    if (e.test(!(m_rules && m_codes && m_progs), E_OUTOFMEM)) return face.error(e);
+
+    Rule * r = m_rules + m_numRules - 1;
+    for (size_t n = m_numRules; r >= m_rules; --n, --r, ac_end = ac_begin, rc_end = rc_begin)
+    {
+        face.error_context((face.error_context() & 0xFFFF00) + EC_ARULE + int((n - 1) << 24));
+        r->preContext = *--precontext;
+        r->sort       = be::peek<uint16>(--sort_key);
+#ifndef NDEBUG
+        r->rule_idx   = uint16(n - 1);
+#endif
+        if (r->sort > 63 || r->preContext >= r->sort || r->preContext > m_maxPreCtxt || r->preContext < m_minPreCtxt)
+            return false;
+        ac_begin      = ac_data + be::peek<uint16>(--o_action);
+        --o_constraint;
+        rc_begin      = be::peek<uint16>(o_constraint) ? rc_data + be::peek<uint16>(o_constraint) : rc_end;
+
+        if (ac_begin > ac_end || ac_begin > ac_data_end || ac_end > ac_data_end
+                || rc_begin > rc_end || rc_begin > rc_data_end || rc_end > rc_data_end
+                || vm::Machine::Code::estimateCodeDataOut(ac_end - ac_begin + rc_end - rc_begin, 2, r->sort) > size_t(prog_pool_end - prog_pool_free))
+            return false;
+        r->action     = new (m_codes+n*2-2) vm::Machine::Code(false, ac_begin, ac_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
+        r->constraint = new (m_codes+n*2-1) vm::Machine::Code(true,  rc_begin, rc_end, r->preContext, r->sort, *m_silf, face, pt, &prog_pool_free);
+
+        if (e.test(!r->action || !r->constraint, E_OUTOFMEM)
+                || e.test(r->action->status() != Code::loaded, r->action->status() + E_CODEFAILURE)
+                || e.test(r->constraint->status() != Code::loaded, r->constraint->status() + E_CODEFAILURE)
+                || e.test(!r->constraint->immutable(), E_MUTABLECCODE))
+            return face.error(e);
+    }
+
+    byte * const moved_progs = prog_pool_free > m_progs ? static_cast<byte *>(realloc(m_progs, prog_pool_free - m_progs)) : 0;
+    if (e.test(!moved_progs, E_OUTOFMEM))
+    {
+        free(m_progs);
+        m_progs = 0;
+        return face.error(e);
+    }
+
+    if (moved_progs != m_progs)
+    {
+        for (Code * c = m_codes, * const ce = c + m_numRules*2; c != ce; ++c)
+        {
+            c->externalProgramMoved(moved_progs - m_progs);
+        }
+        m_progs = moved_progs;
+    }
+
+    // Load the rule entries map
+    face.error_context((face.error_context() & 0xFFFF00) + EC_APASS);
+    //TODO: Coverity: 1315804: FORWARD_NULL
+    RuleEntry * re = m_ruleMap = gralloc<RuleEntry>(num_entries);
+    if (e.test(!re, E_OUTOFMEM)) return face.error(e);
+    for (size_t n = num_entries; n; --n, ++re)
+    {
+        const ptrdiff_t rn = be::read<uint16>(rule_map);
+        if (e.test(rn >= m_numRules, E_BADRULENUM))  return face.error(e);
+        re->rule = m_rules + rn;
+    }
+
+    return true;
+}
+
+static int cmpRuleEntry(const void *a, const void *b) { return (*(RuleEntry *)a < *(RuleEntry *)b ? -1 :
+                                                                (*(RuleEntry *)b < *(RuleEntry *)a ? 1 : 0)); }
+
+bool Pass::readStates(const byte * starts, const byte *states, const byte * o_rule_map, GR_MAYBE_UNUSED Face & face, Error &e)
+{
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::category _states_cat(face.tele.starts);
+#endif
+    m_startStates = gralloc<uint16>(m_maxPreCtxt - m_minPreCtxt + 1);
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::set_category(face.tele.states);
+#endif
+    m_states      = gralloc<State>(m_numStates);
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::set_category(face.tele.transitions);
+#endif
+    m_transitions      = gralloc<uint16>(m_numTransition * m_numColumns);
+
+    if (e.test(!m_startStates || !m_states || !m_transitions, E_OUTOFMEM)) return face.error(e);
+    // load start states
+    for (uint16 * s = m_startStates,
+                * const s_end = s + m_maxPreCtxt - m_minPreCtxt + 1; s != s_end; ++s)
+    {
+        *s = be::read<uint16>(starts);
+        if (e.test(*s >= m_numStates, E_BADSTATE))
+        {
+            face.error_context((face.error_context() & 0xFFFF00) + EC_ASTARTS + int((s - m_startStates) << 24));
+            return face.error(e); // true;
+        }
+    }
+
+    // load state transition table.
+    for (uint16 * t = m_transitions,
+                * const t_end = t + m_numTransition*m_numColumns; t != t_end; ++t)
+    {
+        *t = be::read<uint16>(states);
+        if (e.test(*t >= m_numStates, E_BADSTATE))
+        {
+            face.error_context((face.error_context() & 0xFFFF00) + EC_ATRANS + int(((t - m_transitions) / m_numColumns) << 8));
+            return face.error(e);
+        }
+    }
+
+    State * s = m_states,
+          * const success_begin = m_states + m_numStates - m_numSuccess;
+    const RuleEntry * rule_map_end = m_ruleMap + be::peek<uint16>(o_rule_map + m_numSuccess*sizeof(uint16));
+    for (size_t n = m_numStates; n; --n, ++s)
+    {
+        RuleEntry * const begin = s < success_begin ? 0 : m_ruleMap + be::read<uint16>(o_rule_map),
+                  * const end   = s < success_begin ? 0 : m_ruleMap + be::peek<uint16>(o_rule_map);
+
+        if (e.test(begin >= rule_map_end || end > rule_map_end || begin > end, E_BADRULEMAPPING))
+        {
+            face.error_context((face.error_context() & 0xFFFF00) + EC_ARULEMAP + int(n << 24));
+            return face.error(e);
+        }
+        s->rules = begin;
+        s->rules_end = (end - begin <= FiniteStateMachine::MAX_RULES)? end :
+            begin + FiniteStateMachine::MAX_RULES;
+        if (begin)      // keep UBSan happy can't call qsort with null begin
+            qsort(begin, end - begin, sizeof(RuleEntry), &cmpRuleEntry);
+    }
+
+    return true;
+}
+
+bool Pass::readRanges(const byte * ranges, size_t num_ranges, Error &e)
+{
+    m_cols = gralloc<uint16>(m_numGlyphs);
+    if (e.test(!m_cols, E_OUTOFMEM)) return false;
+    memset(m_cols, 0xFF, m_numGlyphs * sizeof(uint16));
+    for (size_t n = num_ranges; n; --n)
+    {
+        uint16     * ci     = m_cols + be::read<uint16>(ranges),
+                   * ci_end = m_cols + be::read<uint16>(ranges) + 1,
+                     col    = be::read<uint16>(ranges);
+
+        if (e.test(ci >= ci_end || ci_end > m_cols+m_numGlyphs || col >= m_numColumns, E_BADRANGE))
+            return false;
+
+        // A glyph must only belong to one column at a time
+        while (ci != ci_end && *ci == 0xffff)
+            *ci++ = col;
+
+        if (e.test(ci != ci_end, E_BADRANGE))
+            return false;
+    }
+    return true;
+}
+
+
+bool Pass::runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const
+{
+    Slot *s = m.slotMap().segment.first();
+    if (!s || !testPassConstraint(m)) return true;
+    if (reverse)
+    {
+        m.slotMap().segment.reverseSlots();
+        s = m.slotMap().segment.first();
+    }
+    if (m_numRules)
+    {
+        Slot *currHigh = s->next();
+
+#if !defined GRAPHITE2_NTRACING
+        if (fsm.dbgout)  *fsm.dbgout << "rules" << json::array;
+        json::closer rules_array_closer(fsm.dbgout);
+#endif
+
+        m.slotMap().highwater(currHigh);
+        int lc = m_iMaxLoop;
+        do
+        {
+            findNDoRule(s, m, fsm);
+            if (m.status() != Machine::finished) return false;
+            if (s && (s == m.slotMap().highwater() || m.slotMap().highpassed() || --lc == 0)) {
+                if (!lc)
+                    s = m.slotMap().highwater();
+                lc = m_iMaxLoop;
+                if (s)
+                    m.slotMap().highwater(s->next());
+            }
+        } while (s);
+    }
+    //TODO: Use enums for flags
+    const bool collisions = m_numCollRuns || m_kernColls;
+
+    if (!collisions || !m.slotMap().segment.hasCollisionInfo())
+        return true;
+
+    if (m_numCollRuns)
+    {
+        if (!(m.slotMap().segment.flags() & Segment::SEG_INITCOLLISIONS))
+        {
+            m.slotMap().segment.positionSlots(0, 0, 0, m.slotMap().dir(), true);
+//            m.slotMap().segment.flags(m.slotMap().segment.flags() | Segment::SEG_INITCOLLISIONS);
+        }
+        if (!collisionShift(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
+            return false;
+    }
+    if ((m_kernColls) && !collisionKern(&m.slotMap().segment, m.slotMap().dir(), fsm.dbgout))
+        return false;
+    if (collisions && !collisionFinish(&m.slotMap().segment, fsm.dbgout))
+        return false;
+    return true;
+}
+
+bool Pass::runFSM(FiniteStateMachine& fsm, Slot * slot) const
+{
+    fsm.reset(slot, m_maxPreCtxt);
+    if (fsm.slots.context() < m_minPreCtxt)
+        return false;
+
+    uint16 state = m_startStates[m_maxPreCtxt - fsm.slots.context()];
+    uint8  free_slots = SlotMap::MAX_SLOTS;
+    do
+    {
+        fsm.slots.pushSlot(slot);
+        if (slot->gid() >= m_numGlyphs
+         || m_cols[slot->gid()] == 0xffffU
+         || --free_slots == 0
+         || state >= m_numTransition)
+            return free_slots != 0;
+
+        const uint16 * transitions = m_transitions + state*m_numColumns;
+        state = transitions[m_cols[slot->gid()]];
+        if (state >= m_successStart)
+            fsm.rules.accumulate_rules(m_states[state]);
+
+        slot = slot->next();
+    } while (state != 0 && slot);
+
+    fsm.slots.pushSlot(slot);
+    return true;
+}
+
+#if !defined GRAPHITE2_NTRACING
+
+inline
+Slot * input_slot(const SlotMap &  slots, const int n)
+{
+    Slot * s = slots[slots.context() + n];
+    if (!s->isCopied())     return s;
+
+    return s->prev() ? s->prev()->next() : (s->next() ? s->next()->prev() : slots.segment.last());
+}
+
+inline
+Slot * output_slot(const SlotMap &  slots, const int n)
+{
+    Slot * s = slots[slots.context() + n - 1];
+    return s ? s->next() : slots.segment.first();
+}
+
+#endif //!defined GRAPHITE2_NTRACING
+
+void Pass::findNDoRule(Slot * & slot, Machine &m, FiniteStateMachine & fsm) const
+{
+    assert(slot);
+
+    if (runFSM(fsm, slot))
+    {
+        // Search for the first rule which passes the constraint
+        const RuleEntry *        r = fsm.rules.begin(),
+                        * const re = fsm.rules.end();
+        while (r != re && !testConstraint(*r->rule, m))
+        {
+            ++r;
+            if (m.status() != Machine::finished)
+                return;
+        }
+
+#if !defined GRAPHITE2_NTRACING
+        if (fsm.dbgout)
+        {
+            if (fsm.rules.size() != 0)
+            {
+                *fsm.dbgout << json::item << json::object;
+                dumpRuleEventConsidered(fsm, *r);
+                if (r != re)
+                {
+                    const int adv = doAction(r->rule->action, slot, m);
+                    dumpRuleEventOutput(fsm, *r->rule, slot);
+                    if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
+                    adjustSlot(adv, slot, fsm.slots);
+                    *fsm.dbgout << "cursor" << objectid(dslot(&fsm.slots.segment, slot))
+                            << json::close; // Close RuelEvent object
+
+                    return;
+                }
+                else
+                {
+                    *fsm.dbgout << json::close  // close "considered" array
+                            << "output" << json::null
+                            << "cursor" << objectid(dslot(&fsm.slots.segment, slot->next()))
+                            << json::close;
+                }
+            }
+        }
+        else
+#endif
+        {
+            if (r != re)
+            {
+                const int adv = doAction(r->rule->action, slot, m);
+                if (m.status() != Machine::finished) return;
+                if (r->rule->action->deletes()) fsm.slots.collectGarbage(slot);
+                adjustSlot(adv, slot, fsm.slots);
+                return;
+            }
+        }
+    }
+
+    slot = slot->next();
+    return;
+}
+
+#if !defined GRAPHITE2_NTRACING
+
+void Pass::dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const
+{
+    *fsm.dbgout << "considered" << json::array;
+    for (const RuleEntry *r = fsm.rules.begin(); r != &re; ++r)
+    {
+        if (r->rule->preContext > fsm.slots.context())
+            continue;
+        *fsm.dbgout << json::flat << json::object
+                    << "id" << r->rule - m_rules
+                    << "failed" << true
+                    << "input" << json::flat << json::object
+                        << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, -r->rule->preContext)))
+                        << "length" << r->rule->sort
+                        << json::close  // close "input"
+                    << json::close; // close Rule object
+    }
+}
+
+
+void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const
+{
+    *fsm.dbgout     << json::item << json::flat << json::object
+                        << "id"     << &r - m_rules
+                        << "failed" << false
+                        << "input" << json::flat << json::object
+                            << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
+                            << "length" << r.sort - r.preContext
+                            << json::close // close "input"
+                        << json::close  // close Rule object
+                << json::close // close considered array
+                << "output" << json::object
+                    << "range" << json::flat << json::object
+                        << "start"  << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0)))
+                        << "end"    << objectid(dslot(&fsm.slots.segment, last_slot))
+                    << json::close // close "input"
+                    << "slots"  << json::array;
+    const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance();
+    fsm.slots.segment.positionSlots(0, 0, 0, fsm.slots.segment.currdir());
+
+    for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next())
+        *fsm.dbgout     << dslot(&fsm.slots.segment, slot);
+    *fsm.dbgout         << json::close  // close "slots"
+                    << "postshift"  << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos
+                << json::close;         // close "output" object
+
+}
+
+#endif
+
+
+inline
+bool Pass::testPassConstraint(Machine & m) const
+{
+    if (!m_cPConstraint) return true;
+
+    assert(m_cPConstraint.constraint());
+
+    m.slotMap().reset(*m.slotMap().segment.first(), 0);
+    m.slotMap().pushSlot(m.slotMap().segment.first());
+    vm::slotref * map = m.slotMap().begin();
+    const uint32 ret = m_cPConstraint.run(m, map);
+
+#if !defined GRAPHITE2_NTRACING
+    json * const dbgout = m.slotMap().segment.getFace()->logger();
+    if (dbgout)
+        *dbgout << "constraint" << (ret && m.status() == Machine::finished);
+#endif
+
+    return ret && m.status() == Machine::finished;
+}
+
+
+bool Pass::testConstraint(const Rule & r, Machine & m) const
+{
+    const uint16 curr_context = m.slotMap().context();
+    if (unsigned(r.sort + curr_context - r.preContext) > m.slotMap().size()
+        || curr_context - r.preContext < 0) return false;
+
+    vm::slotref * map = m.slotMap().begin() + curr_context - r.preContext;
+    if (map[r.sort - 1] == 0)
+        return false;
+
+    if (!*r.constraint) return true;
+    assert(r.constraint->constraint());
+    for (int n = r.sort; n && map; --n, ++map)
+    {
+        if (!*map) continue;
+        const int32 ret = r.constraint->run(m, map);
+        if (!ret || m.status() != Machine::finished)
+            return false;
+    }
+
+    return true;
+}
+
+
+void SlotMap::collectGarbage(Slot * &aSlot)
+{
+    for(Slot **s = begin(), *const *const se = end() - 1; s != se; ++s) {
+        Slot *& slot = *s;
+        if(slot && (slot->isDeleted() || slot->isCopied()))
+        {
+            if (slot == aSlot)
+                aSlot = slot->prev() ? slot->prev() : slot->next();
+            segment.freeSlot(slot);
+        }
+    }
+}
+
+
+
+int Pass::doAction(const Code *codeptr, Slot * & slot_out, vm::Machine & m) const
+{
+    assert(codeptr);
+    if (!*codeptr) return 0;
+    SlotMap   & smap = m.slotMap();
+    vm::slotref * map = &smap[smap.context()];
+    smap.highpassed(false);
+
+    int32 ret = codeptr->run(m, map);
+
+    if (m.status() != Machine::finished)
+    {
+        slot_out = NULL;
+        smap.highwater(0);
+        return 0;
+    }
+
+    slot_out = *map;
+    return ret;
+}
+
+
+void Pass::adjustSlot(int delta, Slot * & slot_out, SlotMap & smap) const
+{
+    if (!slot_out)
+    {
+        if (smap.highpassed() || slot_out == smap.highwater())
+        {
+            slot_out = smap.segment.last();
+            ++delta;
+            if (!smap.highwater() || smap.highwater() == slot_out)
+                smap.highpassed(false);
+        }
+        else
+        {
+            slot_out = smap.segment.first();
+            --delta;
+        }
+    }
+    if (delta < 0)
+    {
+        while (++delta <= 0 && slot_out)
+        {
+            slot_out = slot_out->prev();
+            if (smap.highpassed() && smap.highwater() == slot_out)
+                smap.highpassed(false);
+        }
+    }
+    else if (delta > 0)
+    {
+        while (--delta >= 0 && slot_out)
+        {
+            if (slot_out == smap.highwater() && slot_out)
+                smap.highpassed(true);
+            slot_out = slot_out->next();
+        }
+    }
+}
+
+bool Pass::collisionShift(Segment *seg, int dir, json * const dbgout) const
+{
+    ShiftCollider shiftcoll(dbgout);
+    // bool isfirst = true;
+    bool hasCollisions = false;
+    Slot *start = seg->first();      // turn on collision fixing for the first slot
+    Slot *end = NULL;
+    bool moved = false;
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        *dbgout << "collisions" << json::array
+            << json::flat << json::object << "num-loops" << m_numCollRuns << json::close;
+#endif
+
+    while (start)
+    {
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)  *dbgout << json::object << "phase" << "1" << "moves" << json::array;
+#endif
+        hasCollisions = false;
+        end = NULL;
+        // phase 1 : position shiftable glyphs, ignoring kernable glyphs
+        for (Slot *s = start; s; s = s->next())
+        {
+            const SlotCollision * c = seg->collisionInfo(s);
+            if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
+                      && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
+                return false;
+            if (s != start && (c->flags() & SlotCollision::COLL_END))
+            {
+                end = s->next();
+                break;
+            }
+        }
+
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+            *dbgout << json::close << json::close; // phase-1
+#endif
+
+        // phase 2 : loop until happy.
+        for (int i = 0; i < m_numCollRuns - 1; ++i)
+        {
+            if (hasCollisions || moved)
+            {
+
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    *dbgout << json::object << "phase" << "2a" << "loop" << i << "moves" << json::array;
+#endif
+                // phase 2a : if any shiftable glyphs are in collision, iterate backwards,
+                // fixing them and ignoring other non-collided glyphs. Note that this handles ONLY
+                // glyphs that are actually in collision from phases 1 or 2b, and working backwards
+                // has the intended effect of breaking logjams.
+                if (hasCollisions)
+                {
+                    hasCollisions = false;
+                    #if 0
+                    moved = true;
+                    for (Slot *s = start; s != end; s = s->next())
+                    {
+                        SlotCollision * c = seg->collisionInfo(s);
+                        c->setShift(Position(0, 0));
+                    }
+                    #endif
+                    Slot *lend = end ? end->prev() : seg->last();
+                    Slot *lstart = start->prev();
+                    for (Slot *s = lend; s != lstart; s = s->prev())
+                    {
+                        SlotCollision * c = seg->collisionInfo(s);
+                        if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_KERN | SlotCollision::COLL_ISCOL))
+                                        == (SlotCollision::COLL_FIX | SlotCollision::COLL_ISCOL)) // ONLY if this glyph is still colliding
+                        {
+                            if (!resolveCollisions(seg, s, lend, shiftcoll, true, dir, moved, hasCollisions, dbgout))
+                                return false;
+                            c->setFlags(c->flags() | SlotCollision::COLL_TEMPLOCK);
+                        }
+                    }
+                }
+
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    *dbgout << json::close << json::close // phase 2a
+                        << json::object << "phase" << "2b" << "loop" << i << "moves" << json::array;
+#endif
+
+                // phase 2b : redo basic diacritic positioning pass for ALL glyphs. Each successive loop adjusts
+                // glyphs from their current adjusted position, which has the effect of gradually minimizing the
+                // resulting adjustment; ie, the final result will be gradually closer to the original location.
+                // Also it allows more flexibility in the final adjustment, since it is moving along the
+                // possible 8 vectors from successively different starting locations.
+                if (moved)
+                {
+                    moved = false;
+                    for (Slot *s = start; s != end; s = s->next())
+                    {
+                        SlotCollision * c = seg->collisionInfo(s);
+                        if (start && (c->flags() & (SlotCollision::COLL_FIX | SlotCollision::COLL_TEMPLOCK
+                                                        | SlotCollision::COLL_KERN)) == SlotCollision::COLL_FIX
+                                  && !resolveCollisions(seg, s, start, shiftcoll, false, dir, moved, hasCollisions, dbgout))
+                            return false;
+                        else if (c->flags() & SlotCollision::COLL_TEMPLOCK)
+                            c->setFlags(c->flags() & ~SlotCollision::COLL_TEMPLOCK);
+                    }
+                }
+        //      if (!hasCollisions) // no, don't leave yet because phase 2b will continue to improve things
+        //          break;
+#if !defined GRAPHITE2_NTRACING
+                if (dbgout)
+                    *dbgout << json::close << json::close; // phase 2
+#endif
+            }
+        }
+        if (!end)
+            break;
+        start = NULL;
+        for (Slot *s = end->prev(); s; s = s->next())
+        {
+            if (seg->collisionInfo(s)->flags() & SlotCollision::COLL_START)
+            {
+                start = s;
+                break;
+            }
+        }
+    }
+    return true;
+}
+
+bool Pass::collisionKern(Segment *seg, int dir, json * const dbgout) const
+{
+    Slot *start = seg->first();
+    float ymin = 1e38f;
+    float ymax = -1e38f;
+    const GlyphCache &gc = seg->getFace()->glyphs();
+
+    // phase 3 : handle kerning of clusters
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        *dbgout << json::object << "phase" << "3" << "moves" << json::array;
+#endif
+
+    for (Slot *s = seg->first(); s; s = s->next())
+    {
+        if (!gc.check(s->gid()))
+            return false;
+        const SlotCollision * c = seg->collisionInfo(s);
+        const Rect &bbox = seg->theGlyphBBoxTemporary(s->gid());
+        float y = s->origin().y + c->shift().y;
+        if (!(c->flags() & SlotCollision::COLL_ISSPACE))
+        {
+            ymax = max(y + bbox.tr.y, ymax);
+            ymin = min(y + bbox.bl.y, ymin);
+        }
+        if (start && (c->flags() & (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
+                        == (SlotCollision::COLL_KERN | SlotCollision::COLL_FIX))
+            resolveKern(seg, s, start, dir, ymin, ymax, dbgout);
+        if (c->flags() & SlotCollision::COLL_END)
+            start = NULL;
+        if (c->flags() & SlotCollision::COLL_START)
+            start = s;
+    }
+
+#if !defined GRAPHITE2_NTRACING
+    if (dbgout)
+        *dbgout << json::close << json::close; // phase 3
+#endif
+    return true;
+}
+
+bool Pass::collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const
+{
+    for (Slot *s = seg->first(); s; s = s->next())
+    {
+        SlotCollision *c = seg->collisionInfo(s);
+        if (c->shift().x != 0 || c->shift().y != 0)
+        {
+            const Position newOffset = c->shift();
+            const Position nullPosition(0, 0);
+            c->setOffset(newOffset + c->offset());
+            c->setShift(nullPosition);
+        }
+    }
+//    seg->positionSlots();
+
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+            *dbgout << json::close;
+#endif
+    return true;
+}
+
+// Can slot s be kerned, or is it attached to something that can be kerned?
+static bool inKernCluster(Segment *seg, Slot *s)
+{
+    SlotCollision *c = seg->collisionInfo(s);
+    if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
+        return true;
+    while (s->attachedTo())
+    {
+        s = s->attachedTo();
+        c = seg->collisionInfo(s);
+        if (c->flags() & SlotCollision::COLL_KERN /** && c->flags() & SlotCollision::COLL_FIX **/ )
+            return true;
+    }
+    return false;
+}
+
+// Fix collisions for the given slot.
+// Return true if everything was fixed, false if there are still collisions remaining.
+// isRev means be we are processing backwards.
+bool Pass::resolveCollisions(Segment *seg, Slot *slotFix, Slot *start,
+        ShiftCollider &coll, GR_MAYBE_UNUSED bool isRev, int dir, bool &moved, bool &hasCol,
+        json * const dbgout) const
+{
+    Slot * nbor;  // neighboring slot
+    SlotCollision *cFix = seg->collisionInfo(slotFix);
+    if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(), cFix->marginWt(),
+            cFix->shift(), cFix->offset(), dir, dbgout))
+        return false;
+    bool collides = false;
+    // When we're processing forward, ignore kernable glyphs that preceed the target glyph.
+    // When processing backward, don't ignore these until we pass slotFix.
+    bool ignoreForKern = !isRev;
+    bool rtl = dir & 1;
+    Slot *base = slotFix;
+    while (base->attachedTo())
+        base = base->attachedTo();
+    Position zero(0., 0.);
+
+    // Look for collisions with the neighboring glyphs.
+    for (nbor = start; nbor; nbor = isRev ? nbor->prev() : nbor->next())
+    {
+        SlotCollision *cNbor = seg->collisionInfo(nbor);
+        bool sameCluster = nbor->isChildOf(base);
+        if (nbor != slotFix         						// don't process if this is the slot of interest
+                      && !(cNbor->ignore())    				// don't process if ignoring
+                      && (nbor == base || sameCluster       // process if in the same cluster as slotFix
+                            || !inKernCluster(seg, nbor))   // or this cluster is not to be kerned
+//                            || (rtl ^ ignoreForKern))       // or it comes before(ltr) or after(rtl)
+                      && (!isRev    // if processing forwards then good to merge otherwise only:
+                            || !(cNbor->flags() & SlotCollision::COLL_FIX)     // merge in immovable stuff
+                            || ((cNbor->flags() & SlotCollision::COLL_KERN) && !sameCluster)     // ignore other kernable clusters
+                            || (cNbor->flags() & SlotCollision::COLL_ISCOL))   // test against other collided glyphs
+                      && !coll.mergeSlot(seg, nbor, cNbor, cNbor->shift(), !ignoreForKern, sameCluster, collides, false, dbgout))
+            return false;
+        else if (nbor == slotFix)
+            // Switching sides of this glyph - if we were ignoring kernable stuff before, don't anymore.
+            ignoreForKern = !ignoreForKern;
+
+        if (nbor != start && (cNbor->flags() & (isRev ? SlotCollision::COLL_START : SlotCollision::COLL_END)))
+            break;
+    }
+    bool isCol = false;
+    if (collides || cFix->shift().x != 0.f || cFix->shift().y != 0.f)
+    {
+        Position shift = coll.resolve(seg, isCol, dbgout);
+        // isCol has been set to true if a collision remains.
+        if (std::fabs(shift.x) < 1e38f && std::fabs(shift.y) < 1e38f)
+        {
+            if (sqr(shift.x-cFix->shift().x) + sqr(shift.y-cFix->shift().y) >= m_colThreshold * m_colThreshold)
+                moved = true;
+            cFix->setShift(shift);
+            if (slotFix->firstChild())
+            {
+                Rect bbox;
+                Position here = slotFix->origin() + shift;
+                float clusterMin = here.x;
+                slotFix->firstChild()->finalise(seg, NULL, here, bbox, 0, clusterMin, rtl, false);
+            }
+        }
+    }
+    else
+    {
+        // This glyph is not colliding with anything.
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+        {
+            *dbgout << json::object
+                            << "missed" << objectid(dslot(seg, slotFix));
+            coll.outputJsonDbg(dbgout, seg, -1);
+            *dbgout << json::close;
+        }
+#endif
+    }
+
+    // Set the is-collision flag bit.
+    if (isCol)
+    { cFix->setFlags(cFix->flags() | SlotCollision::COLL_ISCOL | SlotCollision::COLL_KNOWN); }
+    else
+    { cFix->setFlags((cFix->flags() & ~SlotCollision::COLL_ISCOL) | SlotCollision::COLL_KNOWN); }
+    hasCol |= isCol;
+    return true;
+}
+
+float Pass::resolveKern(Segment *seg, Slot *slotFix, GR_MAYBE_UNUSED Slot *start, int dir,
+    float &ymin, float &ymax, json *const dbgout) const
+{
+    Slot *nbor; // neighboring slot
+    float currSpace = 0.;
+    bool collides = false;
+    unsigned int space_count = 0;
+    Slot *base = slotFix;
+    while (base->attachedTo())
+        base = base->attachedTo();
+    SlotCollision *cFix = seg->collisionInfo(base);
+    const GlyphCache &gc = seg->getFace()->glyphs();
+    const Rect &bbb = seg->theGlyphBBoxTemporary(slotFix->gid());
+    const float by = slotFix->origin().y + cFix->shift().y;
+
+    if (base != slotFix)
+    {
+        cFix->setFlags(cFix->flags() | SlotCollision::COLL_KERN | SlotCollision::COLL_FIX);
+        return 0;
+    }
+    bool seenEnd = (cFix->flags() & SlotCollision::COLL_END) != 0;
+    bool isInit = false;
+    KernCollider coll(dbgout);
+
+    ymax = max(by + bbb.tr.y, ymax);
+    ymin = min(by + bbb.bl.y, ymin);
+    for (nbor = slotFix->next(); nbor; nbor = nbor->next())
+    {
+        if (nbor->isChildOf(base))
+            continue;
+        if (!gc.check(nbor->gid()))
+            return 0.;
+        const Rect &bb = seg->theGlyphBBoxTemporary(nbor->gid());
+        SlotCollision *cNbor = seg->collisionInfo(nbor);
+        if ((bb.bl.y == 0.f && bb.tr.y == 0.f) || (cNbor->flags() & SlotCollision::COLL_ISSPACE))
+        {
+            if (m_kernColls == InWord)
+                break;
+            // Add space for a space glyph.
+            currSpace += nbor->advance();
+            ++space_count;
+        }
+        else
+        {
+            space_count = 0;
+            if (nbor != slotFix && !cNbor->ignore())
+            {
+                seenEnd = true;
+                if (!isInit)
+                {
+                    if (!coll.initSlot(seg, slotFix, cFix->limit(), cFix->margin(),
+                                    cFix->shift(), cFix->offset(), dir, ymin, ymax, dbgout))
+                        return 0.;
+                    isInit = true;
+                }
+                collides |= coll.mergeSlot(seg, nbor, cNbor->shift(), currSpace, dir, dbgout);
+            }
+        }
+        if (cNbor->flags() & SlotCollision::COLL_END)
+        {
+            if (seenEnd && space_count < 2)
+                break;
+            else
+                seenEnd = true;
+        }
+    }
+    if (collides)
+    {
+        Position mv = coll.resolve(seg, slotFix, dir, dbgout);
+        coll.shift(mv, dir);
+        Position delta = slotFix->advancePos() + mv - cFix->shift();
+        slotFix->advance(delta);
+        cFix->setShift(mv);
+        return mv.x;
+    }
+    return 0.;
+}

+ 97 - 0
thirdparty/graphite/src/Position.cpp

@@ -0,0 +1,97 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/Position.h"
+#include <cmath>
+
+using namespace graphite2;
+
+bool Rect::hitTest(Rect &other)
+{
+    if (bl.x > other.tr.x) return false;
+    if (tr.x < other.bl.x) return false;
+    if (bl.y > other.tr.y) return false;
+    if (tr.y < other.bl.y) return false;
+    return true;
+}
+
+Position Rect::overlap(Position &offset, Rect &other, Position &othero)
+{
+    float ax = (bl.x + offset.x) - (other.tr.x + othero.x);
+    float ay = (bl.y + offset.y) - (other.tr.y + othero.y);
+    float bx = (other.bl.x + othero.x) - (tr.x + offset.x);
+    float by = (other.bl.y + othero.y) - (tr.y + offset.y);
+    return Position((ax > bx ? ax : bx), (ay > by ? ay : by));
+}
+
+float boundmin(float move, float lim1, float lim2, float &error)
+{
+    // error is always positive for easy comparison
+    if (move < lim1 && move < lim2)
+    { error = 0.; return move; }
+    else if (lim1 < lim2)
+    { error = std::fabs(move - lim1); return lim1; }
+    else
+    { error = std::fabs(move - lim2); return lim2; }
+}
+
+#if 0
+Position Rect::constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox)
+{
+    // a = max, i = min, s = sum, d = diff
+    float eax, eay, eix, eiy, eas, eis, ead, eid;
+    float beste = INF;
+    Position res;
+    // calculate the movements in each direction and the error (amount of remaining overlap)
+    // first param is movement, second and third are movement over the constraining box
+    float ax = boundmin(obox.tr.x + other.x - box.bl.x - offset.x + 1, tr.x - offset.x, INF, &eax);
+    float ay = boundmin(obox.tr.y + other.y - box.bl.y - offset.y + 1, tr.y - offset.y, INF, &eay);
+    float ix = boundmin(obox.bl.x + other.x - box.tr.x - offset.x + 1, bl.x - offset.x, INF, &eix);
+    float iy = boundmin(obox.bl.y + other.y - box.tr.y - offset.y + 1, bl.y - offset.y, INF, &eiy);
+    float as = boundmin(ISQRT2 * (osdbox.tr.x + other.x + other.y - sdbox.bl.x - offset.x - offset.y) + 1, tr.x - offset.x, tr.y - offset.y, &eas);
+    float is = boundmin(ISQRT2 * (osdbox.bl.x + other.x + other.y - sdbox.tr.x - offset.x - offset.y) + 1, bl.x - offset.x, bl.y - offset.y, &eis);
+    float ad = boundmin(ISQRT2 * (osdbox.tr.y + other.x - other.y - sdbox.bl.y - offset.x + offset.y) + 1, tr.y - offset.y, tr.x - offset.x, &ead);
+    float id = boundmin(ISQRT2 * (osdbox.bl.y + other.x - other.y - sdbox.tr.y - offset.x + offset.y) + 1, bl.y - offset.y, bl.x - offset.x, &eid);
+
+    if (eax < beste)
+    { res = Position(ax, 0); beste = eax; }
+    if (eay < beste)
+    { res = Position(0, ay); beste = eay; }
+    if (eix < beste)
+    { res = Position(ix, 0); beste = eix; }
+    if (eiy < beste)
+    { res = Position(0, iy); beste = eiy; }
+    if (SQRT2 * (eas) < beste)
+    { res = Position(as, ad); beste = SQRT2 * (eas); }
+    if (SQRT2 * (eis) < beste)
+    { res = Position(is, is); beste = SQRT2 * (eis); }
+    if (SQRT2 * (ead) < beste)
+    { res = Position(ad, ad); beste = SQRT2 * (ead); }
+    if (SQRT2 * (eid) < beste)
+    { res = Position(id, id); beste = SQRT2 * (eid); }
+    return res;
+}
+#endif

+ 423 - 0
thirdparty/graphite/src/Segment.cpp

@@ -0,0 +1,423 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/UtfCodec.h"
+#include <cstring>
+#include <cstdlib>
+
+#include "inc/bits.h"
+#include "inc/Segment.h"
+#include "graphite2/Font.h"
+#include "inc/CharInfo.h"
+#include "inc/debug.h"
+#include "inc/Slot.h"
+#include "inc/Main.h"
+#include "inc/CmapCache.h"
+#include "inc/Collider.h"
+#include "graphite2/Segment.h"
+
+
+using namespace graphite2;
+
+Segment::Segment(size_t numchars, const Face* face, uint32 script, int textDir)
+: m_freeSlots(NULL),
+  m_freeJustifies(NULL),
+  m_charinfo(new CharInfo[numchars]),
+  m_collisions(NULL),
+  m_face(face),
+  m_silf(face->chooseSilf(script)),
+  m_first(NULL),
+  m_last(NULL),
+  m_bufSize(numchars + 10),
+  m_numGlyphs(numchars),
+  m_numCharinfo(numchars),
+  m_defaultOriginal(0),
+  m_dir(textDir),
+  m_flags(((m_silf->flags() & 0x20) != 0) << 1),
+  m_passBits(m_silf->aPassBits() ? -1 : 0)
+{
+    freeSlot(newSlot());
+    m_bufSize = log_binary(numchars)+1;
+}
+
+Segment::~Segment()
+{
+    for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
+        free(*i);
+    for (AttributeRope::iterator i = m_userAttrs.begin(); i != m_userAttrs.end(); ++i)
+        free(*i);
+    for (JustifyRope::iterator i = m_justifies.begin(); i != m_justifies.end(); ++i)
+        free(*i);
+    delete[] m_charinfo;
+    free(m_collisions);
+}
+
+void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
+{
+    Slot *aSlot = newSlot();
+
+    if (!aSlot) return;
+    m_charinfo[id].init(cid);
+    m_charinfo[id].feats(iFeats);
+    m_charinfo[id].base(coffset);
+    const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
+    m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0);
+
+    aSlot->child(NULL);
+    aSlot->setGlyph(this, gid, theGlyph);
+    aSlot->originate(id);
+    aSlot->before(id);
+    aSlot->after(id);
+    if (m_last) m_last->next(aSlot);
+    aSlot->prev(m_last);
+    m_last = aSlot;
+    if (!m_first) m_first = aSlot;
+    if (theGlyph && m_silf->aPassBits())
+        m_passBits &= theGlyph->attrs()[m_silf->aPassBits()]
+                    | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
+}
+
+Slot *Segment::newSlot()
+{
+    if (!m_freeSlots)
+    {
+        // check that the segment doesn't grow indefinintely
+        if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR)
+            return NULL;
+        int numUser = m_silf->numUser();
+#if !defined GRAPHITE2_NTRACING
+        if (m_face->logger()) ++numUser;
+#endif
+        Slot *newSlots = grzeroalloc<Slot>(m_bufSize);
+        int16 *newAttrs = grzeroalloc<int16>(m_bufSize * numUser);
+        if (!newSlots || !newAttrs)
+        {
+            free(newSlots);
+            free(newAttrs);
+            return NULL;
+        }
+        for (size_t i = 0; i < m_bufSize; i++)
+        {
+            ::new (newSlots + i) Slot(newAttrs + i * numUser);
+            newSlots[i].next(newSlots + i + 1);
+        }
+        newSlots[m_bufSize - 1].next(NULL);
+        newSlots[0].next(NULL);
+        m_slots.push_back(newSlots);
+        m_userAttrs.push_back(newAttrs);
+        m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL;
+        return newSlots;
+    }
+    Slot *res = m_freeSlots;
+    m_freeSlots = m_freeSlots->next();
+    res->next(NULL);
+    return res;
+}
+
+void Segment::freeSlot(Slot *aSlot)
+{
+    if (aSlot == nullptr) return;
+    if (m_last == aSlot) m_last = aSlot->prev();
+    if (m_first == aSlot) m_first = aSlot->next();
+    if (aSlot->attachedTo())
+        aSlot->attachedTo()->removeChild(aSlot);
+    while (aSlot->firstChild())
+    {
+        if (aSlot->firstChild()->attachedTo() == aSlot)
+        {
+            aSlot->firstChild()->attachTo(nullptr);
+            aSlot->removeChild(aSlot->firstChild());
+        }
+        else
+            aSlot->firstChild(nullptr);
+    }
+    // reset the slot incase it is reused
+    ::new (aSlot) Slot(aSlot->userAttrs());
+    memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
+    // Update generation counter for debug
+#if !defined GRAPHITE2_NTRACING
+    if (m_face->logger())
+        ++aSlot->userAttrs()[m_silf->numUser()];
+#endif
+    // update next pointer
+    if (!m_freeSlots)
+        aSlot->next(nullptr);
+    else
+        aSlot->next(m_freeSlots);
+    m_freeSlots = aSlot;
+}
+
+SlotJustify *Segment::newJustify()
+{
+    if (!m_freeJustifies)
+    {
+        const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels());
+        byte *justs = grzeroalloc<byte>(justSize * m_bufSize);
+        if (!justs) return NULL;
+        for (ptrdiff_t i = m_bufSize - 2; i >= 0; --i)
+        {
+            SlotJustify *p = reinterpret_cast<SlotJustify *>(justs + justSize * i);
+            SlotJustify *next = reinterpret_cast<SlotJustify *>(justs + justSize * (i + 1));
+            p->next = next;
+        }
+        m_freeJustifies = (SlotJustify *)justs;
+        m_justifies.push_back(m_freeJustifies);
+    }
+    SlotJustify *res = m_freeJustifies;
+    m_freeJustifies = m_freeJustifies->next;
+    res->next = NULL;
+    return res;
+}
+
+void Segment::freeJustify(SlotJustify *aJustify)
+{
+    int numJust = m_silf->numJustLevels();
+    if (m_silf->numJustLevels() <= 0) numJust = 1;
+    aJustify->next = m_freeJustifies;
+    memset(aJustify->values, 0, numJust*SlotJustify::NUMJUSTPARAMS*sizeof(int16));
+    m_freeJustifies = aJustify;
+}
+
+// reverse the slots but keep diacritics in their same position after their bases
+void Segment::reverseSlots()
+{
+    m_dir = m_dir ^ 64;                 // invert the reverse flag
+    if (m_first == m_last) return;      // skip 0 or 1 glyph runs
+
+    Slot *t = 0;
+    Slot *curr = m_first;
+    Slot *tlast;
+    Slot *tfirst;
+    Slot *out = 0;
+
+    while (curr && getSlotBidiClass(curr) == 16)
+        curr = curr->next();
+    if (!curr) return;
+    tfirst = curr->prev();
+    tlast = curr;
+
+    while (curr)
+    {
+        if (getSlotBidiClass(curr) == 16)
+        {
+            Slot *d = curr->next();
+            while (d && getSlotBidiClass(d) == 16)
+                d = d->next();
+
+            d = d ? d->prev() : m_last;
+            Slot *p = out->next();    // one after the diacritics. out can't be null
+            if (p)
+                p->prev(d);
+            else
+                tlast = d;
+            t = d->next();
+            d->next(p);
+            curr->prev(out);
+            out->next(curr);
+        }
+        else    // will always fire first time round the loop
+        {
+            if (out)
+                out->prev(curr);
+            t = curr->next();
+            curr->next(out);
+            out = curr;
+        }
+        curr = t;
+    }
+    out->prev(tfirst);
+    if (tfirst)
+        tfirst->next(out);
+    else
+        m_first = out;
+    m_last = tlast;
+}
+
+void Segment::linkClusters(Slot *s, Slot * end)
+{
+    end = end->next();
+
+    for (; s != end && !s->isBase(); s = s->next());
+    Slot * ls = s;
+
+    if (m_dir & 1)
+    {
+        for (; s != end; s = s->next())
+        {
+            if (!s->isBase())   continue;
+
+            s->sibling(ls);
+            ls = s;
+        }
+    }
+    else
+    {
+        for (; s != end; s = s->next())
+        {
+            if (!s->isBase())   continue;
+
+            ls->sibling(s);
+            ls = s;
+        }
+    }
+}
+
+Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd, bool isRtl, bool isFinal)
+{
+    Position currpos(0., 0.);
+    float clusterMin = 0.;
+    Rect bbox;
+    bool reorder = (currdir() != isRtl);
+
+    if (reorder)
+    {
+        Slot *temp;
+        reverseSlots();
+        temp = iStart;
+        iStart = iEnd;
+        iEnd = temp;
+    }
+    if (!iStart)    iStart = m_first;
+    if (!iEnd)      iEnd   = m_last;
+
+    if (!iStart || !iEnd)   // only true for empty segments
+        return currpos;
+
+    if (isRtl)
+    {
+        for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
+        {
+            if (s->isBase())
+                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
+        }
+    }
+    else
+    {
+        for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
+        {
+            if (s->isBase())
+                currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x, isRtl, isFinal);
+        }
+    }
+    if (reorder)
+        reverseSlots();
+    return currpos;
+}
+
+
+void Segment::associateChars(int offset, size_t numChars)
+{
+    int i = 0, j = 0;
+    CharInfo *c, *cend;
+    for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c)
+    {
+        c->before(-1);
+        c->after(-1);
+    }
+    for (Slot * s = m_first; s; s->index(i++), s = s->next())
+    {
+        j = s->before();
+        if (j < 0)  continue;
+
+        for (const int after = s->after(); j <= after; ++j)
+        {
+            c = charinfo(j);
+            if (c->before() == -1 || i < c->before())   c->before(i);
+            if (c->after() < i)                         c->after(i);
+        }
+    }
+    for (Slot *s = m_first; s; s = s->next())
+    {
+        int a;
+        for (a = s->after() + 1; a < offset + int(numChars) && charinfo(a)->after() < 0; ++a)
+        { charinfo(a)->after(s->index()); }
+        --a;
+        s->after(a);
+
+        for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a)
+        { charinfo(a)->before(s->index()); }
+        ++a;
+        s->before(a);
+    }
+}
+
+
+template <typename utf_iter>
+inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars)
+{
+    const Cmap    & cmap = face.cmap();
+    int slotid = 0;
+
+    const typename utf_iter::codeunit_type * const base = c;
+    for (; n_chars; --n_chars, ++c, ++slotid)
+    {
+        const uint32 usv = *c;
+        uint16 gid = cmap[usv];
+        if (!gid)   gid = face.findPseudo(usv);
+        seg.appendSlot(slotid, usv, gid, fid, c - base);
+    }
+}
+
+
+bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
+{
+    assert(face);
+    assert(pFeats);
+    if (!m_charinfo) return false;
+
+    // utf iterator is self recovering so we don't care about the error state of the iterator.
+    switch (enc)
+    {
+    case gr_utf8:   process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
+    case gr_utf16:  process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
+    case gr_utf32:  process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
+    }
+    return true;
+}
+
+void Segment::doMirror(uint16 aMirror)
+{
+    Slot * s;
+    for (s = m_first; s; s = s->next())
+    {
+        unsigned short g = glyphAttr(s->gid(), aMirror);
+        if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1)))
+            s->setGlyph(this, g);
+    }
+}
+
+bool Segment::initCollisions()
+{
+    m_collisions = grzeroalloc<SlotCollision>(slotCount());
+    if (!m_collisions) return false;
+
+    for (Slot *p = m_first; p; p = p->next())
+        if (p->index() < slotCount())
+            ::new (collisionInfo(p)) SlotCollision(this, p);
+        else
+            return false;
+    return true;
+}

+ 439 - 0
thirdparty/graphite/src/Silf.cpp

@@ -0,0 +1,439 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cstdlib>
+#include "graphite2/Segment.h"
+#include "inc/debug.h"
+#include "inc/Endian.h"
+#include "inc/Silf.h"
+#include "inc/Segment.h"
+#include "inc/Rule.h"
+#include "inc/Error.h"
+
+
+using namespace graphite2;
+
+namespace { static const uint32 ERROROFFSET = 0xFFFFFFFF; }
+
+Silf::Silf() throw()
+: m_passes(0),
+  m_pseudos(0),
+  m_classOffsets(0),
+  m_classData(0),
+  m_justs(0),
+  m_numPasses(0),
+  m_numJusts(0),
+  m_sPass(0),
+  m_pPass(0),
+  m_jPass(0),
+  m_bPass(0),
+  m_flags(0),
+  m_dir(0),
+  m_aPseudo(0),
+  m_aBreak(0),
+  m_aUser(0),
+  m_aBidi(0),
+  m_aMirror(0),
+  m_aPassBits(0),
+  m_iMaxComp(0),
+  m_aCollision(0),
+  m_aLig(0),
+  m_numPseudo(0),
+  m_nClass(0),
+  m_nLinear(0),
+  m_gEndLine(0)
+{
+    memset(&m_silfinfo, 0, sizeof m_silfinfo);
+}
+
+Silf::~Silf() throw()
+{
+    releaseBuffers();
+}
+
+void Silf::releaseBuffers() throw()
+{
+    delete [] m_passes;
+    delete [] m_pseudos;
+    free(m_classOffsets);
+    free(m_classData);
+    free(m_justs);
+    m_passes= 0;
+    m_pseudos = 0;
+    m_classOffsets = 0;
+    m_classData = 0;
+    m_justs = 0;
+}
+
+
+bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32 version)
+{
+    const byte * p = silf_start,
+               * const silf_end = p + lSilf;
+    Error e;
+
+    if (e.test(version >= 0x00060000, E_BADSILFVERSION))
+    {
+        releaseBuffers(); return face.error(e);
+    }
+    if (version >= 0x00030000)
+    {
+        if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
+        be::skip<int32>(p);    // ruleVersion
+        be::skip<uint16>(p,2); // passOffset & pseudosOffset
+    }
+    else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
+    const uint16 maxGlyph = be::read<uint16>(p);
+    m_silfinfo.extra_ascent = be::read<uint16>(p);
+    m_silfinfo.extra_descent = be::read<uint16>(p);
+    m_numPasses = be::read<uint8>(p);
+    m_sPass     = be::read<uint8>(p);
+    m_pPass     = be::read<uint8>(p);
+    m_jPass     = be::read<uint8>(p);
+    m_bPass     = be::read<uint8>(p);
+    m_flags     = be::read<uint8>(p);
+    be::skip<uint8>(p,2); //  max{Pre,Post}Context.
+    m_aPseudo   = be::read<uint8>(p);
+    m_aBreak    = be::read<uint8>(p);
+    m_aBidi     = be::read<uint8>(p);
+    m_aMirror   = be::read<uint8>(p);
+    m_aPassBits = be::read<uint8>(p);
+
+    // Read Justification levels.
+    m_numJusts  = be::read<uint8>(p);
+    if (e.test(maxGlyph >= face.glyphs().numGlyphs(), E_BADMAXGLYPH)
+        || e.test(p + m_numJusts * 8 >= silf_end, E_BADNUMJUSTS))
+    {
+        releaseBuffers(); return face.error(e);
+    }
+
+    if (m_numJusts)
+    {
+        m_justs = gralloc<Justinfo>(m_numJusts);
+        if (e.test(!m_justs, E_OUTOFMEM)) return face.error(e);
+
+        for (uint8 i = 0; i < m_numJusts; i++)
+        {
+            ::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
+            be::skip<byte>(p,8);
+        }
+    }
+
+    if (e.test(p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); }
+    m_aLig       = be::read<uint16>(p);
+    m_aUser      = be::read<uint8>(p);
+    m_iMaxComp   = be::read<uint8>(p);
+    m_dir        = be::read<uint8>(p) - 1;
+    m_aCollision = be::read<uint8>(p);
+    be::skip<byte>(p,3);
+    be::skip<uint16>(p, be::read<uint8>(p));    // don't need critical features yet
+    be::skip<byte>(p);                          // reserved
+    if (e.test(p >= silf_end, E_BADCRITFEATURES))   { releaseBuffers(); return face.error(e); }
+    be::skip<uint32>(p, be::read<uint8>(p));    // don't use scriptTag array.
+    if (e.test(p + sizeof(uint16) + sizeof(uint32) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); }
+    m_gEndLine  = be::read<uint16>(p);          // lbGID
+    const byte * o_passes = p;
+    uint32 passes_start = be::read<uint32>(p);
+
+    const size_t num_attrs = face.glyphs().numAttrs();
+    if (e.test(m_aPseudo   >= num_attrs, E_BADAPSEUDO)
+        || e.test(m_aBreak >= num_attrs, E_BADABREAK)
+        || e.test(m_aBidi  >= num_attrs, E_BADABIDI)
+        || e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
+        || e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION)
+        || e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= lSilf, E_BADPASSESSTART)
+        || e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS)
+        || e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
+        || e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS)
+        || e.test(m_aLig > 127, E_BADALIG))
+    {
+        releaseBuffers();
+        return face.error(e);
+    }
+    be::skip<uint32>(p, m_numPasses);
+    if (e.test(unsigned(p - silf_start) + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
+    m_numPseudo = be::read<uint16>(p);
+    be::skip<uint16>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
+    m_pseudos = new Pseudo[m_numPseudo];
+    if (e.test(unsigned(p - silf_start) + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO)
+        || e.test(!m_pseudos, E_OUTOFMEM))
+    {
+        releaseBuffers(); return face.error(e);
+    }
+    for (int i = 0; i < m_numPseudo; i++)
+    {
+        m_pseudos[i].uid = be::read<uint32>(p);
+        m_pseudos[i].gid = be::read<uint16>(p);
+    }
+
+    const size_t clen = readClassMap(p, passes_start + silf_start - p, version, e);
+    m_passes = new Pass[m_numPasses];
+    if (e || e.test(clen > unsigned(passes_start + silf_start - p), E_BADPASSESSTART)
+          || e.test(!m_passes, E_OUTOFMEM))
+    { releaseBuffers(); return face.error(e); }
+
+    for (size_t i = 0; i < m_numPasses; ++i)
+    {
+        uint32 pass_start = be::read<uint32>(o_passes);
+        uint32 pass_end = be::peek<uint32>(o_passes);
+        face.error_context((face.error_context() & 0xFF00) + EC_ASILF + unsigned(i << 16));
+        if (e.test(pass_start > pass_end, E_BADPASSSTART)
+                || e.test(pass_start < passes_start, E_BADPASSSTART)
+                || e.test(pass_end > lSilf, E_BADPASSEND)) {
+            releaseBuffers(); return face.error(e);
+        }
+
+        enum passtype pt = PASS_TYPE_UNKNOWN;
+        if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION;
+        else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING;
+        else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE;
+        else pt = PASS_TYPE_LINEBREAK;
+
+        m_passes[i].init(this);
+        if (!m_passes[i].readPass(silf_start + pass_start, pass_end - pass_start, pass_start, face, pt,
+            version, e))
+        {
+            releaseBuffers();
+            return false;
+        }
+    }
+
+    // fill in gr_faceinfo
+    m_silfinfo.upem = face.glyphs().unitsPerEm();
+    m_silfinfo.has_bidi_pass = (m_bPass != 0xFF);
+    m_silfinfo.justifies = (m_numJusts != 0) || (m_jPass < m_pPass);
+    m_silfinfo.line_ends = (m_flags & 1);
+    m_silfinfo.space_contextuals = gr_faceinfo::gr_space_contextuals((m_flags >> 2) & 0x7);
+    return true;
+}
+
+template<typename T> inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len, Error &e)
+{
+    const T cls_off = 2*sizeof(uint16) + sizeof(T)*(m_nClass+1);
+    const uint32 max_off = (be::peek<T>(p + sizeof(T)*m_nClass) - cls_off)/sizeof(uint16);
+    // Check that the last+1 offset is less than or equal to the class map length.
+    if (e.test(be::peek<T>(p) != cls_off, E_MISALIGNEDCLASSES)
+            || e.test(max_off > (data_len - cls_off)/sizeof(uint16), E_HIGHCLASSOFFSET))
+        return ERROROFFSET;
+
+    // Read in all the offsets.
+    m_classOffsets = gralloc<uint32>(m_nClass+1);
+    if (e.test(!m_classOffsets, E_OUTOFMEM)) return ERROROFFSET;
+    for (uint32 * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o)
+    {
+        *o = (be::read<T>(p) - cls_off)/sizeof(uint16);
+        if (e.test(*o > max_off, E_HIGHCLASSOFFSET))
+            return ERROROFFSET;
+    }
+    return max_off;
+}
+
+size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error &e)
+{
+    if (e.test(data_len < sizeof(uint16)*2, E_BADCLASSSIZE)) return ERROROFFSET;
+
+    m_nClass  = be::read<uint16>(p);
+    m_nLinear = be::read<uint16>(p);
+
+    // Check that numLinear < numClass,
+    // that there is at least enough data for numClasses offsets.
+    if (e.test(m_nLinear > m_nClass, E_TOOMANYLINEAR)
+     || e.test((m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16)) > (data_len - 4), E_CLASSESTOOBIG))
+        return ERROROFFSET;
+
+    uint32 max_off;
+    if (version >= 0x00040000)
+        max_off = readClassOffsets<uint32>(p, data_len, e);
+    else
+        max_off = readClassOffsets<uint16>(p, data_len, e);
+
+    if (max_off == ERROROFFSET) return ERROROFFSET;
+
+    if (e.test((int)max_off < m_nLinear + (m_nClass - m_nLinear) * 6, E_CLASSESTOOBIG))
+        return ERROROFFSET;
+
+    // Check the linear offsets are sane, these must be monotonically increasing.
+    assert(m_nClass >= m_nLinear);
+    for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
+        if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
+            return ERROROFFSET;
+
+    // Fortunately the class data is all uint16s so we can decode these now
+    m_classData = gralloc<uint16>(max_off);
+    if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET;
+    for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
+        *d = be::read<uint16>(p);
+
+    // Check the lookup class invariants for each non-linear class
+    for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
+    {
+        const uint16 * lookup = m_classData + *o;
+        if (e.test(*o + 4 > max_off, E_HIGHCLASSOFFSET)                        // LookupClass doesn't stretch over max_off
+         || e.test(lookup[0] == 0                                                   // A LookupClass with no looks is a suspicious thing ...
+                    || lookup[0] * 2 + *o + 4 > max_off                             // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
+                    || lookup[3] + lookup[1] != lookup[0], E_BADCLASSLOOKUPINFO)    // rangeShift:   numIDs  - searchRange
+         || e.test(((o[1] - *o) & 1) != 0, ERROROFFSET))                         // glyphs are in pairs so difference must be even.
+            return ERROROFFSET;
+    }
+
+    return max_off;
+}
+
+uint16 Silf::findPseudo(uint32 uid) const
+{
+    for (int i = 0; i < m_numPseudo; i++)
+        if (m_pseudos[i].uid == uid) return m_pseudos[i].gid;
+    return 0;
+}
+
+uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const
+{
+    if (cid > m_nClass) return -1;
+
+    const uint16 * cls = m_classData + m_classOffsets[cid];
+    if (cid < m_nLinear)        // output class being used for input, shouldn't happen
+    {
+        for (unsigned int i = 0, n = m_classOffsets[cid + 1] - m_classOffsets[cid]; i < n; ++i, ++cls)
+            if (*cls == gid) return i;
+        return -1;
+    }
+    else
+    {
+        const uint16 *  min = cls + 4,      // lookups array
+                     *  max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16 pairs long
+        do
+        {
+            const uint16 * p = min + (-2 & ((max-min)/2));
+            if  (p[0] > gid)    max = p;
+            else                min = p;
+        }
+        while (max - min > 2);
+        return min[0] == gid ? min[1] : -1;
+    }
+}
+
+uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const
+{
+    if (cid > m_nClass) return 0;
+
+    uint32 loc = m_classOffsets[cid];
+    if (cid < m_nLinear)
+    {
+        if (index < m_classOffsets[cid + 1] - loc)
+            return m_classData[index + loc];
+    }
+    else        // input class being used for output. Shouldn't happen
+    {
+        for (unsigned int i = loc + 4; i < m_classOffsets[cid + 1]; i += 2)
+            if (m_classData[i + 1] == index) return m_classData[i];
+    }
+    return 0;
+}
+
+
+bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
+{
+    assert(seg != 0);
+    size_t             maxSize = seg->slotCount() * MAX_SEG_GROWTH_FACTOR;
+    SlotMap            map(*seg, m_dir, maxSize);
+    FiniteStateMachine fsm(map, seg->getFace()->logger());
+    vm::Machine        m(map);
+    uint8              lbidi = m_bPass;
+#if !defined GRAPHITE2_NTRACING
+    json * const dbgout = seg->getFace()->logger();
+#endif
+
+    if (lastPass == 0)
+    {
+        if (firstPass == lastPass && lbidi == 0xFF)
+            return true;
+        lastPass = m_numPasses;
+    }
+    if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi)))
+        lastPass++;
+    else
+        lbidi = 0xFF;
+
+    for (size_t i = firstPass; i < lastPass; ++i)
+    {
+        // bidi and mirroring
+        if (i == lbidi)
+        {
+#if !defined GRAPHITE2_NTRACING
+            if (dbgout)
+            {
+                *dbgout << json::item << json::object
+//							<< "pindex" << i   // for debugging
+                            << "id"     << -1
+                            << "slotsdir" << (seg->currdir() ? "rtl" : "ltr")
+                            << "passdir" << (m_dir & 1 ? "rtl" : "ltr")
+                            << "slots"  << json::array;
+                seg->positionSlots(0, 0, 0, seg->currdir());
+                for(Slot * s = seg->first(); s; s = s->next())
+                    *dbgout     << dslot(seg, s);
+                *dbgout         << json::close
+                            << "rules"  << json::array << json::close
+                            << json::close;
+            }
+#endif
+            if (seg->currdir() != (m_dir & 1))
+                seg->reverseSlots();
+            if (m_aMirror && (seg->dir() & 3) == 3)
+                seg->doMirror(m_aMirror);
+        --i;
+        lbidi = lastPass;
+        --lastPass;
+        continue;
+        }
+
+#if !defined GRAPHITE2_NTRACING
+        if (dbgout)
+        {
+            *dbgout << json::item << json::object
+//						<< "pindex" << i   // for debugging
+                        << "id"     << i+1
+                        << "slotsdir" << (seg->currdir() ? "rtl" : "ltr")
+                        << "passdir" << ((m_dir & 1) ^ m_passes[i].reverseDir() ? "rtl" : "ltr")
+                        << "slots"  << json::array;
+            seg->positionSlots(0, 0, 0, seg->currdir());
+            for(Slot * s = seg->first(); s; s = s->next())
+                *dbgout     << dslot(seg, s);
+            *dbgout         << json::close;
+        }
+#endif
+
+        // test whether to reorder, prepare for positioning
+        bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir()));
+        if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops())
+                && !m_passes[i].runGraphite(m, fsm, reverse))
+            return false;
+        // only subsitution passes can change segment length, cached subsegments are short for their text
+        if (m.status() != vm::Machine::finished
+            || (seg->slotCount() && seg->slotCount() > maxSize))
+            return false;
+    }
+    return true;
+}

+ 529 - 0
thirdparty/graphite/src/Slot.cpp

@@ -0,0 +1,529 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/Segment.h"
+#include "inc/Slot.h"
+#include "inc/Silf.h"
+#include "inc/CharInfo.h"
+#include "inc/Rule.h"
+#include "inc/Collider.h"
+
+
+using namespace graphite2;
+
+Slot::Slot(int16 *user_attrs) :
+    m_next(NULL), m_prev(NULL),
+    m_glyphid(0), m_realglyphid(0), m_original(0), m_before(0), m_after(0),
+    m_index(0), m_parent(NULL), m_child(NULL), m_sibling(NULL),
+    m_position(0, 0), m_shift(0, 0), m_advance(0, 0),
+    m_attach(0, 0), m_with(0, 0), m_just(0.),
+    m_flags(0), m_attLevel(0), m_bidiCls(-1), m_bidiLevel(0),
+    m_userAttr(user_attrs), m_justs(NULL)
+{
+}
+
+// take care, this does not copy any of the GrSlot pointer fields
+void Slot::set(const Slot & orig, int charOffset, size_t sizeAttr, size_t justLevels, size_t numChars)
+{
+    // leave m_next and m_prev unchanged
+    m_glyphid = orig.m_glyphid;
+    m_realglyphid = orig.m_realglyphid;
+    m_original = orig.m_original + charOffset;
+    if (charOffset + int(orig.m_before) < 0)
+        m_before = 0;
+    else
+        m_before = orig.m_before + charOffset;
+    if (charOffset <= 0 && orig.m_after + charOffset >= numChars)
+        m_after = int(numChars) - 1;
+    else
+        m_after = orig.m_after + charOffset;
+    m_parent = NULL;
+    m_child = NULL;
+    m_sibling = NULL;
+    m_position = orig.m_position;
+    m_shift = orig.m_shift;
+    m_advance = orig.m_advance;
+    m_attach = orig.m_attach;
+    m_with = orig.m_with;
+    m_flags = orig.m_flags;
+    m_attLevel = orig.m_attLevel;
+    m_bidiCls = orig.m_bidiCls;
+    m_bidiLevel = orig.m_bidiLevel;
+    if (m_userAttr && orig.m_userAttr)
+        memcpy(m_userAttr, orig.m_userAttr, sizeAttr * sizeof(*m_userAttr));
+    if (m_justs && orig.m_justs)
+        memcpy(m_justs, orig.m_justs, SlotJustify::size_of(justLevels));
+}
+
+void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
+{
+    m_before += numCharInfo;
+    m_after += numCharInfo;
+    m_position = m_position + relpos;
+}
+
+Position Slot::finalise(const Segment *seg, const Font *font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth)
+{
+    SlotCollision *coll = NULL;
+    if (depth > 100 || (attrLevel && m_attLevel > attrLevel)) return Position(0, 0);
+    float scale = font ? font->scale() : 1.0f;
+    Position shift(m_shift.x * (rtl * -2 + 1) + m_just, m_shift.y);
+    float tAdvance = m_advance.x + m_just;
+    if (isFinal && (coll = seg->collisionInfo(this)))
+    {
+        const Position &collshift = coll->offset();
+        if (!(coll->flags() & SlotCollision::COLL_KERN) || rtl)
+            shift = shift + collshift;
+    }
+    const GlyphFace * glyphFace = seg->getFace()->glyphs().glyphSafe(glyph());
+    if (font)
+    {
+        scale = font->scale();
+        shift *= scale;
+        if (font->isHinted() && glyphFace)
+            tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(glyph());
+        else
+            tAdvance *= scale;
+    }
+    Position res;
+
+    m_position = base + shift;
+    if (!m_parent)
+    {
+        res = base + Position(tAdvance, m_advance.y * scale);
+        clusterMin = m_position.x;
+    }
+    else
+    {
+        float tAdv;
+        m_position += (m_attach - m_with) * scale;
+        tAdv = m_advance.x >= 0.5f ? m_position.x + tAdvance - shift.x : 0.f;
+        res = Position(tAdv, 0);
+        if ((m_advance.x >= 0.5f || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x;
+    }
+
+    if (glyphFace)
+    {
+        Rect ourBbox = glyphFace->theBBox() * scale + m_position;
+        bbox = bbox.widen(ourBbox);
+    }
+
+    if (m_child && m_child != this && m_child->attachedTo() == this)
+    {
+        Position tRes = m_child->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
+        if ((!m_parent || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
+    }
+
+    if (m_parent && m_sibling && m_sibling != this && m_sibling->attachedTo() == m_parent)
+    {
+        Position tRes = m_sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
+        if (tRes.x > res.x) res = tRes;
+    }
+
+    if (!m_parent && clusterMin < base.x)
+    {
+        Position adj = Position(m_position.x - clusterMin, 0.);
+        res += adj;
+        m_position += adj;
+        if (m_child) m_child->floodShift(adj);
+    }
+    return res;
+}
+
+int32 Slot::clusterMetric(const Segment *seg, uint8 metric, uint8 attrLevel, bool rtl)
+{
+    Position base;
+    if (glyph() >= seg->getFace()->glyphs().numGlyphs())
+        return 0;
+    Rect bbox = seg->theGlyphBBoxTemporary(glyph());
+    float clusterMin = 0.;
+    Position res = finalise(seg, NULL, base, bbox, attrLevel, clusterMin, rtl, false);
+
+    switch (metrics(metric))
+    {
+    case kgmetLsb :
+        return int32(bbox.bl.x);
+    case kgmetRsb :
+        return int32(res.x - bbox.tr.x);
+    case kgmetBbTop :
+        return int32(bbox.tr.y);
+    case kgmetBbBottom :
+        return int32(bbox.bl.y);
+    case kgmetBbLeft :
+        return int32(bbox.bl.x);
+    case kgmetBbRight :
+        return int32(bbox.tr.x);
+    case kgmetBbWidth :
+        return int32(bbox.tr.x - bbox.bl.x);
+    case kgmetBbHeight :
+        return int32(bbox.tr.y - bbox.bl.y);
+    case kgmetAdvWidth :
+        return int32(res.x);
+    case kgmetAdvHeight :
+        return int32(res.y);
+    default :
+        return 0;
+    }
+}
+
+#define SLOTGETCOLATTR(x) { SlotCollision *c = seg->collisionInfo(this); return c ? int(c-> x) : 0; }
+
+int Slot::getAttr(const Segment *seg, attrCode ind, uint8 subindex) const
+{
+    if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
+    {
+        int indx = ind - gr_slatJStretch;
+        return getJustify(seg, indx / 5, indx % 5);
+    }
+
+    switch (ind)
+    {
+    case gr_slatAdvX :      return int(m_advance.x);
+    case gr_slatAdvY :      return int(m_advance.y);
+    case gr_slatAttTo :     return m_parent ? 1 : 0;
+    case gr_slatAttX :      return int(m_attach.x);
+    case gr_slatAttY :      return int(m_attach.y);
+    case gr_slatAttXOff :
+    case gr_slatAttYOff :   return 0;
+    case gr_slatAttWithX :  return int(m_with.x);
+    case gr_slatAttWithY :  return int(m_with.y);
+    case gr_slatAttWithXOff:
+    case gr_slatAttWithYOff:return 0;
+    case gr_slatAttLevel :  return m_attLevel;
+    case gr_slatBreak :     return seg->charinfo(m_original)->breakWeight();
+    case gr_slatCompRef :   return 0;
+    case gr_slatDir :       return seg->dir() & 1;
+    case gr_slatInsert :    return isInsertBefore();
+    case gr_slatPosX :      return int(m_position.x); // but need to calculate it
+    case gr_slatPosY :      return int(m_position.y);
+    case gr_slatShiftX :    return int(m_shift.x);
+    case gr_slatShiftY :    return int(m_shift.y);
+    case gr_slatMeasureSol: return -1; // err what's this?
+    case gr_slatMeasureEol: return -1;
+    case gr_slatJWidth:     return int(m_just);
+    case gr_slatUserDefnV1: subindex = 0; GR_FALLTHROUGH;
+      // no break
+    case gr_slatUserDefn :  return subindex < seg->numAttrs() ?  m_userAttr[subindex] : 0;
+    case gr_slatSegSplit :  return seg->charinfo(m_original)->flags() & 3;
+    case gr_slatBidiLevel:  return m_bidiLevel;
+    case gr_slatColFlags :		{ SlotCollision *c = seg->collisionInfo(this); return c ? c->flags() : 0; }
+    case gr_slatColLimitblx:SLOTGETCOLATTR(limit().bl.x)
+    case gr_slatColLimitbly:SLOTGETCOLATTR(limit().bl.y)
+    case gr_slatColLimittrx:SLOTGETCOLATTR(limit().tr.x)
+    case gr_slatColLimittry:SLOTGETCOLATTR(limit().tr.y)
+    case gr_slatColShiftx :	SLOTGETCOLATTR(offset().x)
+    case gr_slatColShifty :	SLOTGETCOLATTR(offset().y)
+    case gr_slatColMargin :	SLOTGETCOLATTR(margin())
+    case gr_slatColMarginWt:SLOTGETCOLATTR(marginWt())
+    case gr_slatColExclGlyph:SLOTGETCOLATTR(exclGlyph())
+    case gr_slatColExclOffx:SLOTGETCOLATTR(exclOffset().x)
+    case gr_slatColExclOffy:SLOTGETCOLATTR(exclOffset().y)
+    case gr_slatSeqClass :	SLOTGETCOLATTR(seqClass())
+    case gr_slatSeqProxClass:SLOTGETCOLATTR(seqProxClass())
+    case gr_slatSeqOrder :	SLOTGETCOLATTR(seqOrder())
+    case gr_slatSeqAboveXoff:SLOTGETCOLATTR(seqAboveXoff())
+    case gr_slatSeqAboveWt: SLOTGETCOLATTR(seqAboveWt())
+    case gr_slatSeqBelowXlim:SLOTGETCOLATTR(seqBelowXlim())
+    case gr_slatSeqBelowWt:	SLOTGETCOLATTR(seqBelowWt())
+    case gr_slatSeqValignHt:SLOTGETCOLATTR(seqValignHt())
+    case gr_slatSeqValignWt:SLOTGETCOLATTR(seqValignWt())
+    default : return 0;
+    }
+}
+
+#define SLOTCOLSETATTR(x) { \
+        SlotCollision *c = seg->collisionInfo(this); \
+        if (c) { c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
+        break; }
+#define SLOTCOLSETCOMPLEXATTR(t, y, x) { \
+        SlotCollision *c = seg->collisionInfo(this); \
+        if (c) { \
+        const t &s = c-> y; \
+        c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
+        break; }
+
+void Slot::setAttr(Segment *seg, attrCode ind, uint8 subindex, int16 value, const SlotMap & map)
+{
+    if (ind == gr_slatUserDefnV1)
+    {
+        ind = gr_slatUserDefn;
+        subindex = 0;
+        if (seg->numAttrs() == 0)
+            return;
+    }
+    else if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
+    {
+        int indx = ind - gr_slatJStretch;
+        return setJustify(seg, indx / 5, indx % 5, value);
+    }
+
+    switch (ind)
+    {
+    case gr_slatAdvX :  m_advance.x = value; break;
+    case gr_slatAdvY :  m_advance.y = value; break;
+    case gr_slatAttTo :
+    {
+        const uint16 idx = uint16(value);
+        if (idx < map.size() && map[idx])
+        {
+            Slot *other = map[idx];
+            if (other == this || other == m_parent || other->isCopied()) break;
+            if (m_parent) { m_parent->removeChild(this); attachTo(NULL); }
+            Slot *pOther = other;
+            int count = 0;
+            bool foundOther = false;
+            while (pOther)
+            {
+                ++count;
+                if (pOther == this) foundOther = true;
+                pOther = pOther->attachedTo();
+            }
+            for (pOther = m_child; pOther; pOther = pOther->m_child)
+                ++count;
+            for (pOther = m_sibling; pOther; pOther = pOther->m_sibling)
+                ++count;
+            if (count < 100 && !foundOther && other->child(this))
+            {
+                attachTo(other);
+                if ((map.dir() != 0) ^ (idx > subindex))
+                    m_with = Position(advance(), 0);
+                else        // normal match to previous root
+                    m_attach = Position(other->advance(), 0);
+            }
+        }
+        break;
+    }
+    case gr_slatAttX :          m_attach.x = value; break;
+    case gr_slatAttY :          m_attach.y = value; break;
+    case gr_slatAttXOff :
+    case gr_slatAttYOff :       break;
+    case gr_slatAttWithX :      m_with.x = value; break;
+    case gr_slatAttWithY :      m_with.y = value; break;
+    case gr_slatAttWithXOff :
+    case gr_slatAttWithYOff :   break;
+    case gr_slatAttLevel :
+        m_attLevel = byte(value);
+        break;
+    case gr_slatBreak :
+        seg->charinfo(m_original)->breakWeight(value);
+        break;
+    case gr_slatCompRef :   break;      // not sure what to do here
+    case gr_slatDir : break;
+    case gr_slatInsert :
+        markInsertBefore(value? true : false);
+        break;
+    case gr_slatPosX :      break; // can't set these here
+    case gr_slatPosY :      break;
+    case gr_slatShiftX :    m_shift.x = value; break;
+    case gr_slatShiftY :    m_shift.y = value; break;
+    case gr_slatMeasureSol :    break;
+    case gr_slatMeasureEol :    break;
+    case gr_slatJWidth :    just(value); break;
+    case gr_slatSegSplit :  seg->charinfo(m_original)->addflags(value & 3); break;
+    case gr_slatUserDefn :  m_userAttr[subindex] = value; break;
+    case gr_slatColFlags :  {
+        SlotCollision *c = seg->collisionInfo(this);
+        if (c)
+            c->setFlags(value);
+        break; }
+    case gr_slatColLimitblx :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(value, s.bl.y), s.tr)))
+    case gr_slatColLimitbly :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(s.bl.x, value), s.tr)))
+    case gr_slatColLimittrx :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(value, s.tr.y))))
+    case gr_slatColLimittry :	SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(s.tr.x, value))))
+    case gr_slatColMargin :		SLOTCOLSETATTR(setMargin(value))
+    case gr_slatColMarginWt :	SLOTCOLSETATTR(setMarginWt(value))
+    case gr_slatColExclGlyph :	SLOTCOLSETATTR(setExclGlyph(value))
+    case gr_slatColExclOffx :	SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(value, s.y)))
+    case gr_slatColExclOffy :	SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(s.x, value)))
+    case gr_slatSeqClass :		SLOTCOLSETATTR(setSeqClass(value))
+	case gr_slatSeqProxClass :	SLOTCOLSETATTR(setSeqProxClass(value))
+    case gr_slatSeqOrder :		SLOTCOLSETATTR(setSeqOrder(value))
+    case gr_slatSeqAboveXoff :	SLOTCOLSETATTR(setSeqAboveXoff(value))
+    case gr_slatSeqAboveWt :	SLOTCOLSETATTR(setSeqAboveWt(value))
+    case gr_slatSeqBelowXlim :	SLOTCOLSETATTR(setSeqBelowXlim(value))
+    case gr_slatSeqBelowWt :	SLOTCOLSETATTR(setSeqBelowWt(value))
+    case gr_slatSeqValignHt :	SLOTCOLSETATTR(setSeqValignHt(value))
+    case gr_slatSeqValignWt :	SLOTCOLSETATTR(setSeqValignWt(value))
+    default :
+        break;
+    }
+}
+
+int Slot::getJustify(const Segment *seg, uint8 level, uint8 subindex) const
+{
+    if (level && level >= seg->silf()->numJustLevels()) return 0;
+
+    if (m_justs)
+        return m_justs->values[level * SlotJustify::NUMJUSTPARAMS + subindex];
+
+    if (level >= seg->silf()->numJustLevels()) return 0;
+    Justinfo *jAttrs = seg->silf()->justAttrs() + level;
+
+    switch (subindex) {
+        case 0 : return seg->glyphAttr(gid(), jAttrs->attrStretch());
+        case 1 : return seg->glyphAttr(gid(), jAttrs->attrShrink());
+        case 2 : return seg->glyphAttr(gid(), jAttrs->attrStep());
+        case 3 : return seg->glyphAttr(gid(), jAttrs->attrWeight());
+        case 4 : return 0;      // not been set yet, so clearly 0
+        default: return 0;
+    }
+}
+
+void Slot::setJustify(Segment *seg, uint8 level, uint8 subindex, int16 value)
+{
+    if (level && level >= seg->silf()->numJustLevels()) return;
+    if (!m_justs)
+    {
+        SlotJustify *j = seg->newJustify();
+        if (!j) return;
+        j->LoadSlot(this, seg);
+        m_justs = j;
+    }
+    m_justs->values[level * SlotJustify::NUMJUSTPARAMS + subindex] = value;
+}
+
+bool Slot::child(Slot *ap)
+{
+    if (this == ap) return false;
+    else if (ap == m_child) return true;
+    else if (!m_child)
+        m_child = ap;
+    else
+        return m_child->sibling(ap);
+    return true;
+}
+
+bool Slot::sibling(Slot *ap)
+{
+    if (this == ap) return false;
+    else if (ap == m_sibling) return true;
+    else if (!m_sibling || !ap)
+        m_sibling = ap;
+    else
+        return m_sibling->sibling(ap);
+    return true;
+}
+
+bool Slot::removeChild(Slot *ap)
+{
+    if (this == ap || !m_child || !ap) return false;
+    else if (ap == m_child)
+    {
+        Slot *nSibling = m_child->nextSibling();
+        m_child->nextSibling(NULL);
+        m_child = nSibling;
+        return true;
+    }
+    for (Slot *p = m_child; p; p = p->m_sibling)
+    {
+        if (p->m_sibling && p->m_sibling == ap)
+        {
+            p->m_sibling = p->m_sibling->m_sibling;
+            ap->nextSibling(NULL);
+            return true;
+        }
+    }
+    return false;
+}
+
+void Slot::setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph)
+{
+    m_glyphid = glyphid;
+    m_bidiCls = -1;
+    if (!theGlyph)
+    {
+        theGlyph = seg->getFace()->glyphs().glyphSafe(glyphid);
+        if (!theGlyph)
+        {
+            m_realglyphid = 0;
+            m_advance = Position(0.,0.);
+            return;
+        }
+    }
+    m_realglyphid = theGlyph->attrs()[seg->silf()->aPseudo()];
+    if (m_realglyphid > seg->getFace()->glyphs().numGlyphs())
+        m_realglyphid = 0;
+    const GlyphFace *aGlyph = theGlyph;
+    if (m_realglyphid)
+    {
+        aGlyph = seg->getFace()->glyphs().glyphSafe(m_realglyphid);
+        if (!aGlyph) aGlyph = theGlyph;
+    }
+    m_advance = Position(aGlyph->theAdvance().x, 0.);
+    if (seg->silf()->aPassBits())
+    {
+        seg->mergePassBits(uint8(theGlyph->attrs()[seg->silf()->aPassBits()]));
+        if (seg->silf()->numPasses() > 16)
+            seg->mergePassBits(theGlyph->attrs()[seg->silf()->aPassBits()+1] << 16);
+    }
+}
+
+void Slot::floodShift(Position adj, int depth)
+{
+    if (depth > 100)
+        return;
+    m_position += adj;
+    if (m_child) m_child->floodShift(adj, depth + 1);
+    if (m_sibling) m_sibling->floodShift(adj, depth + 1);
+}
+
+void SlotJustify::LoadSlot(const Slot *s, const Segment *seg)
+{
+    for (int i = seg->silf()->numJustLevels() - 1; i >= 0; --i)
+    {
+        Justinfo *justs = seg->silf()->justAttrs() + i;
+        int16 *v = values + i * NUMJUSTPARAMS;
+        v[0] = seg->glyphAttr(s->gid(), justs->attrStretch());
+        v[1] = seg->glyphAttr(s->gid(), justs->attrShrink());
+        v[2] = seg->glyphAttr(s->gid(), justs->attrStep());
+        v[3] = seg->glyphAttr(s->gid(), justs->attrWeight());
+    }
+}
+
+Slot * Slot::nextInCluster(const Slot *s) const
+{
+    Slot *base;
+    if (s->firstChild())
+        return s->firstChild();
+    else if (s->nextSibling())
+        return s->nextSibling();
+    while ((base = s->attachedTo()))
+    {
+        // if (base->firstChild() == s && base->nextSibling())
+        if (base->nextSibling())
+            return base->nextSibling();
+        s = base;
+    }
+    return NULL;
+}
+
+bool Slot::isChildOf(const Slot *base) const
+{
+    for (Slot *p = m_parent; p; p = p->m_parent)
+        if (p == base)
+            return true;
+    return false;
+}

+ 62 - 0
thirdparty/graphite/src/Sparse.cpp

@@ -0,0 +1,62 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cassert>
+#include "inc/Sparse.h"
+#include "inc/bits.h"
+
+using namespace graphite2;
+
+const sparse::chunk sparse::empty_chunk = {0,0};
+
+sparse::~sparse() throw()
+{
+    if (m_array.map == &empty_chunk) return;
+    free(m_array.values);
+}
+
+
+sparse::mapped_type sparse::operator [] (const key_type k) const throw()
+{
+    mapped_type         g = key_type(k/SIZEOF_CHUNK - m_nchunks) >> (sizeof k*8 - 1);
+    const chunk &       c = m_array.map[g*k/SIZEOF_CHUNK];
+    const mask_t        m = c.mask >> (SIZEOF_CHUNK - 1 - (k%SIZEOF_CHUNK));
+    g *= m & 1;
+
+    return g*m_array.values[g*(c.offset + bit_set_count(m >> 1))];
+}
+
+
+size_t sparse::capacity() const throw()
+{
+    size_t n = m_nchunks,
+           s = 0;
+
+    for (const chunk *ci=m_array.map; n; --n, ++ci)
+        s += bit_set_count(ci->mask);
+
+    return s;
+}

+ 2053 - 0
thirdparty/graphite/src/TtfUtil.cpp

@@ -0,0 +1,2053 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+
+File: TtfUtil.cpp
+Responsibility: Alan Ward
+Last reviewed: Not yet.
+
+Description
+    Implements the methods for TtfUtil class. This file should remain portable to any C++
+    environment by only using standard C++ and the TTF structurs defined in Tt.h.
+-------------------------------------------------------------------------------*//*:End Ignore*/
+
+
+/***********************************************************************************************
+    Include files
+***********************************************************************************************/
+// Language headers
+//#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <climits>
+#include <cwchar>
+//#include <stdexcept>
+// Platform headers
+// Module headers
+#include "inc/TtfUtil.h"
+#include "inc/TtfTypes.h"
+#include "inc/Endian.h"
+
+/***********************************************************************************************
+    Forward declarations
+***********************************************************************************************/
+
+/***********************************************************************************************
+    Local Constants and static variables
+***********************************************************************************************/
+namespace
+{
+#ifdef ALL_TTFUTILS
+    // max number of components allowed in composite glyphs
+    const int kMaxGlyphComponents = 8;
+#endif
+
+    template <int R, typename T>
+    inline float fixed_to_float(const T f) {
+        return float(f)/float(2^R);
+    }
+
+/*----------------------------------------------------------------------------------------------
+    Table of standard Postscript glyph names. From Martin Hosken. Disagress with ttfdump.exe
+---------------------------------------------------------------------------------------------*/
+#ifdef ALL_TTFUTILS
+    const int kcPostNames = 258;
+
+    const char * rgPostName[kcPostNames] = {
+        ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign",
+        "dollar", "percent", "ampersand", "quotesingle", "parenleft",
+        "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash",
+        "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
+        "nine", "colon", "semicolon", "less", "equal", "greater", "question",
+        "at", "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",
+        "bracketleft", "backslash", "bracketright", "asciicircum",
+        "underscore", "grave", "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", "braceleft", "bar", "braceright", "asciitilde",
+        "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis",
+        "Udieresis", "aacute", "agrave", "acircumflex", "adieresis", "atilde",
+        "aring", "ccedilla", "eacute", "egrave", "ecircumflex", "edieresis",
+        "iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute",
+        "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave",
+        "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling",
+        "section", "bullet", "paragraph", "germandbls", "registered",
+        "copyright", "trademark", "acute", "dieresis", "notequal", "AE",
+        "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen",
+        "mu", "partialdiff", "summation", "product", "pi", "integral",
+        "ordfeminine", "ordmasculine", "Omega", "ae", "oslash", "questiondown",
+        "exclamdown", "logicalnot", "radical", "florin", "approxequal",
+        "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace",
+        "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash",
+        "quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide",
+        "lozenge", "ydieresis", "Ydieresis", "fraction", "currency",
+        "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered",
+        "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
+        "Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute",
+        "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex",
+        "apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi",
+        "circumflex", "tilde", "macron", "breve", "dotaccent", "ring",
+        "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash",
+        "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth",
+        "Yacute", "yacute", "Thorn", "thorn", "minus", "multiply",
+        "onesuperior", "twosuperior", "threesuperior", "onehalf", "onequarter",
+        "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla",
+        "scedilla", "Cacute", "cacute", "Ccaron", "ccaron",
+        "dcroat" };
+#endif
+
+} // end of namespace
+
+/***********************************************************************************************
+    Methods
+***********************************************************************************************/
+
+/* Note on error processing: The code guards against bad glyph ids being used to look up data
+in open ended tables (loca, hmtx). If the glyph id comes from a cmap this shouldn't happen
+but it seems prudent to check for user errors here. The code does assume that data obtained
+from the TTF file is valid otherwise (though the CheckTable method seeks to check for
+obvious problems that might accompany a change in table versions). For example an invalid
+offset in the loca table which could exceed the size of the glyf table is NOT trapped.
+Likewise if numberOf_LongHorMetrics in the hhea table is wrong, this will NOT be trapped,
+which could cause a lookup in the hmtx table to exceed the table length. Of course, TTF tables
+that are completely corrupt will cause unpredictable results. */
+
+/* Note on composite glyphs: Glyphs that have components that are themselves composites
+are not supported. IsDeepComposite can be used to test for this. False is returned from many
+of the methods in this cases. It is unclear how to build composite glyphs in some cases,
+so this code represents my best guess until test cases can be found. See notes on the high-
+level GlyfPoints method. */
+namespace graphite2
+{
+namespace TtfUtil
+{
+
+
+/*----------------------------------------------------------------------------------------------
+    Get offset and size of the offset table needed to find table directory.
+    Return true if success, false otherwise.
+    lSize excludes any table directory entries.
+----------------------------------------------------------------------------------------------*/
+bool GetHeaderInfo(size_t & lOffset, size_t & lSize)
+{
+    lOffset = 0;
+    lSize   = offsetof(Sfnt::OffsetSubTable, table_directory);
+    assert(sizeof(uint32) + 4*sizeof (uint16) == lSize);
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Check the offset table for expected data.
+    Return true if success, false otherwise.
+----------------------------------------------------------------------------------------------*/
+bool CheckHeader(const void * pHdr)
+{
+    const Sfnt::OffsetSubTable * pOffsetTable
+        = reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr);
+
+    return pHdr && be::swap(pOffsetTable->scaler_type) == Sfnt::OffsetSubTable::TrueTypeWin;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get offset and size of the table directory.
+    Return true if successful, false otherwise.
+----------------------------------------------------------------------------------------------*/
+bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize)
+{
+    const Sfnt::OffsetSubTable * pOffsetTable
+        = reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr);
+
+    lOffset = offsetof(Sfnt::OffsetSubTable, table_directory);
+    lSize   = be::swap(pOffsetTable->num_tables)
+        * sizeof(Sfnt::OffsetSubTable::Entry);
+
+    return true;
+}
+
+
+/*----------------------------------------------------------------------------------------------
+    Get offset and size of the specified table.
+    Return true if successful, false otherwise. On false, offset and size will be 0.
+----------------------------------------------------------------------------------------------*/
+bool GetTableInfo(const Tag TableTag, const void * pHdr, const void * pTableDir,
+                           size_t & lOffset, size_t & lSize)
+{
+    const Sfnt::OffsetSubTable * pOffsetTable
+        = reinterpret_cast<const Sfnt::OffsetSubTable *>(pHdr);
+    const size_t num_tables = be::swap(pOffsetTable->num_tables);
+    const Sfnt::OffsetSubTable::Entry
+        * entry_itr = reinterpret_cast<const Sfnt::OffsetSubTable::Entry *>(
+            pTableDir),
+        * const  dir_end = entry_itr + num_tables;
+
+    if (num_tables > 40)
+        return false;
+
+    for (;entry_itr != dir_end; ++entry_itr) // 40 - safe guard
+    {
+        if (be::swap(entry_itr->tag) == TableTag)
+        {
+            lOffset = be::swap(entry_itr->offset);
+            lSize   = be::swap(entry_itr->length);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Check the specified table. Tests depend on the table type.
+    Return true if successful, false otherwise.
+----------------------------------------------------------------------------------------------*/
+bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize)
+{
+    using namespace Sfnt;
+
+    if (pTable == 0 || lTableSize < 4) return false;
+
+    switch(TableId)
+    {
+    case Tag::cmap: // cmap
+    {
+        const Sfnt::CharacterCodeMap * const pCmap
+            = reinterpret_cast<const Sfnt::CharacterCodeMap *>(pTable);
+        if (lTableSize < sizeof(Sfnt::CharacterCodeMap))
+            return false;
+        return be::swap(pCmap->version) == 0;
+    }
+
+    case Tag::head: // head
+    {
+        const Sfnt::FontHeader * const pHead
+            = reinterpret_cast<const Sfnt::FontHeader *>(pTable);
+        if (lTableSize < sizeof(Sfnt::FontHeader))
+            return false;
+        bool r = be::swap(pHead->version) == OneFix
+            && be::swap(pHead->magic_number) == FontHeader::MagicNumber
+            && be::swap(pHead->glyph_data_format)
+                    == FontHeader::GlypDataFormat
+            && (be::swap(pHead->index_to_loc_format)
+                    == FontHeader::ShortIndexLocFormat
+                || be::swap(pHead->index_to_loc_format)
+                    == FontHeader::LongIndexLocFormat)
+            && sizeof(FontHeader) <= lTableSize;
+        return r;
+    }
+
+    case Tag::post: // post
+    {
+        const Sfnt::PostScriptGlyphName * const pPost
+            = reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pTable);
+        if (lTableSize < sizeof(Sfnt::PostScriptGlyphName))
+            return false;
+        const fixed format = be::swap(pPost->format);
+        bool r = format == PostScriptGlyphName::Format1
+            || format == PostScriptGlyphName::Format2
+            || format == PostScriptGlyphName::Format3
+            || format == PostScriptGlyphName::Format25;
+        return r;
+    }
+
+    case Tag::hhea: // hhea
+    {
+        const Sfnt::HorizontalHeader * pHhea =
+            reinterpret_cast<const Sfnt::HorizontalHeader *>(pTable);
+        if (lTableSize < sizeof(Sfnt::HorizontalHeader))
+            return false;
+        bool r = be::swap(pHhea->version) == OneFix
+            && be::swap(pHhea->metric_data_format) == 0
+            && sizeof (Sfnt::HorizontalHeader) <= lTableSize;
+        return r;
+    }
+
+    case Tag::maxp: // maxp
+    {
+        const Sfnt::MaximumProfile * pMaxp =
+            reinterpret_cast<const Sfnt::MaximumProfile *>(pTable);
+        if (lTableSize < sizeof(Sfnt::MaximumProfile))
+            return false;
+        bool r = be::swap(pMaxp->version) == OneFix
+            && sizeof(Sfnt::MaximumProfile) <= lTableSize;
+        return r;
+    }
+
+    case Tag::OS_2: // OS/2
+    {
+        const Sfnt::Compatibility * pOs2
+            = reinterpret_cast<const Sfnt::Compatibility *>(pTable);
+        if (be::swap(pOs2->version) == 0)
+        { // OS/2 table version 1 size
+//          if (sizeof(Sfnt::Compatibility)
+//                  - sizeof(uint32)*2 - sizeof(int16)*2
+//                  - sizeof(uint16)*3 <= lTableSize)
+            if (sizeof(Sfnt::Compatibility0) <= lTableSize)
+                return true;
+        }
+        else if (be::swap(pOs2->version) == 1)
+        { // OS/2 table version 2 size
+//          if (sizeof(Sfnt::Compatibility)
+//                  - sizeof(int16) *2
+//                  - sizeof(uint16)*3 <= lTableSize)
+            if (sizeof(Sfnt::Compatibility1) <= lTableSize)
+                return true;
+        }
+        else if (be::swap(pOs2->version) == 2)
+        { // OS/2 table version 3 size
+            if (sizeof(Sfnt::Compatibility2) <= lTableSize)
+                return true;
+        }
+        else if (be::swap(pOs2->version) == 3 || be::swap(pOs2->version) == 4)
+        { // OS/2 table version 4 size - version 4 changed the meaning of some fields which we don't use
+            if (sizeof(Sfnt::Compatibility3) <= lTableSize)
+                return true;
+        }
+        else
+            return false;
+        break;
+    }
+
+    case Tag::name:
+    {
+        const Sfnt::FontNames * pName
+            = reinterpret_cast<const Sfnt::FontNames *>(pTable);
+        if (lTableSize < sizeof(Sfnt::FontNames))
+            return false;
+        return be::swap(pName->format) == 0;
+    }
+
+    case Tag::glyf:
+    {
+        return (lTableSize >= sizeof(Sfnt::Glyph));
+    }
+
+    default:
+        break;
+    }
+
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the number of glyphs in the font. Should never be less than zero.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+size_t GlyphCount(const void * pMaxp)
+{
+    const Sfnt::MaximumProfile * pTable =
+            reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp);
+    return be::swap(pTable->num_glyphs);
+}
+
+#ifdef ALL_TTFUTILS
+/*----------------------------------------------------------------------------------------------
+    Return the maximum number of components for any composite glyph in the font.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+size_t  MaxCompositeComponentCount(const void * pMaxp)
+{
+    const Sfnt::MaximumProfile * pTable =
+            reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp);
+    return be::swap(pTable->max_component_elements);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Composite glyphs can be composed of glyphs that are themselves composites.
+    This method returns the maximum number of levels like this for any glyph in the font.
+    A non-composite glyph has a level of 1.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+size_t  MaxCompositeLevelCount(const void * pMaxp)
+{
+    const Sfnt::MaximumProfile * pTable =
+            reinterpret_cast<const Sfnt::MaximumProfile *>(pMaxp);
+    return be::swap(pTable->max_component_depth);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the number of glyphs in the font according to a differt source.
+    Should never be less than zero. Return -1 on failure.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+size_t LocaGlyphCount(size_t lLocaSize, const void * pHead) //throw(std::domain_error)
+{
+
+    const Sfnt::FontHeader * pTable
+        = reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+
+    if (be::swap(pTable->index_to_loc_format)
+        == Sfnt::FontHeader::ShortIndexLocFormat)
+    // loca entries are two bytes and have been divided by two
+        return (lLocaSize >> 1) - 1;
+
+    if (be::swap(pTable->index_to_loc_format)
+        == Sfnt::FontHeader::LongIndexLocFormat)
+     // loca entries are four bytes
+        return (lLocaSize >> 2) - 1;
+
+    return -1;
+    //throw std::domain_error("head table in inconsistent state. The font may be corrupted");
+}
+#endif
+
+/*----------------------------------------------------------------------------------------------
+    Return the design units the font is designed with
+----------------------------------------------------------------------------------------------*/
+int DesignUnits(const void * pHead)
+{
+    const Sfnt::FontHeader * pTable =
+            reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+
+    return be::swap(pTable->units_per_em);
+}
+
+#ifdef ALL_TTFUTILS
+/*----------------------------------------------------------------------------------------------
+    Return the checksum from the head table, which serves as a unique identifer for the font.
+----------------------------------------------------------------------------------------------*/
+int HeadTableCheckSum(const void * pHead)
+{
+    const Sfnt::FontHeader * pTable =
+            reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+
+    return be::swap(pTable->check_sum_adjustment);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the create time from the head table. This consists of a 64-bit integer, which
+    we return here as two 32-bit integers.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+void HeadTableCreateTime(const void * pHead,
+    unsigned int * pnDateBC, unsigned int * pnDateAD)
+{
+    const Sfnt::FontHeader * pTable =
+            reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+
+    *pnDateBC = be::swap(pTable->created[0]);
+    *pnDateAD = be::swap(pTable->created[1]);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the modify time from the head table.This consists of a 64-bit integer, which
+    we return here as two 32-bit integers.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+void HeadTableModifyTime(const void * pHead,
+    unsigned int * pnDateBC, unsigned int *pnDateAD)
+{
+    const Sfnt::FontHeader * pTable =
+            reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+   ;
+    *pnDateBC = be::swap(pTable->modified[0]);
+    *pnDateAD = be::swap(pTable->modified[1]);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return true if the font is italic.
+----------------------------------------------------------------------------------------------*/
+bool IsItalic(const void * pHead)
+{
+    const Sfnt::FontHeader * pTable =
+            reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+
+    return ((be::swap(pTable->mac_style) & 0x00000002) != 0);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the ascent for the font
+----------------------------------------------------------------------------------------------*/
+int FontAscent(const void * pOs2)
+{
+    const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2);
+
+    return be::swap(pTable->win_ascent);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the descent for the font
+----------------------------------------------------------------------------------------------*/
+int FontDescent(const void * pOs2)
+{
+    const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2);
+
+    return be::swap(pTable->win_descent);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the bold and italic style bits.
+    Return true if successful. false otherwise.
+    In addition to checking the OS/2 table, one could also check
+        the head table's macStyle field (overridden by the OS/2 table on Win)
+        the sub-family name in the name table (though this can contain oblique, dark, etc too)
+----------------------------------------------------------------------------------------------*/
+bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic)
+{
+    const Sfnt::Compatibility * pTable = reinterpret_cast<const Sfnt::Compatibility *>(pOs2);
+
+    fBold = (be::swap(pTable->fs_selection) & Sfnt::Compatibility::Bold) != 0;
+    fItalic = (be::swap(pTable->fs_selection) & Sfnt::Compatibility::Italic) != 0;
+
+    return true;
+}
+#endif
+
+/*----------------------------------------------------------------------------------------------
+    Method for searching name table.
+----------------------------------------------------------------------------------------------*/
+bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId,
+        int nLangId, int nNameId, size_t & lOffset, size_t & lSize)
+{
+    lOffset = 0;
+    lSize = 0;
+
+    const Sfnt::FontNames * pTable = reinterpret_cast<const Sfnt::FontNames *>(pName);
+    uint16 cRecord = be::swap(pTable->count);
+    uint16 nRecordOffset = be::swap(pTable->string_offset);
+    const Sfnt::NameRecord * pRecord = reinterpret_cast<const Sfnt::NameRecord *>(pTable + 1);
+
+    for (int i = 0; i < cRecord; ++i)
+    {
+        if (be::swap(pRecord->platform_id) == nPlatformId &&
+            be::swap(pRecord->platform_specific_id) == nEncodingId &&
+            be::swap(pRecord->language_id) == nLangId &&
+            be::swap(pRecord->name_id) == nNameId)
+        {
+            lOffset = be::swap(pRecord->offset) + nRecordOffset;
+            lSize = be::swap(pRecord->length);
+            return true;
+        }
+        pRecord++;
+    }
+
+    return false;
+}
+
+#ifdef ALL_TTFUTILS
+/*----------------------------------------------------------------------------------------------
+    Return all the lang-IDs that have data for the given name-IDs. Assume that there is room
+    in the return array (langIdList) for 128 items. The purpose of this method is to return
+    a list of all possible lang-IDs.
+----------------------------------------------------------------------------------------------*/
+int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId,
+        int * nameIdList, int cNameIds, short * langIdList)
+{
+    const Sfnt::FontNames * pTable = reinterpret_cast<const Sfnt::FontNames *>(pName);
+        int cLangIds = 0;
+    uint16 cRecord = be::swap(pTable->count);
+        if (cRecord > 127) return cLangIds;
+    //uint16 nRecordOffset = swapw(pTable->stringOffset);
+    const Sfnt::NameRecord * pRecord = reinterpret_cast<const Sfnt::NameRecord *>(pTable + 1);
+
+    for (int i = 0; i < cRecord; ++i)
+    {
+        if (be::swap(pRecord->platform_id) == nPlatformId &&
+            be::swap(pRecord->platform_specific_id) == nEncodingId)
+        {
+            bool fNameFound = false;
+            int nLangId = be::swap(pRecord->language_id);
+            int nNameId = be::swap(pRecord->name_id);
+            for (int j = 0; j < cNameIds; j++)
+            {
+                if (nNameId == nameIdList[j])
+                {
+                    fNameFound = true;
+                    break;
+                }
+            }
+            if (fNameFound)
+            {
+                // Add it if it's not there.
+                int ilang;
+                for (ilang = 0; ilang < cLangIds; ilang++)
+                    if (langIdList[ilang] == nLangId)
+                        break;
+                if (ilang >= cLangIds)
+                {
+                    langIdList[cLangIds] = short(nLangId);
+                    cLangIds++;
+                }
+                if (cLangIds == 128)
+                    return cLangIds;
+            }
+        }
+        pRecord++;
+    }
+
+    return cLangIds;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the offset and size of the font family name in English for the MS Platform with Unicode
+    writing system. The offset is within the pName data. The string is double byte with MSB
+    first.
+----------------------------------------------------------------------------------------------*/
+bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize)
+{
+    return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033,
+        Sfnt::NameRecord::Family, lOffset, lSize);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the offset and size of the full font name in English for the MS Platform with Unicode
+    writing system. The offset is within the pName data. The string is double byte with MSB
+    first.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize)
+{
+    return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 1, 1033,
+        Sfnt::NameRecord::Fullname, lOffset, lSize);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the offset and size of the font family name in English for the MS Platform with Symbol
+    writing system. The offset is within the pName data. The string is double byte with MSB
+    first.
+----------------------------------------------------------------------------------------------*/
+bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize)
+{
+    return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033,
+        Sfnt::NameRecord::Family, lOffset, lSize);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the offset and size of the full font name in English for the MS Platform with Symbol
+    writing system. The offset is within the pName data. The string is double byte with MSB
+    first.
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize)
+{
+    return GetNameInfo(pName, Sfnt::NameRecord::Microsoft, 0, 1033,
+        Sfnt::NameRecord::Fullname, lOffset, lSize);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the Glyph ID for a given Postscript name. This method finds the first glyph which
+    matches the requested Postscript name. Ideally every glyph should have a unique Postscript
+    name (except for special names such as .notdef), but this is not always true.
+    On failure return value less than zero.
+       -1 - table search failed
+       -2 - format 3 table (no Postscript glyph info)
+       -3 - other failures
+
+    Note: this method is not currently used by the Graphite engine.
+----------------------------------------------------------------------------------------------*/
+int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp,
+                        const char * pPostName)
+{
+    using namespace Sfnt;
+
+    const Sfnt::PostScriptGlyphName * pTable
+        = reinterpret_cast<const Sfnt::PostScriptGlyphName *>(pPost);
+    fixed format = be::swap(pTable->format);
+
+    if (format == PostScriptGlyphName::Format3)
+    { // format 3 - no Postscript glyph info in font
+        return -2;
+    }
+
+    // search for given Postscript name among the standard names
+    int iPostName = -1; // index in standard names
+    for (int i = 0; i < kcPostNames; i++)
+    {
+        if (!strcmp(pPostName, rgPostName[i]))
+        {
+            iPostName = i;
+            break;
+        }
+    }
+
+    if (format == PostScriptGlyphName::Format1)
+    { // format 1 - use standard Postscript names
+        return iPostName;
+    }
+
+    if (format == PostScriptGlyphName::Format25)
+    {
+        if (iPostName == -1)
+            return -1;
+
+        const PostScriptGlyphName25 * pTable25
+            = static_cast<const PostScriptGlyphName25 *>(pTable);
+        int cnGlyphs = GlyphCount(pMaxp);
+        for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs && nGlyphId < kcPostNames;
+                nGlyphId++)
+        { // glyph_name_index25 contains bytes so no byte swapping needed
+          // search for first glyph id that uses the standard name
+            if (nGlyphId + pTable25->offset[nGlyphId] == iPostName)
+                return nGlyphId;
+        }
+    }
+
+    if (format == PostScriptGlyphName::Format2)
+    { // format 2
+        const PostScriptGlyphName2 * pTable2
+            = static_cast<const PostScriptGlyphName2 *>(pTable);
+
+        int cnGlyphs = be::swap(pTable2->number_of_glyphs);
+
+        if (iPostName != -1)
+        { // did match a standard name, look for first glyph id mapped to that name
+            for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++)
+            {
+                if (be::swap(pTable2->glyph_name_index[nGlyphId]) == iPostName)
+                    return nGlyphId;
+            }
+        }
+
+        { // did not match a standard name, search font specific names
+            size_t nStrSizeGoal = strlen(pPostName);
+            const char * pFirstGlyphName = reinterpret_cast<const char *>(
+                &pTable2->glyph_name_index[0] + cnGlyphs);
+            const char * pGlyphName = pFirstGlyphName;
+            int iInNames = 0; // index in font specific names
+            bool fFound = false;
+            const char * const endOfTable
+                = reinterpret_cast<const char *>(pTable2) + lPostSize;
+            while (pGlyphName < endOfTable && !fFound)
+            { // search Pascal strings for first matching name
+                size_t nStringSize = size_t(*pGlyphName);
+                if (nStrSizeGoal != nStringSize ||
+                    strncmp(pGlyphName + 1, pPostName, nStringSize))
+                { // did not match
+                    ++iInNames;
+                    pGlyphName += nStringSize + 1;
+                }
+                else
+                { // did match
+                    fFound = true;
+                }
+            }
+            if (!fFound)
+                return -1; // no font specific name matches request
+
+            iInNames += kcPostNames;
+            for (gid16 nGlyphId = 0; nGlyphId < cnGlyphs; nGlyphId++)
+            { // search for first glyph id that maps to the found string index
+                if (be::swap(pTable2->glyph_name_index[nGlyphId]) == iInNames)
+                    return nGlyphId;
+            }
+            return -1; // no glyph mapped to this index (very strange)
+        }
+    }
+
+    return -3;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Convert a Unicode character string from big endian (MSB first, Motorola) format to little
+    endian (LSB first, Intel) format.
+    nSize is the number of Unicode characters in the string. It should not include any
+    terminating null. If nSize is 0, it is assumed the string is null terminated. nSize
+    defaults to 0.
+    Return true if successful, false otherwise.
+----------------------------------------------------------------------------------------------*/
+void SwapWString(void * pWStr, size_t nSize /* = 0 */) //throw (std::invalid_argument)
+{
+    if (pWStr == 0)
+    {
+//      throw std::invalid_argument("null pointer given");
+        return;
+    }
+
+    uint16 * pStr = reinterpret_cast<uint16 *>(pWStr);
+    uint16 * const pStrEnd = pStr + (nSize == 0 ? wcslen((const wchar_t*)pStr) : nSize);
+
+        for (; pStr != pStrEnd; ++pStr)
+          *pStr = be::swap(*pStr);
+//  std::transform(pStr, pStrEnd, pStr, read<uint16>);
+
+//      for (int i = 0; i < nSize; i++)
+//      { // swap the wide characters in the string
+//          pStr[i] = utf16(be::swap(uint16(pStr[i])));
+//      }
+}
+#endif
+
+/*----------------------------------------------------------------------------------------------
+    Get the left-side bearing and and advance width based on the given tables and Glyph ID
+    Return true if successful, false otherwise. On false, one or both value could be INT_MIN
+----------------------------------------------------------------------------------------------*/
+bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize, const void * pHhea,
+                         int & nLsb, unsigned int & nAdvWid)
+{
+    const Sfnt::HorizontalMetric * phmtx =
+        reinterpret_cast<const Sfnt::HorizontalMetric *>(pHmtx);
+
+    const Sfnt::HorizontalHeader * phhea =
+        reinterpret_cast<const Sfnt::HorizontalHeader *>(pHhea);
+
+    size_t cLongHorMetrics = be::swap(phhea->num_long_hor_metrics);
+    if (nGlyphId < cLongHorMetrics)
+    {   // glyph id is acceptable
+        if ((nGlyphId + 1) * sizeof(Sfnt::HorizontalMetric) > lHmtxSize) return false;
+        nAdvWid = be::swap(phmtx[nGlyphId].advance_width);
+        nLsb = be::swap(phmtx[nGlyphId].left_side_bearing);
+    }
+    else
+    {
+        // guard against bad glyph id
+        size_t lLsbOffset = sizeof(Sfnt::HorizontalMetric) * cLongHorMetrics +
+            sizeof(int16) * (nGlyphId - cLongHorMetrics); // offset in bytes
+        // We test like this as LsbOffset is an offset not a length.
+        if (lLsbOffset >= lHmtxSize - sizeof(int16) || cLongHorMetrics == 0)
+        {
+            nLsb = 0;
+            return false;
+        }
+        nAdvWid = be::swap(phmtx[cLongHorMetrics - 1].advance_width);
+        nLsb = be::peek<int16>(reinterpret_cast<const byte *>(phmtx) + lLsbOffset);
+    }
+
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return a pointer to the requested cmap subtable. By default find the Microsoft Unicode
+    subtable. Pass nEncoding as -1 to find first table that matches only nPlatformId.
+    Return NULL if the subtable cannot be found.
+----------------------------------------------------------------------------------------------*/
+const void * FindCmapSubtable(const void * pCmap, int nPlatformId, /* =3 */ int nEncodingId, /* = 1 */ size_t length)
+{
+    const Sfnt::CharacterCodeMap * pTable = reinterpret_cast<const Sfnt::CharacterCodeMap *>(pCmap);
+    uint16 csuPlatforms = be::swap(pTable->num_subtables);
+    if (length && (sizeof(Sfnt::CharacterCodeMap) + 8 * (csuPlatforms - 1) > length))
+        return NULL;
+    for (int i = 0; i < csuPlatforms; i++)
+    {
+        if (be::swap(pTable->encoding[i].platform_id) == nPlatformId &&
+                (nEncodingId == -1 || be::swap(pTable->encoding[i].platform_specific_id) == nEncodingId))
+        {
+            uint32 offset = be::swap(pTable->encoding[i].offset);
+            const uint8 * pRtn = reinterpret_cast<const uint8 *>(pCmap) + offset;
+            if (length)
+            {
+                if (offset > length - 2) return NULL;
+                uint16 format = be::read<uint16>(pRtn);
+                if (format == 4)
+                {
+                    if (offset > length - 4) return NULL;
+                    uint16 subTableLength = be::peek<uint16>(pRtn);
+                    if (i + 1 == csuPlatforms)
+                    {
+                        if (subTableLength > length - offset)
+                            return NULL;
+                    }
+                    else if (subTableLength > be::swap(pTable->encoding[i+1].offset))
+                        return NULL;
+                }
+                if (format == 12)
+                {
+                    if (offset > length - 6) return NULL;
+                    uint32 subTableLength = be::peek<uint32>(pRtn);
+                    if (i + 1 == csuPlatforms)
+                    {
+                        if (subTableLength > length - offset)
+                            return NULL;
+                    }
+                    else if (subTableLength > be::swap(pTable->encoding[i+1].offset))
+                        return NULL;
+                }
+            }
+            return reinterpret_cast<const uint8 *>(pCmap) + offset;
+        }
+    }
+
+    return 0;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Check the Microsoft Unicode subtable for expected values
+----------------------------------------------------------------------------------------------*/
+bool CheckCmapSubtable4(const void * pCmapSubtable4, const void * pCmapEnd /*, unsigned int maxgid*/)
+{
+    size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable4;
+    if (!pCmapSubtable4) return false;
+    const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable4);
+    // Bob H say some freeware TT fonts have version 1 (eg, CALIGULA.TTF)
+    // so don't check subtable version. 21 Mar 2002 spec changes version to language.
+    if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 4) return false;
+    const Sfnt::CmapSubTableFormat4 * pTable4 = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtable4);
+    if (table_len < sizeof(*pTable4))
+        return false;
+    uint16 length = be::swap(pTable4->length);
+    if (length > table_len)
+        return false;
+    if (length < sizeof(Sfnt::CmapSubTableFormat4))
+        return false;
+    uint16 nRanges = be::swap(pTable4->seg_count_x2) >> 1;
+    if (!nRanges || length < sizeof(Sfnt::CmapSubTableFormat4) + 4 * nRanges * sizeof(uint16))
+        return false;
+    // check last range is properly terminated
+    uint16 chEnd = be::peek<uint16>(pTable4->end_code + nRanges - 1);
+    if (chEnd != 0xFFFF)
+        return false;
+#if 0
+    int lastend = -1;
+    for (int i = 0; i < nRanges; ++i)
+    {
+        uint16 end = be::peek<uint16>(pTable4->end_code + i);
+        uint16 start = be::peek<uint16>(pTable4->end_code + nRanges + 1 + i);
+        int16 delta = be::peek<int16>(pTable4->end_code + 2*nRanges + 1 + i);
+        uint16 offset = be::peek<uint16>(pTable4->end_code + 3*nRanges + 1 + i);
+        if (lastend >= end || lastend >= start)
+            return false;
+        if (offset)
+        {
+            const uint16 *gstart = pTable4->end_code + 3*nRanges + 1 + i + (offset >> 1);
+            const uint16 *gend = gstart + end - start;
+            if ((char *)gend >= (char *)pCmapSubtable4 + length)
+                return false;
+            while (gstart <= gend)
+            {
+                uint16 g = be::peek<uint16>(gstart++);
+                if (g && ((g + delta) & 0xFFFF) > maxgid)
+                    return false;
+            }
+        }
+        else if (((delta + end) & 0xFFFF) > maxgid)
+            return false;
+        lastend = end;
+    }
+#endif
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the Glyph ID for the given Unicode ID in the Microsoft Unicode subtable.
+    (Actually this code only depends on subtable being format 4.)
+    Return 0 if the Unicode ID is not in the subtable.
+----------------------------------------------------------------------------------------------*/
+gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey)
+{
+    const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmapSubtabel4);
+
+    uint16 nSeg = be::swap(pTable->seg_count_x2) >> 1;
+
+    uint16 n;
+    const uint16 * pLeft, * pMid;
+    uint16 cMid, chStart, chEnd;
+
+    if (rangeKey)
+    {
+        pMid = &(pTable->end_code[rangeKey]);
+        chEnd = be::peek<uint16>(pMid);
+    }
+    else
+    {
+        // Binary search of the endCode[] array
+        pLeft = &(pTable->end_code[0]);
+        n = nSeg;
+        while (n > 0)
+        {
+            cMid = n >> 1;           // Pick an element in the middle
+            pMid = pLeft + cMid;
+            chEnd = be::peek<uint16>(pMid);
+            if (nUnicodeId <= chEnd)
+            {
+                if (cMid == 0 || nUnicodeId > be::peek<uint16>(pMid -1))
+                        break;          // Must be this seg or none!
+                n = cMid;            // Continue on left side, omitting mid point
+            }
+            else
+            {
+                pLeft = pMid + 1;    // Continue on right side, omitting mid point
+                n -= (cMid + 1);
+            }
+        }
+
+        if (!n)
+        return 0;
+    }
+
+    // Ok, we're down to one segment and pMid points to the endCode element
+    // Either this is it or none is.
+
+    chStart = be::peek<uint16>(pMid += nSeg + 1);
+    if (chEnd >= nUnicodeId && nUnicodeId >= chStart)
+    {
+        // Found correct segment. Find Glyph Id
+        int16 idDelta = be::peek<uint16>(pMid += nSeg);
+        uint16 idRangeOffset = be::peek<uint16>(pMid += nSeg);
+
+        if (idRangeOffset == 0)
+            return (uint16)(idDelta + nUnicodeId); // must use modulus 2^16
+
+        // Look up value in glyphIdArray
+        const ptrdiff_t offset = (nUnicodeId - chStart) + (idRangeOffset >> 1) +
+                (pMid - reinterpret_cast<const uint16 *>(pTable));
+        if (offset * 2 + 1 >= be::swap<uint16>(pTable->length))
+            return 0;
+        gid16 nGlyphId = be::peek<uint16>(reinterpret_cast<const uint16 *>(pTable)+offset);
+        // If this value is 0, return 0. Else add the idDelta
+        return nGlyphId ? nGlyphId + idDelta : 0;
+    }
+
+    return 0;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the next Unicode value in the cmap. Pass 0 to obtain the first item.
+    Returns 0xFFFF as the last item.
+    pRangeKey is an optional key that is used to optimize the search; its value is the range
+    in which the character is found.
+----------------------------------------------------------------------------------------------*/
+unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId, int * pRangeKey)
+{
+    const Sfnt::CmapSubTableFormat4 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat4 *>(pCmap31);
+
+    uint16 nRange = be::swap(pTable->seg_count_x2) >> 1;
+
+    uint32 nUnicodePrev = (uint32)nUnicodeId;
+
+    const uint16 * pStartCode = &(pTable->end_code[0])
+        + nRange // length of end code array
+        + 1;   // reserved word
+
+    if (nUnicodePrev == 0)
+    {
+        // return the first codepoint.
+        if (pRangeKey)
+            *pRangeKey = 0;
+        return be::peek<uint16>(pStartCode);
+    }
+    else if (nUnicodePrev >= 0xFFFF)
+    {
+        if (pRangeKey)
+            *pRangeKey = nRange - 1;
+        return 0xFFFF;
+    }
+
+    int iRange = (pRangeKey) ? *pRangeKey : 0;
+    // Just in case we have a bad key:
+    while (iRange > 0 && be::peek<uint16>(pStartCode + iRange) > nUnicodePrev)
+        iRange--;
+    while (iRange < nRange - 1 && be::peek<uint16>(pTable->end_code + iRange) < nUnicodePrev)
+        iRange++;
+
+    // Now iRange is the range containing nUnicodePrev.
+    unsigned int nStartCode = be::peek<uint16>(pStartCode + iRange);
+    unsigned int nEndCode = be::peek<uint16>(pTable->end_code + iRange);
+
+    if (nStartCode > nUnicodePrev)
+        // Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable
+        // answer this time around.
+        nUnicodePrev = nStartCode - 1;
+
+    if (nEndCode > nUnicodePrev)
+    {
+        // Next is in the same range; it is the next successive codepoint.
+        if (pRangeKey)
+            *pRangeKey = iRange;
+        return nUnicodePrev + 1;
+    }
+
+    // Otherwise the next codepoint is the first one in the next range.
+    // There is guaranteed to be a next range because there must be one that
+    // ends with 0xFFFF.
+    if (pRangeKey)
+        *pRangeKey = iRange + 1;
+    return (iRange + 1 >= nRange) ? 0xFFFF : be::peek<uint16>(pStartCode + iRange + 1);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Check the Microsoft UCS-4 subtable for expected values.
+----------------------------------------------------------------------------------------------*/
+bool CheckCmapSubtable12(const void *pCmapSubtable12, const void *pCmapEnd /*, unsigned int maxgid*/)
+{
+    size_t table_len = (const byte *)pCmapEnd - (const byte *)pCmapSubtable12;
+    if (!pCmapSubtable12)  return false;
+    const Sfnt::CmapSubTable * pTable = reinterpret_cast<const Sfnt::CmapSubTable *>(pCmapSubtable12);
+    if (table_len < sizeof(*pTable) || be::swap(pTable->format) != 12)
+        return false;
+    const Sfnt::CmapSubTableFormat12 * pTable12 = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmapSubtable12);
+    if (table_len < sizeof(*pTable12))
+        return false;
+    uint32 length = be::swap(pTable12->length);
+    if (length > table_len)
+        return false;
+    if (length < sizeof(Sfnt::CmapSubTableFormat12))
+        return false;
+    uint32 num_groups = be::swap(pTable12->num_groups);
+    if (num_groups > 0x10000000 || length != (sizeof(Sfnt::CmapSubTableFormat12) + (num_groups - 1) * sizeof(uint32) * 3))
+        return false;
+#if 0
+    for (unsigned int i = 0; i < num_groups; ++i)
+    {
+        if (be::swap(pTable12->group[i].end_char_code)  - be::swap(pTable12->group[i].start_char_code) + be::swap(pTable12->group[i].start_glyph_id) > maxgid)
+            return false;
+        if (i > 0 && be::swap(pTable12->group[i].start_char_code) <= be::swap(pTable12->group[i-1].end_char_code))
+            return false;
+    }
+#endif
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the Glyph ID for the given Unicode ID in the Microsoft UCS-4 subtable.
+    (Actually this code only depends on subtable being format 12.)
+    Return 0 if the Unicode ID is not in the subtable.
+----------------------------------------------------------------------------------------------*/
+gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey)
+{
+    const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmap310);
+
+    //uint32 uLength = be::swap(pTable->length); //could use to test for premature end of table
+    uint32 ucGroups = be::swap(pTable->num_groups);
+
+    for (unsigned int i = rangeKey; i < ucGroups; i++)
+    {
+        uint32 uStartCode = be::swap(pTable->group[i].start_char_code);
+        uint32 uEndCode = be::swap(pTable->group[i].end_char_code);
+        if (uUnicodeId >= uStartCode && uUnicodeId <= uEndCode)
+        {
+            uint32 uDiff = uUnicodeId - uStartCode;
+            uint32 uStartGid = be::swap(pTable->group[i].start_glyph_id);
+            return static_cast<gid16>(uStartGid + uDiff);
+        }
+    }
+
+    return 0;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the next Unicode value in the cmap. Pass 0 to obtain the first item.
+    Returns 0x10FFFF as the last item.
+    pRangeKey is an optional key that is used to optimize the search; its value is the range
+    in which the character is found.
+----------------------------------------------------------------------------------------------*/
+unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId, int * pRangeKey)
+{
+    const Sfnt::CmapSubTableFormat12 * pTable = reinterpret_cast<const Sfnt::CmapSubTableFormat12 *>(pCmap310);
+
+    int nRange = be::swap(pTable->num_groups);
+
+    uint32 nUnicodePrev = (uint32)nUnicodeId;
+
+    if (nUnicodePrev == 0)
+    {
+        // return the first codepoint.
+        if (pRangeKey)
+            *pRangeKey = 0;
+        return be::swap(pTable->group[0].start_char_code);
+    }
+    else if (nUnicodePrev >= 0x10FFFF)
+    {
+        if (pRangeKey)
+            *pRangeKey = nRange;
+        return 0x10FFFF;
+    }
+
+    int iRange = (pRangeKey) ? *pRangeKey : 0;
+    // Just in case we have a bad key:
+    while (iRange > 0 && be::swap(pTable->group[iRange].start_char_code) > nUnicodePrev)
+        iRange--;
+    while (iRange < nRange - 1 && be::swap(pTable->group[iRange].end_char_code) < nUnicodePrev)
+        iRange++;
+
+    // Now iRange is the range containing nUnicodePrev.
+
+    unsigned int nStartCode = be::swap(pTable->group[iRange].start_char_code);
+    unsigned int nEndCode = be::swap(pTable->group[iRange].end_char_code);
+
+    if (nStartCode > nUnicodePrev)
+        // Oops, nUnicodePrev is not in the cmap! Adjust so we get a reasonable
+        // answer this time around.
+        nUnicodePrev = nStartCode - 1;
+
+    if (nEndCode > nUnicodePrev)
+    {
+        // Next is in the same range; it is the next successive codepoint.
+        if (pRangeKey)
+            *pRangeKey = iRange;
+        return nUnicodePrev + 1;
+    }
+
+    // Otherwise the next codepoint is the first one in the next range, or 10FFFF if we're done.
+    if (pRangeKey)
+        *pRangeKey = iRange + 1;
+    return (iRange + 1 >= nRange) ? 0x10FFFF : be::swap(pTable->group[iRange + 1].start_char_code);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return the offset stored in the loca table for the given Glyph ID.
+    (This offset is into the glyf table.)
+    Return -1 if the lookup failed.
+    Technically this method should return an unsigned long but it is unlikely the offset will
+        exceed 2^31.
+----------------------------------------------------------------------------------------------*/
+size_t LocaLookup(gid16 nGlyphId,
+        const void * pLoca, size_t lLocaSize,
+        const void * pHead) // throw (std::out_of_range)
+{
+    const Sfnt::FontHeader * pTable = reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+    size_t res = -2;
+
+    // CheckTable verifies the index_to_loc_format is valid
+    if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat)
+    { // loca entries are two bytes and have been divided by two
+        if (lLocaSize > 1 && nGlyphId + 1u < lLocaSize >> 1) // allow sentinel value to be accessed
+        {
+            const uint16 * pShortTable = reinterpret_cast<const uint16 *>(pLoca);
+            res = be::peek<uint16>(pShortTable + nGlyphId) << 1;
+            if (res == static_cast<size_t>(be::peek<uint16>(pShortTable + nGlyphId + 1) << 1))
+                return -1;
+        }
+    }
+    else if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)
+    { // loca entries are four bytes
+        if (lLocaSize > 3 && nGlyphId + 1u < lLocaSize >> 2)
+        {
+            const uint32 * pLongTable = reinterpret_cast<const uint32 *>(pLoca);
+            res = be::peek<uint32>(pLongTable + nGlyphId);
+            if (res == static_cast<size_t>(be::peek<uint32>(pLongTable + nGlyphId + 1)))
+                return -1;
+        }
+    }
+
+    // only get here if glyph id was bad
+    return res;
+    //throw std::out_of_range("glyph id out of range for font");
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return a pointer into the glyf table based on the given offset (from LocaLookup).
+    Return NULL on error.
+----------------------------------------------------------------------------------------------*/
+void * GlyfLookup(const void * pGlyf, size_t nGlyfOffset, size_t nTableLen)
+{
+    const uint8 * pByte = reinterpret_cast<const uint8 *>(pGlyf);
+    if (OVERFLOW_OFFSET_CHECK(pByte, nGlyfOffset) || nGlyfOffset >= nTableLen - sizeof(Sfnt::Glyph))
+        return NULL;
+    return const_cast<uint8 *>(pByte + nGlyfOffset);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the bounding box coordinates for a simple glyf entry (non-composite).
+    Return true if successful, false otherwise.
+----------------------------------------------------------------------------------------------*/
+bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin,
+                      int & xMax, int & yMax)
+{
+    const Sfnt::Glyph * pGlyph = reinterpret_cast<const Sfnt::Glyph *>(pSimpleGlyf);
+
+    xMin = be::swap(pGlyph->x_min);
+    yMin = be::swap(pGlyph->y_min);
+    xMax = be::swap(pGlyph->x_max);
+    yMax = be::swap(pGlyph->y_max);
+
+    return true;
+}
+
+#ifdef ALL_TTFUTILS
+/*----------------------------------------------------------------------------------------------
+    Return the number of contours for a simple glyf entry (non-composite)
+    Returning -1 means this is a composite glyph
+----------------------------------------------------------------------------------------------*/
+int GlyfContourCount(const void * pSimpleGlyf)
+{
+    const Sfnt::Glyph * pGlyph = reinterpret_cast<const Sfnt::Glyph *>(pSimpleGlyf);
+    return be::swap(pGlyph->number_of_contours); // -1 means composite glyph
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the point numbers for the end points of the glyph contours for a simple
+    glyf entry (non-composite).
+    cnPointsTotal - count of contours from GlyfContourCount(); (same as number of end points)
+    prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers
+    cnPoints - count of points placed in above range
+    Return true if successful, false otherwise.
+        False could indicate a multi-level composite glyphs.
+----------------------------------------------------------------------------------------------*/
+bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint,
+                                   int cnPointsTotal, int & cnPoints)
+{
+    const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);
+
+    int cContours = be::swap(pGlyph->number_of_contours);
+    if (cContours < 0)
+        return false; // this method isn't supposed handle composite glyphs
+
+    for (int i = 0; i < cContours && i < cnPointsTotal; i++)
+    {
+        prgnContourEndPoint[i] = be::swap(pGlyph->end_pts_of_contours[i]);
+    }
+
+    cnPoints = cContours;
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the points for a simple glyf entry (non-composite)
+    cnPointsTotal - count of points from largest end point obtained from GlyfContourEndPoints
+    prgnX & prgnY - should point to buffers large enough to hold cnPointsTotal integers
+        The ranges are parallel so that coordinates for point(n) are found at offset n in both
+        ranges. This is raw point data with relative coordinates.
+    prgbFlag - should point to a buffer a large enough to hold cnPointsTotal bytes
+        This range is parallel to the prgnX & prgnY
+    cnPoints - count of points placed in above ranges
+    Return true if successful, false otherwise.
+        False could indicate a composite glyph
+----------------------------------------------------------------------------------------------*/
+bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY,
+        char * prgbFlag, int cnPointsTotal, int & cnPoints)
+{
+    using namespace Sfnt;
+
+    const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);
+    int cContours = be::swap(pGlyph->number_of_contours);
+    // return false for composite glyph
+    if (cContours <= 0)
+        return false;
+    int cPts = be::swap(pGlyph->end_pts_of_contours[cContours - 1]) + 1;
+    if (cPts > cnPointsTotal)
+        return false;
+
+    // skip over bounding box data & point to byte count of instructions (hints)
+    const uint8 * pbGlyph = reinterpret_cast<const uint8 *>
+        (&pGlyph->end_pts_of_contours[cContours]);
+
+    // skip over hints & point to first flag
+    int cbHints = be::swap(*(uint16 *)pbGlyph);
+    pbGlyph += sizeof(uint16);
+    pbGlyph += cbHints;
+
+    // load flags & point to first x coordinate
+    int iFlag = 0;
+    while (iFlag < cPts)
+    {
+        if (!(*pbGlyph & SimpleGlyph::Repeat))
+        { // flag isn't repeated
+            prgbFlag[iFlag] = (char)*pbGlyph;
+            pbGlyph++;
+            iFlag++;
+        }
+        else
+        { // flag is repeated; count specified by next byte
+            char chFlag = (char)*pbGlyph;
+            pbGlyph++;
+            int cFlags = (int)*pbGlyph;
+            pbGlyph++;
+            prgbFlag[iFlag] = chFlag;
+            iFlag++;
+            for (int i = 0; i < cFlags; i++)
+            {
+                prgbFlag[iFlag + i] = chFlag;
+            }
+            iFlag += cFlags;
+        }
+    }
+    if (iFlag != cPts)
+        return false;
+
+    // load x coordinates
+    iFlag = 0;
+    while (iFlag < cPts)
+    {
+        if (prgbFlag[iFlag] & SimpleGlyph::XShort)
+        {
+            prgnX[iFlag] = *pbGlyph;
+            if (!(prgbFlag[iFlag] & SimpleGlyph::XIsPos))
+            {
+                prgnX[iFlag] = -prgnX[iFlag];
+            }
+            pbGlyph++;
+        }
+        else
+        {
+            if (prgbFlag[iFlag] & SimpleGlyph::XIsSame)
+            {
+                prgnX[iFlag] = 0;
+                // do NOT increment pbGlyph
+            }
+            else
+            {
+                prgnX[iFlag] = be::swap(*(int16 *)pbGlyph);
+                pbGlyph += sizeof(int16);
+            }
+        }
+        iFlag++;
+    }
+
+    // load y coordinates
+    iFlag = 0;
+    while (iFlag < cPts)
+    {
+        if (prgbFlag[iFlag] & SimpleGlyph::YShort)
+        {
+            prgnY[iFlag] = *pbGlyph;
+            if (!(prgbFlag[iFlag] & SimpleGlyph::YIsPos))
+            {
+                prgnY[iFlag] = -prgnY[iFlag];
+            }
+            pbGlyph++;
+        }
+        else
+        {
+            if (prgbFlag[iFlag] & SimpleGlyph::YIsSame)
+            {
+                prgnY[iFlag] = 0;
+                // do NOT increment pbGlyph
+            }
+            else
+            {
+                prgnY[iFlag] = be::swap(*(int16 *)pbGlyph);
+                pbGlyph += sizeof(int16);
+            }
+        }
+        iFlag++;
+    }
+
+    cnPoints = cPts;
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Fill prgnCompId with the component Glyph IDs from pSimpleGlyf.
+    Client must allocate space before calling.
+    pSimpleGlyf - assumed to point to a composite glyph
+    cCompIdTotal - the number of elements in prgnCompId
+    cCompId  - the total number of Glyph IDs stored in prgnCompId
+    Return true if successful, false otherwise
+        False could indicate a non-composite glyph or the input array was not big enough
+----------------------------------------------------------------------------------------------*/
+bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId,
+        size_t cnCompIdTotal, size_t & cnCompId)
+{
+    using namespace Sfnt;
+
+    if (GlyfContourCount(pSimpleGlyf) >= 0)
+        return false;
+
+    const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);
+    // for a composite glyph, the special data begins here
+    const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]);
+
+    uint16 GlyphFlags;
+    size_t iCurrentComp = 0;
+    do
+    {
+        GlyphFlags = be::swap(*((uint16 *)pbGlyph));
+        pbGlyph += sizeof(uint16);
+        prgnCompId[iCurrentComp++] = be::swap(*((uint16 *)pbGlyph));
+        pbGlyph += sizeof(uint16);
+        if (iCurrentComp >= cnCompIdTotal)
+            return false;
+        int nOffset = 0;
+        nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words ? 4 : 2;
+        nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0;
+        nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale  ? 4 : 0;
+        nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo  ? 8 :  0;
+        pbGlyph += nOffset;
+    } while (GlyphFlags & CompoundGlyph::MoreComponents);
+
+    cnCompId = iCurrentComp;
+
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return info on how a component glyph is to be placed
+    pSimpleGlyph - assumed to point to a composite glyph
+    nCompId - glyph id for component of interest
+    bOffset - if true, a & b are the x & y offsets for this component
+              if false, b is the point on this component that is attaching to point a on the
+                preceding glyph
+    Return true if successful, false otherwise
+        False could indicate a non-composite glyph or that component wasn't found
+----------------------------------------------------------------------------------------------*/
+bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId,
+                                    bool fOffset, int & a, int & b)
+{
+    using namespace Sfnt;
+
+    if (GlyfContourCount(pSimpleGlyf) >= 0)
+        return false;
+
+    const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);
+    // for a composite glyph, the special data begins here
+    const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]);
+
+    uint16 GlyphFlags;
+    do
+    {
+        GlyphFlags = be::swap(*((uint16 *)pbGlyph));
+        pbGlyph += sizeof(uint16);
+        if (be::swap(*((uint16 *)pbGlyph)) == nCompId)
+        {
+            pbGlyph += sizeof(uint16); // skip over glyph id of component
+            fOffset = (GlyphFlags & CompoundGlyph::ArgsAreXYValues) == CompoundGlyph::ArgsAreXYValues;
+
+            if (GlyphFlags & CompoundGlyph::Arg1Arg2Words )
+            {
+                a = be::swap(*(int16 *)pbGlyph);
+                pbGlyph += sizeof(int16);
+                b = be::swap(*(int16 *)pbGlyph);
+                pbGlyph += sizeof(int16);
+            }
+            else
+            { // args are signed bytes
+                a = *pbGlyph++;
+                b = *pbGlyph++;
+            }
+            return true;
+        }
+        pbGlyph += sizeof(uint16); // skip over glyph id of component
+        int nOffset = 0;
+        nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words  ? 4 : 2;
+        nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0;
+        nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale  ? 4 : 0;
+        nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo  ? 8 :  0;
+        pbGlyph += nOffset;
+    } while (GlyphFlags & CompoundGlyph::MoreComponents);
+
+    // didn't find requested component
+    fOffset = true;
+    a = 0;
+    b = 0;
+    return false;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Return info on how a component glyph is to be transformed
+    pSimpleGlyph - assumed to point to a composite glyph
+    nCompId - glyph id for component of interest
+    flt11, flt11, flt11, flt11 - a 2x2 matrix giving the transform
+    bTransOffset - whether to transform the offset from above method
+        The spec is unclear about the meaning of this flag
+        Currently - initialize to true for MS rasterizer and false for Mac rasterizer, then
+            on return it will indicate whether transform should apply to offset (MSDN CD 10/99)
+    Return true if successful, false otherwise
+        False could indicate a non-composite glyph or that component wasn't found
+----------------------------------------------------------------------------------------------*/
+bool GetComponentTransform(const void * pSimpleGlyf, int nCompId,
+                                    float & flt11, float & flt12, float & flt21, float & flt22,
+                                    bool & fTransOffset)
+{
+    using namespace Sfnt;
+
+    if (GlyfContourCount(pSimpleGlyf) >= 0)
+        return false;
+
+    const Sfnt::SimpleGlyph * pGlyph = reinterpret_cast<const Sfnt::SimpleGlyph *>(pSimpleGlyf);
+    // for a composite glyph, the special data begins here
+    const uint8 * pbGlyph = reinterpret_cast<const uint8 *>(&pGlyph->end_pts_of_contours[0]);
+
+    uint16 GlyphFlags;
+    do
+    {
+        GlyphFlags = be::swap(*((uint16 *)pbGlyph));
+        pbGlyph += sizeof(uint16);
+        if (be::swap(*((uint16 *)pbGlyph)) == nCompId)
+        {
+            pbGlyph += sizeof(uint16); // skip over glyph id of component
+            pbGlyph += GlyphFlags & CompoundGlyph::Arg1Arg2Words  ? 4 : 2; // skip over placement data
+
+            if (fTransOffset) // MS rasterizer
+                fTransOffset = !(GlyphFlags & CompoundGlyph::UnscaledOffset);
+            else // Apple rasterizer
+                fTransOffset = (GlyphFlags & CompoundGlyph::ScaledOffset) != 0;
+
+            if (GlyphFlags & CompoundGlyph::HaveScale)
+            {
+                flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));
+                pbGlyph += sizeof(uint16);
+                flt12 = 0;
+                flt21 = 0;
+                flt22 = flt11;
+            }
+            else if (GlyphFlags & CompoundGlyph::HaveXAndYScale)
+            {
+                flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));
+                pbGlyph += sizeof(uint16);
+                flt12 = 0;
+                flt21 = 0;
+                flt22 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));
+                pbGlyph += sizeof(uint16);
+            }
+            else if (GlyphFlags & CompoundGlyph::HaveTwoByTwo)
+            {
+                flt11 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));
+                pbGlyph += sizeof(uint16);
+                flt12 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));
+                pbGlyph += sizeof(uint16);
+                flt21 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));
+                pbGlyph += sizeof(uint16);
+                flt22 = fixed_to_float<14>(be::swap(*(uint16 *)pbGlyph));
+                pbGlyph += sizeof(uint16);
+            }
+            else
+            { // identity transform
+                flt11 = 1.0;
+                flt12 = 0.0;
+                flt21 = 0.0;
+                flt22 = 1.0;
+            }
+            return true;
+        }
+        pbGlyph += sizeof(uint16); // skip over glyph id of component
+        int nOffset = 0;
+        nOffset += GlyphFlags & CompoundGlyph::Arg1Arg2Words  ? 4 : 2;
+        nOffset += GlyphFlags & CompoundGlyph::HaveScale ? 2 : 0;
+        nOffset += GlyphFlags & CompoundGlyph::HaveXAndYScale  ? 4 : 0;
+        nOffset += GlyphFlags & CompoundGlyph::HaveTwoByTwo  ? 8 :  0;
+        pbGlyph += nOffset;
+    } while (GlyphFlags & CompoundGlyph::MoreComponents);
+
+    // didn't find requested component
+    fTransOffset = false;
+    flt11 = 1;
+    flt12 = 0;
+    flt21 = 0;
+    flt22 = 1;
+    return false;
+}
+#endif
+
+/*----------------------------------------------------------------------------------------------
+    Return a pointer into the glyf table based on the given tables and Glyph ID
+    Since this method doesn't check for spaces, it is good to call IsSpace before using it.
+    Return NULL on error.
+----------------------------------------------------------------------------------------------*/
+void * GlyfLookup(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+                           size_t lGlyfSize, size_t lLocaSize, const void * pHead)
+{
+    // test for valid glyph id
+    // CheckTable verifies the index_to_loc_format is valid
+
+    const Sfnt::FontHeader * pTable
+        = reinterpret_cast<const Sfnt::FontHeader *>(pHead);
+
+    if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::ShortIndexLocFormat)
+    { // loca entries are two bytes (and have been divided by two)
+        if (nGlyphId >= (lLocaSize >> 1) - 1) // don't allow nGlyphId to access sentinel
+        {
+//          throw std::out_of_range("glyph id out of range for font");
+            return NULL;
+        }
+    }
+    if (be::swap(pTable->index_to_loc_format) == Sfnt::FontHeader::LongIndexLocFormat)
+    { // loca entries are four bytes
+        if (nGlyphId >= (lLocaSize >> 2) - 1)
+        {
+//          throw std::out_of_range("glyph id out of range for font");
+            return NULL;
+        }
+    }
+
+    size_t lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead);
+    void * pSimpleGlyf = GlyfLookup(pGlyf, lGlyfOffset, lGlyfSize); // invalid loca offset returns null
+    return pSimpleGlyf;
+}
+
+#ifdef ALL_TTFUTILS
+/*----------------------------------------------------------------------------------------------
+    Determine if a particular Glyph ID has any data in the glyf table. If it is white space,
+    there will be no glyf data, though there will be metric data in hmtx, etc.
+----------------------------------------------------------------------------------------------*/
+bool IsSpace(gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead)
+{
+    size_t lGlyfOffset = LocaLookup(nGlyphId, pLoca, lLocaSize, pHead);
+
+    // the +1 should always work because there is a sentinel value at the end of the loca table
+    size_t lNextGlyfOffset = LocaLookup(nGlyphId + 1, pLoca, lLocaSize, pHead);
+
+    return (lNextGlyfOffset - lGlyfOffset) == 0;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Determine if a particular Glyph ID is a multi-level composite.
+----------------------------------------------------------------------------------------------*/
+bool IsDeepComposite(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+                    size_t lGlyfSize, long lLocaSize, const void * pHead)
+{
+    if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}
+
+    void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+    if (pSimpleGlyf == NULL)
+        return false; // no way to really indicate an error occured here
+
+    if (GlyfContourCount(pSimpleGlyf) >= 0)
+        return false;
+
+    int rgnCompId[kMaxGlyphComponents]; // assumes only a limited number of glyph components
+    size_t cCompIdTotal = kMaxGlyphComponents;
+    size_t cCompId = 0;
+
+    if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))
+        return false;
+
+    for (size_t i = 0; i < cCompId; i++)
+    {
+        pSimpleGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]),
+                            pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+        if (pSimpleGlyf == NULL) {return false;}
+
+        if (GlyfContourCount(pSimpleGlyf) < 0)
+            return true;
+    }
+
+    return false;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the bounding box coordinates based on the given tables and Glyph ID
+    Handles both simple and composite glyphs.
+    Return true if successful, false otherwise. On false, all point values will be INT_MIN
+        False may indicate a white space glyph
+----------------------------------------------------------------------------------------------*/
+bool GlyfBox(gid16  nGlyphId, const void * pGlyf, const void * pLoca,
+        size_t lGlyfSize, size_t lLocaSize, const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax)
+{
+    xMin = yMin = xMax = yMax = INT_MIN;
+
+    if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}
+
+    void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+    if (pSimpleGlyf == NULL) {return false;}
+
+    return GlyfBox(pSimpleGlyf, xMin, yMin, xMax, yMax);
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the number of contours based on the given tables and Glyph ID
+    Handles both simple and composite glyphs.
+    Return true if successful, false otherwise. On false, cnContours will be INT_MIN
+        False may indicate a white space glyph or a multi-level composite glyph.
+----------------------------------------------------------------------------------------------*/
+bool GlyfContourCount(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+    size_t lGlyfSize, size_t lLocaSize, const void * pHead, size_t & cnContours)
+{
+    cnContours = static_cast<size_t>(INT_MIN);
+
+    if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}
+
+    void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+    if (pSimpleGlyf == NULL) {return false;}
+
+    int cRtnContours = GlyfContourCount(pSimpleGlyf);
+    if (cRtnContours >= 0)
+    {
+        cnContours = size_t(cRtnContours);
+        return true;
+    }
+
+    //handle composite glyphs
+
+    int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components
+    size_t cCompIdTotal = kMaxGlyphComponents;
+    size_t cCompId = 0;
+
+    if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))
+        return false;
+
+    cRtnContours = 0;
+    int cTmp = 0;
+    for (size_t i = 0; i < cCompId; i++)
+    {
+        if (IsSpace(static_cast<gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;}
+        pSimpleGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]),
+                                 pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+        if (pSimpleGlyf == 0) {return false;}
+        // return false on multi-level composite
+        if ((cTmp = GlyfContourCount(pSimpleGlyf)) < 0)
+            return false;
+        cRtnContours += cTmp;
+    }
+
+    cnContours = size_t(cRtnContours);
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the point numbers for the end points of the glyph contours based on the given tables
+    and Glyph ID
+    Handles both simple and composite glyphs.
+    cnPoints - count of contours from GlyfContourCount (same as number of end points)
+    prgnContourEndPoints - should point to a buffer large enough to hold cnPoints integers
+    Return true if successful, false otherwise. On false, all end points are INT_MIN
+        False may indicate a white space glyph or a multi-level composite glyph.
+----------------------------------------------------------------------------------------------*/
+bool GlyfContourEndPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+    size_t lGlyfSize, size_t lLocaSize, const void * pHead,
+    int * prgnContourEndPoint, size_t cnPoints)
+{
+        memset(prgnContourEndPoint, 0xFF, cnPoints * sizeof(int));
+    // std::fill_n(prgnContourEndPoint, cnPoints, INT_MIN);
+
+    if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead)) {return false;}
+
+    void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+    if (pSimpleGlyf == NULL) {return false;}
+
+    int cContours = GlyfContourCount(pSimpleGlyf);
+    int cActualPts = 0;
+    if (cContours > 0)
+        return GlyfContourEndPoints(pSimpleGlyf, prgnContourEndPoint, cnPoints, cActualPts);
+
+    // handle composite glyphs
+
+    int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components
+    size_t cCompIdTotal = kMaxGlyphComponents;
+    size_t cCompId = 0;
+
+    if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))
+        return false;
+
+    int * prgnCurrentEndPoint = prgnContourEndPoint;
+    int cCurrentPoints = cnPoints;
+    int nPrevPt = 0;
+    for (size_t i = 0; i < cCompId; i++)
+    {
+        if (IsSpace(static_cast<gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;}
+        pSimpleGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]), pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+        if (pSimpleGlyf == NULL) {return false;}
+        // returns false on multi-level composite
+        if (!GlyfContourEndPoints(pSimpleGlyf, prgnCurrentEndPoint, cCurrentPoints, cActualPts))
+            return false;
+        // points in composite are numbered sequentially as components are added
+        //  must adjust end point numbers for new point numbers
+        for (int j = 0; j < cActualPts; j++)
+            prgnCurrentEndPoint[j] += nPrevPt;
+        nPrevPt = prgnCurrentEndPoint[cActualPts - 1] + 1;
+
+        prgnCurrentEndPoint += cActualPts;
+        cCurrentPoints -= cActualPts;
+    }
+
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Get the points for a glyph based on the given tables and Glyph ID
+    Handles both simple and composite glyphs.
+    cnPoints - count of points from largest end point obtained from GlyfContourEndPoints
+    prgnX & prgnY - should point to buffers large enough to hold cnPoints integers
+        The ranges are parallel so that coordinates for point(n) are found at offset n in
+        both ranges. These points are in absolute coordinates.
+    prgfOnCurve - should point to a buffer a large enough to hold cnPoints bytes (bool)
+        This range is parallel to the prgnX & prgnY
+    Return true if successful, false otherwise. On false, all points may be INT_MIN
+        False may indicate a white space glyph, a multi-level composite, or a corrupt font
+        It's not clear from the TTF spec when the transforms should be applied. Should the
+        transform be done before or after attachment point calcs? (current code - before)
+        Should the transform be applied to other offsets? (currently - no; however commented
+        out code is in place so that if CompoundGlyph::UnscaledOffset on the MS rasterizer is
+        clear (typical) then yes, and if CompoundGlyph::ScaledOffset on the Apple rasterizer is
+        clear (typical?) then no). See GetComponentTransform.
+        It's also unclear where point numbering with attachment poinst starts
+        (currently - first point number is relative to whole glyph, second point number is
+        relative to current glyph).
+----------------------------------------------------------------------------------------------*/
+bool GlyfPoints(gid16 nGlyphId, const void * pGlyf,
+        const void * pLoca, size_t lGlyfSize, size_t lLocaSize, const void * pHead,
+        const int * /*prgnContourEndPoint*/, size_t /*cnEndPoints*/,
+        int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints)
+{
+        memset(prgnX, 0x7F, cnPoints * sizeof(int));
+        memset(prgnY, 0x7F, cnPoints * sizeof(int));
+
+    if (IsSpace(nGlyphId, pLoca, lLocaSize, pHead))
+        return false;
+
+    void * pSimpleGlyf = GlyfLookup(nGlyphId, pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+    if (pSimpleGlyf == NULL)
+        return false;
+
+    int cContours = GlyfContourCount(pSimpleGlyf);
+    int cActualPts;
+    if (cContours > 0)
+    {
+        if (!GlyfPoints(pSimpleGlyf, prgnX, prgnY, (char *)prgfOnCurve, cnPoints, cActualPts))
+            return false;
+        CalcAbsolutePoints(prgnX, prgnY, cnPoints);
+        SimplifyFlags((char *)prgfOnCurve, cnPoints);
+        return true;
+    }
+
+    // handle composite glyphs
+    int rgnCompId[kMaxGlyphComponents]; // assumes no glyph will be made of more than 8 components
+    size_t cCompIdTotal = kMaxGlyphComponents;
+    size_t cCompId = 0;
+
+    // this will fail if there are more components than there is room for
+    if (!GetComponentGlyphIds(pSimpleGlyf, rgnCompId, cCompIdTotal, cCompId))
+        return false;
+
+    int * prgnCurrentX = prgnX;
+    int * prgnCurrentY = prgnY;
+    char * prgbCurrentFlag = (char *)prgfOnCurve; // converting bool to char should be safe
+    int cCurrentPoints = cnPoints;
+    bool fOffset = true, fTransOff = true;
+    int a, b;
+    float flt11, flt12, flt21, flt22;
+    // int * prgnPrevX = prgnX; // in case first att pt number relative to preceding glyph
+    // int * prgnPrevY = prgnY;
+    for (size_t i = 0; i < cCompId; i++)
+    {
+        if (IsSpace(static_cast<gid16>(rgnCompId[i]), pLoca, lLocaSize, pHead)) {return false;}
+        void * pCompGlyf = GlyfLookup(static_cast<gid16>(rgnCompId[i]), pGlyf, pLoca, lGlyfSize, lLocaSize, pHead);
+        if (pCompGlyf == NULL) {return false;}
+        // returns false on multi-level composite
+        if (!GlyfPoints(pCompGlyf, prgnCurrentX, prgnCurrentY, prgbCurrentFlag,
+            cCurrentPoints, cActualPts))
+            return false;
+        if (!GetComponentPlacement(pSimpleGlyf, rgnCompId[i], fOffset, a, b))
+            return false;
+        if (!GetComponentTransform(pSimpleGlyf, rgnCompId[i],
+            flt11, flt12, flt21, flt22, fTransOff))
+            return false;
+        bool fIdTrans = flt11 == 1.0 && flt12 == 0.0 && flt21 == 0.0 && flt22 == 1.0;
+
+        // convert points to absolute coordinates
+        // do before transform and attachment point placement are applied
+        CalcAbsolutePoints(prgnCurrentX, prgnCurrentY, cActualPts);
+
+        // apply transform - see main method note above
+        // do before attachment point calcs
+        if (!fIdTrans)
+            for (int j = 0; j < cActualPts; j++)
+            {
+                int x = prgnCurrentX[j]; // store before transform applied
+                int y = prgnCurrentY[j];
+                prgnCurrentX[j] = (int)(x * flt11 + y * flt12);
+                prgnCurrentY[j] = (int)(x * flt21 + y * flt22);
+            }
+
+        // apply placement - see main method note above
+        int nXOff, nYOff;
+        if (fOffset) // explicit x & y offsets
+        {
+            /* ignore fTransOff for now
+            if (fTransOff && !fIdTrans)
+            {   // transform x & y offsets
+                nXOff = (int)(a * flt11 + b * flt12);
+                nYOff = (int)(a * flt21 + b * flt22);
+            }
+            else */
+            { // don't transform offset
+                nXOff = a;
+                nYOff = b;
+            }
+        }
+        else  // attachment points
+        {   // in case first point is relative to preceding glyph and second relative to current
+            // nXOff = prgnPrevX[a] - prgnCurrentX[b];
+            // nYOff = prgnPrevY[a] - prgnCurrentY[b];
+            // first point number relative to whole composite, second relative to current glyph
+            nXOff = prgnX[a] - prgnCurrentX[b];
+            nYOff = prgnY[a] - prgnCurrentY[b];
+        }
+        for (int j = 0; j < cActualPts; j++)
+        {
+            prgnCurrentX[j] += nXOff;
+            prgnCurrentY[j] += nYOff;
+        }
+
+        // prgnPrevX = prgnCurrentX;
+        // prgnPrevY = prgnCurrentY;
+        prgnCurrentX += cActualPts;
+        prgnCurrentY += cActualPts;
+        prgbCurrentFlag += cActualPts;
+        cCurrentPoints -= cActualPts;
+    }
+
+    SimplifyFlags((char *)prgfOnCurve, cnPoints);
+
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Simplify the meaning of flags to just indicate whether point is on-curve or off-curve.
+---------------------------------------------------------------------------------------------*/
+bool SimplifyFlags(char * prgbFlags, int cnPoints)
+{
+    for (int i = 0; i < cnPoints; i++)
+        prgbFlags[i] = static_cast<char>(prgbFlags[i] & Sfnt::SimpleGlyph::OnCurve);
+    return true;
+}
+
+/*----------------------------------------------------------------------------------------------
+    Convert relative point coordinates to absolute coordinates
+    Points are stored in the font such that they are offsets from one another except for the
+        first point of a glyph.
+---------------------------------------------------------------------------------------------*/
+bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints)
+{
+    int nX = prgnX[0];
+    int nY = prgnY[0];
+    for (int i = 1; i < cnPoints; i++)
+    {
+        prgnX[i] += nX;
+        nX = prgnX[i];
+        prgnY[i] += nY;
+        nY = prgnY[i];
+    }
+
+    return true;
+}
+#endif
+
+/*----------------------------------------------------------------------------------------------
+    Return the length of the 'name' table in bytes.
+    Currently used.
+---------------------------------------------------------------------------------------------*/
+#if 0
+size_t NameTableLength(const byte * pTable)
+{
+    byte * pb = (const_cast<byte *>(pTable)) + 2; // skip format
+    size_t cRecords = *pb++ << 8; cRecords += *pb++;
+    int dbStringOffset0 = (*pb++) << 8; dbStringOffset0 += *pb++;
+    int dbMaxStringOffset = 0;
+    for (size_t irec = 0; irec < cRecords; irec++)
+    {
+        int nPlatform = (*pb++) << 8; nPlatform += *pb++;
+        int nEncoding = (*pb++) << 8; nEncoding += *pb++;
+        int nLanguage = (*pb++) << 8; nLanguage += *pb++;
+        int nName = (*pb++) << 8; nName += *pb++;
+        int cbStringLen = (*pb++) << 8; cbStringLen += *pb++;
+        int dbStringOffset = (*pb++) << 8; dbStringOffset += *pb++;
+        if (dbMaxStringOffset < dbStringOffset + cbStringLen)
+            dbMaxStringOffset = dbStringOffset + cbStringLen;
+    }
+    return dbStringOffset0 + dbMaxStringOffset;
+}
+#endif
+
+} // end of namespace TtfUtil
+} // end of namespace graphite

+ 45 - 0
thirdparty/graphite/src/UtfCodec.cpp

@@ -0,0 +1,45 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "inc/UtfCodec.h"
+//using namespace graphite2;
+
+namespace graphite2 {
+
+}
+
+using namespace graphite2;
+
+const int8 _utf_codec<8>::sz_lut[16] =
+{
+        1,1,1,1,1,1,1,1,    // 1 byte
+        0,0,0,0,            // trailing byte
+        2,2,                // 2 bytes
+        3,                  // 3 bytes
+        4                   // 4 bytes
+};
+
+const byte  _utf_codec<8>::mask_lut[5] = {0x7f, 0xff, 0x3f, 0x1f, 0x0f};

+ 138 - 0
thirdparty/graphite/src/call_machine.cpp

@@ -0,0 +1,138 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// This call threaded interpreter implmentation for machine.h
+// Author: Tim Eves
+
+// Build either this interpreter or the direct_machine implementation.
+// The call threaded interpreter is portable across compilers and
+// architectures as well as being useful to debug (you can set breakpoints on
+// opcodes) but is slower that the direct threaded interpreter by a factor of 2
+
+#include <cassert>
+#include <cstring>
+#include <graphite2/Segment.h>
+#include "inc/Machine.h"
+#include "inc/Segment.h"
+#include "inc/Slot.h"
+#include "inc/Rule.h"
+
+// Disable the unused parameter warning as th compiler is mistaken since dp
+// is always updated (even if by 0) on every opcode.
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+
+#define registers           const byte * & dp, vm::Machine::stack_t * & sp, \
+                            vm::Machine::stack_t * const sb, regbank & reg
+
+// These are required by opcodes.h and should not be changed
+#define STARTOP(name)       bool name(registers) REGPARM(4);\
+                            bool name(registers) {
+#define ENDOP                   return (sp - sb)/Machine::STACK_MAX==0; \
+                            }
+
+#define EXIT(status)        { push(status); return false; }
+
+// This is required by opcode_table.h
+#define do_(name)           instr(name)
+
+
+using namespace graphite2;
+using namespace vm;
+
+struct regbank  {
+    slotref         is;
+    slotref *       map;
+    SlotMap       & smap;
+    slotref * const map_base;
+    const instr * & ip;
+    uint8           direction;
+    int8            flags;
+    Machine::status_t & status;
+};
+
+typedef bool        (* ip_t)(registers);
+
+// Pull in the opcode definitions
+// We pull these into a private namespace so these otherwise common names dont
+// pollute the toplevel namespace.
+namespace {
+#define smap    reg.smap
+#define seg     smap.segment
+#define is      reg.is
+#define ip      reg.ip
+#define map     reg.map
+#define mapb    reg.map_base
+#define flags   reg.flags
+#define dir     reg.direction
+#define status  reg.status
+
+#include "inc/opcodes.h"
+
+#undef smap
+#undef seg
+#undef is
+#undef ip
+#undef map
+#undef mapb
+#undef flags
+#undef dir
+}
+
+Machine::stack_t  Machine::run(const instr   * program,
+                               const byte    * data,
+                               slotref     * & map)
+
+{
+    assert(program != 0);
+
+    // Declare virtual machine registers
+    const instr   * ip = program-1;
+    const byte    * dp = data;
+    stack_t       * sp = _stack + Machine::STACK_GUARD,
+            * const sb = sp;
+    regbank         reg = {*map, map, _map, _map.begin()+_map.context(), ip, _map.dir(), 0, _status};
+
+    // Run the program
+    while ((reinterpret_cast<ip_t>(*++ip))(dp, sp, sb, reg)) {}
+    const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0;
+
+    check_final_stack(sp);
+    map = reg.map;
+    *map = reg.is;
+    return ret;
+}
+
+// Pull in the opcode table
+namespace {
+#include "inc/opcode_table.h"
+}
+
+const opcode_t * Machine::getOpcodeTable() throw()
+{
+    return opcode_table;
+}

+ 140 - 0
thirdparty/graphite/src/direct_machine.cpp

@@ -0,0 +1,140 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// This direct threaded interpreter implmentation for machine.h
+// Author: Tim Eves
+
+// Build either this interpreter or the call_machine implementation.
+// The direct threaded interpreter is relies upon a gcc feature called
+// labels-as-values so is only portable to compilers that support the
+// extension (gcc only as far as I know) however it should build on any
+// architecture gcc supports.
+// This is twice as fast as the call threaded model and is likely faster on
+// inorder processors with short pipelines and little branch prediction such
+// as the ARM and possibly Atom chips.
+
+
+#include <cassert>
+#include <cstring>
+#include "inc/Machine.h"
+#include "inc/Segment.h"
+#include "inc/Slot.h"
+#include "inc/Rule.h"
+
+#define STARTOP(name)           name: {
+#define ENDOP                   }; goto *((sp - sb)/Machine::STACK_MAX ? &&end : *++ip);
+#define EXIT(status)            { push(status); goto end; }
+
+#define do_(name)               &&name
+
+
+using namespace graphite2;
+using namespace vm;
+
+namespace {
+
+// The GCC manual has this to say about labels as values:
+//   The &&foo expressions for the same label might have different values
+//   if the containing function is inlined or cloned. If a program relies
+//   on them being always the same, __attribute__((__noinline__,__noclone__))
+//   should be used to prevent inlining and cloning.
+//
+// is_return in Code.cpp relies on being able to do comparisons, so it needs
+// them to be always the same.
+//
+// The GCC manual further adds:
+//   If &&foo is used in a static variable initializer, inlining and
+//   cloning is forbidden.
+//
+// In this file, &&foo *is* used in a static variable initializer, and it's not
+// entirely clear whether this should prevent inlining of the function or not.
+// In practice, though, clang 7 can end up inlining the function with ThinLTO,
+// which breaks at least is_return. https://bugs.llvm.org/show_bug.cgi?id=39241
+// So all in all, we need at least the __noinline__ attribute. __noclone__
+// is not supported by clang.
+__attribute__((__noinline__))
+const void * direct_run(const bool          get_table_mode,
+                        const instr       * program,
+                        const byte        * data,
+                        Machine::stack_t  * stack,
+                        slotref         * & __map,
+                        uint8                _dir,
+                        Machine::status_t & status,
+                        SlotMap           * __smap=0)
+{
+    // We need to define and return to opcode table from within this function
+    // other inorder to take the addresses of the instruction bodies.
+    #include "inc/opcode_table.h"
+    if (get_table_mode)
+        return opcode_table;
+
+    // Declare virtual machine registers
+    const instr           * ip = program;
+    const byte            * dp = data;
+    Machine::stack_t      * sp = stack + Machine::STACK_GUARD,
+                    * const sb = sp;
+    SlotMap             & smap = *__smap;
+    Segment              & seg = smap.segment;
+    slotref                 is = *__map,
+                         * map = __map,
+                  * const mapb = smap.begin()+smap.context();
+    uint8                  dir = _dir;
+    int8                 flags = 0;
+
+    // start the program
+    goto **ip;
+
+    // Pull in the opcode definitions
+    #include "inc/opcodes.h"
+
+    end:
+    __map  = map;
+    *__map = is;
+    return sp;
+}
+
+}
+
+const opcode_t * Machine::getOpcodeTable() throw()
+{
+    slotref * dummy;
+    Machine::status_t dumstat = Machine::finished;
+    return static_cast<const opcode_t *>(direct_run(true, 0, 0, 0, dummy, 0, dumstat));
+}
+
+
+Machine::stack_t  Machine::run(const instr   * program,
+                               const byte    * data,
+                               slotref     * & is)
+{
+    assert(program != 0);
+
+    const stack_t *sp = static_cast<const stack_t *>(
+                direct_run(false, program, data, _stack, is, _map.dir(), _status, &_map));
+    const stack_t ret = sp == _stack+STACK_GUARD+1 ? *sp-- : 0;
+    check_final_stack(sp);
+    return ret;
+}

+ 65 - 0
thirdparty/graphite/src/gr_char_info.cpp

@@ -0,0 +1,65 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cassert>
+#include "graphite2/Segment.h"
+#include "inc/CharInfo.h"
+
+extern "C"
+{
+
+unsigned int gr_cinfo_unicode_char(const gr_char_info* p/*not NULL*/)
+{
+    assert(p);
+    return p->unicodeChar();
+}
+
+
+int gr_cinfo_break_weight(const gr_char_info* p/*not NULL*/)
+{
+    assert(p);
+    return p->breakWeight();
+}
+
+int gr_cinfo_after(const gr_char_info *p/*not NULL*/)
+{
+    assert(p);
+    return p->after();
+}
+
+int gr_cinfo_before(const gr_char_info *p/*not NULL*/)
+{
+    assert(p);
+    return p->before();
+}
+
+size_t gr_cinfo_base(const gr_char_info *p/*not NULL*/)
+{
+    assert(p);
+    return p->base();
+}
+
+} // extern "C"

+ 267 - 0
thirdparty/graphite/src/gr_face.cpp

@@ -0,0 +1,267 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "graphite2/Font.h"
+#include "inc/Face.h"
+#include "inc/FileFace.h"
+#include "inc/GlyphCache.h"
+#include "inc/CmapCache.h"
+#include "inc/Silf.h"
+#include "inc/json.h"
+
+using namespace graphite2;
+
+#if !defined GRAPHITE2_NTRACING
+extern json *global_log;
+#endif
+
+namespace
+{
+    bool load_face(Face & face, unsigned int options)
+    {
+#ifdef GRAPHITE2_TELEMETRY
+        telemetry::category _misc_cat(face.tele.misc);
+#endif
+        Face::Table silf(face, Tag::Silf, 0x00050000);
+        if (!silf)
+            return false;
+
+        if (!face.readGlyphs(options))
+            return false;
+
+        if (silf)
+        {
+            if (!face.readFeatures() || !face.readGraphite(silf))
+            {
+#if !defined GRAPHITE2_NTRACING
+                if (global_log)
+                {
+                    *global_log << json::object
+                        << "type" << "fontload"
+                        << "failure" << face.error()
+                        << "context" << face.error_context()
+                    << json::close;
+                }
+#endif
+                return false;
+            }
+            else
+                return true;
+        }
+        else
+            return false;
+    }
+
+    inline
+    uint32 zeropad(const uint32 x)
+    {
+        if (x == 0x20202020)                    return 0;
+        if ((x & 0x00FFFFFF) == 0x00202020)     return x & 0xFF000000;
+        if ((x & 0x0000FFFF) == 0x00002020)     return x & 0xFFFF0000;
+        if ((x & 0x000000FF) == 0x00000020)     return x & 0xFFFFFF00;
+        return x;
+    }
+}
+
+extern "C" {
+
+gr_face* gr_make_face_with_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *ops, unsigned int faceOptions)
+                  //the appFaceHandle must stay alive all the time when the gr_face is alive. When finished with the gr_face, call destroy_face
+{
+    if (ops == 0)   return 0;
+
+    Face *res = new Face(appFaceHandle, *ops);
+    if (res && load_face(*res, faceOptions))
+        return static_cast<gr_face *>(res);
+
+    delete res;
+    return 0;
+}
+
+gr_face* gr_make_face(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn tablefn, unsigned int faceOptions)
+{
+    const gr_face_ops ops = {sizeof(gr_face_ops), tablefn, NULL};
+    return gr_make_face_with_ops(appFaceHandle, &ops, faceOptions);
+}
+
+
+gr_face* gr_make_face_with_seg_cache_and_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *ops, unsigned int , unsigned int faceOptions)
+{
+  return gr_make_face_with_ops(appFaceHandle, ops, faceOptions);
+}
+
+gr_face* gr_make_face_with_seg_cache(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn tablefn, unsigned int, unsigned int faceOptions)
+{
+  const gr_face_ops ops = {sizeof(gr_face_ops), tablefn, NULL};
+  return gr_make_face_with_ops(appFaceHandle, &ops, faceOptions);
+}
+
+gr_uint32 gr_str_to_tag(const char *str)
+{
+    uint32 res = 0;
+    switch(max(strlen(str),size_t(4)))
+    {
+        case 4: res |= str[3];       GR_FALLTHROUGH;
+        case 3: res |= str[2] << 8;  GR_FALLTHROUGH;
+        case 2: res |= str[1] << 16; GR_FALLTHROUGH;
+        case 1: res |= str[0] << 24; GR_FALLTHROUGH;
+        default:  break;
+    }
+    return res;
+}
+
+void gr_tag_to_str(gr_uint32 tag, char *str)
+{
+    if (!str) return;
+
+    *str++ = char(tag >> 24);
+    *str++ = char(tag >> 16);
+    *str++ = char(tag >> 8);
+    *str++ = char(tag);
+    *str = '\0';
+}
+
+gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, gr_uint32 langname/*0 means clone default*/) //clones the features. if none for language, clones the default
+{
+    assert(pFace);
+    langname = zeropad(langname);
+    return static_cast<gr_feature_val *>(pFace->theSill().cloneFeatures(langname));
+}
+
+
+const gr_feature_ref* gr_face_find_fref(const gr_face* pFace, gr_uint32 featId)  //When finished with the FeatureRef, call destroy_FeatureRef
+{
+    assert(pFace);
+    featId = zeropad(featId);
+    const FeatureRef* pRef = pFace->featureById(featId);
+    return static_cast<const gr_feature_ref*>(pRef);
+}
+
+unsigned short gr_face_n_fref(const gr_face* pFace)
+{
+    assert(pFace);
+    int res = 0;
+    for (int i = 0; i < pFace->numFeatures(); ++i)
+        if (!(pFace->feature(i)->getFlags() & FeatureRef::HIDDEN))
+            ++res;
+    return res;
+}
+
+const gr_feature_ref* gr_face_fref(const gr_face* pFace, gr_uint16 i) //When finished with the FeatureRef, call destroy_FeatureRef
+{
+    assert(pFace);
+    int count = 0;
+    for (int j = 0; j < pFace->numFeatures(); ++j)
+    {
+        const FeatureRef* pRef = pFace->feature(j);
+        if (!(pRef->getFlags() & FeatureRef::HIDDEN))
+            if (count++ == i)
+                return static_cast<const gr_feature_ref*>(pRef);
+    }
+    return 0;
+}
+
+unsigned short gr_face_n_languages(const gr_face* pFace)
+{
+    assert(pFace);
+    return pFace->theSill().numLanguages();
+}
+
+gr_uint32 gr_face_lang_by_index(const gr_face* pFace, gr_uint16 i)
+{
+    assert(pFace);
+    return pFace->theSill().getLangName(i);
+}
+
+
+void gr_face_destroy(gr_face *face)
+{
+    delete static_cast<Face*>(face);
+}
+
+
+gr_uint16 gr_face_name_lang_for_locale(gr_face *face, const char * locale)
+{
+    if (face)
+    {
+        return face->languageForLocale(locale);
+    }
+    return 0;
+}
+
+unsigned short gr_face_n_glyphs(const gr_face* pFace)
+{
+    return pFace->glyphs().numGlyphs();
+}
+
+const gr_faceinfo *gr_face_info(const gr_face *pFace, gr_uint32 script)
+{
+    if (!pFace) return 0;
+    const Silf *silf = pFace->chooseSilf(script);
+    if (silf) return silf->silfInfo();
+    return 0;
+}
+
+int gr_face_is_char_supported(const gr_face* pFace, gr_uint32 usv, gr_uint32 script)
+{
+    const Cmap & cmap = pFace->cmap();
+    gr_uint16 gid = cmap[usv];
+    if (!gid)
+    {
+        const Silf * silf = pFace->chooseSilf(script);
+        gid = silf->findPseudo(usv);
+    }
+    return (gid != 0);
+}
+
+#ifndef GRAPHITE2_NFILEFACE
+gr_face* gr_make_file_face(const char *filename, unsigned int faceOptions)
+{
+    FileFace* pFileFace = new FileFace(filename);
+    if (*pFileFace)
+    {
+      gr_face* pRes = gr_make_face_with_ops(pFileFace, &FileFace::ops, faceOptions);
+      if (pRes)
+      {
+        pRes->takeFileFace(pFileFace);        //takes ownership
+        return pRes;
+      }
+    }
+
+    //error when loading
+
+    delete pFileFace;
+    return NULL;
+}
+
+gr_face* gr_make_file_face_with_seg_cache(const char* filename, unsigned int, unsigned int faceOptions)   //returns NULL on failure. //TBD better error handling
+                  //when finished with, call destroy_face
+{
+    return gr_make_file_face(filename, faceOptions);
+}
+#endif      //!GRAPHITE2_NFILEFACE
+
+} // extern "C"

+ 138 - 0
thirdparty/graphite/src/gr_features.cpp

@@ -0,0 +1,138 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "graphite2/Font.h"
+#include "inc/Face.h"
+#include "inc/FeatureMap.h"
+#include "inc/FeatureVal.h"
+#include "inc/NameTable.h"
+
+using namespace graphite2;
+
+extern "C" {
+
+
+gr_uint16 gr_fref_feature_value(const gr_feature_ref* pfeatureref, const gr_feature_val* feats)    //returns 0 if either pointer is NULL
+{
+    if (!pfeatureref || !feats) return 0;
+
+    return pfeatureref->getFeatureVal(*feats);
+}
+
+
+int gr_fref_set_feature_value(const gr_feature_ref* pfeatureref, gr_uint16 val, gr_feature_val* pDest)
+{
+    if (!pfeatureref || !pDest) return 0;
+
+    return pfeatureref->applyValToFeature(val, *pDest);
+}
+
+
+gr_uint32 gr_fref_id(const gr_feature_ref* pfeatureref)    //returns 0 if pointer is NULL
+{
+  if (!pfeatureref)
+    return 0;
+
+  return pfeatureref->getId();
+}
+
+
+gr_uint16 gr_fref_n_values(const gr_feature_ref* pfeatureref)
+{
+    if(!pfeatureref)
+        return 0;
+    return pfeatureref->getNumSettings();
+}
+
+
+gr_int16 gr_fref_value(const gr_feature_ref* pfeatureref, gr_uint16 settingno)
+{
+    if(!pfeatureref || (settingno >= pfeatureref->getNumSettings()))
+    {
+        return 0;
+    }
+    return pfeatureref->getSettingValue(settingno);
+}
+
+
+void* gr_fref_label(const gr_feature_ref* pfeatureref, gr_uint16 *langId, gr_encform utf, gr_uint32 *length)
+{
+    if(!pfeatureref)
+    {
+        langId = 0;
+        length = 0;
+        return NULL;
+    }
+    uint16 label = pfeatureref->getNameId();
+    NameTable * names = pfeatureref->getFace().nameTable();
+    if (!names)
+    {
+        langId = 0;
+        length = 0;
+        return NULL;
+    }
+    return names->getName(*langId, label, utf, *length);
+}
+
+
+void* gr_fref_value_label(const gr_feature_ref*pfeatureref, gr_uint16 setting,
+    gr_uint16 *langId, gr_encform utf, gr_uint32 *length)
+{
+    if(!pfeatureref || (setting >= pfeatureref->getNumSettings()))
+    {
+        langId = 0;
+        length = 0;
+        return NULL;
+    }
+    uint16 label = pfeatureref->getSettingName(setting);
+    NameTable * names = pfeatureref->getFace().nameTable();
+    if (!names)
+    {
+        langId = 0;
+        length = 0;
+        return NULL;
+    }
+    return names->getName(*langId, label, utf, *length);
+}
+
+
+void gr_label_destroy(void * label)
+{
+    free(label);
+}
+
+gr_feature_val* gr_featureval_clone(const gr_feature_val* pfeatures/*may be NULL*/)
+{                      //When finished with the Features, call features_destroy
+    return static_cast<gr_feature_val*>(pfeatures ? new Features(*pfeatures) : new Features);
+}
+
+void gr_featureval_destroy(gr_feature_val *p)
+{
+    delete static_cast<Features*>(p);
+}
+
+
+} // extern "C"

+ 74 - 0
thirdparty/graphite/src/gr_font.cpp

@@ -0,0 +1,74 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "graphite2/Font.h"
+#include "inc/Font.h"
+
+
+using namespace graphite2;
+
+extern "C" {
+
+void gr_engine_version(int *nMajor, int *nMinor, int *nBugFix)
+{
+    if (nMajor) *nMajor = GR2_VERSION_MAJOR;
+    if (nMinor) *nMinor = GR2_VERSION_MINOR;
+    if (nBugFix) *nBugFix = GR2_VERSION_BUGFIX;
+}
+
+gr_font* gr_make_font(float ppm/*pixels per em*/, const gr_face *face)
+{
+    return gr_make_font_with_advance_fn(ppm, 0, 0, face);
+}
+
+
+gr_font* gr_make_font_with_ops(float ppm/*pixels per em*/, const void* appFontHandle/*non-NULL*/, const gr_font_ops * font_ops, const gr_face * face/*needed for scaling*/)
+{                 //the appFontHandle must stay alive all the time when the gr_font is alive. When finished with the gr_font, call destroy_gr_font
+    if (face == 0 || ppm <= 0)  return 0;
+
+    Font * const res = new Font(ppm, *face, appFontHandle, font_ops);
+    if (*res)
+        return static_cast<gr_font*>(res);
+    else
+    {
+        delete res;
+        return 0;
+    }
+}
+
+gr_font* gr_make_font_with_advance_fn(float ppm/*pixels per em*/, const void* appFontHandle/*non-NULL*/, gr_advance_fn getAdvance, const gr_face * face/*needed for scaling*/)
+{
+    const gr_font_ops ops = {sizeof(gr_font_ops), getAdvance, NULL};
+    return gr_make_font_with_ops(ppm, appFontHandle, &ops, face);
+}
+
+void gr_font_destroy(gr_font *font)
+{
+    delete static_cast<Font*>(font);
+}
+
+
+} // extern "C"

+ 267 - 0
thirdparty/graphite/src/gr_logging.cpp

@@ -0,0 +1,267 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include <cstdio>
+
+#include "graphite2/Log.h"
+#include "inc/debug.h"
+#include "inc/CharInfo.h"
+#include "inc/Slot.h"
+#include "inc/Segment.h"
+#include "inc/json.h"
+#include "inc/Collider.h"
+
+#if defined _WIN32
+#include "windows.h"
+#endif
+
+using namespace graphite2;
+
+#if !defined GRAPHITE2_NTRACING
+json *global_log = 0;
+#endif
+
+extern "C" {
+
+bool gr_start_logging(GR_MAYBE_UNUSED gr_face * face, const char *log_path)
+{
+    if (!log_path)  return false;
+
+#if !defined GRAPHITE2_NTRACING
+    gr_stop_logging(face);
+#if defined _WIN32
+    int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, 0, 0);
+    if (n == 0 || n > MAX_PATH - 12) return false;
+
+    LPWSTR wlog_path = gralloc<WCHAR>(n);
+    if (!wlog_path) return false;
+    FILE *log = 0;
+    if (wlog_path && MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, wlog_path, n))
+        log = _wfopen(wlog_path, L"wt");
+
+    free(wlog_path);
+#else   // _WIN32
+    FILE *log = fopen(log_path, "wt");
+#endif  // _WIN32
+    if (!log)   return false;
+
+    if (face)
+    {
+        face->setLogger(log);
+        if (!face->logger()) return false;
+
+        *face->logger() << json::array;
+#ifdef GRAPHITE2_TELEMETRY
+        *face->logger() << face->tele;
+#endif
+    }
+    else
+    {
+        global_log = new json(log);
+        *global_log << json::array;
+    }
+
+    return true;
+#else   // GRAPHITE2_NTRACING
+    return false;
+#endif  // GRAPHITE2_NTRACING
+}
+
+bool graphite_start_logging(FILE * /* log */, GrLogMask /* mask */)
+{
+//#if !defined GRAPHITE2_NTRACING
+//  graphite_stop_logging();
+//
+//    if (!log) return false;
+//
+//    dbgout = new json(log);
+//    if (!dbgout) return false;
+//
+//    *dbgout << json::array;
+//    return true;
+//#else
+    return false;
+//#endif
+}
+
+void gr_stop_logging(GR_MAYBE_UNUSED gr_face * face)
+{
+#if !defined GRAPHITE2_NTRACING
+    if (face && face->logger())
+    {
+        FILE * log = face->logger()->stream();
+        face->setLogger(0);
+        fclose(log);
+    }
+    else if (!face && global_log)
+    {
+        FILE * log = global_log->stream();
+        delete global_log;
+        global_log = 0;
+        fclose(log);
+    }
+#endif
+}
+
+void graphite_stop_logging()
+{
+//    if (dbgout) delete dbgout;
+//    dbgout = 0;
+}
+
+} // extern "C"
+
+#ifdef GRAPHITE2_TELEMETRY
+size_t   * graphite2::telemetry::_category = 0UL;
+#endif
+
+#if !defined GRAPHITE2_NTRACING
+
+#ifdef GRAPHITE2_TELEMETRY
+
+json & graphite2::operator << (json & j, const telemetry & t) throw()
+{
+    j << json::object
+            << "type"   << "telemetry"
+            << "silf"   << t.silf
+            << "states" << t.states
+            << "starts" << t.starts
+            << "transitions" << t.transitions
+            << "glyphs" << t.glyph
+            << "code"   << t.code
+            << "misc"   << t.misc
+            << "total"  << (t.silf + t.states + t.starts + t.transitions + t.glyph + t.code + t.misc)
+        << json::close;
+    return j;
+}
+#else
+json & graphite2::operator << (json & j, const telemetry &) throw()
+{
+    return j;
+}
+#endif
+
+
+json & graphite2::operator << (json & j, const CharInfo & ci) throw()
+{
+    return j << json::object
+                << "offset"         << ci.base()
+                << "unicode"        << ci.unicodeChar()
+                << "break"          << ci.breakWeight()
+                << "flags"          << ci.flags()
+                << "slot" << json::flat << json::object
+                    << "before" << ci.before()
+                    << "after"  << ci.after()
+                    << json::close
+                << json::close;
+}
+
+
+json & graphite2::operator << (json & j, const dslot & ds) throw()
+{
+    assert(ds.first);
+    assert(ds.second);
+    const Segment & seg = *ds.first;
+    const Slot & s = *ds.second;
+    const SlotCollision *cslot = seg.collisionInfo(ds.second);
+
+    j << json::object
+        << "id"             << objectid(ds)
+        << "gid"            << s.gid()
+        << "charinfo" << json::flat << json::object
+            << "original"       << s.original()
+            << "before"         << s.before()
+            << "after"          << s.after()
+            << json::close
+        << "origin"         << s.origin()
+        << "shift"          << Position(float(s.getAttr(0, gr_slatShiftX, 0)),
+                                        float(s.getAttr(0, gr_slatShiftY, 0)))
+        << "advance"        << s.advancePos()
+        << "insert"         << s.isInsertBefore()
+        << "break"          << s.getAttr(&seg, gr_slatBreak, 0);
+    if (s.just() > 0)
+        j << "justification"    << s.just();
+    if (s.getBidiLevel() > 0)
+        j << "bidi"     << s.getBidiLevel();
+    if (!s.isBase())
+        j << "parent" << json::flat << json::object
+            << "id"             << objectid(dslot(&seg, s.attachedTo()))
+            << "level"          << s.getAttr(0, gr_slatAttLevel, 0)
+            << "offset"         << s.attachOffset()
+            << json::close;
+    j << "user" << json::flat << json::array;
+    for (int n = 0; n!= seg.numAttrs(); ++n)
+        j   << s.userAttrs()[n];
+    j       << json::close;
+    if (s.firstChild())
+    {
+        j   << "children" << json::flat << json::array;
+        for (const Slot *c = s.firstChild(); c; c = c->nextSibling())
+            j   << objectid(dslot(&seg, c));
+        j       << json::close;
+    }
+    if (cslot)
+    {
+		// Note: the reason for using Positions to lump together related attributes is to make the
+		// JSON output slightly more compact.
+        j << "collision" << json::flat << json::object
+//              << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself
+              << "offset" << cslot->offset()
+              << "limit" << cslot->limit()
+              << "flags" << cslot->flags()
+              << "margin" << Position(cslot->margin(), cslot->marginWt())
+              << "exclude" << cslot->exclGlyph()
+              << "excludeoffset" << cslot->exclOffset();
+		if (cslot->seqOrder() != 0)
+		{
+			j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass())
+				<< "seqorder" << cslot->seqOrder()
+				<< "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt())
+				<< "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt())
+				<< "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt());
+		}
+        j << json::close;
+    }
+    return j << json::close;
+}
+
+
+graphite2::objectid::objectid(const dslot & ds) throw()
+{
+    const Slot * const p = ds.second;
+    uint32 s = uint32(reinterpret_cast<size_t>(p));
+    sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), uint16(p ? p->userAttrs()[ds.first->silf()->numUser()] : 0), uint16(s));
+    name[sizeof name-1] = 0;
+}
+
+graphite2::objectid::objectid(const Segment * const p) throw()
+{
+    uint32 s = uint32(reinterpret_cast<size_t>(p));
+    sprintf(name, "%.4x-%.2x-%.4hx", uint16(s >> 16), 0, uint16(s));
+    name[sizeof name-1] = 0;
+}
+
+#endif

+ 175 - 0
thirdparty/graphite/src/gr_segment.cpp

@@ -0,0 +1,175 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "graphite2/Segment.h"
+#include "inc/UtfCodec.h"
+#include "inc/Segment.h"
+
+using namespace graphite2;
+
+namespace
+{
+
+  gr_segment* makeAndInitialize(const Font *font, const Face *face, uint32 script, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars, int dir)
+  {
+      if (script == 0x20202020) script = 0;
+      else if ((script & 0x00FFFFFF) == 0x00202020) script = script & 0xFF000000;
+      else if ((script & 0x0000FFFF) == 0x00002020) script = script & 0xFFFF0000;
+      else if ((script & 0x000000FF) == 0x00000020) script = script & 0xFFFFFF00;
+      // if (!font) return NULL;
+      Segment* pRes=new Segment(nChars, face, script, dir);
+
+
+      if (!pRes->read_text(face, pFeats, enc, pStart, nChars) || !pRes->runGraphite())
+      {
+        delete pRes;
+        return NULL;
+      }
+      pRes->finalise(font, true);
+
+      return static_cast<gr_segment*>(pRes);
+  }
+
+  template <typename utf_iter>
+  inline size_t count_unicode_chars(utf_iter first, const utf_iter last, const void **error)
+  {
+      size_t n_chars = 0;
+      uint32 usv = 0;
+
+      if (last)
+      {
+          if (!first.validate(last))
+          {
+              if (error)  *error = last - 1;
+              return 0;
+          }
+          for (;first != last; ++first, ++n_chars)
+              if ((usv = *first) == 0 || first.error()) break;
+      }
+      else
+      {
+          while ((usv = *first) != 0 && !first.error())
+          {
+              ++first;
+              ++n_chars;
+          }
+      }
+
+      if (error)  *error = first.error() ? first : 0;
+      return n_chars;
+  }
+}
+
+
+extern "C" {
+
+size_t gr_count_unicode_characters(gr_encform enc, const void* buffer_begin, const void* buffer_end/*don't go on or past end, If NULL then ignored*/, const void** pError)   //Also stops on nul. Any nul is not in the count
+{
+    assert(buffer_begin);
+
+    switch (enc)
+    {
+    case gr_utf8:   return count_unicode_chars<utf8::const_iterator>(buffer_begin, buffer_end, pError); break;
+    case gr_utf16:  return count_unicode_chars<utf16::const_iterator>(buffer_begin, buffer_end, pError); break;
+    case gr_utf32:  return count_unicode_chars<utf32::const_iterator>(buffer_begin, buffer_end, pError); break;
+    default:        return 0;
+    }
+}
+
+
+gr_segment* gr_make_seg(const gr_font *font, const gr_face *face, gr_uint32 script, const gr_feature_val* pFeats, gr_encform enc, const void* pStart, size_t nChars, int dir)
+{
+    if (!face) return nullptr;
+
+    const gr_feature_val * tmp_feats = 0;
+    if (pFeats == 0)
+        pFeats = tmp_feats = static_cast<const gr_feature_val*>(face->theSill().cloneFeatures(0));
+    gr_segment * seg = makeAndInitialize(font, face, script, pFeats, enc, pStart, nChars, dir);
+    delete static_cast<const FeatureVal*>(tmp_feats);
+
+    return seg;
+}
+
+
+void gr_seg_destroy(gr_segment* p)
+{
+    delete static_cast<Segment*>(p);
+}
+
+
+float gr_seg_advance_X(const gr_segment* pSeg/*not NULL*/)
+{
+    assert(pSeg);
+    return pSeg->advance().x;
+}
+
+
+float gr_seg_advance_Y(const gr_segment* pSeg/*not NULL*/)
+{
+    assert(pSeg);
+    return pSeg->advance().y;
+}
+
+
+unsigned int gr_seg_n_cinfo(const gr_segment* pSeg/*not NULL*/)
+{
+    assert(pSeg);
+    return static_cast<unsigned int>(pSeg->charInfoCount());
+}
+
+
+const gr_char_info* gr_seg_cinfo(const gr_segment* pSeg/*not NULL*/, unsigned int index/*must be <number_of_CharInfo*/)
+{
+    assert(pSeg);
+    return static_cast<const gr_char_info*>(pSeg->charinfo(index));
+}
+
+unsigned int gr_seg_n_slots(const gr_segment* pSeg/*not NULL*/)
+{
+    assert(pSeg);
+    return static_cast<unsigned int>(pSeg->slotCount());
+}
+
+const gr_slot* gr_seg_first_slot(gr_segment* pSeg/*not NULL*/)
+{
+    assert(pSeg);
+    return static_cast<const gr_slot*>(pSeg->first());
+}
+
+const gr_slot* gr_seg_last_slot(gr_segment* pSeg/*not NULL*/)
+{
+    assert(pSeg);
+    return static_cast<const gr_slot*>(pSeg->last());
+}
+
+float gr_seg_justify(gr_segment* pSeg/*not NULL*/, const gr_slot* pSlot/*not NULL*/, const gr_font *pFont, double width, enum gr_justFlags flags, const gr_slot *pFirst, const gr_slot *pLast)
+{
+    assert(pSeg);
+    assert(pSlot);
+    return pSeg->justify(const_cast<gr_slot *>(pSlot), pFont, float(width), justFlags(flags), const_cast<gr_slot *>(pFirst), const_cast<gr_slot *>(pLast));
+}
+
+} // extern "C"

+ 173 - 0
thirdparty/graphite/src/gr_slot.cpp

@@ -0,0 +1,173 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#include "graphite2/Segment.h"
+#include "inc/Segment.h"
+#include "inc/Slot.h"
+#include "inc/Font.h"
+
+
+extern "C" {
+
+
+const gr_slot* gr_slot_next_in_segment(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return static_cast<const gr_slot*>(p->next());
+}
+
+const gr_slot* gr_slot_prev_in_segment(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return static_cast<const gr_slot*>(p->prev());
+}
+
+const gr_slot* gr_slot_attached_to(const gr_slot* p/*not NULL*/)        //returns NULL iff base. If called repeatedly on result, will get to a base
+{
+    assert(p);
+    return static_cast<const gr_slot*>(p->attachedTo());
+}
+
+
+const gr_slot* gr_slot_first_attachment(const gr_slot* p/*not NULL*/)        //returns NULL iff no attachments.
+{        //if slot_first_attachment(p) is not NULL, then slot_attached_to(slot_first_attachment(p))==p.
+    assert(p);
+    return static_cast<const gr_slot*>(p->firstChild());
+}
+
+
+const gr_slot* gr_slot_next_sibling_attachment(const gr_slot* p/*not NULL*/)        //returns NULL iff no more attachments.
+{        //if slot_next_sibling_attachment(p) is not NULL, then slot_attached_to(slot_next_sibling_attachment(p))==slot_attached_to(p).
+    assert(p);
+    return static_cast<const gr_slot*>(p->nextSibling());
+}
+
+
+unsigned short gr_slot_gid(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return p->glyph();
+}
+
+
+float gr_slot_origin_X(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return p->origin().x;
+}
+
+
+float gr_slot_origin_Y(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return p->origin().y;
+}
+
+
+float gr_slot_advance_X(const gr_slot* p/*not NULL*/, const gr_face *face, const gr_font *font)
+{
+    assert(p);
+    float scale = 1.0;
+    float res = p->advance();
+    if (font)
+    {
+        scale = font->scale();
+        int gid = p->glyph();
+        if (face && font->isHinted() && gid < face->glyphs().numGlyphs())
+            res = (res - face->glyphs().glyph(gid)->theAdvance().x) * scale + font->advance(gid);
+        else
+            res = res * scale;
+    }
+    return res;
+}
+
+float gr_slot_advance_Y(const gr_slot *p/*not NULL*/, GR_MAYBE_UNUSED const gr_face *face, const gr_font *font)
+{
+    assert(p);
+    float res = p->advancePos().y;
+    if (font)
+        return res * font->scale();
+    else
+        return res;
+}
+
+int gr_slot_before(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return p->before();
+}
+
+
+int gr_slot_after(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return p->after();
+}
+
+unsigned int gr_slot_index(const gr_slot *p/*not NULL*/)
+{
+    assert(p);
+    return p->index();
+}
+
+int gr_slot_attr(const gr_slot* p/*not NULL*/, const gr_segment* pSeg/*not NULL*/, gr_attrCode index, gr_uint8 subindex)
+{
+    assert(p);
+    return p->getAttr(pSeg, index, subindex);
+}
+
+
+int gr_slot_can_insert_before(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return (p->isInsertBefore())? 1 : 0;
+}
+
+
+int gr_slot_original(const gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    return p->original();
+}
+
+void gr_slot_linebreak_before(gr_slot* p/*not NULL*/)
+{
+    assert(p);
+    gr_slot *prev = (gr_slot *)p->prev();
+    prev->sibling(NULL);
+    prev->next(NULL);
+    p->prev(NULL);
+}
+
+#if 0       //what should this be
+size_t id(const gr_slot* p/*not NULL*/)
+{
+    return (size_t)p->id();
+}
+#endif
+
+
+} // extern "C"

+ 66 - 0
thirdparty/graphite/src/inc/CharInfo.h

@@ -0,0 +1,66 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+#include "inc/Main.h"
+
+
+namespace graphite2 {
+
+class CharInfo
+{
+
+public:
+    CharInfo() : m_char(0), m_before(-1), m_after(-1), m_base(0), m_featureid(0), m_break(0), m_flags(0) {}
+    void init(int cid) { m_char = cid; }
+    unsigned int unicodeChar() const { return m_char; }
+    void feats(int offset) { m_featureid = offset; }
+    int fid() const { return m_featureid; }
+    int breakWeight() const { return m_break; }
+    void breakWeight(int val) { m_break = val; }
+    int after() const { return m_after; }
+    void after(int val) { m_after = val; }
+    int before() const { return m_before; }
+    void before(int val) { m_before = val; }
+    size_t base() const { return m_base; }
+    void base(size_t offset) { m_base = offset; }
+    void addflags(uint8 val) { m_flags |= val; }
+    uint8 flags() const { return m_flags; }
+
+    CLASS_NEW_DELETE
+private:
+    int m_char;     // Unicode character from character stream
+    int m_before;   // slot index before us, comes before
+    int m_after;    // slot index after us, comes after
+    size_t  m_base; // offset into input string corresponding to this charinfo
+    uint8 m_featureid;  // index into features list in the segment
+    int8 m_break;   // breakweight coming from lb table
+    uint8 m_flags;  // 0,1 segment split.
+};
+
+} // namespace graphite2
+
+struct gr_char_info : public graphite2::CharInfo {};

+ 82 - 0
thirdparty/graphite/src/inc/CmapCache.h

@@ -0,0 +1,82 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "inc/Main.h"
+#include "inc/Face.h"
+
+namespace graphite2 {
+
+class Face;
+
+class Cmap
+{
+public:
+
+    virtual ~Cmap() throw() {}
+
+    virtual uint16 operator [] (const uint32) const throw() { return 0; }
+
+    virtual operator bool () const throw() { return false; }
+
+    CLASS_NEW_DELETE;
+};
+
+class DirectCmap : public Cmap
+{
+    DirectCmap(const DirectCmap &);
+    DirectCmap & operator = (const DirectCmap &);
+
+public:
+    DirectCmap(const Face &);
+    virtual uint16 operator [] (const uint32 usv) const throw();
+    virtual operator bool () const throw();
+
+    CLASS_NEW_DELETE;
+private:
+    const Face::Table   _cmap;
+    const void        * _smp,
+                      * _bmp;
+};
+
+class CachedCmap : public Cmap
+{
+    CachedCmap(const CachedCmap &);
+    CachedCmap & operator = (const CachedCmap &);
+
+public:
+    CachedCmap(const Face &);
+    virtual ~CachedCmap() throw();
+    virtual uint16 operator [] (const uint32 usv) const throw();
+    virtual operator bool () const throw();
+    CLASS_NEW_DELETE;
+private:
+    bool m_isBmpOnly;
+    uint16 ** m_blocks;
+};
+
+} // namespace graphite2

+ 171 - 0
thirdparty/graphite/src/inc/Code.h

@@ -0,0 +1,171 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// This class represents loaded graphite stack machine code.  It performs
+// basic sanity checks, on the incoming code to prevent more obvious problems
+// from crashing graphite.
+// Author: Tim Eves
+
+#pragma once
+
+#include <cassert>
+#include <graphite2/Types.h>
+#include "inc/Main.h"
+#include "inc/Machine.h"
+
+namespace graphite2 {
+
+class Silf;
+class Face;
+
+enum passtype {
+    PASS_TYPE_UNKNOWN = 0,
+    PASS_TYPE_LINEBREAK,
+    PASS_TYPE_SUBSTITUTE,
+    PASS_TYPE_POSITIONING,
+    PASS_TYPE_JUSTIFICATION
+};
+
+namespace vm {
+
+class Machine::Code
+{
+public:
+    enum status_t
+    {
+        loaded,
+        alloc_failed,
+        invalid_opcode,
+        unimplemented_opcode_used,
+        out_of_range_data,
+        jump_past_end,
+        arguments_exhausted,
+        missing_return,
+        nested_context_item,
+        underfull_stack
+    };
+
+private:
+    class decoder;
+
+    instr *     _code;
+    byte  *     _data;
+    size_t      _data_size,
+                _instr_count;
+    byte        _max_ref;
+    mutable status_t _status;
+    bool        _constraint,
+                _modify,
+                _delete;
+    mutable bool _own;
+
+    void release_buffers() throw ();
+    void failure(const status_t) throw();
+
+public:
+    static size_t estimateCodeDataOut(size_t num_bytecodes, int nRules, int nSlots);
+
+    Code() throw();
+    Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
+         uint8 pre_context, uint16 rule_length, const Silf &, const Face &,
+         enum passtype pt, byte * * const _out = 0);
+    Code(const Machine::Code &) throw();
+    ~Code() throw();
+
+    Code & operator=(const Code &rhs) throw();
+    operator bool () const throw()                  { return _code && status() == loaded; }
+    status_t      status() const throw()            { return _status; }
+    bool          constraint() const throw()        { return _constraint; }
+    size_t        dataSize() const throw()          { return _data_size; }
+    size_t        instructionCount() const throw()  { return _instr_count; }
+    bool          immutable() const throw()         { return !(_delete || _modify); }
+    bool          deletes() const throw()           { return _delete; }
+    size_t        maxRef() const throw()            { return _max_ref; }
+    void          externalProgramMoved(ptrdiff_t) throw();
+
+    int32 run(Machine &m, slotref * & map) const;
+
+    CLASS_NEW_DELETE;
+};
+
+inline
+size_t  Machine::Code::estimateCodeDataOut(size_t n_bc, int nRules, int nSlots)
+{
+    // max is: all codes are instructions + 1 for each rule + max tempcopies
+    // allocate space for separate maximal code and data then merge them later
+    return (n_bc + nRules + nSlots) * sizeof(instr) + n_bc * sizeof(byte);
+}
+
+
+inline Machine::Code::Code() throw()
+: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0),
+  _status(loaded), _constraint(false), _modify(false), _delete(false),
+  _own(false)
+{
+}
+
+inline Machine::Code::Code(const Machine::Code &obj) throw ()
+ :  _code(obj._code),
+    _data(obj._data),
+    _data_size(obj._data_size),
+    _instr_count(obj._instr_count),
+    _max_ref(obj._max_ref),
+    _status(obj._status),
+    _constraint(obj._constraint),
+    _modify(obj._modify),
+    _delete(obj._delete),
+    _own(obj._own)
+{
+    obj._own = false;
+}
+
+inline Machine::Code & Machine::Code::operator=(const Machine::Code &rhs) throw() {
+    if (_instr_count > 0)
+        release_buffers();
+    _code        = rhs._code;
+    _data        = rhs._data;
+    _data_size   = rhs._data_size;
+    _instr_count = rhs._instr_count;
+    _status      = rhs._status;
+    _constraint  = rhs._constraint;
+    _modify      = rhs._modify;
+    _delete      = rhs._delete;
+    _own         = rhs._own;
+    rhs._own = false;
+    return *this;
+}
+
+inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw()
+{
+    if (_code && !_own)
+    {
+        _code += dist / signed(sizeof(instr));
+        _data += dist;
+    }
+}
+
+} // namespace vm
+} // namespace graphite2

+ 245 - 0
thirdparty/graphite/src/inc/Collider.h

@@ -0,0 +1,245 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "inc/List.h"
+#include "inc/Position.h"
+#include "inc/Intervals.h"
+#include "inc/debug.h"
+
+namespace graphite2 {
+
+class json;
+class Slot;
+class Segment;
+
+#define SLOTCOLSETUINTPROP(x, y) uint16 x() const { return _ ##x; } void y (uint16 v) { _ ##x = v; }
+#define SLOTCOLSETINTPROP(x, y) int16 x() const { return _ ##x; } void y (int16 v) { _ ##x = v; }
+#define SLOTCOLSETPOSITIONPROP(x, y) const Position &x() const { return _ ##x; } void y (const Position &v) { _ ##x = v; }
+
+// Slot attributes related to collision-fixing
+class SlotCollision
+{
+public:
+    enum {
+    //  COLL_TESTONLY = 0,  // default - test other glyphs for collision with this one, but don't move this one
+        COLL_FIX = 1,       // fix collisions involving this glyph
+        COLL_IGNORE = 2,    // ignore this glyph altogether
+        COLL_START = 4,     // start of range of possible collisions
+        COLL_END = 8,       // end of range of possible collisions
+        COLL_KERN = 16,     // collisions with this glyph are fixed by adding kerning space after it
+        COLL_ISCOL = 32,    // this glyph has a collision
+        COLL_KNOWN = 64,    // we've figured out what's happening with this glyph
+        COLL_ISSPACE = 128,		// treat this glyph as a space with regard to kerning
+        COLL_TEMPLOCK = 256,    // Lock glyphs that have been given priority positioning
+        ////COLL_JUMPABLE = 128,    // moving glyphs may jump this stationary glyph in any direction - DELETE
+        ////COLL_OVERLAP = 256,    // use maxoverlap to restrict - DELETE
+    };
+
+    // Behavior for the collision.order attribute. To GDL this is an enum, to us it's a bitfield, with only 1 bit set
+    // Allows for easier inversion.
+    enum {
+        SEQ_ORDER_LEFTDOWN = 1,
+        SEQ_ORDER_RIGHTUP = 2,
+        SEQ_ORDER_NOABOVE = 4,
+        SEQ_ORDER_NOBELOW = 8,
+        SEQ_ORDER_NOLEFT = 16,
+        SEQ_ORDER_NORIGHT = 32
+    };
+
+    SlotCollision(Segment *seg, Slot *slot);
+    void initFromSlot(Segment *seg, Slot *slot);
+
+    const Rect &limit() const { return _limit; }
+    void setLimit(const Rect &r) { _limit = r; }
+    SLOTCOLSETPOSITIONPROP(shift, setShift)
+    SLOTCOLSETPOSITIONPROP(offset, setOffset)
+    SLOTCOLSETPOSITIONPROP(exclOffset, setExclOffset)
+    SLOTCOLSETUINTPROP(margin, setMargin)
+    SLOTCOLSETUINTPROP(marginWt, setMarginWt)
+    SLOTCOLSETUINTPROP(flags, setFlags)
+    SLOTCOLSETUINTPROP(exclGlyph, setExclGlyph)
+    SLOTCOLSETUINTPROP(seqClass, setSeqClass)
+    SLOTCOLSETUINTPROP(seqProxClass, setSeqProxClass)
+    SLOTCOLSETUINTPROP(seqOrder, setSeqOrder)
+    SLOTCOLSETINTPROP(seqAboveXoff, setSeqAboveXoff)
+    SLOTCOLSETUINTPROP(seqAboveWt, setSeqAboveWt)
+    SLOTCOLSETINTPROP(seqBelowXlim, setSeqBelowXlim)
+    SLOTCOLSETUINTPROP(seqBelowWt, setSeqBelowWt)
+    SLOTCOLSETUINTPROP(seqValignHt, setSeqValignHt)
+    SLOTCOLSETUINTPROP(seqValignWt, setSeqValignWt)
+
+    float getKern(int dir) const;
+    bool ignore() const;
+
+private:
+    Rect        _limit;
+    Position    _shift;     // adjustment within the given pass
+    Position    _offset;    // total adjustment for collisions
+    Position    _exclOffset;
+    uint16		_margin;
+    uint16		_marginWt;
+    uint16		_flags;
+    uint16		_exclGlyph;
+    uint16		_seqClass;
+	uint16		_seqProxClass;
+    uint16		_seqOrder;
+    int16		_seqAboveXoff;
+    uint16		_seqAboveWt;
+    int16		_seqBelowXlim;
+    uint16		_seqBelowWt;
+    uint16		_seqValignHt;
+    uint16		_seqValignWt;
+
+};  // end of class SlotColllision
+
+struct BBox;
+struct SlantBox;
+
+class ShiftCollider
+{
+public:
+    typedef std::pair<float, float> fpair;
+    typedef Vector<fpair> vfpairs;
+    typedef vfpairs::iterator ivfpairs;
+
+    ShiftCollider(json *dbgout);
+    ~ShiftCollider() throw() { };
+
+    bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint,
+                float margin, float marginMin, const Position &currShift,
+                const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout);
+    bool mergeSlot(Segment *seg, Slot *slot, const SlotCollision *cinfo, const Position &currShift, bool isAfter,
+                bool sameCluster, bool &hasCol, bool isExclusion, GR_MAYBE_UNUSED json * const dbgout);
+    Position resolve(Segment *seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout);
+    void addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int mode);
+    void removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int mode);
+    const Position &origin() const { return _origin; }
+
+#if !defined GRAPHITE2_NTRACING
+	void outputJsonDbg(json * const dbgout, Segment *seg, int axis);
+	void outputJsonDbgStartSlot(json * const dbgout, Segment *seg);
+	void outputJsonDbgEndSlot(json * const dbgout, Position resultPos, int bestAxis, bool isCol);
+	void outputJsonDbgOneVector(json * const dbgout, Segment *seg, int axis, float tleft, float bestCost, float bestVal);
+	void outputJsonDbgRawRanges(json * const dbgout, int axis);
+	void outputJsonDbgRemovals(json * const dbgout, int axis, Segment *seg);
+#endif
+
+    CLASS_NEW_DELETE;
+
+protected:
+    Zones _ranges[4];   // possible movements in 4 directions (horizontally, vertically, diagonally);
+    Slot *  _target;    // the glyph to fix
+    Rect    _limit;
+    Position _currShift;
+    Position _currOffset;
+    Position _origin;   // Base for all relative calculations
+    float   _margin;
+	float	_marginWt;
+    float   _len[4];
+    uint16  _seqClass;
+	uint16	_seqProxClass;
+    uint16  _seqOrder;
+
+	//bool _scraping[4];
+
+};	// end of class ShiftCollider
+
+inline
+ShiftCollider::ShiftCollider(GR_MAYBE_UNUSED json *dbgout)
+: _target(0),
+  _margin(0.0),
+  _marginWt(0.0),
+  _seqClass(0),
+  _seqProxClass(0),
+  _seqOrder(0)
+{
+#if !defined GRAPHITE2_NTRACING
+    for (int i = 0; i < 4; ++i)
+        _ranges[i].setdebug(dbgout);
+#endif
+}
+
+class KernCollider
+{
+public:
+    KernCollider(json *dbg);
+    ~KernCollider() throw() { };
+    bool initSlot(Segment *seg, Slot *aSlot, const Rect &constraint, float margin,
+            const Position &currShift, const Position &offsetPrev, int dir,
+            float ymin, float ymax, json * const dbgout);
+    bool mergeSlot(Segment *seg, Slot *slot, const Position &currShift, float currSpace, int dir, json * const dbgout);
+    Position resolve(Segment *seg, Slot *slot, int dir, json * const dbgout);
+    void shift(const Position &mv, int dir);
+
+    CLASS_NEW_DELETE;
+
+private:
+    Slot *  _target;        // the glyph to fix
+    Rect    _limit;
+    float   _margin;
+    Position _offsetPrev;   // kern from a previous pass
+    Position _currShift;    // NOT USED??
+    float _miny;	        // y-coordinates offset by global slot position
+    float _maxy;
+    Vector<float> _edges;   // edges of horizontal slices
+    float _sliceWidth;      // width of each slice
+    float _mingap;
+    float _xbound;        // max or min edge
+    bool  _hit;
+
+#if !defined GRAPHITE2_NTRACING
+    // Debugging
+    Segment * _seg;
+    Vector<float> _nearEdges; // closest potential collision in each slice
+    Vector<Slot*> _slotNear;
+#endif
+};	// end of class KernCollider
+
+
+inline
+float sqr(float x) {
+    return x * x;
+}
+
+inline
+KernCollider::KernCollider(GR_MAYBE_UNUSED json *dbg)
+: _target(0),
+  _margin(0.0f),
+  _miny(-1e38f),
+  _maxy(1e38f),
+  _sliceWidth(0.0f),
+  _mingap(0.0f),
+  _xbound(0.0),
+  _hit(false)
+{
+#if !defined GRAPHITE2_NTRACING
+    _seg = 0;
+#endif
+};
+
+};  // end of namespace graphite2

+ 104 - 0
thirdparty/graphite/src/inc/Compression.h

@@ -0,0 +1,104 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2015, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+namespace
+{
+
+#if defined(_MSC_VER)
+typedef unsigned __int8 u8;
+typedef unsigned __int16 u16;
+typedef unsigned __int32 u32;
+typedef unsigned __int64 u64;
+#else
+#include <stdint.h>
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+#endif
+
+ptrdiff_t const     MINMATCH = 4,
+                    LASTLITERALS = 5,
+                    MINCODA  = LASTLITERALS+1,
+                    MINSRCSIZE = 13;
+
+template<int S>
+inline
+void unaligned_copy(void * d, void const * s) {
+  ::memcpy(d, s, S);
+}
+
+inline
+size_t align(size_t p) {
+    return (p + sizeof(unsigned long)-1) & ~(sizeof(unsigned long)-1);
+}
+
+inline
+u8 * safe_copy(u8 * d, u8 const * s, size_t n) {
+    while (n--) *d++ = *s++;
+    return d;
+}
+
+inline
+u8 * overrun_copy(u8 * d, u8 const * s, size_t n) {
+    size_t const WS = sizeof(unsigned long);
+    u8 const * e = s + n;
+    do
+    {
+        unaligned_copy<WS>(d, s);
+        d += WS;
+        s += WS;
+    }
+    while (s < e);
+    d-=(s-e);
+
+    return d;
+}
+
+
+inline
+u8 * fast_copy(u8 * d, u8 const * s, size_t n) {
+    size_t const WS = sizeof(unsigned long);
+    size_t wn = n/WS;
+    while (wn--)
+    {
+        unaligned_copy<WS>(d, s);
+        d += WS;
+        s += WS;
+    }
+    n &= WS-1;
+    return safe_copy(d, s, n);
+}
+
+
+} // end of anonymous namespace

+ 54 - 0
thirdparty/graphite/src/inc/Decompressor.h

@@ -0,0 +1,54 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2015, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+#pragma once
+
+#include <cstddef>
+
+namespace lz4
+{
+
+// decompress an LZ4 block
+// Parameters:
+//      @in         -   Input buffer containing an LZ4 block.
+//      @in_size    -   Size of the input LZ4 block in bytes.
+//      @out        -   Output buffer to hold decompressed results.
+//      @out_size   -   The size of the buffer pointed to by @out.
+// Invariants:
+//      @in         -   This buffer must be at least 1 machine word in length,
+//                      regardless of the actual LZ4 block size.
+//      @in_size    -   This must be at least 4 and must also be <= to the
+//                      allocated buffer @in.
+//      @out        -   This must be bigger than the input buffer and at least
+//                      13 bytes.
+//      @out_size   -   Must always be big enough to hold the expected size.
+// Return:
+//      -1          -  Decompression failed.
+//      size        -  Actual number of bytes decompressed.
+int decompress(void const *in, size_t in_size, void *out, size_t out_size);
+
+} // end of namespace shrinker

+ 111 - 0
thirdparty/graphite/src/inc/Endian.h

@@ -0,0 +1,111 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+/*
+Description:
+    A set of fast template based decoders for decoding values of any C integer
+    type up to long int size laid out with most significant byte first or least
+    significant byte first (aka big endian or little endian).  These are CPU
+    byte order agnostic and will function the same regardless of the CPUs native
+    byte order.
+
+    Being template based means if the either le or be class is not used then
+    template code of unused functions will not be instantiated by the compiler
+    and thus shouldn't cause any overhead.
+*/
+
+#include <cstddef>
+
+#pragma once
+
+
+class be
+{
+    template<int S>
+    inline static unsigned long int _peek(const unsigned char * p) {
+        return _peek<S/2>(p) << (S/2)*8 | _peek<S/2>(p+S/2);
+    }
+public:
+    template<typename T>
+    inline static T peek(const void * p) {
+        return T(_peek<sizeof(T)>(static_cast<const unsigned char *>(p)));
+    }
+
+    template<typename T>
+    inline static T read(const unsigned char * &p) {
+        const T r = T(_peek<sizeof(T)>(p));
+        p += sizeof r;
+        return r;
+    }
+
+    template<typename T>
+    inline static T swap(const T x) {
+        return T(_peek<sizeof(T)>(reinterpret_cast<const unsigned char *>(&x)));
+    }
+
+    template<typename T>
+    inline static void skip(const unsigned char * &p, size_t n=1) {
+        p += sizeof(T)*n;
+    }
+};
+
+template<>
+inline unsigned long int be::_peek<1>(const unsigned char * p) { return *p; }
+
+
+class le
+{
+    template<int S>
+    inline static unsigned long int _peek(const unsigned char * p) {
+        return _peek<S/2>(p) | _peek<S/2>(p+S/2)  << (S/2)*8;
+    }
+public:
+    template<typename T>
+    inline static T peek(const void * p) {
+        return T(_peek<sizeof(T)>(static_cast<const unsigned char *>(p)));
+    }
+
+    template<typename T>
+    inline static T read(const unsigned char * &p) {
+        const T r = T(_peek<sizeof(T)>(p));
+        p += sizeof r;
+        return r;
+    }
+
+    template<typename T>
+    inline static T swap(const T x) {
+        return T(_peek<sizeof(T)>(reinterpret_cast<const unsigned char *>(&x)));
+    }
+
+    template<typename T>
+    inline static void skip(const unsigned char * &p, size_t n=1) {
+        p += sizeof(T)*n;
+    }
+};
+
+template<>
+inline unsigned long int le::_peek<1>(const unsigned char * p) { return *p; }

+ 134 - 0
thirdparty/graphite/src/inc/Error.h

@@ -0,0 +1,134 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2013, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+// numbers are explicitly assigned for future proofing
+
+namespace graphite2
+{
+
+class Error
+{
+public:
+    Error() : _e(0) {};
+    operator bool() { return (_e != 0); }
+    int error() { return _e; }
+    void error(int e) { _e = e; }
+    bool test(bool pr, int err) { return (_e = int(pr) * err); }
+
+private:
+    int _e;
+};
+
+enum errcontext {
+    EC_READGLYPHS = 1,      // while reading glyphs
+    EC_READSILF = 2,        // in Silf table
+    EC_ASILF = 3,           // in Silf %d
+    EC_APASS = 4,           // in Silf %d, pass %d
+    EC_PASSCCODE = 5,       // in pass constraint code for Silf %d, pass %d
+    EC_ARULE = 6,           // in Silf %d, pass %d, rule %d
+    EC_ASTARTS = 7,         // in Silf %d, pass %d, start state %d
+    EC_ATRANS = 8,          // in Silf %d, pass %d, fsm state %d
+    EC_ARULEMAP = 9         // in Silf %d, pass %d, state %d
+};
+
+enum errors {
+    E_OUTOFMEM = 1,         // Out of memory
+    E_NOGLYPHS = 2,         // There are no glyphs in the font
+    E_BADUPEM = 3,          // The units per em for the font is bad (0)
+    E_BADCMAP = 4,          // The font does not contain any useful cmaps
+    E_NOSILF = 5,           // Missing Silf table
+    E_TOOOLD = 6,           // Silf table version is too old
+    E_BADSIZE = 7,          // context object has the wrong structural size
+// Silf Subtable Errors take a Silf subtable number * 256 in the context
+    E_BADMAXGLYPH = 8,      // Silf max glyph id is too high
+    E_BADNUMJUSTS = 9,      // Number of Silf justification blocks is too high
+    E_BADENDJUSTS = 10,     // Silf justification blocks take too much of the Silf table space
+    E_BADCRITFEATURES = 11, // Critical features section in a Silf table is too big
+    E_BADSCRIPTTAGS = 12,   // Silf script tags area is too big
+    E_BADAPSEUDO = 13,      // The pseudo glyph attribute number is too high
+    E_BADABREAK = 14,       // The linebreak glyph attribute number is too high
+    E_BADABIDI = 15,        // The bidi glyph attribute number is too high
+    E_BADAMIRROR = 16,      // The mirrored glyph attribute number is too high
+    E_BADNUMPASSES = 17,    // The number of passes is > 128
+    E_BADPASSESSTART = 18,  // The Silf table is too small to hold any passes
+    E_BADPASSBOUND = 19,    // The positioning pass number is too low or the substitution pass number is too high
+    E_BADPPASS = 20,        // The positioning pass number is too high
+    E_BADSPASS = 21,        // the substitution pass number is too high
+    E_BADJPASSBOUND = 22,   // the justification pass must be higher than the positioning pass
+    E_BADJPASS = 23,        // the justification pass is too high
+    E_BADALIG = 24,         // the number of initial ligature component glyph attributes is too high
+    E_BADBPASS = 25,        // the bidi pass number is specified and is either too high or too low
+    E_BADNUMPSEUDO = 26,    // The number of pseudo glyphs is too high
+    E_BADCLASSSIZE = 27,    // The size of the classes block is bad
+    E_TOOMANYLINEAR = 28,   // The number of linear classes in the silf table is too high
+    E_CLASSESTOOBIG = 29,   // There are too many classes for the space allocated in the Silf subtable
+    E_MISALIGNEDCLASSES = 30,   // The class offsets in the class table don't line up with the number of classes
+    E_HIGHCLASSOFFSET = 31, // The class offsets point out of the class table
+    E_BADCLASSOFFSET = 32,  // A class offset is less than one following it
+    E_BADCLASSLOOKUPINFO = 33,  // The search header info for a non-linear class has wrong values in it
+// Pass subtable errors. Context has pass number * 65536
+    E_BADPASSSTART = 34,    // The start offset for a particular pass is bad
+    E_BADPASSEND = 35,      // The end offset for a particular pass is bad
+    E_BADPASSLENGTH = 36,   // The length of the pass is too small
+    E_BADNUMTRANS = 37,     // The number of transition states in the fsm is bad
+    E_BADNUMSUCCESS = 38,   // The number of success states in the fsm is bad
+    E_BADNUMSTATES = 39,    // The number of states in the fsm is bad
+    E_NORANGES = 40,        // There are no columns in the fsm
+    E_BADRULEMAPLEN = 41,   // The size of the success state to rule mapping is bad
+    E_BADCTXTLENBOUNDS = 42,    // The precontext maximum is greater than its minimum
+    E_BADCTXTLENS = 43,     // The lists of rule lengths or pre context lengths is bad
+    E_BADPASSCCODEPTR = 44, // The pass constraint code position does not align with where the forward reference says it should be
+    E_BADRULECCODEPTR = 45, // The rule constraint code position does not align with where the forward reference says it should be
+    E_BADCCODELEN = 46,     // Bad rule/pass constraint code length
+    E_BADACTIONCODEPTR = 47,    // The action code position does not align with where the forward reference says it should be
+    E_MUTABLECCODE = 48,    // Constraint code edits slots. It shouldn't.
+    E_BADSTATE = 49,        // Bad state transition referencing an illegal state
+    E_BADRULEMAPPING = 50,  // The structure of the rule mapping is bad
+    E_BADRANGE = 51,        // Bad column range structure including a glyph in more than one column
+    E_BADRULENUM = 52,      // A reference to a rule is out of range (too high)
+    E_BADACOLLISION = 53,   // Bad Silf table collision attribute number (too high)
+    E_BADEMPTYPASS = 54,    // Can't have empty passes (no rules) except for collision passes
+    E_BADSILFVERSION = 55,  // The Silf table has a bad version (probably too high)
+    E_BADCOLLISIONPASS = 56,    // Collision flags set on a non positioning pass
+    E_BADNUMCOLUMNS = 57,   // Arbitrarily limit number of columns in fsm
+// Code errors
+    E_CODEFAILURE = 60,     // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h
+    E_CODEALLOC = 61,       // Out of memory
+    E_INVALIDOPCODE = 62,   // Invalid op code
+    E_UNIMPOPCODE = 63,     // Unimplemented op code encountered
+    E_OUTOFRANGECODE = 64,  // Code argument out of range
+    E_BADJUMPCODE = 65,     // Code jumps past end of op codes
+    E_CODEBADARGS = 66,     // Code arguments exhausted
+    E_CODENORETURN = 67,    // Missing return type op code at end of code
+    E_CODENESTEDCTXT = 68,   // Nested context encountered in code
+// Compression errors
+    E_BADSCHEME = 69,
+    E_SHRINKERFAILED = 70,
+};
+
+}

+ 225 - 0
thirdparty/graphite/src/inc/Face.h

@@ -0,0 +1,225 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <cstdio>
+
+#include "graphite2/Font.h"
+
+#include "inc/Main.h"
+#include "inc/FeatureMap.h"
+#include "inc/TtfUtil.h"
+#include "inc/Silf.h"
+#include "inc/Error.h"
+
+namespace graphite2 {
+
+class Cmap;
+class FileFace;
+class GlyphCache;
+class NameTable;
+class json;
+class Font;
+
+
+using TtfUtil::Tag;
+
+// These are the actual tags, as distinct from the consecutive IDs in TtfUtil.h
+
+class Face
+{
+    // Prevent any kind of copying
+    Face(const Face&);
+    Face& operator=(const Face&);
+
+public:
+    class Table;
+    static float default_glyph_advance(const void* face_ptr, gr_uint16 glyphid);
+
+    Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops);
+    virtual ~Face();
+
+    virtual bool        runGraphite(Segment *seg, const Silf *silf) const;
+
+public:
+    bool                readGlyphs(uint32 faceOptions);
+    bool                readGraphite(const Table & silf);
+    bool                readFeatures();
+    void                takeFileFace(FileFace* pFileFace/*takes ownership*/);
+
+    const SillMap     & theSill() const;
+    const GlyphCache  & glyphs() const;
+    Cmap              & cmap() const;
+    NameTable         * nameTable() const;
+    void                setLogger(FILE *log_file);
+    json              * logger() const throw();
+
+    const Silf        * chooseSilf(uint32 script) const;
+    uint16              languageForLocale(const char * locale) const;
+
+    // Features
+    uint16              numFeatures() const;
+    const FeatureRef  * featureById(uint32 id) const;
+    const FeatureRef  * feature(uint16 index) const;
+
+    // Glyph related
+    int32  getGlyphMetric(uint16 gid, uint8 metric) const;
+    uint16 findPseudo(uint32 uid) const;
+
+    // Errors
+    unsigned int        error() const { return m_error; }
+    bool                error(Error e) { m_error = e.error(); return false; }
+    unsigned int        error_context() const { return m_error; }
+    void                error_context(unsigned int errcntxt) { m_errcntxt = errcntxt; }
+
+    CLASS_NEW_DELETE;
+private:
+    SillMap                 m_Sill;
+    gr_face_ops             m_ops;
+    const void            * m_appFaceHandle;    // non-NULL
+    FileFace              * m_pFileFace;        //owned
+    mutable GlyphCache    * m_pGlyphFaceCache;  // owned - never NULL
+    mutable Cmap          * m_cmap;             // cmap cache if available
+    mutable NameTable     * m_pNames;
+    mutable json          * m_logger;
+    unsigned int            m_error;
+    unsigned int            m_errcntxt;
+protected:
+    Silf                  * m_silfs;    // silf subtables.
+    uint16                  m_numSilf;  // num silf subtables in the silf table
+private:
+    uint16 m_ascent,
+           m_descent;
+#ifdef GRAPHITE2_TELEMETRY
+public:
+    mutable telemetry   tele;
+#endif
+};
+
+
+
+inline
+const SillMap & Face::theSill() const
+{
+    return m_Sill;
+}
+
+inline
+uint16 Face::numFeatures() const
+{
+    return m_Sill.theFeatureMap().numFeats();
+}
+
+inline
+const FeatureRef * Face::featureById(uint32 id) const
+{
+    return m_Sill.theFeatureMap().findFeatureRef(id);
+}
+
+inline
+const FeatureRef *Face::feature(uint16 index) const
+{
+    return m_Sill.theFeatureMap().feature(index);
+}
+
+inline
+const GlyphCache & Face::glyphs() const
+{
+    return *m_pGlyphFaceCache;
+}
+
+inline
+Cmap & Face::cmap() const
+{
+    return *m_cmap;
+};
+
+inline
+json * Face::logger() const throw()
+{
+    return m_logger;
+}
+
+
+
+class Face::Table
+{
+    const Face *            _f;
+    mutable const byte *    _p;
+    size_t                  _sz;
+    bool                    _compressed;
+
+    Error decompress();
+
+    void release();
+
+public:
+    Table() throw();
+    Table(const Face & face, const Tag n, uint32 version=0xffffffff) throw();
+    ~Table() throw();
+    Table(const Table && rhs) throw();
+
+    operator const byte * () const throw();
+
+    size_t  size() const throw();
+    Table & operator = (const Table && rhs) throw();
+};
+
+inline
+Face::Table::Table() throw()
+: _f(0), _p(0), _sz(0), _compressed(false)
+{
+}
+
+inline
+Face::Table::Table(const Table && rhs) throw()
+: _f(rhs._f), _p(rhs._p), _sz(rhs._sz), _compressed(rhs._compressed)
+{
+    rhs._p = 0;
+}
+
+inline
+Face::Table::~Table() throw()
+{
+    release();
+}
+
+inline
+Face::Table::operator const byte * () const throw()
+{
+    return _p;
+}
+
+inline
+size_t  Face::Table::size() const throw()
+{
+    return _sz;
+}
+
+} // namespace graphite2
+
+struct gr_face : public graphite2::Face {};

+ 198 - 0
thirdparty/graphite/src/inc/FeatureMap.h

@@ -0,0 +1,198 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+#include "inc/Main.h"
+#include "inc/FeatureVal.h"
+
+namespace graphite2 {
+
+// Forward declarations for implmentation types
+class FeatureMap;
+class Face;
+
+
+class FeatureSetting
+{
+public:
+    FeatureSetting(int16 theValue, uint16 labelId) : m_label(labelId), m_value(theValue) {};
+    uint16 label() const { return m_label; }
+    int16 value() const { return m_value; }
+
+    CLASS_NEW_DELETE;
+private:
+    FeatureSetting(const FeatureSetting & fs) : m_label(fs.m_label), m_value(fs.m_value) {};
+
+    uint16 m_label;
+    int16 m_value;
+};
+
+class FeatureRef
+{
+    typedef uint32      chunk_t;
+    static const uint8  SIZEOF_CHUNK = sizeof(chunk_t)*8;
+
+public:
+    enum flags_t : uint16 {
+        HIDDEN = 0x0800
+    };
+    FeatureRef() throw();
+    FeatureRef(const Face & face, unsigned short & bits_offset, uint32 max_val,
+               uint32 name, uint16 uiName, flags_t flags,
+               FeatureSetting *settings, uint16 num_set) throw();
+    ~FeatureRef() throw();
+
+    bool applyValToFeature(uint32 val, Features& pDest) const; //defined in GrFaceImp.h
+    void maskFeature(Features & pDest) const {
+    if (m_index < pDest.size())                 //defensive
+        pDest[m_index] |= m_mask;
+    }
+
+    uint32 getFeatureVal(const Features& feats) const; //defined in GrFaceImp.h
+
+    uint32 getId() const { return m_id; }
+    uint16 getNameId() const { return m_nameid; }
+    uint16 getNumSettings() const { return m_numSet; }
+    uint16 getSettingName(uint16 index) const { return m_nameValues[index].label(); }
+    int16  getSettingValue(uint16 index) const { return m_nameValues[index].value(); }
+    flags_t getFlags() const { return m_flags; }
+    uint32 maxVal() const { return m_max; }
+    const Face & getFace() const { assert(m_face); return *m_face;}
+    const FeatureMap* getFeatureMap() const;// { return m_pFace;}
+
+    CLASS_NEW_DELETE;
+private:
+    FeatureRef(const FeatureRef & rhs);
+
+    const Face     * m_face;
+    FeatureSetting * m_nameValues; // array of name table ids for feature values
+    chunk_t m_mask,             // bit mask to get the value from the vector
+            m_max;              // max value the value can take
+    uint32  m_id;               // feature identifier/name
+    uint16  m_nameid,           // Name table id for feature name
+            m_numSet;           // number of values (number of entries in m_nameValues)
+    flags_t m_flags;            // feature flags see FeatureRef::flags_t.
+    byte    m_bits,             // how many bits to shift the value into place
+            m_index;            // index into the array to find the ulong to mask
+
+private:        //unimplemented
+    FeatureRef& operator=(const FeatureRef&);
+};
+
+inline
+FeatureRef::FeatureRef() throw()
+: m_face(0),
+  m_nameValues(0),
+  m_mask(0), m_max(0),
+  m_id(0), m_nameid(0), m_numSet(0), 
+  m_flags(flags_t(0)),
+  m_bits(0), m_index(0)
+{
+}
+
+
+class NameAndFeatureRef
+{
+  public:
+    NameAndFeatureRef(uint32 name = 0) : m_name(name) , m_pFRef(NULL){}
+    NameAndFeatureRef(FeatureRef const & p) : m_name(p.getId()), m_pFRef(&p) {}
+
+    bool operator<(const NameAndFeatureRef& rhs) const //orders by m_name
+        {   return m_name<rhs.m_name; }
+
+    CLASS_NEW_DELETE
+
+    uint32 m_name;
+    const FeatureRef* m_pFRef;
+};
+
+class FeatureMap
+{
+public:
+    FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL) {}
+    ~FeatureMap() { delete[] m_feats; delete[] m_pNamedFeats; }
+
+    bool readFeats(const Face & face);
+    const FeatureRef *findFeatureRef(uint32 name) const;
+    FeatureRef *feature(uint16 index) const { return m_feats + index; }
+    //GrFeatureRef *featureRef(byte index) { return index < m_numFeats ? m_feats + index : NULL; }
+    const FeatureRef *featureRef(byte index) const { return index < m_numFeats ? m_feats + index : NULL; }
+    FeatureVal* cloneFeatures(uint32 langname/*0 means default*/) const;      //call destroy_Features when done.
+    uint16 numFeats() const { return m_numFeats; };
+    CLASS_NEW_DELETE
+private:
+friend class SillMap;
+    uint16 m_numFeats;
+
+    FeatureRef *m_feats;
+    NameAndFeatureRef* m_pNamedFeats;   //owned
+    FeatureVal m_defaultFeatures;        //owned
+
+private:        //defensive on m_feats, m_pNamedFeats, and m_defaultFeatures
+    FeatureMap(const FeatureMap&);
+    FeatureMap& operator=(const FeatureMap&);
+};
+
+
+class SillMap
+{
+private:
+    class LangFeaturePair
+    {
+        LangFeaturePair(const LangFeaturePair &);
+        LangFeaturePair & operator = (const LangFeaturePair &);
+
+    public:
+        LangFeaturePair() :  m_lang(0), m_pFeatures(0) {}
+        ~LangFeaturePair() { delete m_pFeatures; }
+
+        uint32 m_lang;
+        Features* m_pFeatures;      //owns
+        CLASS_NEW_DELETE
+    };
+public:
+    SillMap() : m_langFeats(NULL), m_numLanguages(0) {}
+    ~SillMap() { delete[] m_langFeats; }
+    bool readFace(const Face & face);
+    bool readSill(const Face & face);
+    FeatureVal* cloneFeatures(uint32 langname/*0 means default*/) const;      //call destroy_Features when done.
+    uint16 numLanguages() const { return m_numLanguages; };
+    uint32 getLangName(uint16 index) const { return (index < m_numLanguages)? m_langFeats[index].m_lang : 0; };
+
+    const FeatureMap & theFeatureMap() const { return m_FeatureMap; };
+private:
+    FeatureMap m_FeatureMap;        //of face
+    LangFeaturePair * m_langFeats;
+    uint16 m_numLanguages;
+
+private:        //defensive on m_langFeats
+    SillMap(const SillMap&);
+    SillMap& operator=(const SillMap&);
+};
+
+} // namespace graphite2
+
+struct gr_feature_ref : public graphite2::FeatureRef {};

+ 68 - 0
thirdparty/graphite/src/inc/FeatureVal.h

@@ -0,0 +1,68 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+#include <cstring>
+#include <cassert>
+#include "inc/Main.h"
+#include "inc/List.h"
+
+namespace graphite2 {
+
+class FeatureRef;
+class FeatureMap;
+
+class FeatureVal : public Vector<uint32>
+{
+public:
+    FeatureVal() : m_pMap(0) { }
+    FeatureVal(int num, const FeatureMap & pMap) : Vector<uint32>(num), m_pMap(&pMap) {}
+    FeatureVal(const FeatureVal & rhs) : Vector<uint32>(rhs), m_pMap(rhs.m_pMap) {}
+
+    FeatureVal & operator = (const FeatureVal & rhs) { Vector<uint32>::operator = (rhs); m_pMap = rhs.m_pMap; return *this; }
+
+    bool operator ==(const FeatureVal & b) const
+    {
+        size_t n = size();
+        if (n != b.size())      return false;
+
+        for(const_iterator l = begin(), r = b.begin(); n && *l == *r; --n, ++l, ++r);
+
+        return n == 0;
+    }
+
+    CLASS_NEW_DELETE
+private:
+    friend class FeatureRef;        //so that FeatureRefs can manipulate m_vec directly
+    const FeatureMap* m_pMap;
+};
+
+typedef FeatureVal Features;
+
+} // namespace graphite2
+
+
+struct gr_feature_val : public graphite2::FeatureVal {};

+ 80 - 0
thirdparty/graphite/src/inc/FileFace.h

@@ -0,0 +1,80 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2012, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+//#include "inc/FeatureMap.h"
+//#include "inc/GlyphsCache.h"
+//#include "inc/Silf.h"
+
+#ifndef GRAPHITE2_NFILEFACE
+
+#include <cstdio>
+#include <cassert>
+
+#include "graphite2/Font.h"
+
+#include "inc/Main.h"
+#include "inc/TtfTypes.h"
+#include "inc/TtfUtil.h"
+
+namespace graphite2 {
+
+
+class FileFace
+{
+    static const void * get_table_fn(const void* appFaceHandle, unsigned int name, size_t *len);
+    static void         rel_table_fn(const void* appFaceHandle, const void *table_buffer);
+
+public:
+    static const gr_face_ops ops;
+
+    FileFace(const char *filename);
+    ~FileFace();
+
+    operator bool () const throw();
+    CLASS_NEW_DELETE;
+
+private:        //defensive
+    FILE          * _file;
+    size_t          _file_len;
+
+    TtfUtil::Sfnt::OffsetSubTable         * _header_tbl;
+    TtfUtil::Sfnt::OffsetSubTable::Entry  * _table_dir;
+
+    FileFace(const FileFace&);
+    FileFace& operator=(const FileFace&);
+};
+
+inline
+FileFace::operator bool() const throw()
+{
+    return _file && _header_tbl && _table_dir;
+}
+
+} // namespace graphite2
+
+#endif      //!GRAPHITE2_NFILEFACE

+ 90 - 0
thirdparty/graphite/src/inc/Font.h

@@ -0,0 +1,90 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+#include <cassert>
+#include "graphite2/Font.h"
+#include "inc/Main.h"
+#include "inc/Face.h"
+
+namespace graphite2 {
+
+#define INVALID_ADVANCE -1e38f      // can't be a static const because non-integral
+
+class Font
+{
+public:
+    Font(float ppm, const Face & face, const void * appFontHandle=0, const gr_font_ops * ops=0);
+    virtual ~Font();
+
+    float advance(unsigned short glyphid) const;
+    float scale() const;
+    bool isHinted() const;
+    const Face & face() const;
+    operator bool () const throw()  { return m_advances; }
+
+    CLASS_NEW_DELETE;
+private:
+    gr_font_ops         m_ops;
+    const void  * const m_appFontHandle;
+    float             * m_advances;  // One advance per glyph in pixels. Nan if not defined
+    const Face        & m_face;
+    float               m_scale;      // scales from design units to ppm
+    bool                m_hinted;
+
+    Font(const Font&);
+    Font& operator=(const Font&);
+};
+
+inline
+float Font::advance(unsigned short glyphid) const
+{
+    if (m_advances[glyphid] == INVALID_ADVANCE)
+        m_advances[glyphid] = (*m_ops.glyph_advance_x)(m_appFontHandle, glyphid);
+    return m_advances[glyphid];
+}
+
+inline
+float Font::scale() const
+{
+    return m_scale;
+}
+
+inline
+bool Font::isHinted() const
+{
+    return m_hinted;
+}
+
+inline
+const Face & Font::face() const
+{
+    return m_face;
+}
+
+} // namespace graphite2
+
+struct gr_font : public graphite2::Font {};

+ 223 - 0
thirdparty/graphite/src/inc/GlyphCache.h

@@ -0,0 +1,223 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2012, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+
+#include "graphite2/Font.h"
+#include "inc/Main.h"
+#include "inc/Position.h"
+#include "inc/GlyphFace.h"
+
+namespace graphite2 {
+
+class Face;
+class FeatureVal;
+class Segment;
+
+
+struct SlantBox
+{
+    static const SlantBox empty;
+
+//    SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {};
+    float width() const { return sa - si; }
+    float height() const { return da - di; }
+    float si; // min
+    float di; // min
+    float sa; // max
+    float da; // max
+};
+
+
+struct BBox
+{
+    BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {};
+    float width() const { return xa - xi; }
+    float height() const { return ya - yi; }
+    float xi; // min
+    float yi; // min
+    float xa; // max
+    float ya; // max
+};
+
+
+class GlyphBox
+{
+    GlyphBox(const GlyphBox &);
+    GlyphBox & operator = (const GlyphBox &);
+
+public:
+    GlyphBox(uint8 numsubs, unsigned short bitmap, Rect *slanted) : _num(numsubs), _bitmap(bitmap), _slant(*slanted) {};
+
+    void addSubBox(int subindex, int boundary, Rect *val) { _subs[subindex * 2 + boundary] = *val; }
+    Rect &subVal(int subindex, int boundary) { return _subs[subindex * 2 + boundary]; }
+    const Rect &slant() const { return _slant; }
+    uint8 num() const { return _num; }
+    const Rect *subs() const { return _subs; }
+
+private:
+    uint8   _num;
+    unsigned short  _bitmap;
+    Rect    _slant;
+    Rect    _subs[1];
+};
+
+class GlyphCache
+{
+    class Loader;
+
+    GlyphCache(const GlyphCache&);
+    GlyphCache& operator=(const GlyphCache&);
+
+public:
+    GlyphCache(const Face & face, const uint32 face_options);
+    ~GlyphCache();
+
+    unsigned short  numGlyphs() const throw();
+    unsigned short  numAttrs() const throw();
+    unsigned short  unitsPerEm() const throw();
+
+    const GlyphFace *glyph(unsigned short glyphid) const;      //result may be changed by subsequent call with a different glyphid
+    const GlyphFace *glyphSafe(unsigned short glyphid) const;
+    float            getBoundingMetric(unsigned short glyphid, uint8 metric) const;
+    uint8            numSubBounds(unsigned short glyphid) const;
+    float            getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const;
+    const Rect &     slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : _empty_slant_box; }
+    const SlantBox & getBoundingSlantBox(unsigned short glyphid) const;
+    const BBox &     getBoundingBBox(unsigned short glyphid) const;
+    const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const;
+    const BBox &     getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const;
+    bool             check(unsigned short glyphid) const;
+    bool             hasBoxes() const { return _boxes != 0; }
+
+    CLASS_NEW_DELETE;
+
+private:
+    const Rect            _empty_slant_box;
+    const Loader        * _glyph_loader;
+    const GlyphFace *   * _glyphs;
+    GlyphBox        *   * _boxes;
+    unsigned short        _num_glyphs,
+                          _num_attrs,
+                          _upem;
+};
+
+inline
+unsigned short GlyphCache::numGlyphs() const throw()
+{
+    return _num_glyphs;
+}
+
+inline
+unsigned short GlyphCache::numAttrs() const throw()
+{
+    return _num_attrs;
+}
+
+inline
+unsigned short  GlyphCache::unitsPerEm() const throw()
+{
+    return _upem;
+}
+
+inline
+bool GlyphCache::check(unsigned short glyphid) const
+{
+    return _boxes && glyphid < _num_glyphs;
+}
+
+inline
+const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const
+{
+    return glyphid < _num_glyphs ? glyph(glyphid) : NULL;
+}
+
+inline
+float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8 metric) const
+{
+    if (glyphid >= _num_glyphs) return 0.;
+    switch (metric) {
+        case 0: return (float)(glyph(glyphid)->theBBox().bl.x);                          // x_min
+        case 1: return (float)(glyph(glyphid)->theBBox().bl.y);                          // y_min
+        case 2: return (float)(glyph(glyphid)->theBBox().tr.x);                          // x_max
+        case 3: return (float)(glyph(glyphid)->theBBox().tr.y);                          // y_max
+        case 4: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.x : 0.f);    // sum_min
+        case 5: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.y : 0.f);    // diff_min
+        case 6: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.x : 0.f);    // sum_max
+        case 7: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.y : 0.f);    // diff_max
+        default: return 0.;
+    }
+}
+
+inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const
+{
+    return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : SlantBox::empty;
+}
+
+inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const
+{
+    return *(BBox *)(&(glyph(glyphid)->theBBox()));
+}
+
+inline
+float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8 subindex, uint8 metric) const
+{
+    GlyphBox *b = _boxes[glyphid];
+    if (b == NULL || subindex >= b->num()) return 0;
+
+    switch (metric) {
+        case 0: return b->subVal(subindex, 0).bl.x;
+        case 1: return b->subVal(subindex, 0).bl.y;
+        case 2: return b->subVal(subindex, 0).tr.x;
+        case 3: return b->subVal(subindex, 0).tr.y;
+        case 4: return b->subVal(subindex, 1).bl.x;
+        case 5: return b->subVal(subindex, 1).bl.y;
+        case 6: return b->subVal(subindex, 1).tr.x;
+        case 7: return b->subVal(subindex, 1).tr.y;
+        default: return 0.;
+    }
+}
+
+inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8 subindex) const
+{
+    GlyphBox *b = _boxes[glyphid];
+    return *(SlantBox *)(b->subs() + 2 * subindex + 1);
+}
+
+inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8 subindex) const
+{
+    GlyphBox *b = _boxes[glyphid];
+    return *(BBox *)(b->subs() + 2 * subindex);
+}
+
+inline
+uint8 GlyphCache::numSubBounds(unsigned short glyphid) const
+{
+    return _boxes[glyphid] ? _boxes[glyphid]->num() : 0;
+}
+
+} // namespace graphite2

+ 83 - 0
thirdparty/graphite/src/inc/GlyphFace.h

@@ -0,0 +1,83 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "inc/Main.h"
+#include "inc/Position.h"
+#include "inc/Sparse.h"
+
+namespace graphite2 {
+
+enum metrics {
+    kgmetLsb = 0, kgmetRsb,
+    kgmetBbTop, kgmetBbBottom, kgmetBbLeft, kgmetBbRight,
+    kgmetBbHeight, kgmetBbWidth,
+    kgmetAdvWidth, kgmetAdvHeight,
+    kgmetAscent, kgmetDescent
+};
+
+
+class GlyphFace
+{
+public:
+    GlyphFace();
+    template<typename I>
+    GlyphFace(const Rect & bbox, const Position & adv, I first, const I last);
+
+    const Position    & theAdvance() const;
+    const Rect        & theBBox() const { return m_bbox; }
+    const sparse      & attrs() const { return m_attrs; }
+    int32               getMetric(uint8 metric) const;
+
+    CLASS_NEW_DELETE;
+private:
+    Rect     m_bbox;        // bounding box metrics in design units
+    Position m_advance;     // Advance width and height in design units
+    sparse   m_attrs;
+};
+
+
+// Inlines: class GlyphFace
+//
+inline
+GlyphFace::GlyphFace()
+{}
+
+template<typename I>
+GlyphFace::GlyphFace(const Rect & bbox, const Position & adv, I first, const I last)
+: m_bbox(bbox),
+  m_advance(adv),
+  m_attrs(first, last)
+{
+}
+
+inline
+const Position & GlyphFace::theAdvance() const {
+    return m_advance;
+}
+
+} // namespace graphite2

+ 234 - 0
thirdparty/graphite/src/inc/Intervals.h

@@ -0,0 +1,234 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <utility>
+
+#include "inc/Main.h"
+#include "inc/List.h"
+#include "inc/json.h"
+#include "inc/Position.h"
+
+// An IntervalSet represents the possible movement of a given glyph in a given direction
+// (horizontally, vertically, or diagonally).
+// A vector is needed to represent disjoint ranges, eg, -300..-150, 20..200, 500..750.
+// Each pair represents the min/max of a sub-range.
+
+namespace graphite2 {
+
+class Segment;
+
+enum zones_t {SD, XY};
+
+class Zones
+{
+    struct Exclusion
+    {
+        template<zones_t O>
+        static Exclusion weighted(float xmin, float xmax, float f, float a0,
+                float m, float xi, float ai, float c, bool nega);
+
+        float   x,  // x position
+                xm, // xmax position
+                c,  // constant + sum(MiXi^2)
+                sm, // sum(Mi)
+                smx; // sum(MiXi)
+        bool    open;
+
+        Exclusion(float x, float w, float smi, float smxi, float c);
+        Exclusion & operator += (Exclusion const & rhs);
+        uint8 outcode(float p) const;
+
+        Exclusion   split_at(float p);
+        void        left_trim(float p);
+
+        bool        track_cost(float & cost, float & x, float origin) const;
+
+    private:
+        float test_position(float origin) const;
+        float cost(float x) const;
+     };
+
+    typedef Vector<Exclusion>                   exclusions;
+
+    typedef exclusions::iterator                iterator;
+    typedef Exclusion *                         pointer;
+    typedef Exclusion &                         reference;
+    typedef std::reverse_iterator<iterator>     reverse_iterator;
+
+public:
+    typedef exclusions::const_iterator              const_iterator;
+    typedef Exclusion const *                       const_pointer;
+    typedef Exclusion const &                       const_reference;
+    typedef std::reverse_iterator<const_iterator>   const_reverse_iterator;
+
+#if !defined GRAPHITE2_NTRACING
+    struct Debug
+    {
+        Exclusion       _excl;
+        bool            _isdel;
+        Vector<void *>  _env;
+
+        Debug(Exclusion *e, bool isdel, json *dbg) : _excl(*e), _isdel(isdel), _env(dbg->getenvs()) { };
+    };
+
+    typedef Vector<Debug>                       debugs;
+    typedef debugs::const_iterator                    idebugs;
+    void addDebug(Exclusion *e);
+    void removeDebug(float pos, float posm);
+    void setdebug(json *dbgout) { _dbg = dbgout; }
+    idebugs dbgs_begin() const { return _dbgs.begin(); }
+    idebugs dbgs_end() const { return _dbgs.end(); }
+    void jsonDbgOut(Segment *seg) const;
+    Position position() const { return Position(_pos, _posm); }
+#endif
+
+    Zones();
+    template<zones_t O>
+    void initialise(float xmin, float xmax, float margin_len, float margin_weight, float ao);
+
+    void exclude(float xmin, float xmax);
+    void exclude_with_margins(float xmin, float xmax, int axis);
+
+    template<zones_t O>
+    void weighted(float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
+    void weightedAxis(int axis, float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
+
+    float closest( float origin, float &cost) const;
+
+    const_iterator begin() const { return _exclusions.begin(); }
+    const_iterator end() const { return _exclusions.end(); }
+
+private:
+    exclusions  _exclusions;
+#if !defined GRAPHITE2_NTRACING
+    json      * _dbg;
+    debugs      _dbgs;
+#endif
+    float       _margin_len,
+                _margin_weight,
+                _pos,
+                _posm;
+
+    void            insert(Exclusion e);
+    void            remove(float x, float xm);
+    const_iterator  find_exclusion_under(float x) const;
+};
+
+
+inline
+Zones::Zones()
+: _margin_len(0), _margin_weight(0), _pos(0), _posm(0)
+{
+#if !defined GRAPHITE2_NTRACING
+    _dbg = 0;
+#endif
+    _exclusions.reserve(8);
+}
+
+inline
+Zones::Exclusion::Exclusion(float x_, float xm_, float smi, float smxi, float c_)
+: x(x_), xm(xm_), c(c_), sm(smi), smx(smxi), open(false)
+{ }
+
+template<zones_t O>
+inline
+void Zones::initialise(float xmin, float xmax, float margin_len,
+        float margin_weight, float a0)
+{
+    _margin_len = margin_len;
+    _margin_weight = margin_weight;
+    _pos = xmin;
+    _posm = xmax;
+    _exclusions.clear();
+    _exclusions.push_back(Exclusion::weighted<O>(xmin, xmax, 1, a0, 0, 0, 0, 0, false));
+    _exclusions.front().open = true;
+#if !defined GRAPHITE2_NTRACING
+    _dbgs.clear();
+#endif
+}
+
+inline
+void Zones::exclude(float xmin, float xmax) {
+    remove(xmin, xmax);
+}
+
+template<zones_t O>
+inline
+void Zones::weighted(float xmin, float xmax, float f, float a0,
+        float m, float xi, float ai, float c, bool nega) {
+    insert(Exclusion::weighted<O>(xmin, xmax, f, a0, m, xi, ai, c, nega));
+}
+
+inline
+void Zones::weightedAxis(int axis, float xmin, float xmax, float f, float a0,
+        float m, float xi, float ai, float c, bool nega) {
+    if (axis < 2)
+        weighted<XY>(xmin, xmax, f, a0, m, xi, ai, c, nega);
+    else
+        weighted<SD>(xmin, xmax, f, a0, m, xi, ai, c, nega);
+}
+
+#if !defined GRAPHITE2_NTRACING
+inline
+void Zones::addDebug(Exclusion *e) {
+    if (_dbg)
+        _dbgs.push_back(Debug(e, false, _dbg));
+}
+
+inline
+void Zones::removeDebug(float pos, float posm) {
+    if (_dbg)
+    {
+        Exclusion e(pos, posm, 0, 0, 0);
+        _dbgs.push_back(Debug(&e, true, _dbg));
+    }
+}
+#endif
+
+template<>
+inline
+Zones::Exclusion Zones::Exclusion::weighted<XY>(float xmin, float xmax, float f, float a0,
+        float m, float xi, GR_MAYBE_UNUSED float ai, float c, GR_MAYBE_UNUSED bool nega) {
+    return Exclusion(xmin, xmax,
+            m + f,
+            m * xi,
+            m * xi * xi + f * a0 * a0 + c);
+}
+
+template<>
+inline
+Zones::Exclusion Zones::Exclusion::weighted<SD>(float xmin, float xmax, float f, float a0,
+        float m, float xi, float ai,float c, bool nega) {
+    float xia = nega ? xi - ai : xi + ai;
+    return Exclusion(xmin, xmax,
+            0.25f * (m + 2.f * f),
+            0.25f * m * xia,
+            0.25f * (m * xia * xia + 2.f * f * a0 * a0) + c);
+}
+
+} // end of namespace graphite2

+ 168 - 0
thirdparty/graphite/src/inc/List.h

@@ -0,0 +1,168 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+// designed to have a limited subset of the std::vector api
+#pragma once
+
+#include <cstddef>
+#include <cassert>
+#include <cstring>
+#include <cstdlib>
+#include <new>
+
+#include "Main.h"
+
+namespace graphite2 {
+
+template <typename T>
+inline
+ptrdiff_t distance(T* first, T* last) { return last-first; }
+
+
+template <typename T>
+class Vector
+{
+    T * m_first, *m_last, *m_end;
+public:
+    typedef       T &   reference;
+    typedef const T &   const_reference;
+    typedef       T *   iterator;
+    typedef const T *   const_iterator;
+
+    Vector() : m_first(0), m_last(0), m_end(0) {}
+    Vector(size_t n, const T& value = T())      : m_first(0), m_last(0), m_end(0) { insert(begin(), n, value); }
+    Vector(const Vector<T> &rhs)                : m_first(0), m_last(0), m_end(0) { insert(begin(), rhs.begin(), rhs.end()); }
+    template <typename I>
+    Vector(I first, const I last)               : m_first(0), m_last(0), m_end(0) { insert(begin(), first, last); }
+    ~Vector() { clear(); free(m_first); }
+
+    iterator            begin()         { return m_first; }
+    const_iterator      begin() const   { return m_first; }
+
+    iterator            end()           { return m_last; }
+    const_iterator      end() const     { return m_last; }
+
+    bool                empty() const   { return m_first == m_last; }
+    size_t              size() const    { return m_last - m_first; }
+    size_t              capacity() const{ return m_end - m_first; }
+
+    void                reserve(size_t n);
+    void                resize(size_t n, const T & v = T());
+
+    reference           front()         { assert(size() > 0); return *begin(); }
+    const_reference     front() const   { assert(size() > 0); return *begin(); }
+    reference           back()          { assert(size() > 0); return *(end()-1); }
+    const_reference     back() const    { assert(size() > 0); return *(end()-1); }
+
+    Vector<T>         & operator = (const Vector<T> & rhs) { assign(rhs.begin(), rhs.end()); return *this; }
+    reference           operator [] (size_t n)          { assert(size() > n); return m_first[n]; }
+    const_reference     operator [] (size_t n) const    { assert(size() > n); return m_first[n]; }
+
+    void                assign(size_t n, const T& u)    { clear(); insert(begin(), n, u); }
+    void                assign(const_iterator first, const_iterator last)      { clear(); insert(begin(), first, last); }
+    iterator            insert(iterator p, const T & x) { p = _insert_default(p, 1); new (p) T(x); return p; }
+    void                insert(iterator p, size_t n, const T & x);
+    void                insert(iterator p, const_iterator first, const_iterator last);
+    void                pop_back()              { assert(size() > 0); --m_last; }
+    void                push_back(const T &v)   { if (m_last == m_end) reserve(size()+1); new (m_last++) T(v); }
+
+    void                clear()                 { erase(begin(), end()); }
+    iterator            erase(iterator p)       { return erase(p, p+1); }
+    iterator            erase(iterator first, iterator last);
+
+private:
+    iterator            _insert_default(iterator p, size_t n);
+};
+
+template <typename T>
+inline
+void Vector<T>::reserve(size_t n)
+{
+    if (n > capacity())
+    {
+        const ptrdiff_t sz = size();
+        size_t requested;
+        if (checked_mul(n,sizeof(T), requested))  std::abort();
+        m_first = static_cast<T*>(realloc(m_first, requested));
+        if (!m_first)   std::abort();
+        m_last  = m_first + sz;
+        m_end   = m_first + n;
+    }
+}
+
+template <typename T>
+inline
+void Vector<T>::resize(size_t n, const T & v) {
+    const ptrdiff_t d = n-size();
+    if (d < 0)      erase(end()+d, end());
+    else if (d > 0) insert(end(), d, v);
+}
+
+template<typename T>
+inline
+typename Vector<T>::iterator Vector<T>::_insert_default(iterator p, size_t n)
+{
+    assert(begin() <= p && p <= end());
+    const ptrdiff_t i = p - begin();
+    reserve(((size() + n + 7) >> 3) << 3);
+    p = begin() + i;
+    // Move tail if there is one
+    if (p != end()) memmove(p + n, p, distance(p,end())*sizeof(T));
+    m_last += n;
+    return p;
+}
+
+template<typename T>
+inline
+void Vector<T>::insert(iterator p, size_t n, const T & x)
+{
+    p = _insert_default(p, n);
+    // Copy in elements
+    for (; n; --n, ++p) { new (p) T(x); }
+}
+
+template<typename T>
+inline
+void Vector<T>::insert(iterator p, const_iterator first, const_iterator last)
+{
+    p = _insert_default(p, distance(first, last));
+    // Copy in elements
+    for (;first != last; ++first, ++p) { new (p) T(*first); }
+}
+
+template<typename T>
+inline
+typename Vector<T>::iterator Vector<T>::erase(iterator first, iterator last)
+{
+    for (iterator e = first; e != last; ++e) e->~T();
+    const size_t sz = distance(first, last);
+    if (m_last != last) memmove(first, last, distance(last,end())*sizeof(T));
+    m_last -= sz;
+    return first;
+}
+
+} // namespace graphite2

+ 207 - 0
thirdparty/graphite/src/inc/Machine.h

@@ -0,0 +1,207 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// This general interpreter interface.
+// Author: Tim Eves
+
+// Build one of direct_machine.cpp or call_machine.cpp to implement this
+// interface.
+
+#pragma once
+#include <cstring>
+#include <limits>
+#include <graphite2/Types.h>
+#include "inc/Main.h"
+
+#if defined(__GNUC__)
+#if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ * 10) < 430
+#define     HOT
+#if defined(__x86_64)
+#define     REGPARM(n)      __attribute__((regparm(n)))
+#else
+#define     REGPARM(n)
+#endif
+#else
+#define     HOT             __attribute__((hot))
+#if defined(__x86_64)
+#define     REGPARM(n)      __attribute__((hot, regparm(n)))
+#else
+#define     REGPARM(n)
+#endif
+#endif
+#else
+#define     HOT
+#define     REGPARM(n)
+#endif
+
+#if defined(__MINGW32__)
+// MinGW's <limits> at some point includes winnt.h which #define's a
+// DELETE macro, which conflicts with enum opcode below, so we undefine
+// it here.
+#undef DELETE
+#endif
+
+namespace graphite2 {
+
+// Forward declarations
+class Segment;
+class Slot;
+class SlotMap;
+
+
+namespace vm
+{
+
+
+typedef void * instr;
+typedef Slot * slotref;
+
+enum {VARARGS = 0xff, MAX_NAME_LEN=32};
+
+enum opcode {
+    NOP = 0,
+
+    PUSH_BYTE,      PUSH_BYTEU,     PUSH_SHORT,     PUSH_SHORTU,    PUSH_LONG,
+
+    ADD,            SUB,            MUL,            DIV,
+    MIN_,           MAX_,
+    NEG,
+    TRUNC8,         TRUNC16,
+
+    COND,
+
+    AND,            OR,             NOT,
+    EQUAL,          NOT_EQ,
+    LESS,           GTR,            LESS_EQ,        GTR_EQ,
+
+    NEXT,           NEXT_N,         COPY_NEXT,
+    PUT_GLYPH_8BIT_OBS,              PUT_SUBS_8BIT_OBS,   PUT_COPY,
+    INSERT,         DELETE,
+    ASSOC,
+    CNTXT_ITEM,
+
+    ATTR_SET,       ATTR_ADD,       ATTR_SUB,
+    ATTR_SET_SLOT,
+    IATTR_SET_SLOT,
+    PUSH_SLOT_ATTR,                 PUSH_GLYPH_ATTR_OBS,
+    PUSH_GLYPH_METRIC,              PUSH_FEAT,
+    PUSH_ATT_TO_GATTR_OBS,          PUSH_ATT_TO_GLYPH_METRIC,
+    PUSH_ISLOT_ATTR,
+
+    PUSH_IGLYPH_ATTR,    // not implemented
+
+    POP_RET,                        RET_ZERO,           RET_TRUE,
+    IATTR_SET,                      IATTR_ADD,          IATTR_SUB,
+    PUSH_PROC_STATE,                PUSH_VERSION,
+    PUT_SUBS,                       PUT_SUBS2,          PUT_SUBS3,
+    PUT_GLYPH,                      PUSH_GLYPH_ATTR,    PUSH_ATT_TO_GLYPH_ATTR,
+    BITOR,                          BITAND,             BITNOT,
+    BITSET,                         SET_FEAT,
+    MAX_OPCODE,
+    // private opcodes for internal use only, comes after all other on disk opcodes
+    TEMP_COPY = MAX_OPCODE
+};
+
+struct opcode_t
+{
+    instr           impl[2];
+    uint8           param_sz;
+    char            name[MAX_NAME_LEN];
+};
+
+
+class Machine
+{
+public:
+    typedef int32  stack_t;
+    static size_t const STACK_ORDER  = 10,
+                        STACK_MAX    = 1 << STACK_ORDER,
+                        STACK_GUARD  = 2;
+
+    class Code;
+
+    enum status_t {
+        finished = 0,
+        stack_underflow,
+        stack_not_empty,
+        stack_overflow,
+        slot_offset_out_bounds,
+        died_early
+    };
+
+    Machine(SlotMap &) throw();
+    static const opcode_t *   getOpcodeTable() throw();
+
+    CLASS_NEW_DELETE;
+
+    SlotMap   & slotMap() const throw();
+    status_t    status() const throw();
+//    operator bool () const throw();
+
+private:
+    void    check_final_stack(const stack_t * const sp);
+    stack_t run(const instr * program, const byte * data,
+                slotref * & map) HOT;
+
+    SlotMap       & _map;
+    stack_t         _stack[STACK_MAX + 2*STACK_GUARD];
+    status_t        _status;
+};
+
+inline Machine::Machine(SlotMap & map) throw()
+: _map(map), _status(finished)
+{
+    // Initialise stack guard +1 entries as the stack pointer points to the
+    //  current top of stack, hence the first push will never write entry 0.
+    // Initialising the guard space like this is unnecessary and is only
+    //  done to keep valgrind happy during fuzz testing.  Hopefully loop
+    //  unrolling will flatten this.
+    for (size_t n = STACK_GUARD + 1; n; --n)  _stack[n-1] = 0;
+}
+
+inline SlotMap& Machine::slotMap() const throw()
+{
+    return _map;
+}
+
+inline Machine::status_t Machine::status() const throw()
+{
+    return _status;
+}
+
+inline void Machine::check_final_stack(const stack_t * const sp)
+{
+    if (_status != finished) return;
+
+    stack_t const * const base  = _stack + STACK_GUARD,
+                  * const limit = base + STACK_MAX;
+    if      (sp <  base)    _status = stack_underflow;       // This should be impossible now.
+    else if (sp >= limit)   _status = stack_overflow;        // So should this.
+    else if (sp != base)    _status = stack_not_empty;
+}
+
+} // namespace vm
+} // namespace graphite2

+ 199 - 0
thirdparty/graphite/src/inc/Main.h

@@ -0,0 +1,199 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <cstdlib>
+#include "graphite2/Types.h"
+
+#ifdef GRAPHITE2_CUSTOM_HEADER
+#include GRAPHITE2_CUSTOM_HEADER
+#endif
+
+namespace graphite2 {
+
+typedef gr_uint8        uint8;
+typedef gr_uint8        byte;
+typedef gr_uint16       uint16;
+typedef gr_uint32       uint32;
+typedef gr_int8         int8;
+typedef gr_int16        int16;
+typedef gr_int32        int32;
+typedef size_t          uintptr;
+
+#ifdef GRAPHITE2_TELEMETRY
+struct telemetry
+{
+    class category;
+
+    static size_t   * _category;
+    static void set_category(size_t & t) throw()    { _category = &t; }
+    static void stop() throw()                      { _category = 0; }
+    static void count_bytes(size_t n) throw()       { if (_category) *_category += n; }
+
+    size_t  misc,
+            silf,
+            glyph,
+            code,
+            states,
+            starts,
+            transitions;
+
+    telemetry() : misc(0), silf(0), glyph(0), code(0), states(0), starts(0), transitions(0) {}
+};
+
+class telemetry::category
+{
+    size_t * _prev;
+public:
+    category(size_t & t) : _prev(_category) { _category = &t; }
+    ~category() { _category = _prev; }
+};
+
+#else
+struct telemetry  {};
+#endif
+
+// Checked multiplaction to catch overflow or underflow when allocating memory
+#if defined(__has_builtin)
+  #if __has_builtin(__builtin_mul_overflow)
+    #define HAVE_BUILTIN_OVERFLOW
+  #endif
+#elif defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__INTEL_COMPILER)
+  #define HAVE_BUILTIN_OVERFLOW
+#endif
+#if defined(__has_include)
+  #if __has_include(<intsafe.h>) && !defined(__CYGWIN__)
+    #define HAVE_INTSAFE_H
+  #endif
+#elif defined(_WIN32)
+  #define HAVE_INTSAFE_H
+#endif
+
+// Need to import intsafe into the top level namespace
+#if defined(HAVE_INTSAFE_H)
+} // namespace graphite2
+
+#include <intsafe.h>
+
+namespace graphite2 {
+#endif
+
+#if defined(HAVE_BUILTIN_OVERFLOW)
+inline
+bool checked_mul(const size_t a, const size_t b, size_t & t) {
+    return __builtin_mul_overflow(a, b, &t);
+}
+#elif defined(HAVE_INTSAFE_H)
+inline
+bool checked_mul(const size_t a, const size_t b, size_t & t) {
+    return SizeTMult(a, b, &t) == INTSAFE_E_ARITHMETIC_OVERFLOW;
+}
+#else
+inline
+bool checked_mul(const size_t a, const size_t b, size_t & t) {
+  t = a*b;
+  return (((a | b) & (~size_t(0) << (sizeof(size_t) << 2))) && (t / a != b));
+}
+#endif
+
+// typesafe wrapper around malloc for simple types
+// use free(pointer) to deallocate
+
+template <typename T> T * gralloc(size_t n)
+{
+    size_t total;
+    if (checked_mul(n, sizeof(T), total))
+      return 0;
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::count_bytes(total);
+#endif
+    return static_cast<T*>(malloc(total));
+}
+
+template <typename T> T * grzeroalloc(size_t n)
+{
+#ifdef GRAPHITE2_TELEMETRY
+    telemetry::count_bytes(sizeof(T) * n);
+#endif
+    return static_cast<T*>(calloc(n, sizeof(T)));
+}
+
+template <typename T>
+inline T min(const T a, const T b)
+{
+    return a < b ? a : b;
+}
+
+template <typename T>
+inline T max(const T a, const T b)
+{
+    return a > b ? a : b;
+}
+
+} // namespace graphite2
+
+#define CLASS_NEW_DELETE \
+    void * operator new   (size_t size){ return gralloc<byte>(size);} \
+    void * operator new   (size_t, void * p) throw() { return p; } \
+    void * operator new[] (size_t size) {return gralloc<byte>(size);} \
+    void * operator new[] (size_t, void * p) throw() { return p; } \
+    void operator delete   (void * p) throw() { free(p);} \
+    void operator delete   (void *, void *) throw() {} \
+    void operator delete[] (void * p)throw() { free(p); } \
+    void operator delete[] (void *, void *) throw() {}
+
+#if defined(__GNUC__)  || defined(__clang__)
+#define GR_MAYBE_UNUSED __attribute__((unused))
+#else
+#define GR_MAYBE_UNUSED
+#endif
+
+#ifndef __has_cpp_attribute
+#  define __has_cpp_attribute(x) 0
+#endif
+
+#if __has_cpp_attribute(clang::fallthrough)
+#  define GR_FALLTHROUGH [[clang::fallthrough]]
+#elif __has_cpp_attribute(gnu::fallthrough)
+#  define GR_FALLTHROUGH [[gnu::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 GR_FALLTHROUGH __fallthrough
+#elif __GNUC__ >= 7
+    #define GR_FALLTHROUGH __attribute__ ((fallthrough))
+#else
+    #define GR_FALLTHROUGH /* fallthrough */
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4800)
+#pragma warning(disable: 4355)
+#endif

+ 65 - 0
thirdparty/graphite/src/inc/NameTable.h

@@ -0,0 +1,65 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <graphite2/Segment.h>
+#include "inc/TtfTypes.h"
+#include "inc/locale2lcid.h"
+
+namespace graphite2 {
+
+class NameTable
+{
+    NameTable(const NameTable &);
+    NameTable & operator = (const NameTable &);
+
+public:
+    NameTable(const void * data, size_t length, uint16 platfromId=3, uint16 encodingID = 1);
+    ~NameTable() { free(const_cast<TtfUtil::Sfnt::FontNames *>(m_table)); }
+    enum eNameFallback {
+        eNoFallback = 0,
+        eEnUSFallbackOnly = 1,
+        eEnOrAnyFallback = 2
+    };
+    uint16 setPlatformEncoding(uint16 platfromId=3, uint16 encodingID = 1);
+    void * getName(uint16 & languageId, uint16 nameId, gr_encform enc, uint32 & length);
+    uint16 getLanguageId(const char * bcp47Locale);
+
+    CLASS_NEW_DELETE
+private:
+    uint16 m_platformId;
+    uint16 m_encodingId;
+    uint16 m_languageCount;
+    uint16 m_platformOffset; // offset of first NameRecord with for platform 3, encoding 1
+    uint16 m_platformLastRecord;
+    uint16 m_nameDataLength;
+    const TtfUtil::Sfnt::FontNames * m_table;
+    const uint8 * m_nameData;
+    Locale2Lang m_locale2Lang;
+};
+
+} // namespace graphite2

+ 118 - 0
thirdparty/graphite/src/inc/Pass.h

@@ -0,0 +1,118 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <cstdlib>
+#include "inc/Code.h"
+
+namespace graphite2 {
+
+class Segment;
+class Face;
+class Silf;
+struct Rule;
+struct RuleEntry;
+struct State;
+class FiniteStateMachine;
+class Error;
+class ShiftCollider;
+class KernCollider;
+class json;
+
+enum passtype;
+
+class Pass
+{
+public:
+    Pass();
+    ~Pass();
+
+    bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face,
+        enum passtype pt, uint32 version, Error &e);
+    bool runGraphite(vm::Machine & m, FiniteStateMachine & fsm, bool reverse) const;
+    void init(Silf *silf) { m_silf = silf; }
+    byte collisionLoops() const { return m_numCollRuns; }
+    bool reverseDir() const { return m_isReverseDir; }
+
+    CLASS_NEW_DELETE
+private:
+    void    findNDoRule(Slot* & iSlot, vm::Machine &, FiniteStateMachine& fsm) const;
+    int     doAction(const vm::Machine::Code* codeptr, Slot * & slot_out, vm::Machine &) const;
+    bool    testPassConstraint(vm::Machine & m) const;
+    bool    testConstraint(const Rule & r, vm::Machine &) const;
+    bool    readRules(const byte * rule_map, const size_t num_entries,
+                     const byte *precontext, const uint16 * sort_key,
+                     const uint16 * o_constraint, const byte *constraint_data,
+                     const uint16 * o_action, const byte * action_data,
+                     Face &, enum passtype pt, Error &e);
+    bool    readStates(const byte * starts, const byte * states, const byte * o_rule_map, Face &, Error &e);
+    bool    readRanges(const byte * ranges, size_t num_ranges, Error &e);
+    uint16  glyphToCol(const uint16 gid) const;
+    bool    runFSM(FiniteStateMachine & fsm, Slot * slot) const;
+    void    dumpRuleEventConsidered(const FiniteStateMachine & fsm, const RuleEntry & re) const;
+    void    dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * os) const;
+    void    adjustSlot(int delta, Slot * & slot_out, SlotMap &) const;
+    bool    collisionShift(Segment *seg, int dir, json * const dbgout) const;
+    bool    collisionKern(Segment *seg, int dir, json * const dbgout) const;
+    bool    collisionFinish(Segment *seg, GR_MAYBE_UNUSED json * const dbgout) const;
+    bool    resolveCollisions(Segment *seg, Slot *slot, Slot *start, ShiftCollider &coll, bool isRev,
+                     int dir, bool &moved, bool &hasCol, json * const dbgout) const;
+    float   resolveKern(Segment *seg, Slot *slot, Slot *start, int dir,
+                     float &ymin, float &ymax, json *const dbgout) const;
+
+    const Silf        * m_silf;
+    uint16            * m_cols;
+    Rule              * m_rules; // rules
+    RuleEntry         * m_ruleMap;
+    uint16            * m_startStates; // prectxt length
+    uint16            * m_transitions;
+    State             * m_states;
+    vm::Machine::Code * m_codes;
+    byte              * m_progs;
+
+    byte   m_numCollRuns;
+    byte   m_kernColls;
+    byte   m_iMaxLoop;
+    uint16 m_numGlyphs;
+    uint16 m_numRules;
+    uint16 m_numStates;
+    uint16 m_numTransition;
+    uint16 m_numSuccess;
+    uint16 m_successStart;
+    uint16 m_numColumns;
+    byte m_minPreCtxt;
+    byte m_maxPreCtxt;
+    byte m_colThreshold;
+    bool m_isReverseDir;
+    vm::Machine::Code m_cPConstraint;
+
+private:        //defensive
+    Pass(const Pass&);
+    Pass& operator=(const Pass&);
+};
+
+} // namespace graphite2

+ 68 - 0
thirdparty/graphite/src/inc/Position.h

@@ -0,0 +1,68 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+namespace graphite2 {
+
+class Position
+{
+public:
+    Position() : x(0), y(0) { }
+    Position(const float inx, const float iny) : x(inx), y(iny) {}
+    Position operator + (const Position& a) const { return Position(x + a.x, y + a.y); }
+    Position operator - (const Position& a) const { return Position(x - a.x, y - a.y); }
+    Position operator * (const float m) const { return Position(x * m, y * m); }
+    Position &operator += (const Position &a) { x += a.x; y += a.y; return *this; }
+    Position &operator *= (const float m) { x *= m; y *= m; return *this; }
+
+    float x;
+    float y;
+};
+
+class Rect
+{
+public :
+    Rect() {}
+    Rect(const Position& botLeft, const Position& topRight): bl(botLeft), tr(topRight) {}
+    Rect widen(const Rect& other) { return Rect(Position(bl.x > other.bl.x ? other.bl.x : bl.x, bl.y > other.bl.y ? other.bl.y : bl.y), Position(tr.x > other.tr.x ? tr.x : other.tr.x, tr.y > other.tr.y ? tr.y : other.tr.y)); }
+    Rect operator + (const Position &a) const { return Rect(Position(bl.x + a.x, bl.y + a.y), Position(tr.x + a.x, tr.y + a.y)); }
+    Rect operator - (const Position &a) const { return Rect(Position(bl.x - a.x, bl.y - a.y), Position(tr.x - a.x, tr.y - a.y)); }
+    Rect operator * (float m) const { return Rect(Position(bl.x, bl.y) * m, Position(tr.x, tr.y) * m); }
+    float width() const { return tr.x - bl.x; }
+    float height() const { return tr.y - bl.y; }
+
+    bool hitTest(Rect &other);
+
+    // returns Position(overlapx, overlapy) where overlap<0 if overlapping else positive)
+    Position overlap(Position &offset, Rect &other, Position &otherOffset);
+    //Position constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox);
+
+    Position bl;
+    Position tr;
+};
+
+} // namespace graphite2

+ 305 - 0
thirdparty/graphite/src/inc/Rule.h

@@ -0,0 +1,305 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+
+#pragma once
+
+#include "inc/Code.h"
+#include "inc/Slot.h"
+
+namespace graphite2 {
+
+struct Rule {
+  const vm::Machine::Code * constraint,
+                 * action;
+  unsigned short   sort;
+  byte             preContext;
+#ifndef NDEBUG
+  uint16           rule_idx;
+#endif
+
+  Rule();
+  ~Rule() {}
+
+  CLASS_NEW_DELETE;
+
+private:
+  Rule(const Rule &);
+  Rule & operator = (const Rule &);
+};
+
+inline
+Rule::Rule()
+: constraint(0),
+  action(0),
+  sort(0),
+  preContext(0)
+{
+#ifndef NDEBUG
+  rule_idx = 0;
+#endif
+}
+
+
+struct RuleEntry
+{
+  const Rule   * rule;
+
+  inline
+  bool operator < (const RuleEntry &r) const
+  {
+    const unsigned short lsort = rule->sort, rsort = r.rule->sort;
+    return lsort > rsort || (lsort == rsort && rule < r.rule);
+  }
+
+  inline
+  bool operator == (const RuleEntry &r) const
+  {
+    return rule == r.rule;
+  }
+};
+
+
+struct State
+{
+  const RuleEntry     * rules,
+                      * rules_end;
+
+  bool   empty() const;
+};
+
+inline
+bool State::empty() const
+{
+    return rules_end == rules;
+}
+
+
+class SlotMap
+{
+public:
+  enum {MAX_SLOTS=64};
+  SlotMap(Segment & seg, uint8 direction, size_t maxSize);
+
+  Slot       * * begin();
+  Slot       * * end();
+  size_t         size() const;
+  unsigned short context() const;
+  void           reset(Slot &, unsigned short);
+
+  Slot * const & operator[](int n) const;
+  Slot       * & operator [] (int);
+  void           pushSlot(Slot * const slot);
+  void           collectGarbage(Slot *& aSlot);
+
+  Slot         * highwater() { return m_highwater; }
+  void           highwater(Slot *s) { m_highwater = s; m_highpassed = false; }
+  bool           highpassed() const { return m_highpassed; }
+  void           highpassed(bool v) { m_highpassed = v; }
+
+  uint8          dir() const { return m_dir; }
+  int            decMax() { return --m_maxSize; }
+
+  Segment &    segment;
+private:
+  Slot         * m_slot_map[MAX_SLOTS+1];
+  unsigned short m_size;
+  unsigned short m_precontext;
+  Slot         * m_highwater;
+  int            m_maxSize;
+  uint8          m_dir;
+  bool           m_highpassed;
+};
+
+
+class FiniteStateMachine
+{
+public:
+  enum {MAX_RULES=128};
+
+private:
+  class Rules
+  {
+  public:
+      Rules();
+      void              clear();
+      const RuleEntry * begin() const;
+      const RuleEntry * end() const;
+      size_t            size() const;
+
+      void accumulate_rules(const State &state);
+
+  private:
+      RuleEntry * m_begin,
+                * m_end,
+                  m_rules[MAX_RULES*2];
+  };
+
+public:
+  FiniteStateMachine(SlotMap & map, json * logger);
+  void      reset(Slot * & slot, const short unsigned int max_pre_ctxt);
+
+  Rules     rules;
+  SlotMap   & slots;
+  json    * const dbgout;
+};
+
+
+inline
+FiniteStateMachine::FiniteStateMachine(SlotMap& map, json * logger)
+: slots(map),
+  dbgout(logger)
+{
+}
+
+inline
+void FiniteStateMachine::reset(Slot * & slot, const short unsigned int max_pre_ctxt)
+{
+  rules.clear();
+  int ctxt = 0;
+  for (; ctxt != max_pre_ctxt && slot->prev(); ++ctxt, slot = slot->prev());
+  slots.reset(*slot, ctxt);
+}
+
+inline
+FiniteStateMachine::Rules::Rules()
+  : m_begin(m_rules), m_end(m_rules)
+{
+}
+
+inline
+void FiniteStateMachine::Rules::clear()
+{
+  m_end = m_begin;
+}
+
+inline
+const RuleEntry * FiniteStateMachine::Rules::begin() const
+{
+  return m_begin;
+}
+
+inline
+const RuleEntry * FiniteStateMachine::Rules::end() const
+{
+  return m_end;
+}
+
+inline
+size_t FiniteStateMachine::Rules::size() const
+{
+  return m_end - m_begin;
+}
+
+inline
+void FiniteStateMachine::Rules::accumulate_rules(const State &state)
+{
+  // Only bother if there are rules in the State object.
+  if (state.empty()) return;
+
+  // Merge the new sorted rules list into the current sorted result set.
+  const RuleEntry * lre = begin(), * rre = state.rules;
+  RuleEntry * out = m_rules + (m_begin == m_rules)*MAX_RULES;
+  const RuleEntry * const lrend = out + MAX_RULES,
+                  * const rrend = state.rules_end;
+  m_begin = out;
+  while (lre != end() && out != lrend)
+  {
+    if (*lre < *rre)      *out++ = *lre++;
+    else if (*rre < *lre) { *out++ = *rre++; }
+    else                { *out++ = *lre++; ++rre; }
+
+    if (rre == rrend)
+    {
+      while (lre != end() && out != lrend) { *out++ = *lre++; }
+      m_end = out;
+      return;
+    }
+  }
+  while (rre != rrend && out != lrend) { *out++ = *rre++; }
+  m_end = out;
+}
+
+inline
+SlotMap::SlotMap(Segment & seg, uint8 direction, size_t maxSize)
+: segment(seg), m_size(0), m_precontext(0), m_highwater(0),
+    m_maxSize(int(maxSize)), m_dir(direction), m_highpassed(false)
+{
+    m_slot_map[0] = 0;
+}
+
+inline
+Slot * * SlotMap::begin()
+{
+  return &m_slot_map[1]; // allow map to go 1 before slot_map when inserting
+                         // at start of segment.
+}
+
+inline
+Slot * * SlotMap::end()
+{
+  return m_slot_map + m_size + 1;
+}
+
+inline
+size_t SlotMap::size() const
+{
+  return m_size;
+}
+
+inline
+short unsigned int SlotMap::context() const
+{
+  return m_precontext;
+}
+
+inline
+void SlotMap::reset(Slot & slot, short unsigned int ctxt)
+{
+  m_size = 0;
+  m_precontext = ctxt;
+  *m_slot_map = slot.prev();
+}
+
+inline
+void SlotMap::pushSlot(Slot*const slot)
+{
+  m_slot_map[++m_size] = slot;
+}
+
+inline
+Slot * const & SlotMap::operator[](int n) const
+{
+  return m_slot_map[n + 1];
+}
+
+inline
+Slot * & SlotMap::operator[](int n)
+{
+  return m_slot_map[n + 1];
+}
+
+} // namespace graphite2

+ 236 - 0
thirdparty/graphite/src/inc/Segment.h

@@ -0,0 +1,236 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "inc/Main.h"
+
+#include <cassert>
+
+#include "inc/CharInfo.h"
+#include "inc/Face.h"
+#include "inc/FeatureVal.h"
+#include "inc/GlyphCache.h"
+#include "inc/GlyphFace.h"
+#include "inc/Slot.h"
+#include "inc/Position.h"
+#include "inc/List.h"
+#include "inc/Collider.h"
+
+#define MAX_SEG_GROWTH_FACTOR  64
+
+namespace graphite2 {
+
+typedef Vector<Features>        FeatureList;
+typedef Vector<Slot *>          SlotRope;
+typedef Vector<int16 *>         AttributeRope;
+typedef Vector<SlotJustify *>   JustifyRope;
+
+class Font;
+class Segment;
+class Silf;
+
+enum SpliceParam {
+/** sub-Segments longer than this are not cached
+ * (in Unicode code points) */
+    eMaxSpliceSize = 96
+};
+
+enum justFlags {
+    gr_justStartInline = 1,
+    gr_justEndInline = 2
+};
+
+class SegmentScopeState
+{
+private:
+    friend class Segment;
+    Slot * realFirstSlot;
+    Slot * slotBeforeScope;
+    Slot * slotAfterScope;
+    Slot * realLastSlot;
+    size_t numGlyphsOutsideScope;
+};
+
+class Segment
+{
+    // Prevent copying of any kind.
+    Segment(const Segment&);
+    Segment& operator=(const Segment&);
+
+public:
+
+    enum {
+        SEG_INITCOLLISIONS = 1,
+        SEG_HASCOLLISIONS = 2
+    };
+
+    size_t slotCount() const { return m_numGlyphs; }      //one slot per glyph
+    void extendLength(ptrdiff_t num) { m_numGlyphs += num; }
+    Position advance() const { return m_advance; }
+    bool runGraphite() { if (m_silf) return m_face->runGraphite(this, m_silf); else return true;};
+    void chooseSilf(uint32 script) { m_silf = m_face->chooseSilf(script); }
+    const Silf *silf() const { return m_silf; }
+    size_t charInfoCount() const { return m_numCharinfo; }
+    const CharInfo *charinfo(unsigned int index) const { return index < m_numCharinfo ? m_charinfo + index : NULL; }
+    CharInfo *charinfo(unsigned int index) { return index < m_numCharinfo ? m_charinfo + index : NULL; }
+
+    Segment(size_t numchars, const Face* face, uint32 script, int dir);
+    ~Segment();
+    uint8 flags() const { return m_flags; }
+    void flags(uint8 f) { m_flags = f; }
+    Slot *first() { return m_first; }
+    void first(Slot *p) { m_first = p; }
+    Slot *last() { return m_last; }
+    void last(Slot *p) { m_last = p; }
+    void appendSlot(int i, int cid, int gid, int fid, size_t coffset);
+    Slot *newSlot();
+    void freeSlot(Slot *);
+    SlotJustify *newJustify();
+    void freeJustify(SlotJustify *aJustify);
+    Position positionSlots(const Font *font=0, Slot *first=0, Slot *last=0, bool isRtl = false, bool isFinal = true);
+    void associateChars(int offset, size_t num);
+    void linkClusters(Slot *first, Slot *last);
+    uint16 getClassGlyph(uint16 cid, uint16 offset) const { return m_silf->getClassGlyph(cid, offset); }
+    uint16 findClassIndex(uint16 cid, uint16 gid) const { return m_silf->findClassIndex(cid, gid); }
+    int addFeatures(const Features& feats) { m_feats.push_back(feats); return int(m_feats.size()) - 1; }
+    uint32 getFeature(int index, uint8 findex) const { const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); if (!pFR) return 0; else return pFR->getFeatureVal(m_feats[index]); }
+    void setFeature(int index, uint8 findex, uint32 val) {
+        const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex);
+        if (pFR)
+        {
+            if (val > pFR->maxVal()) val = pFR->maxVal();
+            pFR->applyValToFeature(val, m_feats[index]);
+        } }
+    int8 dir() const { return m_dir; }
+    void dir(int8 val) { m_dir = val; }
+    bool currdir() const { return ((m_dir >> 6) ^ m_dir) & 1; }
+    uint8 passBits() const { return m_passBits; }
+    void mergePassBits(const uint8 val) { m_passBits &= val; }
+    int16 glyphAttr(uint16 gid, uint16 gattr) const { const GlyphFace * p = m_face->glyphs().glyphSafe(gid); return p ? p->attrs()[gattr] : 0; }
+    int32 getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const;
+    float glyphAdvance(uint16 gid) const { return m_face->glyphs().glyph(gid)->theAdvance().x; }
+    const Rect &theGlyphBBoxTemporary(uint16 gid) const { return m_face->glyphs().glyph(gid)->theBBox(); }   //warning value may become invalid when another glyph is accessed
+    Slot *findRoot(Slot *is) const { return is->attachedTo() ? findRoot(is->attachedTo()) : is; }
+    int numAttrs() const { return m_silf->numUser(); }
+    int defaultOriginal() const { return m_defaultOriginal; }
+    const Face * getFace() const { return m_face; }
+    const Features & getFeatures(unsigned int /*charIndex*/) { assert(m_feats.size() == 1); return m_feats[0]; }
+    void bidiPass(int paradir, uint8 aMirror);
+    int8 getSlotBidiClass(Slot *s) const;
+    void doMirror(uint16 aMirror);
+    Slot *addLineEnd(Slot *nSlot);
+    void delLineEnd(Slot *s);
+    bool hasJustification() const { return m_justifies.size() != 0; }
+    void reverseSlots();
+
+    bool isWhitespace(const int cid) const;
+    bool hasCollisionInfo() const { return (m_flags & SEG_HASCOLLISIONS) && m_collisions; }
+    SlotCollision *collisionInfo(const Slot *s) const { return m_collisions ? m_collisions + s->index() : 0; }
+    CLASS_NEW_DELETE
+
+public:       //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32 script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir);
+    bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
+    void finalise(const Font *font, bool reverse=false);
+    float justify(Slot *pSlot, const Font *font, float width, enum justFlags flags, Slot *pFirst, Slot *pLast);
+    bool initCollisions();
+
+private:
+    Position        m_advance;          // whole segment advance
+    SlotRope        m_slots;            // Vector of slot buffers
+    AttributeRope   m_userAttrs;        // Vector of userAttrs buffers
+    JustifyRope     m_justifies;        // Slot justification info buffers
+    FeatureList     m_feats;            // feature settings referenced by charinfos in this segment
+    Slot          * m_freeSlots;        // linked list of free slots
+    SlotJustify   * m_freeJustifies;    // Slot justification blocks free list
+    CharInfo      * m_charinfo;         // character info, one per input character
+    SlotCollision * m_collisions;
+    const Face    * m_face;             // GrFace
+    const Silf    * m_silf;
+    Slot          * m_first;            // first slot in segment
+    Slot          * m_last;             // last slot in segment
+    size_t          m_bufSize,          // how big a buffer to create when need more slots
+                    m_numGlyphs,
+                    m_numCharinfo;      // size of the array and number of input characters
+    int             m_defaultOriginal;  // number of whitespace chars in the string
+    int8            m_dir;
+    uint8           m_flags,            // General purpose flags
+                    m_passBits;         // if bit set then skip pass
+};
+
+inline
+int8 Segment::getSlotBidiClass(Slot *s) const
+{
+    int8 res = s->getBidiClass();
+    if (res != -1) return res;
+    res = int8(glyphAttr(s->gid(), m_silf->aBidi()));
+    s->setBidiClass(res);
+    return res;
+}
+
+inline
+void Segment::finalise(const Font *font, bool reverse)
+{
+    if (!m_first || !m_last) return;
+
+    m_advance = positionSlots(font, m_first, m_last, m_silf->dir(), true);
+    //associateChars(0, m_numCharinfo);
+    if (reverse && currdir() != (m_dir & 1))
+        reverseSlots();
+    linkClusters(m_first, m_last);
+}
+
+inline
+int32 Segment::getGlyphMetric(Slot *iSlot, uint8 metric, uint8 attrLevel, bool rtl) const {
+    if (attrLevel > 0)
+    {
+        Slot *is = findRoot(iSlot);
+        return is->clusterMetric(this, metric, attrLevel, rtl);
+    }
+    else
+        return m_face->getGlyphMetric(iSlot->gid(), metric);
+}
+
+inline
+bool Segment::isWhitespace(const int cid) const
+{
+    return ((cid >= 0x0009) * (cid <= 0x000D)
+         + (cid == 0x0020)
+         + (cid == 0x0085)
+         + (cid == 0x00A0)
+         + (cid == 0x1680)
+         + (cid == 0x180E)
+         + (cid >= 0x2000) * (cid <= 0x200A)
+         + (cid == 0x2028)
+         + (cid == 0x2029)
+         + (cid == 0x202F)
+         + (cid == 0x205F)
+         + (cid == 0x3000)) != 0;
+}
+
+} // namespace graphite2
+
+struct gr_segment : public graphite2::Segment {};

+ 128 - 0
thirdparty/graphite/src/inc/Silf.h

@@ -0,0 +1,128 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "graphite2/Font.h"
+#include "inc/Main.h"
+#include "inc/Pass.h"
+
+namespace graphite2 {
+
+class Face;
+class Segment;
+class FeatureVal;
+class VMScratch;
+class Error;
+
+class Pseudo
+{
+public:
+    uint32 uid;
+    uint32 gid;
+    CLASS_NEW_DELETE;
+};
+
+class Justinfo
+{
+public:
+    Justinfo(uint8 stretch, uint8 shrink, uint8 step, uint8 weight) :
+        m_astretch(stretch), m_ashrink(shrink), m_astep(step),
+        m_aweight(weight) {};
+    uint8 attrStretch() const { return m_astretch; }
+    uint8 attrShrink() const { return m_ashrink; }
+    uint8 attrStep() const { return m_astep; }
+    uint8 attrWeight() const { return m_aweight; }
+
+private:
+    uint8   m_astretch;
+    uint8   m_ashrink;
+    uint8   m_astep;
+    uint8   m_aweight;
+};
+
+class Silf
+{
+    // Prevent copying
+    Silf(const Silf&);
+    Silf& operator=(const Silf&);
+
+public:
+    Silf() throw();
+    ~Silf() throw();
+
+    bool readGraphite(const byte * const pSilf, size_t lSilf, Face &face, uint32 version);
+    bool runGraphite(Segment *seg, uint8 firstPass=0, uint8 lastPass=0, int dobidi = 0) const;
+    uint16 findClassIndex(uint16 cid, uint16 gid) const;
+    uint16 getClassGlyph(uint16 cid, unsigned int index) const;
+    uint16 findPseudo(uint32 uid) const;
+    uint8 numUser() const { return m_aUser; }
+    uint8 aPseudo() const { return m_aPseudo; }
+    uint8 aBreak() const { return m_aBreak; }
+    uint8 aMirror() const {return m_aMirror; }
+    uint8 aPassBits() const { return m_aPassBits; }
+    uint8 aBidi() const { return m_aBidi; }
+    uint8 aCollision() const { return m_aCollision; }
+    uint8 substitutionPass() const { return m_sPass; }
+    uint8 positionPass() const { return m_pPass; }
+    uint8 justificationPass() const { return m_jPass; }
+    uint8 bidiPass() const { return m_bPass; }
+    uint8 numPasses() const { return m_numPasses; }
+    uint8 maxCompPerLig() const { return m_iMaxComp; }
+    uint16 numClasses() const { return m_nClass; }
+    byte  flags() const { return m_flags; }
+    byte  dir() const { return m_dir; }
+    uint8 numJustLevels() const { return m_numJusts; }
+    Justinfo *justAttrs() const { return m_justs; }
+    uint16 endLineGlyphid() const { return m_gEndLine; }
+    const gr_faceinfo *silfInfo() const { return &m_silfinfo; }
+
+    CLASS_NEW_DELETE;
+
+private:
+    size_t readClassMap(const byte *p, size_t data_len, uint32 version, Error &e);
+    template<typename T> inline uint32 readClassOffsets(const byte *&p, size_t data_len, Error &e);
+
+    Pass          * m_passes;
+    Pseudo        * m_pseudos;
+    uint32        * m_classOffsets;
+    uint16        * m_classData;
+    Justinfo      * m_justs;
+    uint8           m_numPasses;
+    uint8           m_numJusts;
+    uint8           m_sPass, m_pPass, m_jPass, m_bPass,
+                    m_flags, m_dir;
+
+    uint8       m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
+                m_iMaxComp, m_aCollision;
+    uint16      m_aLig, m_numPseudo, m_nClass, m_nLinear,
+                m_gEndLine;
+    gr_faceinfo m_silfinfo;
+
+    void releaseBuffers() throw();
+};
+
+} // namespace graphite2

+ 170 - 0
thirdparty/graphite/src/inc/Slot.h

@@ -0,0 +1,170 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include "graphite2/Types.h"
+#include "graphite2/Segment.h"
+#include "inc/Main.h"
+#include "inc/Font.h"
+#include "inc/Position.h"
+
+namespace graphite2 {
+
+typedef gr_attrCode attrCode;
+
+class GlyphFace;
+class Segment;
+
+struct SlotJustify
+{
+    static const int NUMJUSTPARAMS = 5;
+
+    SlotJustify(const SlotJustify &);
+    SlotJustify & operator = (const SlotJustify &);
+
+public:
+    static size_t size_of(size_t levels) { return sizeof(SlotJustify) + ((levels > 1 ? levels : 1)*NUMJUSTPARAMS - 1)*sizeof(int16); }
+
+    void LoadSlot(const Slot *s, const Segment *seg);
+
+    SlotJustify *next;
+    int16 values[1];
+};
+
+class Slot
+{
+    enum Flag
+    {
+        DELETED     = 1,
+        INSERTED    = 2,
+        COPIED      = 4,
+        POSITIONED  = 8,
+        ATTACHED    = 16
+    };
+
+public:
+    struct iterator;
+
+    unsigned short gid() const { return m_glyphid; }
+    Position origin() const { return m_position; }
+    float advance() const { return m_advance.x; }
+    void advance(Position &val) { m_advance = val; }
+    Position advancePos() const { return m_advance; }
+    int before() const { return m_before; }
+    int after() const { return m_after; }
+    uint32 index() const { return m_index; }
+    void index(uint32 val) { m_index = val; }
+
+    Slot(int16 *m_userAttr = NULL);
+    void set(const Slot & slot, int charOffset, size_t numUserAttr, size_t justLevels, size_t numChars);
+    Slot *next() const { return m_next; }
+    void next(Slot *s) { m_next = s; }
+    Slot *prev() const { return m_prev; }
+    void prev(Slot *s) { m_prev = s; }
+    uint16 glyph() const { return m_realglyphid ? m_realglyphid : m_glyphid; }
+    void setGlyph(Segment *seg, uint16 glyphid, const GlyphFace * theGlyph = NULL);
+    void setRealGid(uint16 realGid) { m_realglyphid = realGid; }
+    void adjKern(const Position &pos) { m_shift = m_shift + pos; m_advance = m_advance + pos; }
+    void origin(const Position &pos) { m_position = pos + m_shift; }
+    void originate(int ind) { m_original = ind; }
+    int original() const { return m_original; }
+    void before(int ind) { m_before = ind; }
+    void after(int ind) { m_after = ind; }
+    bool isBase() const { return (!m_parent); }
+    void update(int numSlots, int numCharInfo, Position &relpos);
+    Position finalise(const Segment* seg, const Font* font, Position & base, Rect & bbox, uint8 attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth = 0);
+    bool isDeleted() const { return (m_flags & DELETED) ? true : false; }
+    void markDeleted(bool state) { if (state) m_flags |= DELETED; else m_flags &= ~DELETED; }
+    bool isCopied() const { return (m_flags & COPIED) ? true : false; }
+    void markCopied(bool state) { if (state) m_flags |= COPIED; else m_flags &= ~COPIED; }
+    bool isPositioned() const { return (m_flags & POSITIONED) ? true : false; }
+    void markPositioned(bool state) { if (state) m_flags |= POSITIONED; else m_flags &= ~POSITIONED; }
+    bool isInsertBefore() const { return !(m_flags & INSERTED); }
+    uint8 getBidiLevel() const { return m_bidiLevel; }
+    void setBidiLevel(uint8 level) { m_bidiLevel = level; }
+    int8 getBidiClass(const Segment *seg);
+    int8 getBidiClass() const { return m_bidiCls; }
+    void setBidiClass(int8 cls) { m_bidiCls = cls; }
+    int16 *userAttrs() const { return m_userAttr; }
+    void userAttrs(int16 *p) { m_userAttr = p; }
+    void markInsertBefore(bool state) { if (!state) m_flags |= INSERTED; else m_flags &= ~INSERTED; }
+    void setAttr(Segment* seg, attrCode ind, uint8 subindex, int16 val, const SlotMap & map);
+    int getAttr(const Segment *seg, attrCode ind, uint8 subindex) const;
+    int getJustify(const Segment *seg, uint8 level, uint8 subindex) const;
+    void setJustify(Segment *seg, uint8 level, uint8 subindex, int16 value);
+    bool isLocalJustify() const { return m_justs != NULL; };
+    void attachTo(Slot *ap) { m_parent = ap; }
+    Slot *attachedTo() const { return m_parent; }
+    Position attachOffset() const { return m_attach - m_with; }
+    Slot* firstChild() const { return m_child; }
+    void firstChild(Slot *ap) { m_child = ap; }
+    bool child(Slot *ap);
+    Slot* nextSibling() const { return m_sibling; }
+    void nextSibling(Slot *ap) { m_sibling = ap; }
+    bool sibling(Slot *ap);
+    bool removeChild(Slot *ap);
+    int32 clusterMetric(const Segment* seg, uint8 metric, uint8 attrLevel, bool rtl);
+    void positionShift(Position a) { m_position += a; }
+    void floodShift(Position adj, int depth = 0);
+    float just() const { return m_just; }
+    void just(float j) { m_just = j; }
+    Slot *nextInCluster(const Slot *s) const;
+    bool isChildOf(const Slot *base) const;
+
+    CLASS_NEW_DELETE
+
+private:
+    Slot *m_next;           // linked list of slots
+    Slot *m_prev;
+    unsigned short m_glyphid;        // glyph id
+    uint16 m_realglyphid;
+    uint32 m_original;      // charinfo that originated this slot (e.g. for feature values)
+    uint32 m_before;        // charinfo index of before association
+    uint32 m_after;         // charinfo index of after association
+    uint32 m_index;         // slot index given to this slot during finalising
+    Slot *m_parent;         // index to parent we are attached to
+    Slot *m_child;          // index to first child slot that attaches to us
+    Slot *m_sibling;        // index to next child that attaches to our parent
+    Position m_position;    // absolute position of glyph
+    Position m_shift;       // .shift slot attribute
+    Position m_advance;     // .advance slot attribute
+    Position m_attach;      // attachment point on us
+    Position m_with;        // attachment point position on parent
+    float    m_just;        // Justification inserted space
+    uint8    m_flags;       // holds bit flags
+    byte     m_attLevel;    // attachment level
+    int8     m_bidiCls;     // bidirectional class
+    byte     m_bidiLevel;   // bidirectional level
+    int16   *m_userAttr;    // pointer to user attributes
+    SlotJustify *m_justs;   // pointer to justification parameters
+
+    friend class Segment;
+};
+
+} // namespace graphite2
+
+struct gr_slot : public graphite2::Slot {};

+ 168 - 0
thirdparty/graphite/src/inc/Sparse.h

@@ -0,0 +1,168 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+#include <iterator>
+#include <utility>
+
+#include "inc/Main.h"
+
+namespace graphite2 {
+
+
+// A read-only packed fast sparse array of uint16 with uint16 keys.
+// Like most container classes this has capacity and size properties and these
+// refer to the number of stored entries and the number of addressable entries
+// as normal. However due the sparse nature the capacity is always <= than the
+// size.
+class sparse
+{
+public:
+    typedef uint16  key_type;
+    typedef uint16  mapped_type;
+    typedef std::pair<const key_type, mapped_type> value_type;
+
+private:
+    typedef unsigned long   mask_t;
+
+    static const unsigned char  SIZEOF_CHUNK = (sizeof(mask_t) - sizeof(key_type))*8;
+
+    struct chunk
+    {
+        mask_t          mask:SIZEOF_CHUNK;
+        key_type        offset;
+    };
+
+    static const chunk  empty_chunk;
+    sparse(const sparse &);
+    sparse & operator = (const sparse &);
+
+public:
+    template<typename I>
+    sparse(I first, const I last);
+    sparse() throw();
+    ~sparse() throw();
+
+    operator bool () const throw();
+    mapped_type     operator [] (const key_type k) const throw();
+
+    size_t capacity() const throw();
+    size_t size()     const throw();
+
+    size_t _sizeof() const throw();
+
+    CLASS_NEW_DELETE;
+
+private:
+    union {
+        chunk         * map;
+        mapped_type   * values;
+    }           m_array;
+    key_type    m_nchunks;
+};
+
+
+inline
+sparse::sparse() throw() : m_nchunks(0)
+{
+    m_array.map = const_cast<graphite2::sparse::chunk *>(&empty_chunk);
+}
+
+
+template <typename I>
+sparse::sparse(I attr, const I last)
+: m_nchunks(0)
+{
+    m_array.map = 0;
+
+    // Find the maximum extent of the key space.
+    size_t n_values=0;
+    long lastkey = -1;
+    for (I i = attr; i != last; ++i, ++n_values)
+    {
+        const typename std::iterator_traits<I>::value_type v = *i;
+        if (v.second == 0)      { --n_values; continue; }
+        if (v.first <= lastkey) { m_nchunks = 0; return; }
+
+        lastkey = v.first;
+        const key_type k = v.first / SIZEOF_CHUNK;
+        if (k >= m_nchunks) m_nchunks = k+1;
+    }
+    if (m_nchunks == 0)
+    {
+        m_array.map=const_cast<graphite2::sparse::chunk *>(&empty_chunk);
+        return;
+    }
+
+    m_array.values = grzeroalloc<mapped_type>((m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1)
+                                                 / sizeof(mapped_type)
+                                                 + n_values);
+
+    if (m_array.values == 0)
+        return;
+
+    // coverity[forward_null : FALSE] Since m_array is union and m_array.values is not NULL
+    chunk * ci = m_array.map;
+    ci->offset = (m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1)/sizeof(mapped_type);
+    mapped_type * vi = m_array.values + ci->offset;
+    for (; attr != last; ++attr, ++vi)
+    {
+        const typename std::iterator_traits<I>::value_type v = *attr;
+        if (v.second == 0)  { --vi; continue; }
+
+        chunk * const ci_ = m_array.map + v.first/SIZEOF_CHUNK;
+
+        if (ci != ci_)
+        {
+            ci = ci_;
+            ci->offset = key_type(vi - m_array.values);
+        }
+
+        ci->mask |= 1UL << (SIZEOF_CHUNK - 1 - (v.first % SIZEOF_CHUNK));
+        *vi = v.second;
+    }
+}
+
+
+inline
+sparse::operator bool () const throw()
+{
+    return m_array.map != 0;
+}
+
+inline
+size_t sparse::size() const throw()
+{
+    return m_nchunks*SIZEOF_CHUNK;
+}
+
+inline
+size_t sparse::_sizeof() const throw()
+{
+    return sizeof(sparse) + capacity()*sizeof(mapped_type) + m_nchunks*sizeof(chunk);
+}
+
+} // namespace graphite2

+ 419 - 0
thirdparty/graphite/src/inc/TtfTypes.h

@@ -0,0 +1,419 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+
+File: TtfTypes.h
+Responsibility: Tim Eves
+Last reviewed: Not yet.
+
+Description:
+Provides types required to represent the TTF basic types.
+-------------------------------------------------------------------------------*//*:End Ignore*/
+
+
+//**********************************************************************************************
+//  Include files
+//**********************************************************************************************
+namespace graphite2
+{
+namespace TtfUtil
+{
+//**********************************************************************************************
+//  Forward declarations
+//**********************************************************************************************
+
+
+//**********************************************************************************************
+//  Type declarations
+//**********************************************************************************************
+typedef unsigned char   uint8;
+typedef uint8           byte;
+typedef signed char     int8;
+typedef unsigned short  uint16;
+typedef short           int16;
+typedef unsigned int    uint32;
+typedef int             int32;
+
+typedef int16   short_frac;
+typedef int32   fixed;
+typedef int16   fword;
+typedef uint16  ufword;
+typedef int16   f2dot14;
+typedef uint32  long_date_time[2];
+
+//**********************************************************************************************
+//  Constants and enum types
+//**********************************************************************************************/
+enum
+{
+    OneFix = 1<<16
+};
+
+//**********************************************************************************************
+//  Table declarations
+//**********************************************************************************************
+namespace Sfnt
+{
+#pragma pack(push,1) // We need this or the structure members aren't aligned
+                        // correctly.  Fortunately this form of pragma is supposed
+                        // to be recognised by VS C++ too (at least according to
+                        // MSDN).
+
+    struct OffsetSubTable
+    {
+        uint32  scaler_type;
+        uint16  num_tables,
+            search_range,
+            entry_selector,
+            range_shift;
+        struct Entry
+        {
+            uint32  tag,
+                checksum,
+                offset,
+                length;
+        } table_directory[1];
+
+        enum ScalerType
+        {
+            TrueTypeMac = 0x74727565U,
+            TrueTypeWin = 0x00010000U,
+            Type1   = 0x74797031U
+        };
+    };
+
+
+
+
+    struct CharacterCodeMap
+    {
+        uint16  version,
+            num_subtables;
+        struct
+        {
+            uint16  platform_id,
+                platform_specific_id;
+            uint32  offset;
+        } encoding[1];
+    };
+
+    struct CmapSubTable
+    {
+        uint16  format,
+            length,
+            language;
+    };
+
+    struct CmapSubTableFormat4 : CmapSubTable
+    {
+        uint16  seg_count_x2,
+            search_range,
+            entry_selector,
+            range_shift,
+            end_code[1];
+        // There are arrarys after this which need their
+        // start positions calculated since end_code is
+        // seg_count uint16s long.
+    };
+
+    struct CmapSubTableFormat12
+    {
+        fixed   format;
+        uint32  length,
+            language,
+            num_groups;
+        struct
+        {
+            uint32  start_char_code,
+                end_char_code,
+                start_glyph_id;
+        } group[1];
+    };
+
+
+
+    struct FontHeader
+    {
+        fixed   version,
+            font_revision;
+        uint32  check_sum_adjustment,
+            magic_number;
+        uint16  flags,
+            units_per_em;
+        long_date_time  created,
+                modified;
+        fword   x_min,
+            y_min,
+            x_max,
+            y_max;
+        uint16  mac_style,
+            lowest_rec_ppem;
+        int16   font_direction_hint,
+            index_to_loc_format,
+            glyph_data_format;
+        enum
+        {
+            MagicNumber = 0x5F0F3CF5,
+            GlypDataFormat = 0
+        };
+        enum {ShortIndexLocFormat, LongIndexLocFormat};
+    };
+
+
+
+
+    struct PostScriptGlyphName
+    {
+        fixed   format,
+            italic_angle;
+        fword   underline_position,
+            underline_thickness;
+        uint32  is_fixed_pitch,
+            min_mem_type42,
+            max_mem_type42,
+            min_mem_type1,
+            max_mem_type1;
+        enum
+        {
+            Format1  = 0x10000,
+            Format2  = 0x20000,
+            Format25 = 0x28000,
+            Format3  = 0x30000,
+            Format4  = 0x40000
+        };
+    };
+
+    struct PostScriptGlyphName2 : PostScriptGlyphName
+    {
+        uint16  number_of_glyphs,
+            glyph_name_index[1];
+    };
+
+    struct PostScriptGlyphName25 : PostScriptGlyphName
+    {
+        uint16  number_of_glyphs;
+        int8    offset[1];
+    };
+
+    struct PostScriptGlyphName3 : PostScriptGlyphName {};
+
+    struct PostScriptGlyphName4 : PostScriptGlyphName
+    {
+        uint16 glyph_to_char_map[1];
+    };
+
+
+    struct HorizontalHeader
+    {
+        fixed   version;
+        fword   ascent,
+            descent,
+            line_gap;
+        ufword  advance_width_max;
+        fword   min_left_side_bearing,
+            max_left_side_bearing,
+            x_max_element;
+        int16   caret_slope_rise,
+            caret_slope_run;
+        fword   caret_offset;
+        int16   reserved[4],
+            metric_data_format;
+        uint16  num_long_hor_metrics;
+    };
+
+    struct MaximumProfile
+    {
+        fixed   version;
+        uint16  num_glyphs,
+            max_points,
+            max_contours,
+            max_component_points,
+            max_component_contours,
+            max_zones,
+            max_twilight_points,
+            max_storage,
+            max_function_defs,
+            max_instruction_defs,
+            max_stack_elements,
+            max_size_of_instructions,
+            max_component_elements,
+            max_component_depth;
+    };
+
+
+    typedef byte    Panose[10];
+
+    struct Compatibility0
+    {
+        uint16  version;
+        int16   x_avg_char_width;
+        uint16  weight_class,
+            width_class;
+        int16   fs_type,
+            y_subscript_x_size,
+            y_subscript_y_size,
+            y_subscript_x_offset,
+            y_subscript_y_offset,
+            y_superscript_x_size,
+            y_superscript_y_size,
+            y_superscript_x_offset,
+            y_superscript_y_offset,
+            y_strikeout_size,
+            y_strikeout_position,
+            family_class;
+        Panose  panose;
+        uint32  unicode_range[4];
+        int8    ach_vend_id[4];
+        uint16  fs_selection,
+            fs_first_char_index,
+            fs_last_char_index, // Acording to Apple's spec this is where v0 should end
+            typo_ascender,
+            typo_descender,
+            type_linegap,
+            win_ascent,
+            win_descent;
+
+        enum
+        {
+            Italic    =0x01,
+            Underscore=0x02,
+            Negative  =0x04,
+            Outlined  =0x08,
+            StrikeOut =0x10,
+            Bold      =0x20
+        };
+    };
+
+    struct Compatibility1 : Compatibility0
+    {
+        uint32  codepage_range[2];
+    };
+
+    struct Compatibility2 : Compatibility1
+    {
+            int16   x_height,
+                cap_height;
+            uint16  default_char,
+                break_char,
+                max_context;
+    };
+
+    struct Compatibility3 : Compatibility2 {};
+
+    typedef Compatibility3  Compatibility;
+
+
+    struct NameRecord
+    {
+        uint16  platform_id,
+            platform_specific_id,
+            language_id,
+            name_id,
+            length,
+            offset;
+        enum    {Unicode, Mactintosh, Reserved, Microsoft};
+        enum
+        {
+            Copyright, Family, Subfamily, UniqueSubfamily,
+            Fullname, Version, PostScript
+        };
+    };
+
+    struct LangTagRecord
+    {
+        uint16 length,
+            offset;
+    };
+
+    struct FontNames
+    {
+        uint16  format,
+            count,
+            string_offset;
+        NameRecord name_record[1];
+    };
+
+
+    struct HorizontalMetric
+    {
+        uint16  advance_width;
+        int16   left_side_bearing;
+    };
+
+
+    struct Glyph
+    {
+        int16   number_of_contours;
+        fword   x_min,
+            y_min,
+            x_max,
+            y_max;
+    };
+
+    struct SimpleGlyph : Glyph
+    {
+        uint16  end_pts_of_contours[1];
+        enum
+        {
+            OnCurve = 0x01,
+            XShort  = 0x02,
+            YShort  = 0x04,
+            Repeat  = 0x08,
+            XIsSame = 0x10,
+            XIsPos  = 0x10,
+            YIsSame = 0x20,
+            YIsPos  = 0x20
+        };
+    };
+
+    struct CompoundGlyph : Glyph
+    {
+        uint16  flags,
+            glyph_index;
+        enum
+        {
+            Arg1Arg2Words   = 0x01,
+            ArgsAreXYValues = 0x02,
+            RoundXYToGrid   = 0x04,
+            HaveScale       = 0x08,
+            MoreComponents  = 0x20,
+            HaveXAndYScale  = 0x40,
+            HaveTwoByTwo    = 0x80,
+            HaveInstructions = 0x100,
+            UseMyMetrics    = 0x200,
+            OverlapCompund  = 0x400,
+            ScaledOffset    = 0x800,
+            UnscaledOffset  = 0x1000
+        };
+    };
+
+#pragma pack(pop)
+} // end of namespace Sfnt
+
+} // end of namespace TtfUtil
+} // end of namespace graphite2

+ 208 - 0
thirdparty/graphite/src/inc/TtfUtil.h

@@ -0,0 +1,208 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+/*--------------------------------------------------------------------*//*:Ignore this sentence.
+
+File: TtfUtil.h
+Responsibility: Alan Ward
+Last reviewed: Not yet.
+
+Description:
+    Utility class for handling TrueType font files.
+----------------------------------------------------------------------------------------------*/
+
+
+#include <cstddef>
+
+namespace graphite2
+{
+namespace TtfUtil
+{
+
+#define OVERFLOW_OFFSET_CHECK(p, o) (o + reinterpret_cast<size_t>(p) < reinterpret_cast<size_t>(p))
+
+typedef long fontTableId32;
+typedef unsigned short gid16;
+
+#define TTF_TAG(a,b,c,d) ((a << 24UL) + (b << 16UL) + (c << 8UL) + (d))
+
+// Enumeration used to specify a table in a TTF file
+class Tag
+{
+    unsigned int _v;
+public:
+    Tag(const char n[5]) throw()            : _v(TTF_TAG(n[0],n[1],n[2],n[3])) {}
+    Tag(const unsigned int tag) throw()    : _v(tag) {}
+
+    operator unsigned int () const throw () { return _v; }
+
+    enum
+    {
+        Feat = TTF_TAG('F','e','a','t'),
+        Glat = TTF_TAG('G','l','a','t'),
+        Gloc = TTF_TAG('G','l','o','c'),
+        Sile = TTF_TAG('S','i','l','e'),
+        Silf = TTF_TAG('S','i','l','f'),
+        Sill = TTF_TAG('S','i','l','l'),
+        cmap = TTF_TAG('c','m','a','p'),
+        cvt  = TTF_TAG('c','v','t',' '),
+        cryp = TTF_TAG('c','r','y','p'),
+        head = TTF_TAG('h','e','a','d'),
+        fpgm = TTF_TAG('f','p','g','m'),
+        gdir = TTF_TAG('g','d','i','r'),
+        glyf = TTF_TAG('g','l','y','f'),
+        hdmx = TTF_TAG('h','d','m','x'),
+        hhea = TTF_TAG('h','h','e','a'),
+        hmtx = TTF_TAG('h','m','t','x'),
+        loca = TTF_TAG('l','o','c','a'),
+        kern = TTF_TAG('k','e','r','n'),
+        LTSH = TTF_TAG('L','T','S','H'),
+        maxp = TTF_TAG('m','a','x','p'),
+        name = TTF_TAG('n','a','m','e'),
+        OS_2 = TTF_TAG('O','S','/','2'),
+        post = TTF_TAG('p','o','s','t'),
+        prep = TTF_TAG('p','r','e','p')
+    };
+};
+
+/*----------------------------------------------------------------------------------------------
+    Class providing utility methods to parse a TrueType font file (TTF).
+    Callling application handles all file input and memory allocation.
+    Assumes minimal knowledge of TTF file format.
+----------------------------------------------------------------------------------------------*/
+    ////////////////////////////////// tools to find & check TTF tables
+    bool GetHeaderInfo(size_t & lOffset, size_t & lSize);
+    bool CheckHeader(const void * pHdr);
+    bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize);
+    bool GetTableInfo(const Tag TableTag, const void * pHdr, const void * pTableDir,
+        size_t & lOffset, size_t & lSize);
+    bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize);
+
+    ////////////////////////////////// simple font wide info
+    size_t  GlyphCount(const void * pMaxp);
+#ifdef ALL_TTFUTILS
+    size_t  MaxCompositeComponentCount(const void * pMaxp);
+    size_t  MaxCompositeLevelCount(const void * pMaxp);
+    size_t  LocaGlyphCount(size_t lLocaSize, const void * pHead); // throw (std::domain_error);
+#endif
+    int DesignUnits(const void * pHead);
+#ifdef ALL_TTFUTILS
+    int HeadTableCheckSum(const void * pHead);
+    void HeadTableCreateTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD);
+    void HeadTableModifyTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD);
+    bool IsItalic(const void * pHead);
+    int FontAscent(const void * pOs2);
+    int FontDescent(const void * pOs2);
+    bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic);
+    bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize);
+    bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize);
+    bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize);
+    bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize);
+    int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp,
+        const char * pPostName);
+#endif
+
+    ////////////////////////////////// utility methods helpful for name table
+    bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId,
+        int nLangId, int nNameId, size_t & lOffset, size_t & lSize);
+    //size_t NameTableLength(const byte * pTable);
+#ifdef ALL_TTFUTILS
+    int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId,
+        int *nameIdList, int cNameIds, short *langIdList);
+    void SwapWString(void * pWStr, size_t nSize = 0); // throw (std::invalid_argument);
+#endif
+
+    ////////////////////////////////// cmap lookup tools
+    const void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3,
+        int nEncodingId = 1, size_t length = 0);
+    bool CheckCmapSubtable4(const void * pCmap31, const void * pCmapEnd /*, unsigned int maxgid*/);
+    gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey = 0);
+    unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId,
+        int * pRangeKey = 0);
+    bool CheckCmapSubtable12(const void *pCmap310, const void * pCmapEnd /*, unsigned int maxgid*/);
+    gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey = 0);
+    unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId,
+        int * pRangeKey = 0);
+
+    ///////////////////////////////// horizontal metric data for a glyph
+    bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize,
+        const void * pHhea, int & nLsb, unsigned int & nAdvWid);
+
+    ////////////////////////////////// primitives for loca and glyf lookup
+    size_t LocaLookup(gid16 nGlyphId, const void * pLoca, size_t lLocaSize,
+        const void * pHead); // throw (std::out_of_range);
+    void * GlyfLookup(const void * pGlyf, size_t lGlyfOffset, size_t lTableLen);
+
+    ////////////////////////////////// primitves for simple glyph data
+    bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin,
+        int & xMax, int & yMax);
+
+#ifdef ALL_TTFUTILS
+    int GlyfContourCount(const void * pSimpleGlyf);
+    bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint,
+        int cnPointsTotal, size_t & cnPoints);
+    bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY,
+        char * prgbFlag, int cnPointsTotal, int & cnPoints);
+
+    // primitive to find the glyph ids in a composite glyph
+    bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId,
+        size_t cnCompIdTotal, size_t & cnCompId);
+    // primitive to find the placement data for a component in a composite glyph
+    bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId,
+        bool fOffset, int & a, int & b);
+    // primitive to find the transform data for a component in a composite glyph
+    bool GetComponentTransform(const void * pSimpleGlyf, int nCompId,
+        float & flt11, float & flt12, float & flt21, float & flt22, bool & fTransOffset);
+#endif
+
+    ////////////////////////////////// operate on composite or simple glyph (auto glyf lookup)
+    void * GlyfLookup(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+        size_t lGlyfSize, size_t lLocaSize, const void * pHead); // primitive used by below methods
+
+#ifdef ALL_TTFUTILS
+    // below are primary user methods for handling glyf data
+    bool IsSpace(gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead);
+    bool IsDeepComposite(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+        size_t lGlyfSize, size_t lLocaSize, const void * pHead);
+
+    bool GlyfBox(gid16 nGlyphId, const void * pGlyf, const void * pLoca, size_t lGlyfSize, size_t lLocaSize,
+        const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax);
+    bool GlyfContourCount(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+        size_t lGlyfSize, size_t lLocaSize, const void *pHead, size_t & cnContours);
+    bool GlyfContourEndPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+        size_t lGlyfSize, size_t lLocaSize, const void * pHead, int * prgnContourEndPoint, size_t cnPoints);
+    bool GlyfPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
+        size_t lGlyfSize, size_t lLocaSize, const void * pHead, const int * prgnContourEndPoint, size_t cnEndPoints,
+        int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints);
+
+    // utitily method used by high-level GlyfPoints
+    bool SimplifyFlags(char * prgbFlags, int cnPoints);
+    bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints);
+#endif
+
+} // end of namespace TtfUtil
+} // end of namespace graphite2

+ 251 - 0
thirdparty/graphite/src/inc/UtfCodec.h

@@ -0,0 +1,251 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+#include <cstdlib>
+#include "inc/Main.h"
+
+namespace graphite2 {
+
+typedef uint32  uchar_t;
+
+template <int N>
+struct _utf_codec
+{
+    typedef uchar_t codeunit_t;
+
+    static void     put(codeunit_t * cp, const uchar_t , int8 & len) throw();
+    static uchar_t  get(const codeunit_t * cp, int8 & len) throw();
+    static bool     validate(const codeunit_t * s, const codeunit_t * const e) throw();
+};
+
+
+template <>
+struct _utf_codec<32>
+{
+private:
+    static const uchar_t    limit = 0x110000;
+public:
+    typedef uint32  codeunit_t;
+
+    inline
+    static void put(codeunit_t * cp, const uchar_t usv, int8 & l) throw()
+    {
+        *cp = usv; l = 1;
+    }
+
+    inline
+    static uchar_t get(const codeunit_t * cp, int8 & l) throw()
+    {
+        if (cp[0] < limit)  { l = 1;  return cp[0]; }
+        else                { l = -1; return 0xFFFD; }
+    }
+
+    inline
+    static bool validate(const codeunit_t * s, const codeunit_t * const e) throw()
+    {
+        return s <= e;
+    }
+};
+
+
+template <>
+struct _utf_codec<16>
+{
+private:
+    static const int32  lead_offset      = 0xD800 - (0x10000 >> 10);
+    static const int32  surrogate_offset = 0x10000 - (0xD800 << 10) - 0xDC00;
+public:
+    typedef uint16  codeunit_t;
+
+    inline
+    static void put(codeunit_t * cp, const uchar_t usv, int8 & l) throw()
+    {
+        if (usv < 0x10000)  { l = 1; cp[0] = codeunit_t(usv); }
+        else
+        {
+            cp[0] = codeunit_t(lead_offset + (usv >> 10));
+            cp[1] = codeunit_t(0xDC00 + (usv & 0x3FF));
+            l = 2;
+        }
+    }
+
+    inline
+    static uchar_t get(const codeunit_t * cp, int8 & l) throw()
+    {
+        const uint32    uh = cp[0];
+        l = 1;
+
+        if (uh < 0xD800|| uh > 0xDFFF) { return uh; }
+        if (uh > 0xDBFF) { l = -1; return 0xFFFD; }
+        const uint32 ul = cp[1];
+        if (ul < 0xDC00 || ul > 0xDFFF) { l = -1; return 0xFFFD; }
+        ++l;
+        return (uh<<10) + ul + surrogate_offset;
+    }
+
+    inline
+    static bool validate(const codeunit_t * s, const codeunit_t * const e) throw()
+    {
+        const ptrdiff_t n = e-s;
+        if (n <= 0) return n == 0;
+        const uint32 u = *(e-1); // Get the last codepoint
+        return (u < 0xD800 || u > 0xDBFF);
+    }
+};
+
+
+template <>
+struct _utf_codec<8>
+{
+private:
+    static const int8 sz_lut[16];
+    static const byte mask_lut[5];
+    static const uchar_t    limit = 0x110000;
+
+public:
+    typedef uint8   codeunit_t;
+
+    inline
+    static void put(codeunit_t * cp, const uchar_t usv, int8 & l) throw()
+    {
+        if (usv < 0x80)     {l = 1; cp[0] = usv; return; }
+        if (usv < 0x0800)   {l = 2; cp[0] = 0xC0 + (usv >> 6);  cp[1] = 0x80 + (usv & 0x3F); return; }
+        if (usv < 0x10000)  {l = 3; cp[0] = 0xE0 + (usv >> 12); cp[1] = 0x80 + ((usv >> 6) & 0x3F);  cp[2] = 0x80 + (usv & 0x3F); return; }
+        else                {l = 4; cp[0] = 0xF0 + (usv >> 18); cp[1] = 0x80 + ((usv >> 12) & 0x3F); cp[2] = 0x80 + ((usv >> 6) & 0x3F); cp[3] = 0x80 + (usv & 0x3F); return; }
+    }
+
+    inline
+    static uchar_t get(const codeunit_t * cp, int8 & l) throw()
+    {
+        const int8 seq_sz = sz_lut[*cp >> 4];
+        uchar_t u = *cp & mask_lut[seq_sz];
+        l = 1;
+        bool toolong = false;
+
+        switch(seq_sz) {
+            case 4:     u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong  = (u < 0x10); GR_FALLTHROUGH;
+                // no break
+            case 3:     u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x20); GR_FALLTHROUGH;
+                // no break
+            case 2:     u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x80); GR_FALLTHROUGH;
+                // no break
+            case 1:     break;
+            case 0:     l = -1; return 0xFFFD;
+        }
+
+        if (l != seq_sz || toolong  || u >= limit)
+        {
+            l = -l;
+            return 0xFFFD;
+        }
+        return u;
+    }
+
+    inline
+    static bool validate(const codeunit_t * s, const codeunit_t * const e) throw()
+    {
+        const ptrdiff_t n = e-s;
+        if (n <= 0) return n == 0;
+        s += (n-1);
+        if (*s < 0x80) return true;
+        if (*s >= 0xC0) return false;
+        if (n == 1) return true;
+        if (*--s < 0x80) return true;
+        if (*s >= 0xE0) return false;
+        if (n == 2 || *s >= 0xC0) return true;
+        if (*--s < 0x80) return true;
+        if (*s >= 0xF0) return false;
+        return true;
+    }
+
+};
+
+
+template <typename C>
+class _utf_iterator
+{
+    typedef _utf_codec<sizeof(C)*8> codec;
+
+    C             * cp;
+    mutable int8    sl;
+
+public:
+    typedef C           codeunit_type;
+    typedef uchar_t     value_type;
+    typedef uchar_t   * pointer;
+
+    class reference
+    {
+        const _utf_iterator & _i;
+
+        reference(const _utf_iterator & i): _i(i) {}
+    public:
+        operator value_type () const throw ()                   { return codec::get(_i.cp, _i.sl); }
+        reference & operator = (const value_type usv) throw()   { codec::put(_i.cp, usv, _i.sl); return *this; }
+
+        friend class _utf_iterator;
+    };
+
+
+    _utf_iterator(const void * us=0)    : cp(reinterpret_cast<C *>(const_cast<void *>(us))), sl(1) { }
+
+    _utf_iterator   & operator ++ ()    { cp += abs(sl); return *this; }
+    _utf_iterator   operator ++ (int)   { _utf_iterator tmp(*this); operator++(); return tmp; }
+
+    bool operator == (const _utf_iterator & rhs) const throw() { return cp >= rhs.cp; }
+    bool operator != (const _utf_iterator & rhs) const throw() { return !operator==(rhs); }
+
+    reference   operator * () const throw() { return *this; }
+    pointer     operator ->() const throw() { return &operator *(); }
+
+    operator codeunit_type * () const throw() { return cp; }
+
+    bool error() const throw()  { return sl < 1; }
+    bool validate(const _utf_iterator & e)  { return codec::validate(cp, e.cp); }
+};
+
+template <typename C>
+struct utf
+{
+    typedef typename _utf_codec<sizeof(C)*8>::codeunit_t codeunit_t;
+
+    typedef _utf_iterator<C>        iterator;
+    typedef _utf_iterator<const C>  const_iterator;
+
+    inline
+    static bool validate(codeunit_t * s, codeunit_t * e) throw() {
+        return _utf_codec<sizeof(C)*8>::validate(s,e);
+    }
+};
+
+
+typedef utf<uint32> utf32;
+typedef utf<uint16> utf16;
+typedef utf<uint8>  utf8;
+
+} // namespace graphite2

+ 150 - 0
thirdparty/graphite/src/inc/bits.h

@@ -0,0 +1,150 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2012, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+
+namespace graphite2
+{
+
+
+#if defined GRAPHITE2_BUILTINS && (defined __GNUC__ || defined __clang__)
+
+template<typename T>
+inline unsigned int bit_set_count(T v)
+{
+    return __builtin_popcount(v);
+}
+
+template<>
+inline unsigned int bit_set_count(int16 v)
+{
+    return __builtin_popcount(static_cast<uint16>(v));
+}
+
+template<>
+inline unsigned int bit_set_count(int8 v)
+{
+    return __builtin_popcount(static_cast<uint8>(v));
+}
+
+template<>
+inline unsigned int bit_set_count(unsigned long v)
+{
+    return __builtin_popcountl(v);
+}
+
+template<>
+inline unsigned int bit_set_count(signed long v)
+{
+    return __builtin_popcountl(v);
+}
+
+template<>
+inline unsigned int bit_set_count(unsigned long long v)
+{
+    return __builtin_popcountll(v);
+}
+
+template<>
+inline unsigned int bit_set_count(signed long long v)
+{
+    return __builtin_popcountll(v);
+}
+
+#else
+
+template<typename T>
+inline unsigned int bit_set_count(T v)
+{
+	static size_t const ONES = ~0;
+
+	v = v - ((v >> 1) & T(ONES/3));                      // temp
+    v = (v & T(ONES/15*3)) + ((v >> 2) & T(ONES/15*3));  // temp
+    v = (v + (v >> 4)) & T(ONES/255*15);                 // temp
+    return (T)(v * T(ONES/255)) >> (sizeof(T)-1)*8;      // count
+}
+
+#endif
+
+//TODO: Changed these to uintmax_t when we go to C++11
+template<int S>
+inline size_t _mask_over_val(size_t v)
+{
+    v = _mask_over_val<S/2>(v);
+    v |= v >> S*4;
+    return v;
+}
+
+//TODO: Changed these to uintmax_t when we go to C++11
+template<>
+inline size_t _mask_over_val<1>(size_t v)
+{
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    return v;
+}
+
+template<typename T>
+inline T mask_over_val(T v)
+{
+    return T(_mask_over_val<sizeof(T)>(v));
+}
+
+template<typename T>
+inline unsigned long next_highest_power2(T v)
+{
+    return _mask_over_val<sizeof(T)>(v-1)+1;
+}
+
+template<typename T>
+inline unsigned int log_binary(T v)
+{
+    return bit_set_count(mask_over_val(v))-1;
+}
+
+template<typename T>
+inline T has_zero(const T x)
+{
+    return (x - T(~T(0)/255)) & ~x & T(~T(0)/255*128);
+}
+
+template<typename T>
+inline T zero_bytes(const T x, unsigned char n)
+{
+    const T t = T(~T(0)/255*n);
+    return T((has_zero(x^t) >> 7)*n);
+}
+
+#if 0
+inline float float_round(float x, uint32 m)
+{
+    *reinterpret_cast<unsigned int *>(&x) &= m;
+    return *reinterpret_cast<float *>(&x);
+}
+#endif
+
+}

+ 89 - 0
thirdparty/graphite/src/inc/debug.h

@@ -0,0 +1,89 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+//  debug.h
+//
+//  Created on: 22 Dec 2011
+//      Author: tim
+
+#pragma once
+
+#if !defined GRAPHITE2_NTRACING
+
+#include <utility>
+#include "inc/json.h"
+#include "inc/Position.h"
+
+namespace graphite2
+{
+
+class CharInfo;
+class Segment;
+class Slot;
+
+typedef std::pair<const Segment * const, const Slot * const>    dslot;
+struct objectid
+{
+    char name[16];
+    objectid(const dslot &) throw();
+    objectid(const Segment * const p) throw();
+};
+
+
+json & operator << (json & j, const Position &) throw();
+json & operator << (json & j, const Rect &) throw();
+json & operator << (json & j, const CharInfo &) throw();
+json & operator << (json & j, const dslot &) throw();
+json & operator << (json & j, const objectid &) throw();
+json & operator << (json & j, const telemetry &) throw();
+
+
+
+inline
+json & operator << (json & j, const Position & p) throw()
+{
+    return j << json::flat << json::array << p.x << p.y << json::close;
+}
+
+
+inline
+json & operator << (json & j, const Rect & p) throw()
+{
+    return j << json::flat << json::array << p.bl.x << p.bl.y << p.tr.x << p.tr.y << json::close;
+}
+
+
+inline
+json & operator << (json & j, const objectid & sid) throw()
+{
+    return j << sid.name;
+}
+
+
+} // namespace graphite2
+
+#endif //!defined GRAPHITE2_NTRACING
+

+ 178 - 0
thirdparty/graphite/src/inc/json.h

@@ -0,0 +1,178 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// JSON pretty printer for graphite font debug output logging.
+// Created on: 15 Dec 2011
+//     Author: Tim Eves
+
+#pragma once
+
+#include "inc/Main.h"
+#include <cassert>
+#include <cstdio>
+#include <cstdint>
+#include "inc/List.h"
+
+namespace graphite2 {
+
+class json
+{
+    // Prevent copying
+    json(const json &);
+    json & operator = (const json &);
+
+    typedef void (*_context_t)(json &);
+
+    FILE * const    _stream;
+    char            _contexts[128], // context stack
+                  * _context,       // current context (top of stack)
+                  * _flatten;       // if !0 points to context above which
+                                    //  pretty printed output should occur.
+    Vector<void *>  _env;
+
+    void context(const char current) throw();
+    void indent(const int d=0) throw();
+    void push_context(const char, const char) throw();
+    void pop_context() throw();
+
+public:
+    class closer;
+
+    using string = const char *;
+    using number = double;
+    enum class integer : std::intmax_t {};
+    enum class integer_u : std::uintmax_t {};
+    using boolean = bool;
+    static const std::nullptr_t  null;
+
+    void setenv(unsigned int index, void *val) { _env.reserve(index + 1); if (index >= _env.size()) _env.insert(_env.end(), _env.size() - index + 1, 0); _env[index] = val; }
+    void *getenv(unsigned int index) const { return _env[index]; }
+    const Vector<void *> &getenvs() const { return _env; }
+
+    static void flat(json &) throw();
+    static void close(json &) throw();
+    static void object(json &) throw();
+    static void array(json &) throw();
+    static void item(json &) throw();
+
+    json(FILE * stream) throw();
+    ~json() throw ();
+
+    FILE * stream() const throw();
+
+    json & operator << (string) throw();
+    json & operator << (number) throw();
+    json & operator << (integer) throw();
+    json & operator << (integer_u) throw();
+    json & operator << (boolean) throw();
+    json & operator << (std::nullptr_t) throw();
+    json & operator << (_context_t) throw();
+
+    operator bool() const throw();
+    bool good() const throw();
+    bool eof() const throw();
+
+    CLASS_NEW_DELETE;
+};
+
+class json::closer
+{
+    // Prevent copying.
+    closer(const closer &);
+    closer & operator = (const closer &);
+
+    json * const    _j;
+public:
+    closer(json * const j) : _j(j) {}
+    ~closer() throw() { if (_j)  *_j << close; }
+};
+
+inline
+json::json(FILE * s) throw()
+: _stream(s), _context(_contexts), _flatten(0)
+{
+    if (good())
+        fflush(s);
+}
+
+
+inline
+json::~json() throw ()
+{
+    while (_context > _contexts)    pop_context();
+}
+
+inline
+FILE * json::stream() const throw()     { return _stream; }
+
+
+inline
+json & json::operator << (json::_context_t ctxt) throw()
+{
+    ctxt(*this);
+    return *this;
+}
+
+inline
+json & operator << (json & j, signed char d) throw()   { return j << json::integer(d); }
+
+inline
+json & operator << (json & j, unsigned char d) throw() { return j << json::integer_u(d); }
+
+inline
+json & operator << (json & j, short int d) throw()   { return j << json::integer(d); }
+
+inline
+json & operator << (json & j, unsigned short int d) throw() { return j << json::integer_u(d); }
+
+inline
+json & operator << (json & j, int d) throw()         { return j << json::integer(d); }
+
+inline
+json & operator << (json & j, unsigned int d) throw()       { return j << json::integer_u(d); }
+
+inline
+json & operator << (json & j, long int d) throw()         { return j << json::integer(d); }
+
+inline
+json & operator << (json & j, unsigned long int d) throw()       { return j << json::integer_u(d); }
+
+inline
+json & operator << (json & j, long long int d) throw()         { return j << json::integer(d); }
+
+inline
+json & operator << (json & j, unsigned long long int d) throw()       { return j << json::integer_u(d); }
+
+inline
+json::operator bool() const throw()     { return good(); }
+
+inline
+bool json::good() const throw()         { return _stream && ferror(_stream) == 0; }
+
+inline
+bool json::eof() const throw()          { return feof(_stream) != 0; }
+
+} // namespace graphite2

+ 450 - 0
thirdparty/graphite/src/inc/locale2lcid.h

@@ -0,0 +1,450 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+#include <cstring>
+#include <cassert>
+
+#include "inc/Main.h"
+
+
+namespace graphite2 {
+
+struct IsoLangEntry
+{
+    unsigned short mnLang;
+    char maLangStr[4];
+    char maCountry[3];
+};
+
+// Windows Language ID, Locale ISO-639 language, country code as used in
+// naming table of OpenType fonts
+const IsoLangEntry LANG_ENTRIES[] = {
+    { 0x0401, "ar","SA" }, // Arabic Saudi Arabia
+    { 0x0402, "bg","BG" }, // Bulgarian Bulgaria
+    { 0x0403, "ca","ES" }, // Catalan Catalan
+    { 0x0404, "zh","TW" }, // Chinese Taiwan
+    { 0x0405, "cs","CZ" }, // Czech Czech Republic
+    { 0x0406, "da","DK" }, // Danish Denmark
+    { 0x0407, "de","DE" }, // German Germany
+    { 0x0408, "el","GR" }, // Greek Greece
+    { 0x0409, "en","US" }, // English United States
+    { 0x040A, "es","ES" }, // Spanish (Traditional Sort) Spain
+    { 0x040B, "fi","FI" }, // Finnish Finland
+    { 0x040C, "fr","FR" }, // French France
+    { 0x040D, "he","IL" }, // Hebrew Israel
+    { 0x040E, "hu","HU" }, // Hungarian Hungary
+    { 0x040F, "is","IS" }, // Icelandic Iceland
+    { 0x0410, "it","IT" }, // Italian Italy
+    { 0x0411, "jp","JP" }, // Japanese Japan
+    { 0x0412, "ko","KR" }, // Korean Korea
+    { 0x0413, "nl","NL" }, // Dutch Netherlands
+    { 0x0414, "no","NO" }, // Norwegian (Bokmal) Norway
+    { 0x0415, "pl","PL" }, // Polish Poland
+    { 0x0416, "pt","BR" }, // Portuguese Brazil
+    { 0x0417, "rm","CH" }, // Romansh Switzerland
+    { 0x0418, "ro","RO" }, // Romanian Romania
+    { 0x0419, "ru","RU" }, // Russian Russia
+    { 0x041A, "hr","HR" }, // Croatian Croatia
+    { 0x041B, "sk","SK" }, // Slovak Slovakia
+    { 0x041C, "sq","AL" }, // Albanian Albania
+    { 0x041D, "sv","SE" }, // Swedish Sweden
+    { 0x041E, "th","TH" }, // Thai Thailand
+    { 0x041F, "tr","TR" }, // Turkish Turkey
+    { 0x0420, "ur","PK" }, // Urdu Islamic Republic of Pakistan
+    { 0x0421, "id","ID" }, // Indonesian Indonesia
+    { 0x0422, "uk","UA" }, // Ukrainian Ukraine
+    { 0x0423, "be","BY" }, // Belarusian Belarus
+    { 0x0424, "sl","SI" }, // Slovenian Slovenia
+    { 0x0425, "et","EE" }, // Estonian Estonia
+    { 0x0426, "lv","LV" }, // Latvian Latvia
+    { 0x0427, "lt","LT" }, // Lithuanian Lithuania
+    { 0x0428, "tg","TJ" }, // Tajik (Cyrillic) Tajikistan
+    { 0x042A, "vi","VN" }, // Vietnamese Vietnam
+    { 0x042B, "hy","AM" }, // Armenian Armenia
+    { 0x042C, "az","AZ" }, // Azeri (Latin) Azerbaijan
+    { 0x042D, "eu","" }, // Basque Basque
+    { 0x042E, "hsb","DE" }, // Upper Sorbian Germany
+    { 0x042F, "mk","MK" }, // Macedonian (FYROM) Former Yugoslav Republic of Macedonia
+    { 0x0432, "tn","ZA" }, // Setswana South Africa
+    { 0x0434, "xh","ZA" }, // isiXhosa South Africa
+    { 0x0435, "zu","ZA" }, // isiZulu South Africa
+    { 0x0436, "af","ZA" }, // Afrikaans South Africa
+    { 0x0437, "ka","GE" }, // Georgian Georgia
+    { 0x0438, "fo","FO" }, // Faroese Faroe Islands
+    { 0x0439, "hi","IN" }, // Hindi India
+    { 0x043A, "mt","MT" }, // Maltese Malta
+    { 0x043B, "se","NO" }, // Sami (Northern) Norway
+    { 0x043E, "ms","MY" }, // Malay Malaysia
+    { 0x043F, "kk","KZ" }, // Kazakh Kazakhstan
+    { 0x0440, "ky","KG" }, // Kyrgyz Kyrgyzstan
+    { 0x0441, "sw","KE" }, // Kiswahili Kenya
+    { 0x0442, "tk","TM" }, // Turkmen Turkmenistan
+    { 0x0443, "uz","UZ" }, // Uzbek (Latin) Uzbekistan
+    { 0x0444, "tt","RU" }, // Tatar Russia
+    { 0x0445, "bn","IN" }, // Bengali India
+    { 0x0446, "pa","IN" }, // Punjabi India
+    { 0x0447, "gu","IN" }, // Gujarati India
+    { 0x0448, "or","IN" }, // Oriya India
+    { 0x0448, "wo","SN" }, // Wolof Senegal
+    { 0x0449, "ta","IN" }, // Tamil India
+    { 0x044A, "te","IN" }, // Telugu India
+    { 0x044B, "kn","IN" }, // Kannada India
+    { 0x044C, "ml","IN" }, // Malayalam India
+    { 0x044D, "as","IN" }, // Assamese India
+    { 0x044E, "mr","IN" }, // Marathi India
+    { 0x044F, "sa","IN" }, // Sanskrit India
+    { 0x0450, "mn","MN" }, // Mongolian (Cyrillic) Mongolia
+    { 0x0451, "bo","CN" }, // Tibetan PRC
+    { 0x0452, "cy","GB" }, // Welsh United Kingdom
+    { 0x0453, "km","KH" }, // Khmer Cambodia
+    { 0x0454, "lo","LA" }, // Lao Lao P.D.R.
+    { 0x0455, "my","MM" }, // Burmese Myanmar - not listed in Microsoft docs anymore
+    { 0x0456, "gl","ES" }, // Galician Galician
+    { 0x0457, "kok","IN" }, // Konkani India
+    { 0x045A, "syr","TR" }, // Syriac Syria
+    { 0x045B, "si","LK" }, // Sinhala Sri Lanka
+    { 0x045D, "iu","CA" }, // Inuktitut Canada
+    { 0x045E, "am","ET" }, // Amharic Ethiopia
+    { 0x0461, "ne","NP" }, // Nepali Nepal
+    { 0x0462, "fy","NL" }, // Frisian Netherlands
+    { 0x0463, "ps","AF" }, // Pashto Afghanistan
+    { 0x0464, "fil","PH" }, // Filipino Philippines
+    { 0x0465, "dv","MV" }, // Divehi Maldives
+    { 0x0468, "ha","NG" }, // Hausa (Latin) Nigeria
+    { 0x046A, "yo","NG" }, // Yoruba Nigeria
+    { 0x046B, "qu","BO" }, // Quechua Bolivia
+    { 0x046C, "st","ZA" }, // Sesotho sa Leboa South Africa
+    { 0x046D, "ba","RU" }, // Bashkir Russia
+    { 0x046E, "lb","LU" }, // Luxembourgish Luxembourg
+    { 0x046F, "kl","GL" }, // Greenlandic Greenland
+    { 0x0470, "ig","NG" }, // Igbo Nigeria
+    { 0x0478, "ii","CN" }, // Yi PRC
+    { 0x047A, "arn","CL" }, // Mapudungun Chile
+    { 0x047C, "moh","CA" }, // Mohawk Mohawk
+    { 0x047E, "br","FR" }, // Breton France
+    { 0x0480, "ug","CN" }, // Uighur PRC
+    { 0x0481, "mi","NZ" }, // Maori New Zealand
+    { 0x0482, "oc","FR" }, // Occitan France
+    { 0x0483, "co","FR" }, // Corsican France
+    { 0x0484, "gsw","FR" }, // Alsatian France
+    { 0x0485, "sah","RU" }, // Yakut Russia
+    { 0x0486, "qut","GT" }, // K'iche Guatemala
+    { 0x0487, "rw","RW" }, // Kinyarwanda Rwanda
+    { 0x048C, "gbz","AF" }, // Dari Afghanistan
+    { 0x0801, "ar","IQ" }, // Arabic Iraq
+    { 0x0804, "zn","CH" }, // Chinese People's Republic of China
+    { 0x0807, "de","CH" }, // German Switzerland
+    { 0x0809, "en","GB" }, // English United Kingdom
+    { 0x080A, "es","MX" }, // Spanish Mexico
+    { 0x080C, "fr","BE" }, // French Belgium
+    { 0x0810, "it","CH" }, // Italian Switzerland
+    { 0x0813, "nl","BE" }, // Dutch Belgium
+    { 0x0814, "nn","NO" }, // Norwegian (Nynorsk) Norway
+    { 0x0816, "pt","PT" }, // Portuguese Portugal
+    { 0x081A, "sh","RS" }, // Serbian (Latin) Serbia
+    { 0x081D, "sv","FI" }, // Sweden Finland
+    { 0x082C, "az","AZ" }, // Azeri (Cyrillic) Azerbaijan
+    { 0x082E, "dsb","DE" }, // Lower Sorbian Germany
+    { 0x083B, "se","SE" }, // Sami (Northern) Sweden
+    { 0x083C, "ga","IE" }, // Irish Ireland
+    { 0x083E, "ms","BN" }, // Malay Brunei Darussalam
+    { 0x0843, "uz","UZ" }, // Uzbek (Cyrillic) Uzbekistan
+    { 0x0845, "bn","BD" }, // Bengali Bangladesh
+    { 0x0850, "mn","MN" }, // Mongolian (Traditional) People's Republic of China
+    { 0x085D, "iu","CA" }, // Inuktitut (Latin) Canada
+    { 0x085F, "ber","DZ" }, // Tamazight (Latin) Algeria
+    { 0x086B, "es","EC" }, // Quechua Ecuador
+    { 0x0C01, "ar","EG" }, // Arabic Egypt
+    { 0x0C04, "zh","HK" }, // Chinese Hong Kong S.A.R.
+    { 0x0C07, "de","AT" }, // German Austria
+    { 0x0C09, "en","AU" }, // English Australia
+    { 0x0C0A, "es","ES" }, // Spanish (Modern Sort) Spain
+    { 0x0C0C, "fr","CA" }, // French Canada
+    { 0x0C1A, "sr","CS" }, // Serbian (Cyrillic) Serbia
+    { 0x0C3B, "se","FI" }, // Sami (Northern) Finland
+    { 0x0C6B, "qu","PE" }, // Quechua Peru
+    { 0x1001, "ar","LY" }, // Arabic Libya
+    { 0x1004, "zh","SG" }, // Chinese Singapore
+    { 0x1007, "de","LU" }, // German Luxembourg
+    { 0x1009, "en","CA" }, // English Canada
+    { 0x100A, "es","GT" }, // Spanish Guatemala
+    { 0x100C, "fr","CH" }, // French Switzerland
+    { 0x101A, "hr","BA" }, // Croatian (Latin) Bosnia and Herzegovina
+    { 0x103B, "smj","NO" }, // Sami (Lule) Norway
+    { 0x1401, "ar","DZ" }, // Arabic Algeria
+    { 0x1404, "zh","MO" }, // Chinese Macao S.A.R.
+    { 0x1407, "de","LI" }, // German Liechtenstein
+    { 0x1409, "en","NZ" }, // English New Zealand
+    { 0x140A, "es","CR" }, // Spanish Costa Rica
+    { 0x140C, "fr","LU" }, // French Luxembourg
+    { 0x141A, "bs","BA" }, // Bosnian (Latin) Bosnia and Herzegovina
+    { 0x143B, "smj","SE" }, // Sami (Lule) Sweden
+    { 0x1801, "ar","MA" }, // Arabic Morocco
+    { 0x1809, "en","IE" }, // English Ireland
+    { 0x180A, "es","PA" }, // Spanish Panama
+    { 0x180C, "fr","MC" }, // French Principality of Monoco
+    { 0x181A, "sh","BA" }, // Serbian (Latin) Bosnia and Herzegovina
+    { 0x183B, "sma","NO" }, // Sami (Southern) Norway
+    { 0x1C01, "ar","TN" }, // Arabic Tunisia
+    { 0x1C09, "en","ZA" }, // English South Africa
+    { 0x1C0A, "es","DO" }, // Spanish Dominican Republic
+    { 0x1C1A, "sr","BA" }, // Serbian (Cyrillic) Bosnia and Herzegovina
+    { 0x1C3B, "sma","SE" }, // Sami (Southern) Sweden
+    { 0x2001, "ar","OM" }, // Arabic Oman
+    { 0x2009, "en","JM" }, // English Jamaica
+    { 0x200A, "es","VE" }, // Spanish Venezuela
+    { 0x201A, "bs","BA" }, // Bosnian (Cyrillic) Bosnia and Herzegovina
+    { 0x203B, "sms","FI" }, // Sami (Skolt) Finland
+    { 0x2401, "ar","YE" }, // Arabic Yemen
+    { 0x2409, "en","BS" }, // English Caribbean
+    { 0x240A, "es","CO" }, // Spanish Colombia
+    { 0x243B, "smn","FI" }, // Sami (Inari) Finland
+    { 0x2801, "ar","SY" }, // Arabic Syria
+    { 0x2809, "en","BZ" }, // English Belize
+    { 0x280A, "es","PE" }, // Spanish Peru
+    { 0x2C01, "ar","JO" }, // Arabic Jordan
+    { 0x2C09, "en","TT" }, // English Trinidad and Tobago
+    { 0x2C0A, "es","AR" }, // Spanish Argentina
+    { 0x3001, "ar","LB" }, // Arabic Lebanon
+    { 0x3009, "en","ZW" }, // English Zimbabwe
+    { 0x300A, "es","EC" }, // Spanish Ecuador
+    { 0x3401, "ar","KW" }, // Arabic Kuwait
+    { 0x3409, "en","PH" }, // English Republic of the Philippines
+    { 0x340A, "es","CL" }, // Spanish Chile
+    { 0x3801, "ar","AE" }, // Arabic U.A.E.
+    { 0x380A, "es","UY" }, // Spanish Uruguay
+    { 0x3C01, "ar","BH" }, // Arabic Bahrain
+    { 0x3C0A, "es","PY" }, // Spanish Paraguay
+    { 0x4001, "ar","QA" }, // Arabic Qatar
+    { 0x4009, "en","IN" }, // English India
+    { 0x400A, "es","BO" }, // Spanish Bolivia
+    { 0x4409, "en","MY" }, // English Malaysia
+    { 0x440A, "es","SV" }, // Spanish El Salvador
+    { 0x4809, "en","SG" }, // English Singapore
+    { 0x480A, "es","HN" }, // Spanish Honduras
+    { 0x4C0A, "es","NI" }, // Spanish Nicaragua
+    { 0x500A, "es","PR" }, // Spanish Puerto Rico
+    { 0x540A, "es","US" } // Spanish United States
+};
+
+class Locale2Lang
+{
+    Locale2Lang(const Locale2Lang &);
+    Locale2Lang & operator = (const Locale2Lang &);
+
+public:
+    Locale2Lang() : mSeedPosition(128)
+    {
+        memset((void*)mLangLookup, 0, sizeof(mLangLookup));
+        // create a tri lookup on first 2 letters of language code
+        static const int maxIndex = sizeof(LANG_ENTRIES)/sizeof(IsoLangEntry);
+        for (int i = 0; i < maxIndex; i++)
+        {
+            size_t a = LANG_ENTRIES[i].maLangStr[0] - 'a';
+            size_t b = LANG_ENTRIES[i].maLangStr[1] - 'a';
+            if (mLangLookup[a][b])
+            {
+                const IsoLangEntry ** old = mLangLookup[a][b];
+                int len = 1;
+                while (old[len]) len++;
+                len += 2;
+                mLangLookup[a][b] = gralloc<const IsoLangEntry *>(len);
+                if (!mLangLookup[a][b])
+                {
+                    mLangLookup[a][b] = old;
+                    continue;
+                }
+                mLangLookup[a][b][--len] = NULL;
+                mLangLookup[a][b][--len] = &LANG_ENTRIES[i];
+                while (--len >= 0)
+                {
+                    assert(len >= 0);
+                    mLangLookup[a][b][len] = old[len];
+                }
+                free(old);
+            }
+            else
+            {
+                mLangLookup[a][b] = gralloc<const IsoLangEntry *>(2);
+                if (!mLangLookup[a][b]) continue;
+                mLangLookup[a][b][1] = NULL;
+                mLangLookup[a][b][0] = &LANG_ENTRIES[i];
+            }
+        }
+        while (2 * mSeedPosition < maxIndex)
+            mSeedPosition *= 2;
+    };
+    ~Locale2Lang()
+    {
+        for (int i = 0; i != 26; ++i)
+            for (int j = 0; j != 26; ++j)
+                free(mLangLookup[i][j]);
+    }
+    unsigned short getMsId(const char * locale) const
+    {
+        size_t length = strlen(locale);
+        size_t langLength = length;
+        const char * language = locale;
+        const char * script = NULL;
+        const char * region = NULL;
+        size_t regionLength = 0;
+        const char * dash = strchr(locale, '-');
+        if (dash && (dash != locale))
+        {
+            langLength = (dash - locale);
+            size_t nextPartLength = length - langLength - 1;
+            if (nextPartLength >= 2)
+            {
+                script = ++dash;
+                dash = strchr(dash, '-');
+                if (dash)
+                {
+                    nextPartLength = (dash - script);
+                    region = ++dash;
+                }
+                if (nextPartLength == 2 &&
+                    (locale[langLength+1] > 0x40) && (locale[langLength+1] < 0x5B) &&
+                    (locale[langLength+2] > 0x40) && (locale[langLength+2] < 0x5B))
+                {
+                    region = script;
+                    regionLength = nextPartLength;
+                    script = NULL;
+                }
+                else if (nextPartLength == 4)
+                {
+                    if (dash)
+                    {
+                        dash = strchr(dash, '-');
+                        if (dash)
+                        {
+                            nextPartLength = (dash - region);
+                        }
+                        else
+                        {
+                            nextPartLength = langLength - (region - locale);
+                        }
+                        regionLength = nextPartLength;
+                    }
+                }
+            }
+        }
+        size_t a = 'e' - 'a';
+        size_t b = 'n' - 'a';
+        unsigned short langId = 0;
+        int i = 0;
+        switch (langLength)
+        {
+            case 2:
+            {
+                a = language[0] - 'a';
+                b = language[1] - 'a';
+                if ((a < 26) && (b < 26) && mLangLookup[a][b])
+                {
+                    while (mLangLookup[a][b][i])
+                    {
+                        if (mLangLookup[a][b][i]->maLangStr[2] != '\0')
+                        {
+                            ++i;
+                            continue;
+                        }
+                        if (region && (strncmp(mLangLookup[a][b][i]->maCountry, region, regionLength) == 0))
+                        {
+                            langId = mLangLookup[a][b][i]->mnLang;
+                            break;
+                        }
+                        else if (langId == 0)
+                        {
+                            // possible fallback code
+                            langId = mLangLookup[a][b][i]->mnLang;
+                        }
+                        ++i;
+                    }
+                }
+            }
+            break;
+            case 3:
+            {
+                a = language[0] - 'a';
+                b = language[1] - 'a';
+                if (mLangLookup[a][b])
+                {
+                    while (mLangLookup[a][b][i])
+                    {
+                        if (mLangLookup[a][b][i]->maLangStr[2] != language[2])
+                        {
+                            ++i;
+                            continue;
+                        }
+                        if (region && (strncmp(mLangLookup[a][b][i]->maCountry, region, regionLength) == 0))
+                        {
+                            langId = mLangLookup[a][b][i]->mnLang;
+                            break;
+                        }
+                        else if (langId == 0)
+                        {
+                            // possible fallback code
+                            langId = mLangLookup[a][b][i]->mnLang;
+                        }
+                        ++i;
+                    }
+                }
+            }
+            break;
+            default:
+                break;
+        }
+        if (langId == 0) langId = 0x409;
+        return langId;
+    }
+    const IsoLangEntry * findEntryById(unsigned short langId) const
+    {
+        static const int maxIndex = sizeof(LANG_ENTRIES)/sizeof(IsoLangEntry);
+        int window = mSeedPosition;
+        int guess = mSeedPosition - 1;
+        while (LANG_ENTRIES[guess].mnLang != langId)
+        {
+            window /= 2;
+            if (window == 0) return NULL;
+            guess += (LANG_ENTRIES[guess].mnLang > langId)? -window : window;
+            while (guess >= maxIndex)
+            {
+                window /= 2;
+                guess -= window;
+                assert(window);
+            }
+        }
+        return &LANG_ENTRIES[guess];
+    }
+
+    CLASS_NEW_DELETE;
+
+private:
+    const IsoLangEntry ** mLangLookup[26][26];
+    int mSeedPosition;
+};
+
+} // namespace graphite2

+ 124 - 0
thirdparty/graphite/src/inc/opcode_table.h

@@ -0,0 +1,124 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// This file will be pulled into and integrated into a machine implmentation
+// DO NOT build directly
+#pragma once
+
+#define do2(n)  do_(n) ,do_(n)
+#define NILOP   0U
+
+// types or parameters are: (.. is inclusive)
+//      number - any byte
+//      output_class - 0 .. silf.m_nClass
+//      input_class - 0 .. silf.m_nClass
+//      sattrnum - 0 .. 29 (gr_slatJWidth) , 55 (gr_slatUserDefn)
+//      attrid - 0 .. silf.numUser() where sattrnum == 55; 0..silf.m_iMaxComp where sattrnum == 15 otherwise 0
+//      gattrnum - 0 .. face->getGlyphFaceCache->numAttrs()
+//      gmetric - 0 .. 11 (kgmetDescent)
+//      featidx - 0 .. face.numFeatures()
+//      level - any byte
+static const opcode_t opcode_table[] =
+{
+    {{do2(nop)},                                    0, "NOP"},
+
+    {{do2(push_byte)},                              1, "PUSH_BYTE"},                // number
+    {{do2(push_byte_u)},                            1, "PUSH_BYTE_U"},              // number
+    {{do2(push_short)},                             2, "PUSH_SHORT"},               // number number
+    {{do2(push_short_u)},                           2, "PUSH_SHORT_U"},             // number number
+    {{do2(push_long)},                              4, "PUSH_LONG"},                // number number number number
+
+    {{do2(add)},                                    0, "ADD"},
+    {{do2(sub)},                                    0, "SUB"},
+    {{do2(mul)},                                    0, "MUL"},
+    {{do2(div_)},                                   0, "DIV"},
+    {{do2(min_)},                                   0, "MIN"},
+    {{do2(max_)},                                   0, "MAX"},
+    {{do2(neg)},                                    0, "NEG"},
+    {{do2(trunc8)},                                 0, "TRUNC8"},
+    {{do2(trunc16)},                                0, "TRUNC16"},
+
+    {{do2(cond)},                                   0, "COND"},
+    {{do2(and_)},                                   0, "AND"},      // 0x10
+    {{do2(or_)},                                    0, "OR"},
+    {{do2(not_)},                                   0, "NOT"},
+    {{do2(equal)},                                  0, "EQUAL"},
+    {{do2(not_eq_)},                                0, "NOT_EQ"},
+    {{do2(less)},                                   0, "LESS"},
+    {{do2(gtr)},                                    0, "GTR"},
+    {{do2(less_eq)},                                0, "LESS_EQ"},
+    {{do2(gtr_eq)},                                 0, "GTR_EQ"},   // 0x18
+
+    {{do_(next), NILOP},                            0, "NEXT"},
+    {{NILOP, NILOP},                                1, "NEXT_N"},                   // number <= smap.end - map
+    {{do_(next), NILOP},                            0, "COPY_NEXT"},
+    {{do_(put_glyph_8bit_obs), NILOP},              1, "PUT_GLYPH_8BIT_OBS"},       // output_class
+    {{do_(put_subs_8bit_obs), NILOP},               3, "PUT_SUBS_8BIT_OBS"},        // slot input_class output_class
+    {{do_(put_copy), NILOP},                        1, "PUT_COPY"},                 // slot
+    {{do_(insert), NILOP},                          0, "INSERT"},
+    {{do_(delete_), NILOP},                         0, "DELETE"},   // 0x20
+    {{do_(assoc), NILOP},                     VARARGS, "ASSOC"},
+    {{NILOP ,do_(cntxt_item)},                      2, "CNTXT_ITEM"},               // slot offset
+
+    {{do_(attr_set), NILOP},                        1, "ATTR_SET"},                 // sattrnum
+    {{do_(attr_add), NILOP},                        1, "ATTR_ADD"},                 // sattrnum
+    {{do_(attr_sub), NILOP},                        1, "ATTR_SUB"},                 // sattrnum
+    {{do_(attr_set_slot), NILOP},                   1, "ATTR_SET_SLOT"},            // sattrnum
+    {{do_(iattr_set_slot), NILOP},                  2, "IATTR_SET_SLOT"},           // sattrnum attrid
+    {{do2(push_slot_attr)},                         2, "PUSH_SLOT_ATTR"},           // sattrnum slot
+    {{do2(push_glyph_attr_obs)},                    2, "PUSH_GLYPH_ATTR_OBS"},      // gattrnum slot
+    {{do2(push_glyph_metric)},                      3, "PUSH_GLYPH_METRIC"},        // gmetric slot level
+    {{do2(push_feat)},                              2, "PUSH_FEAT"},                // featidx slot
+
+    {{do2(push_att_to_gattr_obs)},                  2, "PUSH_ATT_TO_GATTR_OBS"},    // gattrnum slot
+    {{do2(push_att_to_glyph_metric)},               3, "PUSH_ATT_TO_GLYPH_METRIC"}, // gmetric slot level
+    {{do2(push_islot_attr)},                        3, "PUSH_ISLOT_ATTR"},          // sattrnum slot attrid
+
+    {{NILOP,NILOP},                                 3, "PUSH_IGLYPH_ATTR"},
+
+    {{do2(pop_ret)},                                0, "POP_RET"},  // 0x30
+    {{do2(ret_zero)},                               0, "RET_ZERO"},
+    {{do2(ret_true)},                               0, "RET_TRUE"},
+
+    {{do_(iattr_set), NILOP},                       2, "IATTR_SET"},                // sattrnum attrid
+    {{do_(iattr_add), NILOP},                       2, "IATTR_ADD"},                // sattrnum attrid
+    {{do_(iattr_sub), NILOP},                       2, "IATTR_SUB"},                // sattrnum attrid
+    {{do2(push_proc_state)},                        1, "PUSH_PROC_STATE"},          // dummy
+    {{do2(push_version)},                           0, "PUSH_VERSION"},
+    {{do_(put_subs), NILOP},                        5, "PUT_SUBS"},                 // slot input_class input_class output_class output_class
+    {{NILOP,NILOP},                                 0, "PUT_SUBS2"},
+    {{NILOP,NILOP},                                 0, "PUT_SUBS3"},
+    {{do_(put_glyph), NILOP},                       2, "PUT_GLYPH"},                // output_class output_class
+    {{do2(push_glyph_attr)},                        3, "PUSH_GLYPH_ATTR"},          // gattrnum gattrnum slot
+    {{do2(push_att_to_glyph_attr)},                 3, "PUSH_ATT_TO_GLYPH_ATTR"},   // gattrnum gattrnum slot
+    {{do2(bor)},                                    0, "BITOR"},
+    {{do2(band)},                                   0, "BITAND"},
+    {{do2(bnot)},                                   0, "BITNOT"},   // 0x40
+    {{do2(setbits)},                                4, "BITSET"},
+    {{do_(set_feat), NILOP},                        2, "SET_FEAT"},                 // featidx slot
+    // private opcodes for internal use only, comes after all other on disk opcodes.
+    {{do_(temp_copy), NILOP},                       0, "TEMP_COPY"}
+};

+ 691 - 0
thirdparty/graphite/src/inc/opcodes.h

@@ -0,0 +1,691 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2010, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+#pragma once
+// This file will be pulled into and integrated into a machine implmentation
+// DO NOT build directly and under no circumstances ever #include headers in
+// here or you will break the direct_machine.
+//
+// Implementers' notes
+// ==================
+// You have access to a few primitives and the full C++ code:
+//    declare_params(n) Tells the interpreter how many bytes of parameter
+//                      space to claim for this instruction uses and
+//                      initialises the param pointer.  You *must* before the
+//                      first use of param.
+//    use_params(n)     Claim n extra bytes of param space beyond what was
+//                      claimed using delcare_param.
+//    param             A const byte pointer for the parameter space claimed by
+//                      this instruction.
+//    binop(op)         Implement a binary operation on the stack using the
+//                      specified C++ operator.
+//    NOT_IMPLEMENTED   Any instruction body containing this will exit the
+//                      program with an assertion error.  Instructions that are
+//                      not implemented should also be marked NILOP in the
+//                      opcodes tables this will cause the code class to spot
+//                      them in a live code stream and throw a runtime_error
+//                      instead.
+//    push(n)           Push the value n onto the stack.
+//    pop()             Pop the top most value and return it.
+//
+//    You have access to the following named fast 'registers':
+//        sp        = The pointer to the current top of stack, the last value
+//                    pushed.
+//        seg       = A reference to the Segment this code is running over.
+//        is        = The current slot index
+//        isb       = The original base slot index at the start of this rule
+//        isf       = The first positioned slot
+//        isl       = The last positioned slot
+//        ip        = The current instruction pointer
+//        endPos    = Position of advance of last cluster
+//        dir       = writing system directionality of the font
+
+
+// #define NOT_IMPLEMENTED     assert(false)
+// #define NOT_IMPLEMENTED
+
+#define binop(op)           const uint32 a = pop(); *sp = uint32(*sp) op a
+#define sbinop(op)          const int32 a = pop(); *sp = int32(*sp) op a
+#define use_params(n)       dp += n
+
+#define declare_params(n)   const byte * param = dp; \
+                            use_params(n);
+
+#define push(n)             { *++sp = n; }
+#define pop()               (*sp--)
+#define slotat(x)           (map[(x)])
+#define DIE                 { is=seg.last(); status = Machine::died_early; EXIT(1); }
+#define POSITIONED          1
+
+STARTOP(nop)
+    do {} while (0);
+ENDOP
+
+STARTOP(push_byte)
+    declare_params(1);
+    push(int8(*param));
+ENDOP
+
+STARTOP(push_byte_u)
+    declare_params(1);
+    push(uint8(*param));
+ENDOP
+
+STARTOP(push_short)
+    declare_params(2);
+    const int16 r   = int16(param[0]) << 8
+                    | uint8(param[1]);
+    push(r);
+ENDOP
+
+STARTOP(push_short_u)
+    declare_params(2);
+    const uint16 r  = uint16(param[0]) << 8
+                    | uint8(param[1]);
+    push(r);
+ENDOP
+
+STARTOP(push_long)
+    declare_params(4);
+    const  int32 r  = int32(param[0]) << 24
+                    | uint32(param[1]) << 16
+                    | uint32(param[2]) << 8
+                    | uint8(param[3]);
+    push(r);
+ENDOP
+
+STARTOP(add)
+    binop(+);
+ENDOP
+
+STARTOP(sub)
+    binop(-);
+ENDOP
+
+STARTOP(mul)
+    binop(*);
+ENDOP
+
+STARTOP(div_)
+    const int32 b = pop();
+    const int32 a = int32(*sp);
+    if (b == 0 || (a == std::numeric_limits<int32>::min() && b == -1)) DIE;
+    *sp = int32(*sp) / b;
+ENDOP
+
+STARTOP(min_)
+    const int32 a = pop(), b = *sp;
+    if (a < b) *sp = a;
+ENDOP
+
+STARTOP(max_)
+    const int32 a = pop(), b = *sp;
+    if (a > b) *sp = a;
+ENDOP
+
+STARTOP(neg)
+    *sp = uint32(-int32(*sp));
+ENDOP
+
+STARTOP(trunc8)
+    *sp = uint8(*sp);
+ENDOP
+
+STARTOP(trunc16)
+    *sp = uint16(*sp);
+ENDOP
+
+STARTOP(cond)
+    const uint32 f = pop(), t = pop(), c = pop();
+    push(c ? t : f);
+ENDOP
+
+STARTOP(and_)
+    binop(&&);
+ENDOP
+
+STARTOP(or_)
+    binop(||);
+ENDOP
+
+STARTOP(not_)
+    *sp = !*sp;
+ENDOP
+
+STARTOP(equal)
+    binop(==);
+ENDOP
+
+STARTOP(not_eq_)
+    binop(!=);
+ENDOP
+
+STARTOP(less)
+    sbinop(<);
+ENDOP
+
+STARTOP(gtr)
+    sbinop(>);
+ENDOP
+
+STARTOP(less_eq)
+    sbinop(<=);
+ENDOP
+
+STARTOP(gtr_eq)
+    sbinop(>=);
+ENDOP
+
+STARTOP(next)
+    if (map - &smap[0] >= int(smap.size())) DIE
+    if (is)
+    {
+        if (is == smap.highwater())
+            smap.highpassed(true);
+        is = is->next();
+    }
+    ++map;
+ENDOP
+
+//STARTOP(next_n)
+//    use_params(1);
+//    NOT_IMPLEMENTED;
+    //declare_params(1);
+    //const size_t num = uint8(*param);
+//ENDOP
+
+//STARTOP(copy_next)
+//     if (is) is = is->next();
+//     ++map;
+// ENDOP
+
+STARTOP(put_glyph_8bit_obs)
+    declare_params(1);
+    const unsigned int output_class = uint8(*param);
+    is->setGlyph(&seg, seg.getClassGlyph(output_class, 0));
+ENDOP
+
+STARTOP(put_subs_8bit_obs)
+    declare_params(3);
+    const int           slot_ref     = int8(param[0]);
+    const unsigned int  input_class  = uint8(param[1]),
+                        output_class = uint8(param[2]);
+    uint16 index;
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        index = seg.findClassIndex(input_class, slot->gid());
+        is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
+    }
+ENDOP
+
+STARTOP(put_copy)
+    declare_params(1);
+    const int  slot_ref = int8(*param);
+    if (is && !is->isDeleted())
+    {
+        slotref ref = slotat(slot_ref);
+        if (ref && ref != is)
+        {
+            int16 *tempUserAttrs = is->userAttrs();
+            if (is->attachedTo() || is->firstChild()) DIE
+            Slot *prev = is->prev();
+            Slot *next = is->next();
+            memcpy(tempUserAttrs, ref->userAttrs(), seg.numAttrs() * sizeof(uint16));
+            memcpy(is, ref, sizeof(Slot));
+            is->firstChild(NULL);
+            is->nextSibling(NULL);
+            is->userAttrs(tempUserAttrs);
+            is->next(next);
+            is->prev(prev);
+            if (is->attachedTo())
+                is->attachedTo()->child(is);
+        }
+        is->markCopied(false);
+        is->markDeleted(false);
+    }
+ENDOP
+
+STARTOP(insert)
+    if (smap.decMax() <= 0) DIE;
+    Slot *newSlot = seg.newSlot();
+    if (!newSlot) DIE;
+    Slot *iss = is;
+    while (iss && iss->isDeleted()) iss = iss->next();
+    if (!iss)
+    {
+        if (seg.last())
+        {
+            seg.last()->next(newSlot);
+            newSlot->prev(seg.last());
+            newSlot->before(seg.last()->before());
+            seg.last(newSlot);
+        }
+        else
+        {
+            seg.first(newSlot);
+            seg.last(newSlot);
+        }
+    }
+    else if (iss->prev())
+    {
+        iss->prev()->next(newSlot);
+        newSlot->prev(iss->prev());
+        newSlot->before(iss->prev()->after());
+    }
+    else
+    {
+        newSlot->prev(NULL);
+        newSlot->before(iss->before());
+        seg.first(newSlot);
+    }
+    newSlot->next(iss);
+    if (iss)
+    {
+        iss->prev(newSlot);
+        newSlot->originate(iss->original());
+        newSlot->after(iss->before());
+    }
+    else if (newSlot->prev())
+    {
+        newSlot->originate(newSlot->prev()->original());
+        newSlot->after(newSlot->prev()->after());
+    }
+    else
+    {
+        newSlot->originate(seg.defaultOriginal());
+    }
+    if (is == smap.highwater())
+        smap.highpassed(false);
+    is = newSlot;
+    seg.extendLength(1);
+    if (map != &smap[-1])
+        --map;
+ENDOP
+
+STARTOP(delete_)
+    if (!is || is->isDeleted()) DIE
+    is->markDeleted(true);
+    if (is->prev())
+        is->prev()->next(is->next());
+    else
+        seg.first(is->next());
+
+    if (is->next())
+        is->next()->prev(is->prev());
+    else
+        seg.last(is->prev());
+
+
+    if (is == smap.highwater())
+            smap.highwater(is->next());
+    if (is->prev())
+        is = is->prev();
+    seg.extendLength(-1);
+ENDOP
+
+STARTOP(assoc)
+    declare_params(1);
+    unsigned int  num = uint8(*param);
+    const int8 *  assocs = reinterpret_cast<const int8 *>(param+1);
+    use_params(num);
+    int max = -1;
+    int min = -1;
+
+    while (num-- > 0)
+    {
+        int sr = *assocs++;
+        slotref ts = slotat(sr);
+        if (ts && (min == -1 || ts->before() < min)) min = ts->before();
+        if (ts && ts->after() > max) max = ts->after();
+    }
+    if (min > -1)   // implies max > -1
+    {
+        is->before(min);
+        is->after(max);
+    }
+ENDOP
+
+STARTOP(cntxt_item)
+    // It turns out this is a cunningly disguised condition forward jump.
+    declare_params(3);
+    const int       is_arg = int8(param[0]);
+    const size_t    iskip  = uint8(param[1]),
+                    dskip  = uint8(param[2]);
+
+    if (mapb + is_arg != map)
+    {
+        ip += iskip;
+        dp += dskip;
+        push(true);
+    }
+ENDOP
+
+STARTOP(attr_set)
+    declare_params(1);
+    const attrCode      slat = attrCode(uint8(*param));
+    const          int  val  = pop();
+    is->setAttr(&seg, slat, 0, val, smap);
+ENDOP
+
+STARTOP(attr_add)
+    declare_params(1);
+    const attrCode      slat = attrCode(uint8(*param));
+    const     uint32_t  val  = pop();
+    if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
+    {
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
+        flags |= POSITIONED;
+    }
+    uint32_t res = uint32_t(is->getAttr(&seg, slat, 0));
+    is->setAttr(&seg, slat, 0, int32_t(val + res), smap);
+ENDOP
+
+STARTOP(attr_sub)
+    declare_params(1);
+    const attrCode      slat = attrCode(uint8(*param));
+    const     uint32_t  val  = pop();
+    if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
+    {
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
+        flags |= POSITIONED;
+    }
+    uint32_t res = uint32_t(is->getAttr(&seg, slat, 0));
+    is->setAttr(&seg, slat, 0, int32_t(res - val), smap);
+ENDOP
+
+STARTOP(attr_set_slot)
+    declare_params(1);
+    const attrCode  slat   = attrCode(uint8(*param));
+    const int       offset = int(map - smap.begin())*int(slat == gr_slatAttTo);
+    const int       val    = pop()  + offset;
+    is->setAttr(&seg, slat, offset, val, smap);
+ENDOP
+
+STARTOP(iattr_set_slot)
+    declare_params(2);
+    const attrCode  slat = attrCode(uint8(param[0]));
+    const uint8     idx  = uint8(param[1]);
+    const int       val  = int(pop()  + (map - smap.begin())*int(slat == gr_slatAttTo));
+    is->setAttr(&seg, slat, idx, val, smap);
+ENDOP
+
+STARTOP(push_slot_attr)
+    declare_params(2);
+    const attrCode      slat     = attrCode(uint8(param[0]));
+    const int           slot_ref = int8(param[1]);
+    if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
+    {
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
+        flags |= POSITIONED;
+    }
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        int res = slot->getAttr(&seg, slat, 0);
+        push(res);
+    }
+ENDOP
+
+STARTOP(push_glyph_attr_obs)
+    declare_params(2);
+    const unsigned int  glyph_attr = uint8(param[0]);
+    const int           slot_ref   = int8(param[1]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
+ENDOP
+
+STARTOP(push_glyph_metric)
+    declare_params(3);
+    const unsigned int  glyph_attr  = uint8(param[0]);
+    const int           slot_ref    = int8(param[1]);
+    const signed int    attr_level  = uint8(param[2]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+        push(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir));
+ENDOP
+
+STARTOP(push_feat)
+    declare_params(2);
+    const unsigned int  feat        = uint8(param[0]);
+    const int           slot_ref    = int8(param[1]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        uint8 fid = seg.charinfo(slot->original())->fid();
+        push(seg.getFeature(fid, feat));
+    }
+ENDOP
+
+STARTOP(push_att_to_gattr_obs)
+    declare_params(2);
+    const unsigned int  glyph_attr  = uint8(param[0]);
+    const int           slot_ref    = int8(param[1]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        slotref att = slot->attachedTo();
+        if (att) slot = att;
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
+    }
+ENDOP
+
+STARTOP(push_att_to_glyph_metric)
+    declare_params(3);
+    const unsigned int  glyph_attr  = uint8(param[0]);
+    const int           slot_ref    = int8(param[1]);
+    const signed int    attr_level  = uint8(param[2]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        slotref att = slot->attachedTo();
+        if (att) slot = att;
+        push(int32(seg.getGlyphMetric(slot, glyph_attr, attr_level, dir)));
+    }
+ENDOP
+
+STARTOP(push_islot_attr)
+    declare_params(3);
+    const attrCode  slat     = attrCode(uint8(param[0]));
+    const int           slot_ref = int8(param[1]),
+                        idx      = uint8(param[2]);
+    if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
+    {
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
+        flags |= POSITIONED;
+    }
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        int res = slot->getAttr(&seg, slat, idx);
+        push(res);
+    }
+ENDOP
+
+#if 0
+STARTOP(push_iglyph_attr) // not implemented
+    NOT_IMPLEMENTED;
+ENDOP
+#endif
+
+STARTOP(pop_ret)
+    const uint32 ret = pop();
+    EXIT(ret);
+ENDOP
+
+STARTOP(ret_zero)
+    EXIT(0);
+ENDOP
+
+STARTOP(ret_true)
+    EXIT(1);
+ENDOP
+
+STARTOP(iattr_set)
+    declare_params(2);
+    const attrCode      slat = attrCode(uint8(param[0]));
+    const uint8         idx  = uint8(param[1]);
+    const          int  val  = pop();
+    is->setAttr(&seg, slat, idx, val, smap);
+ENDOP
+
+STARTOP(iattr_add)
+    declare_params(2);
+    const attrCode      slat = attrCode(uint8(param[0]));
+    const uint8         idx  = uint8(param[1]);
+    const     uint32_t  val  = pop();
+    if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
+    {
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
+        flags |= POSITIONED;
+    }
+    uint32_t res = uint32_t(is->getAttr(&seg, slat, idx));
+    is->setAttr(&seg, slat, idx, int32_t(val + res), smap);
+ENDOP
+
+STARTOP(iattr_sub)
+    declare_params(2);
+    const attrCode      slat = attrCode(uint8(param[0]));
+    const uint8         idx  = uint8(param[1]);
+    const     uint32_t  val  = pop();
+    if ((slat == gr_slatPosX || slat == gr_slatPosY) && (flags & POSITIONED) == 0)
+    {
+        seg.positionSlots(0, *smap.begin(), *(smap.end()-1), seg.currdir());
+        flags |= POSITIONED;
+    }
+    uint32_t res = uint32_t(is->getAttr(&seg, slat, idx));
+    is->setAttr(&seg, slat, idx, int32_t(res - val), smap);
+ENDOP
+
+STARTOP(push_proc_state)
+    use_params(1);
+    push(1);
+ENDOP
+
+STARTOP(push_version)
+    push(0x00030000);
+ENDOP
+
+STARTOP(put_subs)
+    declare_params(5);
+    const int        slot_ref     = int8(param[0]);
+    const unsigned int  input_class  = uint8(param[1]) << 8
+                                     | uint8(param[2]);
+    const unsigned int  output_class = uint8(param[3]) << 8
+                                     | uint8(param[4]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        int index = seg.findClassIndex(input_class, slot->gid());
+        is->setGlyph(&seg, seg.getClassGlyph(output_class, index));
+    }
+ENDOP
+
+#if 0
+STARTOP(put_subs2) // not implemented
+    NOT_IMPLEMENTED;
+ENDOP
+
+STARTOP(put_subs3) // not implemented
+    NOT_IMPLEMENTED;
+ENDOP
+#endif
+
+STARTOP(put_glyph)
+    declare_params(2);
+    const unsigned int output_class  = uint8(param[0]) << 8
+                                     | uint8(param[1]);
+    is->setGlyph(&seg, seg.getClassGlyph(output_class, 0));
+ENDOP
+
+STARTOP(push_glyph_attr)
+    declare_params(3);
+    const unsigned int  glyph_attr  = uint8(param[0]) << 8
+                                    | uint8(param[1]);
+    const int           slot_ref    = int8(param[2]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
+ENDOP
+
+STARTOP(push_att_to_glyph_attr)
+    declare_params(3);
+    const unsigned int  glyph_attr  = uint8(param[0]) << 8
+                                    | uint8(param[1]);
+    const int           slot_ref    = int8(param[2]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        slotref att = slot->attachedTo();
+        if (att) slot = att;
+        push(int32(seg.glyphAttr(slot->gid(), glyph_attr)));
+    }
+ENDOP
+
+STARTOP(temp_copy)
+    slotref newSlot = seg.newSlot();
+    if (!newSlot || !is) DIE;
+    int16 *tempUserAttrs = newSlot->userAttrs();
+    memcpy(newSlot, is, sizeof(Slot));
+    memcpy(tempUserAttrs, is->userAttrs(), seg.numAttrs() * sizeof(uint16));
+    newSlot->userAttrs(tempUserAttrs);
+    newSlot->markCopied(true);
+    *map = newSlot;
+ENDOP
+
+STARTOP(band)
+    binop(&);
+ENDOP
+
+STARTOP(bor)
+    binop(|);
+ENDOP
+
+STARTOP(bnot)
+    *sp = ~*sp;
+ENDOP
+
+STARTOP(setbits)
+    declare_params(4);
+    const uint16 m  = uint16(param[0]) << 8
+                    | uint8(param[1]);
+    const uint16 v  = uint16(param[2]) << 8
+                    | uint8(param[3]);
+    *sp = ((*sp) & ~m) | v;
+ENDOP
+
+STARTOP(set_feat)
+    declare_params(2);
+    const unsigned int  feat        = uint8(param[0]);
+    const int           slot_ref    = int8(param[1]);
+    slotref slot = slotat(slot_ref);
+    if (slot)
+    {
+        uint8 fid = seg.charinfo(slot->original())->fid();
+        seg.setFeature(fid, feat, pop());
+    }
+ENDOP

+ 147 - 0
thirdparty/graphite/src/json.cpp

@@ -0,0 +1,147 @@
+/*  GRAPHITE2 LICENSING
+
+    Copyright 2011, SIL International
+    All rights reserved.
+
+    This library is free software; you can redistribute it and/or modify
+    it under the terms of the GNU Lesser General Public License as published
+    by the Free Software Foundation; either version 2.1 of License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should also have received a copy of the GNU Lesser General Public
+    License along with this library in the file named "LICENSE".
+    If not, write to the Free Software Foundation, 51 Franklin Street,
+    Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
+    internet at http://www.fsf.org/licenses/lgpl.html.
+
+Alternatively, the contents of this file may be used under the terms of the
+Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
+License, as published by the Free Software Foundation, either version 2
+of the License or (at your option) any later version.
+*/
+// JSON debug logging
+// Author: Tim Eves
+
+#if !defined GRAPHITE2_NTRACING
+
+#include <cstdio>
+#include <limits>
+#include "inc/json.h"
+
+#if defined(_MSC_VER)
+#define FORMAT_INTMAX "%lli"
+#define FORMAT_UINTMAX "%llu"
+#else
+#define FORMAT_INTMAX "%ji"
+#define FORMAT_UINTMAX "%ju"
+#endif
+
+using namespace graphite2;
+
+namespace
+{
+    enum
+    {
+        seq = ',',
+        obj='}', member=':', empty_obj='{',
+        arr=']', empty_arr='['
+    };
+}
+
+const std::nullptr_t json::null = nullptr;
+
+inline
+void json::context(const char current) throw()
+{
+    fprintf(_stream, "%c", *_context);
+    indent();
+    *_context = current;
+}
+
+
+void json::indent(const int d) throw()
+{
+    if (*_context == member || (_flatten  && _flatten < _context))
+        fputc(' ', _stream);
+    else
+        fprintf(_stream,  "\n%*s",  4*int(_context - _contexts + d), "");
+}
+
+
+inline
+void json::push_context(const char prefix, const char suffix) throw()
+{
+    assert(_context - _contexts < ptrdiff_t(sizeof _contexts));
+
+    if (_context == _contexts)
+        *_context = suffix;
+    else
+        context(suffix);
+    *++_context = prefix;
+}
+
+
+void json::pop_context() throw()
+{
+    assert(_context > _contexts);
+
+    if (*_context == seq)   indent(-1);
+    else                    fputc(*_context, _stream);
+
+    fputc(*--_context, _stream);
+    if (_context == _contexts)  fputc('\n', _stream);
+    fflush(_stream);
+
+    if (_flatten >= _context)   _flatten = 0;
+    *_context = seq;
+}
+
+
+// These four functions cannot be inlined as pointers to these
+// functions are needed for operator << (_context_t) to work.
+void json::flat(json & j) throw()   { if (!j._flatten)  j._flatten = j._context; }
+void json::close(json & j) throw()  { j.pop_context(); }
+void json::object(json & j) throw() { j.push_context('{', '}'); }
+void json::array(json & j) throw()  { j.push_context('[', ']'); }
+void json::item(json & j) throw()
+{
+    while (j._context > j._contexts+1 && j._context[-1] != arr)
+        j.pop_context();
+}
+
+
+json & json::operator << (json::string s) throw()
+{
+    const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq;
+    context(ctxt);
+    fprintf(_stream, "\"%s\"", s);
+    if (ctxt == member) fputc(' ', _stream);
+
+    return *this;
+}
+
+json & json::operator << (json::number f) throw()
+{
+    context(seq);
+    if (std::numeric_limits<json::number>::infinity() == f)
+        fputs("Infinity", _stream);
+    else if (-std::numeric_limits<json::number>::infinity() == f)
+        fputs("-Infinity", _stream);
+    else if (std::numeric_limits<json::number>::quiet_NaN() == f ||
+            std::numeric_limits<json::number>::signaling_NaN() == f)
+        fputs("NaN", _stream);
+    else
+        fprintf(_stream, "%g", f);
+    return *this;
+}
+json & json::operator << (json::integer d) throw()  { context(seq); fprintf(_stream, FORMAT_INTMAX, intmax_t(d)); return *this; }
+json & json::operator << (json::integer_u d) throw()  { context(seq); fprintf(_stream, FORMAT_UINTMAX, uintmax_t(d)); return *this; }
+json & json::operator << (json::boolean b) throw()  { context(seq); fputs(b ? "true" : "false", _stream); return *this; }
+json & json::operator << (std::nullptr_t)  throw()  { context(seq); fputs("null",_stream); return *this; }
+
+#endif

+ 14 - 0
thirdparty/harfbuzz/AUTHORS

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

+ 38 - 0
thirdparty/harfbuzz/COPYING

@@ -0,0 +1,38 @@
+HarfBuzz is licensed under the so-called "Old MIT" license.  Details follow.
+For parts of HarfBuzz that are licensed under different licenses see individual
+files names COPYING in subdirectories where applicable.
+
+Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020  Google, Inc.
+Copyright © 2018,2019,2020  Ebrahim Byagowi
+Copyright © 2019,2020  Facebook, Inc. 
+Copyright © 2012  Mozilla Foundation
+Copyright © 2011  Codethink Limited
+Copyright © 2008,2010  Nokia Corporation and/or its subsidiary(-ies)
+Copyright © 2009  Keith Stribley
+Copyright © 2009  Martin Hosken and SIL International
+Copyright © 2007  Chris Wilson
+Copyright © 2006  Behdad Esfahbod
+Copyright © 2005  David Turner
+Copyright © 2004,2007,2008,2009,2010  Red Hat, Inc.
+Copyright © 1998-2004  David Turner and Werner Lemberg
+
+For full copyright notices consult the individual files in the package.
+
+
+Permission is hereby granted, without written agreement and without
+license or royalty fees, to use, copy, modify, and distribute this
+software and its documentation for any purpose, provided that the
+above copyright notice and the following two paragraphs appear in
+all copies of this software.
+
+IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
+THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

+ 2412 - 0
thirdparty/harfbuzz/NEWS

@@ -0,0 +1,2412 @@
+Overview of changes leading to 2.7.2
+Saturday, August 29, 2020
+====================================
+- Fix a regression in the previous release that caused a crash with Kaithi.
+- More OOM fixes.
+
+
+Overview of changes leading to 2.7.1
+Thursday, August 13, 2020
+====================================
+- ot-funcs now handles variable empty glyphs better when hvar/vvar isn't present.
+- Reverted a GDEF processing regression.
+- A couple of fixes to handle OOM better.
+
+
+Overview of changes leading to 2.7.0
+Saturday, July 25, 2020
+====================================
+- Use an implementation for round that always rounds up, some minor fluctuations
+  are expected on var font specially when hb-ot callback is used.
+- Fix an AAT's `kerx` issue on broken rendering of Devanagari Sangam MN.
+- Remove AAT's `lcar` table support from _get_ligature_carets API, not even much
+  use on macOS installed fonts (only two files).  GDEF support is the recommended
+  one and expected to work properly after issues fixed two releases ago.
+- Minor memory fixes to handle OOM better specially in hb-ft.
+- Minor .so files versioning scheme change and remove stable/unstable scheme
+  differences, was never used in practice (always default to stable scheme).
+- We are now suggesting careful packaging of the library using meson,
+  https://github.com/harfbuzz/harfbuzz/wiki/Notes-on-migration-to-meson
+  for more information.
+- Distribution package URL is changed, either use GitHub generated tarballs,
+  `https://github.com/harfbuzz/harfbuzz/archive/$pkgver.tar.gz`
+  or, even more preferably use commit hash of the release and git checkouts like,
+  `git+https://github.com/harfbuzz/harfbuzz#commit=$commit`
+
+
+Overview of changes leading to 2.6.8
+Monday, June 22, 2020
+====================================
+- New API to fetch glyph alternates from GSUB table.
+- hb-coretext build fix for macOS < 10.10.
+- Meson build fixes, cmake port removal is postponed but please prepare for
+  it and give us feedback.
+  Autotools is still our main build system however please consider
+  experimenting with meson also for packaging the library.
+- New API:
++hb_ot_layout_lookup_get_glyph_alternates()
+
+
+Overview of changes leading to 2.6.7
+Wednesday, June 3, 2020
+====================================
+- Update to Unicode 13.0.0.
+- Fix hb_ot_layout_get_ligature_carets for fonts without lcar table, it was
+  completely broken for all the other fonts since 2.1.2.
+- As a part of our migration to meson, this release will be the last one
+  to provide cmake port files but autotools still is our main build system.
+  There is a possibility that the next version or the after be released
+  using meson.
+
+
+Overview of changes leading to 2.6.6
+Tuesday, May 12, 2020
+====================================
+- A fix in AAT kerning for Geeza Pro.
+- Better support for resource fork fonts on macOS.
+
+
+Overview of changes leading to 2.6.5
+Friday, April 17, 2020
+====================================
+- Add experimental meson build system.  Autotools is still the primary
+  and supported build system.
+- AAT is now always preferred for horizontal scripts when both AAT and OT
+  layout tables exist at the same time.
+- Subsetter improvements.
+- New API:
++hb_ft_font_lock_face()
++hb_ft_font_unlock_face()
+
+
+Overview of changes leading to 2.6.4
+Monday, October 29, 2019
+====================================
+- Small bug fix.
+- Build fixes.
+
+
+Overview of changes leading to 2.6.3
+Monday, October 28, 2019
+====================================
+- Misc small fixes, mostly to build-related issues.
+- New API:
++hb_font_get_nominal_glyphs()
+
+
+Overview of changes leading to 2.6.2
+Monday, September 30, 2019
+====================================
+- Misc small fixes, mostly to build-related issues.
+
+
+Overview of changes leading to 2.6.1
+Thursday, August 22, 2019
+====================================
+- Fix regression with hb_font_create_sub_font scaling introduced in 2.6.0.
+- Change interpretation of font PTEM size / CoreText font size handling.
+  See https://github.com/harfbuzz/harfbuzz/pull/1484
+- hb-ot-font: Prefer symbol cmap subtable if present.
+- Apply 'dist'/'abvm'/'blwm' features to all scripts.
+- Drop experimental DirectWrite API.
+
+
+Overview of changes leading to 2.6.0
+Tuesday, August 13, 2019
+====================================
+- New OpenType metrics, baseline, and metadata table access APIs.
+- New API to set font variations to a named-instance.
+- New hb-gdi.h header and API for creating hb_face_t from HFONT.
+- Amalgam: Provide a single-file harfbuzz.cc file for easier alternate building.
+- More size-reduction configurable options, enabled by HB_TINY.
+- New API:
++hb_font_set_var_named_instance()
++hb_gdi_face_create()
++hb_ot_layout_baseline_tag_t
++hb_ot_layout_get_baseline()
++hb_ot_meta_tag_t
++hb_ot_meta_get_entry_tags()
++hb_ot_meta_reference_entry()
++hb_ot_metrics_tag_t
++hb_ot_metrics_get_position()
++hb_ot_metrics_get_variation()
++hb_ot_metrics_get_x_variation()
++hb_ot_metrics_get_y_variation()
+
+
+Overview of changes leading to 2.5.3
+Wednesday, June 26, 2019
+====================================
+- Fix UCD script data for Unicode 10+ scripts.  This was broken since 2.5.0.
+- More optimizations for HB_TINY.
+
+
+Overview of changes leading to 2.5.2
+Thursday, June 20, 2019
+====================================
+- More hb-config.hh facilities to shrink library size, namely when built as
+  HB_TINY.
+- New documentation of custom configurations in CONFIG.md.
+- Fix build on gcc 4.8.  That's supported again.
+- Universal Shaping Engine improvements thanks to David Corbett.
+- API Changes: Undeprecate some horizontal-kerning API and re-enable in hb-ft,
+  such that Type1 fonts will continue kerning.
+
+
+Overview of changes leading to 2.5.1
+Friday, May 31, 2019
+====================================
+- Fix build with various versions of Visual Studio.
+- Improved documentation, thanks to Nathan Willis.
+- Bugfix in subsetting glyf table.
+- Improved scripts for cross-compiling for Windows using mingw.
+- Rename HB_MATH_GLYPH_PART_FLAG_EXTENDER to HB_OT_MATH_GLYPH_PART_FLAG_EXTENDER.
+  A deprecated macro is added for backwards-compatibility.
+
+
+Overview of changes leading to 2.5.0
+Friday, May 24, 2019
+====================================
+- This release does not include much functional changes, but includes major internal
+  code-base changes.  We now require C++11.  Support for gcc 4.8 and earlier has been
+  dropped.
+- New hb-config.hh facility for compiling smaller library for embedded and web usecases.
+- New Unicode Character Databse implementation that is half the size of previously-used
+  UCDN.
+- Subsetter improvements.
+- Improved documentation, thanks to Nathan Willis.
+- Misc shaping fixes.
+
+
+Overview of changes leading to 2.4.0
+Monday, March 25, 2019
+====================================
+- Unicode 12.
+- Misc fixes.
+- Subsetter improvements.
+- New API:
+HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE
+hb_directwrite_face_create()
+
+
+Overview of changes leading to 2.3.1
+Wednesday, January 30, 2019
+====================================
+- AAT bug fixes.
+- Misc internal housekeeping cleanup.
+
+
+Overview of changes leading to 2.3.0
+Thursday, December 20, 2018
+====================================
+- Fix regression on big-endian architectures.  Ouch!
+- Misc bug and build fixes.
+- Fix subsetting of simple GSUB/GDEF.
+- Merge CFF / CFF2 support contributed by Adobe.  This mostly involves
+  the subsetter, but also get_glyph_extents on CFF fonts.
+
+New API in hb-aat.h:
++hb_aat_layout_has_substitution()
++hb_aat_layout_has_positioning()
++hb_aat_layout_has_tracking()
+
+
+Overview of changes leading to 2.2.0
+Thursday, November 29, 2018
+====================================
+- Misc shaping bug fixes.
+- Add font variations named-instance API.
+- Deprecate font variations axis enumeration API and add replacement.
+- AAT shaping improvements:
+  o Fixed 'kern' table Format 2 implementation.
+  o Implement 'feat' table API for feature detection.
+  o Blacklist 'GSUB' table of fonts from 'MUTF' foundry that also have 'morx'.
+
+New API:
++hb_aat_layout_feature_type_t
++hb_aat_layout_feature_selector_t
++hb_aat_layout_get_feature_types()
++hb_aat_layout_feature_type_get_name_id
++hb_aat_layout_feature_selector_info_t
++HB_AAT_LAYOUT_NO_SELECTOR_INDEX
++hb_aat_layout_feature_type_get_selector_infos()
++hb_ot_var_axis_flags_t
++hb_ot_var_axis_info_t
++hb_ot_var_get_axis_infos()
++hb_ot_var_find_axis_info()
++hb_ot_var_get_named_instance_count()
++hb_ot_var_named_instance_get_subfamily_name_id()
++hb_ot_var_named_instance_get_postscript_name_id()
++hb_ot_var_named_instance_get_design_coords()
+
+Deprecated API:
++HB_OT_VAR_NO_AXIS_INDEX
++hb_ot_var_axis_t
++hb_ot_var_get_axes()
++hb_ot_var_find_axis()
+
+
+Overview of changes leading to 2.1.3
+Friday, November 16, 2018
+====================================
+- Fix AAT 'mort' shaping, which was broken in 2.1.2
+
+
+Overview of changes leading to 2.1.2
+Friday, November 16, 2018
+====================================
+- Various internal changes.
+- AAT shaping improvements:
+  o Implement kern table Format 1 state-machine-based kerning.
+  o Implement cross-stream kerning (cursive positioning, etc).
+  o Ignore emptyish GSUB tables (zero scripts) if morx present.
+  o Don't apply GPOS if morx is being applied.  Matches Apple.
+
+
+-Overview of changes leading to 2.1.1
+Monday, November 5, 2018
+====================================
+- AAT improvements:
+  o Implement 'mort' table.
+  o Implement 'kern' subtables Format 1 and Format 3.
+
+
+Overview of changes leading to 2.1.0
+Tuesday, October 30, 2018
+====================================
+- AAT shaping improvements:
+  o Allow user controlling AAT features, for whole buffer only currently.
+  o Several 'morx' fixes.
+  o Implement tuple-kerns in 'kerx'; Fixes kerning with Apple default
+    San Francisco fonts.
+- Support for color fonts:
+  o COLR/CPAL API to fetch color layers.
+  o SVG table to fetch SVG documents.
+  o CBDT/sbix API to fetch PNG images.
+- New 'name' table API.
+- hb-ot-font now uses 'VORG' table to correctly position CFF glyphs
+  in vertical layout.
+- Various fuzzer-found bug fixes.
+
+Changed API:
+
+A type and a macro added in 2.0.0 were renamed:
+
+hb_name_id_t -> hb_ot_name_id_t
+HB_NAME_ID_INVALID -> HB_OT_NAME_ID_INVALID
+
+New API:
+
++hb_color_t
++HB_COLOR
++hb_color_get_alpha()
++hb_color_get_red()
++hb_color_get_green()
++hb_color_get_blue()
++hb_ot_color_has_palettes()
++hb_ot_color_palette_get_count()
++hb_ot_color_palette_get_name_id()
++hb_ot_color_palette_color_get_name_id()
++hb_ot_color_palette_flags_t
++hb_ot_color_palette_get_flags()
++hb_ot_color_palette_get_colors()
++hb_ot_color_has_layers()
++hb_ot_color_layer_t
++hb_ot_color_glyph_get_layers()
++hb_ot_color_has_svg()
++hb_ot_color_glyph_reference_svg()
++hb_ot_color_has_png()
++hb_ot_color_glyph_reference_png()
+
++hb_ot_name_id_t
++HB_OT_NAME_ID_INVALID
++HB_OT_NAME_ID_COPYRIGHT
++HB_OT_NAME_ID_FONT_FAMILY
++HB_OT_NAME_ID_FONT_SUBFAMILY
++HB_OT_NAME_ID_UNIQUE_ID
++HB_OT_NAME_ID_FULL_NAME
++HB_OT_NAME_ID_VERSION_STRING
++HB_OT_NAME_ID_POSTSCRIPT_NAME
++HB_OT_NAME_ID_TRADEMARK
++HB_OT_NAME_ID_MANUFACTURER
++HB_OT_NAME_ID_DESIGNER
++HB_OT_NAME_ID_DESCRIPTION
++HB_OT_NAME_ID_VENDOR_URL
++HB_OT_NAME_ID_DESIGNER_URL
++HB_OT_NAME_ID_LICENSE
++HB_OT_NAME_ID_LICENSE_URL
++HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY
++HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY
++HB_OT_NAME_ID_MAC_FULL_NAME
++HB_OT_NAME_ID_SAMPLE_TEXT
++HB_OT_NAME_ID_CID_FINDFONT_NAME
++HB_OT_NAME_ID_WWS_FAMILY
++HB_OT_NAME_ID_WWS_SUBFAMILY
++HB_OT_NAME_ID_LIGHT_BACKGROUND
++HB_OT_NAME_ID_DARK_BACKGROUND
++HB_OT_NAME_ID_VARIATIONS_PS_PREFIX
++hb_ot_name_entry_t
++hb_ot_name_list_names()
++hb_ot_name_get_utf8()
++hb_ot_name_get_utf16()
++hb_ot_name_get_utf32()
+
+
+Overview of changes leading to 2.0.2
+Saturday, October 20, 2018
+====================================
+- Fix two minor memory access issues in AAT tables.
+
+
+Overview of changes leading to 2.0.1
+Friday, October 19, 2018
+====================================
+- Fix hb-version.h reported release version that went wrong (1.8.0)
+  with previous release.
+- Fix extrapolation in 'trak' table.
+- Fix hb-font infinite-recursion issue with some font funcs and
+  subclassed fonts.
+- Implement variation-kerning format in kerx table, although without
+  variation.
+- Fix return value of hb_map_is_empty().
+
+
+Overview of changes leading to 2.0.0
+Thursday, October 18, 2018
+====================================
+- Added AAT shaping support (morx/kerx/trak).
+  Automatically used if GSUB/GPOS are not available respectively.
+  Set HB_OPTIONS=aat env var to have morx/kerx preferred over
+  GSUB/GPOS.
+- Apply TrueType kern table internally, instead of relying on
+  hb_font_t callbacks.
+- Khmer shaper significantly rewritten to better match Uniscribe.
+- Indic3 tags ('dev3', etc) are passed to USE shaper.
+- .dfont Mac font containers implemented.
+- Script- and language-mapping revamped to better use BCP 47.
+- Misc USE and Indic fixes.
+- Misc everything fixes.
+- Too many things to list.  Biggest release since 0.9.1, with
+  over 500 commits in just over 5 weeks!  Didn't intend it to
+  be a big release.  Just happened to become.
+- hb-ft now locks underlying FT_Face during use.
+
+API changes:
+
+- Newly-created hb_font_t's now have our internal "hb-ot-font"
+  callbacks set on them, so they should work out of the box
+  without any callbacks set.  If callbacks are set, everything
+  is back to what it was before, the fallback callbacks are
+  null.  If you to get the internal implementation modified,
+  sub_font it.
+
+- New hb_font_funcs_set_nominal_glyphs_func() allows speeding
+  up character to glyph mapping.
+
+New API:
++HB_FEATURE_GLOBAL_START
++HB_FEATURE_GLOBAL_END
++hb_buffer_set_invisible_glyph()
++hb_buffer_get_invisible_glyph()
++hb_font_funcs_set_nominal_glyphs_func()
++hb_ot_layout_table_select_script()
++hb_ot_layout_script_select_language()
++hb_ot_layout_feature_get_name_ids()
++hb_ot_layout_feature_get_characters()
++hb_name_id_t
++HB_NAME_ID_INVALID
++HB_OT_MAX_TAGS_PER_SCRIPT
++hb_ot_tags_from_script_and_language()
++hb_ot_tags_to_script_and_language()
+
+Deprecated API:
+-hb_font_funcs_set_glyph_func()
+-hb_unicode_eastasian_width_func_t
+-hb_unicode_funcs_set_eastasian_width_func()
+-hb_unicode_eastasian_width()
+-hb_unicode_decompose_compatibility_func_t
+-HB_UNICODE_MAX_DECOMPOSITION_LEN
+-hb_unicode_funcs_set_decompose_compatibility_func()
+-hb_unicode_decompose_compatibility()
+-hb_font_funcs_set_glyph_h_kerning_func()
+-hb_font_funcs_set_glyph_v_kerning_func()
+-hb_font_get_glyph_h_kerning()
+-hb_font_get_glyph_v_kerning()
+-hb_font_get_glyph_kerning_for_direction()
+-hb_ot_layout_table_choose_script()
+-hb_ot_layout_script_find_language()
+-hb_ot_tags_from_script()
+-hb_ot_tag_from_language()
+
+
+Overview of changes leading to 1.9.0
+Monday, September 10, 2018
+====================================
+- Added 'cmap' API to hb_face_t.
+- Face-builder API.
+- hb-ot-font re-creation should be much leaner now, as the
+  font tables it uses are cached on hb_face_t now.
+- Internal source header file name changes:
+  hb-*-private.hh is renamed to hb-*.hh.
+
+New API:
++HB_UNICODE_MAX
++hb_face_collect_unicodes()
++hb_face_collect_variation_selectors()
++hb_face_collect_variation_unicodes()
++hb_face_builder_create()
++hb_face_builder_add_table()
+
+
+Overview of changes leading to 1.8.8
+Tuesday, August 14, 2018
+====================================
+- Fix hb-icu crash on architectures where compare_exchange_weak() can
+  fail falsely.  This bug was introduced in 1.8.4.
+  https://bugs.chromium.org/p/chromium/issues/detail?id=873568
+- More internal refactoring of atomic operations and singletons.
+- API changes:
+  The following functions do NOT reference their return value before
+  returning:
+  * hb_unicode_funcs_get_default()
+  * hb_glib_get_unicode_funcs()
+  * hb_icu_get_unicode_funcs()
+  This is consistent with their naming ("get", instead of "reference")
+  as well as how they are used in the wild (ie. no one calls destroy()
+  on their return value.)
+
+
+Overview of changes leading to 1.8.7
+Wednesday, August 8, 2018
+====================================
+- Fix assertion failure with GDEF-blacklisted fonts.
+
+
+Overview of changes leading to 1.8.6
+Tuesday, August 7, 2018
+====================================
+- Internal code shuffling.
+- New API to speed up getting advance widths for implementations
+  that have heavy overhead in get_h_advance callback:
++hb_font_funcs_set_glyph_h_advances_func
++hb_font_funcs_set_glyph_v_advances_func
++hb_font_get_glyph_advances_for_direction
++hb_font_get_glyph_h_advances
++hb_font_get_glyph_h_advances_func_t
++hb_font_get_glyph_v_advances
++hb_font_get_glyph_v_advances_func_t
+
+
+Overview of changes leading to 1.8.5
+Wednesday, August 1, 2018
+====================================
+- Major Khmer shaper improvements to better match Microsoft.
+- Indic bug fixes.
+- Internal improvements to atomic operations.
+
+
+Overview of changes leading to 1.8.4
+Tuesday, July 17, 2018
+====================================
+- Fix build on non-C++11.
+- Use C++-style GCC atomics and C++11 atomics.
+
+
+Overview of changes leading to 1.8.3
+Wednesday, July 11, 2018
+====================================
+- A couple of Indic / USE bug fixes.
+- Disable vectorization, as it was causing unaligned access bus error on
+  certain 32bit architectures.
+
+
+Overview of changes leading to 1.8.2
+Tuesday, July 3, 2018
+====================================
+- Fix infinite loop in Khmer shaper.
+- Improve hb_blob_create_from_file() for streams.
+
+
+Overview of changes leading to 1.8.1
+Tuesday, June 12, 2018
+====================================
+- Fix hb-version.h file generation; last two releases went out with wrong ones.
+- Add correctness bug in hb_set_t operations, introduced in 1.7.7.
+- Remove HB_SUBSET_BUILTIN build option.  Not necessary.
+
+
+Overview of changes leading to 1.8.0
+Tuesday, June 5, 2018
+====================================
+- Update to Unicode 11.0.0.
+
+
+Overview of changes leading to 1.7.7
+Tuesday, June 5, 2018
+====================================
+- Lots of internal changes, but not yet exposed externally.
+- All HarfBuzz objects are significantly smaller in size now.
+- Sinhala: Position repha on top of post-consonant, not base.
+  This better matches Windows 10 behavior, which was changed
+  from previous Windows versions.
+- New build options:
+  o New cpp macro HB_NO_ATEXIT
+  o New cpp macro HB_SUBSET_BUILTIN
+- Significant libharfbuzz-subset changes. API subject to change.
+- New API in libharfbuzz:
+
++hb_blob_create_from_file()
++hb_face_count()
+
+A hashmap implementation:
++hb-map.h
++HB_MAP_VALUE_INVALID
++hb_map_t
++hb_map_create()
++hb_map_get_empty()
++hb_map_reference()
++hb_map_destroy()
++hb_map_set_user_data()
++hb_map_get_user_data()
++hb_map_allocation_successful()
++hb_map_clear()
++hb_map_is_empty()
++hb_map_get_population()
++hb_map_set()
++hb_map_get()
++hb_map_del()
++hb_map_has()
+
+
+Overview of changes leading to 1.7.6
+Wednesday, March 7, 2018
+====================================
+
+- Fix to hb_set_t binary operations. Ouch.
+- New experimental harfbuzz-subset library. All of hb-subset.h
+  is experimental right now and API WILL change.
+
+- New API:
+hb_blob_copy_writable_or_fail()
+HB_OT_TAG_BASE
+hb_set_previous()
+hb_set_previous_range()
+
+
+Overview of changes leading to 1.7.5
+Tuesday, January 30, 2018
+====================================
+
+- Separate Khmer shaper from Indic.
+- First stab at AAT morx. Not hooked up.
+- Misc bug fixes.
+
+
+Overview of changes leading to 1.7.4
+Wednesday, December 20, 2017
+====================================
+
+- Fix collect_glyphs() regression caused by hb_set_t changes.
+
+
+Overview of changes leading to 1.7.3
+Monday, December 18, 2017
+====================================
+
+- hb_set_t performance tuning and optimizations.
+- Speed up collect_glyphs() and reject garbage data.
+- In hb_coretext_font_create() set font point-size (ptem).
+- Misc fixes.
+
+
+Overview of changes leading to 1.7.2
+Monday, December 4, 2017
+====================================
+
+- Optimize hb_set_add_range().
+- Misc fixes.
+- New API:
+hb_coretext_font_create()
+
+
+Overview of changes leading to 1.7.1
+Tuesday, November 14, 2017
+====================================
+
+- Fix atexit object destruction regression.
+- Fix minor integer-overflow.
+
+
+Overview of changes leading to 1.7.0
+Monday, November 13, 2017
+====================================
+
+- Minor Indic fixes.
+- Implement kerning and glyph names in hb-ot-font.
+- Various DSO optimization re .data and .bss sizes.
+- Make C++11 optional; build fixes.
+- Mark all other backends "unsafe-to-break".
+- Graphite fix.
+
+
+Overview of changes leading to 1.6.3
+Thursday, October 26th, 2017
+====================================
+
+- Fix hb_set_t some more.  Should be solid now.
+- Implement get_glyph_name() for hb-ot-font.
+- Misc fixes.
+
+
+Overview of changes leading to 1.6.2
+Monday, October 23nd, 2017
+====================================
+
+- Yesterday's release had a bad crasher; don't use it.  That's what
+  happens when one works on Sunday...
+  https://github.com/harfbuzz/harfbuzz/issues/578
+- Build fixes for FreeBSD and Chrome Android.
+
+
+Overview of changes leading to 1.6.1
+Sunday, October 22nd, 2017
+====================================
+
+- Don't skip over COMBINING GRAPHEME JOINER when ligating, etc.
+  To be refined: https://github.com/harfbuzz/harfbuzz/issues/554
+- Faster hb_set_t implementation.
+- Don't use deprecated ICU API.
+- Fix undefined-behavior in Myanmar shaper, introduced in 1.6.0
+- Deprecated API:
+  hb_set_invert()
+
+
+Overview of changes leading to 1.6.0
+Friday, October the 13th, 2017
+====================================
+
+- Update to Unicode 10.
+
+- Various Indic and Universal Shaping Engine fixes as a result of
+  HarfBuzz Hackfest with Jonathan Kew at Web Engines Hackfest at
+  the Igalia offices in A Coruña, Spain.  Thanks Igalia for having
+  us!
+
+- Implement Unicode Arabic Mark Ordering Algorithm UTR#53.
+
+- Implement optical sizing / tracking in CoreText backend, using
+  new API hb_font_set_ptem().
+
+- Allow notifying hb_font_t that underlying FT_Face changed sizing,
+  using new API hb_ft_font_changed().
+
+- More Graphite backend RTL fixes.
+
+- Fix caching of variable font shaping plans.
+
+- hb-view / hb-shape now accept following new arguments:
+
+  o --unicodes: takes a list of hex numbers that represent Unicode
+    codepoints.
+
+New API:
++hb_face_get_table_tags()
++hb_font_set_ptem()
++hb_font_get_ptem()
++hb_ft_font_changed()
+
+
+Overview of changes leading to 1.5.1
+Tuesday, September 5, 2017
+====================================
+
+- Fix "unsafe-to-break" in fallback shaping and other corner cases.
+  All our tests pass with --verify now, meaning unsafe-to-break API
+  works as expected.
+- Add --unicodes to hb-view / hb-shape.
+- [indic] Treat Consonant_With_Stacker as consonant.  This will need
+  further tweaking.
+- hb_buffer_diff() tweaks.
+
+
+Overview of changes leading to 1.5.0
+Wednesday, August 23, 2017
+====================================
+
+- Misc new API, for appending a buffer to another, and for comparing
+  contents of two buffers for types of differences.
+
+- New "unsafe-to-break" API.  Can be used to speed up reshaping
+  in line-breaking situations.  Essentially, after shaping, it returns
+  positions in the input string (some of the cluster boundaries) that
+  are "safe to break" in that if the text is segmented at that position
+  and two sides reshaped and concatenated, the shaping result is
+  exactly the same as shaping the text in one piece.
+
+  hb-view and hb-shape and hb-shape now take --verify, which verifies
+  the above property.
+
+  Some corner cases of the implementation are still not quite working.
+  Those will be fixed in subsequent releases.
+
+- New API:
+
+hb_buffer_append()
+
+hb_glyph_flags_t
+HB_GLYPH_FLAG_UNSAFE_TO_BREAK
+HB_GLYPH_FLAG_DEFINED
+hb_glyph_info_get_glyph_flags()
+
+HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS
+
+hb_buffer_diff_flags_t
+HB_BUFFER_DIFF_FLAG_EQUAL
+HB_BUFFER_DIFF_FLAG_CONTENT_TYPE_MISMATCH
+HB_BUFFER_DIFF_FLAG_LENGTH_MISMATCH
+HB_BUFFER_DIFF_FLAG_NOTDEF_PRESENT
+HB_BUFFER_DIFF_FLAG_DOTTED_CIRCLE_PRESENT
+HB_BUFFER_DIFF_FLAG_CODEPOINT_MISMATCH
+HB_BUFFER_DIFF_FLAG_CLUSTER_MISMATCH
+HB_BUFFER_DIFF_FLAG_GLYPH_FLAGS_MISMATCH
+HB_BUFFER_DIFF_FLAG_POSITION_MISMATCH
+hb_buffer_diff
+
+
+Overview of changes leading to 1.4.8
+Tuesday, August 8, 2017
+====================================
+
+- Major fix to avar table handling.
+- Rename hb-shape --show-message to --trace.
+- Build fixes.
+
+
+Overview of changes leading to 1.4.7
+Tuesday, July 18, 2017
+====================================
+
+- Multiple Indic, Tibetan, and Cham fixes.
+- CoreText: Allow disabling kerning.
+- Adjust Arabic feature order again.
+- Misc build fixes.
+
+
+Overview of changes leading to 1.4.6
+Sunday, April 23, 2017
+====================================
+
+- Graphite2: Fix RTL positioning issue.
+- Backlist GDEF of more versions of Padauk and Tahoma.
+- New, experimental, cmake alternative build system.
+
+
+Overview of changes leading to 1.4.5
+Friday, March 10, 2017
+====================================
+
+- Revert "Fix Context lookup application when moving back after a glyph..."
+  This introduced memory access problems.  To be fixed properly soon.
+
+
+Overview of changes leading to 1.4.4
+Sunday, March 5, 2017
+====================================
+
+- Fix Context lookup application when moving back after a glyph deletion.
+- Fix buffer-overrun in Bengali.
+
+
+Overview of changes leading to 1.4.3
+Saturday, February 25, 2017
+====================================
+
+- Route Adlam script to Arabic shaper.
+- Misc fixes.
+- New API:
+  hb_font_set_face()
+- Deprecate API:
+  hb_graphite2_font_get_gr_font()
+
+
+Overview of changes leading to 1.4.2
+Monday, January 23, 2017
+====================================
+
+- Implement OpenType Font Variation tables avar/fvar/HVAR/VVAR.
+- hb-shape and hb-view now accept --variations.
+- New API:
+
+hb_variation_t
+hb_variation_from_string()
+hb_variation_to_string()
+
+hb_font_set_variations()
+hb_font_set_var_coords_design()
+hb_font_get_var_coords_normalized()
+
+hb-ot-var.h:
+hb_ot_var_axis_t
+hb_ot_var_has_data()
+hb_ot_var_get_axis_count()
+hb_ot_var_get_axes()
+hb_ot_var_find_axis()
+hb_ot_var_normalize_variations()
+hb_ot_var_normalize_coords()
+
+- MVAR to be implemented later.  Access to named instances to be
+  implemented later as well.
+
+- Misc fixes.
+
+
+Overview of changes leading to 1.4.1
+Thursday, January 5, 2017
+====================================
+
+- Always build and use UCDN for Unicode data by default.
+  Reduces dependence on version of Unicode data in glib,
+  specially in the Windows bundles we are shipping, which
+  have very old glib.
+
+
+Overview of changes leading to 1.4.0
+Thursday, January 5, 2017
+====================================
+
+- Merged "OpenType GX" branch which adds core of support for
+  OpenType 1.8 Font Variations.  To that extent, the relevant
+  new API is:
+
+New API:
+hb_font_set_var_coords_normalized()
+
+  with supporting API:
+
+New API:
+HB_OT_LAYOUT_NO_VARIATIONS_INDEX
+hb_ot_layout_table_find_feature_variations()
+hb_ot_layout_feature_with_variations_get_lookups()
+hb_shape_plan_create2()
+hb_shape_plan_create_cached2()
+
+  Currently variations in GSUB/GPOS/GDEF are fully supported,
+  and no other tables are supported.  In particular, fvar/avar
+  are NOT supported, hence the hb_font_set_var_coords_normalized()
+  taking normalized coordinates.  API to take design coordinates
+  will be added in the future.
+
+  HVAR/VVAR/MVAR support will also be added to hb-ot-font in the
+  future.
+
+- Fix regression in GDEF glyph class processing.
+- Add decompositions for Chakma, Limbu, and Balinese in USE shaper.
+- Misc fixes.
+
+
+Overview of changes leading to 1.3.4
+Monday, December 5, 2016
+====================================
+
+- Fix vertical glyph origin in hb-ot-font.
+- Implement CBDT/CBLC color font glyph extents in hb-ot-font.
+
+
+Overview of changes leading to 1.3.3
+Wednesday, September 28, 2016
+====================================
+
+- Implement parsing of OpenType MATH table.
+New API:
+HB_OT_TAG_MATH
+HB_OT_MATH_SCRIPT
+hb_ot_math_constant_t
+hb_ot_math_kern_t
+hb_ot_math_glyph_variant_t
+hb_ot_math_glyph_part_flags_t
+hb_ot_math_glyph_part_t
+hb_ot_math_has_data
+hb_ot_math_get_constant
+hb_ot_math_get_glyph_italics_correction
+hb_ot_math_get_glyph_top_accent_attachment
+hb_ot_math_get_glyph_kerning
+hb_ot_math_is_glyph_extended_shape
+hb_ot_math_get_glyph_variants
+hb_ot_math_get_min_connector_overlap
+hb_ot_math_get_glyph_assembly
+
+
+Overview of changes leading to 1.3.2
+Wednesday, September 27, 2016
+====================================
+
+- Fix build of hb-coretext on older OS X versions.
+
+
+Overview of changes leading to 1.3.1
+Wednesday, September 7, 2016
+====================================
+
+- Blacklist bad GDEF of more fonts (Padauk).
+- More CoreText backend crash fixes with OS X 10.9.5.
+- Misc fixes.
+
+
+Overview of changes leading to 1.3.0
+Thursday, July 21, 2016
+====================================
+
+- Update to Unicode 9.0.0
+- Move Javanese from Indic shaper to Universal Shaping Engine.
+- Allow MultipleSubst to delete a glyph (matching Windows engine).
+- Update Universal Shaping Engine to latest draft from Microsoft.
+- DirectWrite backend improvements.  Note: this backend is for testing ONLY.
+- CoreText backend improvements with unreachable fonts.
+- Implement symbol fonts (cmap 3.0.0) in hb-ft and hb-ot-font.
+- Blacklist bad GDEF of more fonts (Tahoma & others).
+- Misc fixes.
+
+
+Overview of changes leading to 1.2.7
+Monday, May 2, 2016
+====================================
+
+- Blacklist another version of Times New Roman (Bold) Italic from Windows 7.
+- Fix Mongolian Free Variation Selectors shaping with certain fonts.
+- Fix Tibetan shorthand contractions shaping.
+- Improved list of language tag mappings.
+- Unbreak build on Windows CE.
+- Make 'glyf' table loading lazy in hb-ot-font.
+
+
+Overview of changes leading to 1.2.6
+Friday, April 8, 2016
+====================================
+
+- Blacklist GDEF table of another set of Times New Roman (Bold) Italic.
+- DirectWrite backend improvements.  Note: DirectWrite backend is
+  exclusively for our internal testing and should NOT be used in any
+  production system whatsoever.
+
+
+Overview of changes leading to 1.2.5
+Monday, April 4, 2016
+====================================
+
+- Fix GDEF mark-filtering-set, which was broken in 1.2.3.
+
+
+Overview of changes leading to 1.2.4
+Thursday, March 17, 2016
+====================================
+
+- Synthesize GDEF glyph class for any glyph that does not have one in GDEF.
+  I really hope we don't discover broken fonts that shape badly with this
+  change.
+- Misc build and other minor fixes.
+- API changes:
+  - Added HB_NDEBUG.  It's fine for production systems to define this to
+    disable high-overhead debugging checks.  However, I also reduced the
+    overhead of those checks, so it's a non-issue right now.  You can
+    forget it.  Just not defining anything at all is fine.
+
+
+Overview of changes leading to 1.2.3
+Thursday, February 25, 2016
+====================================
+
+- Blacklist GDEF table of certain versions of Times New Roman (Bold) Italic,
+  due to bug in glyph class of ASCII double-quote character.  This should
+  address "regression" introduced in 1.2.0 when we switched mark zeroing
+  in most shapers from BY_UNICODE_LATE to BY_GDEF_LATE.
+  This fourth release in a week should finally stablize things...
+
+- hb-ot-font's get_glyph() implementation saw some optimizations.  Though,
+  might be really hard to measure in real-world situations.
+
+- Also, two rather small API changes:
+
+We now disable some time-consuming internal bookkeeping if built with NDEBUG
+defined.  This is a first time that we use NDEBUG to disable debug code.  If
+there exist production systems that do NOT want to enable NDEBUG, please let
+me know and I'll add HB_NDEBUG.
+
+Added get_nominal_glyph() and get_variation_glyph() instead of get_glyph()
+
+New API:
+- hb_font_get_nominal_glyph_func_t
+- hb_font_get_variation_glyph_func_t
+- hb_font_funcs_set_nominal_glyph_func()
+- hb_font_funcs_set_variation_glyph_func()
+- hb_font_get_nominal_glyph()
+- hb_font_get_variation_glyph()
+
+Deprecated API:
+- hb_font_get_glyph_func_t
+- hb_font_funcs_set_glyph_func()
+
+Clients that implement their own font-funcs are encouraged to replace
+their get_glyph() implementation with a get_nominal_glyph() and
+get_variation_glyph() pair.  The variation version can assume that
+variation_selector argument is not zero.  Old (deprecated) functions
+will continue working indefinitely using internal gymnastics; it is
+just more efficient to use the new functions.
+
+
+Overview of changes leading to 1.2.2
+Wednesday, February 24, 2016
+====================================
+
+- Fix regression with mark positioning with fonts that have
+  non-zero mark advances.  This was introduced in 1.2.0 while
+  trying to make mark and cursive attachments to work together.
+  I have partially reverted that, so this version is much more
+  like what we had before.  All clients who updated to 1.2.0
+  should update to this version.
+
+
+Overview of changes leading to 1.2.1
+Tuesday, February 23, 2016
+====================================
+
+- CoreText: Fix bug with wrong scale if font scale was changed later.
+  https://github.com/libass/libass/issues/212
+- CoreText: Drastically speed up font initialization.
+- CoreText: Fix tiny leak.
+- Group ZWJ/ZWNJ with previous syllable under cluster-level=0.
+  https://github.com/harfbuzz/harfbuzz/issues/217
+- Add test/shaping/README.md about how to add tests to the suite.
+
+
+Overview of changes leading to 1.2.0
+Friday, February 19, 2016
+====================================
+
+- Fix various issues (hangs mostly) in case of memory allocation failure.
+- Change mark zeroing types of most shapers from BY_UNICODE_LATE to
+  BY_GDEF_LATE.  This seems to be what Uniscribe does.
+- Change mark zeroing of USE shaper from NONE to BY_GDEF_EARLY.  That's
+  what Windows does.
+- Allow GPOS cursive connection on marks, and fix the interaction with
+  mark attachment.  This work resulted in some changes to how mark
+  attachments work.  See:
+  https://github.com/harfbuzz/harfbuzz/issues/211
+  https://github.com/harfbuzz/harfbuzz/commit/86c68c7a2c971efe8e35b1f1bd99401dc8b688d2
+- Graphite2 shaper: improved negative advance handling (eg. Nastaliq).
+- Add nmake-based build system for Windows.
+- Minor speedup.
+- Misc. improvements.
+
+
+Overview of changes leading to 1.1.3
+Monday, January 11, 2016
+====================================
+
+- Ported Indic shaper to Unicode 8.0 data.
+- Universal Shaping Engine fixes.
+- Speed up CoreText shaper when font fallback happens in CoreText.
+- Documentation improvements, thanks to Khaled Hosny.
+- Very rough directwrite shaper for testing, thanks to Ebrahim Byagowi.
+- Misc bug fixes.
+- New API:
+
+  * Font extents:
+      hb_font_extents_t
+      hb_font_get_font_extents_func_t
+      hb_font_get_font_h_extents_func_t
+      hb_font_get_font_v_extents_func_t
+      hb_font_funcs_set_font_h_extents_func
+      hb_font_funcs_set_font_v_extents_func
+      hb_font_get_h_extents
+      hb_font_get_v_extents
+      hb_font_get_extents_for_direction
+
+  * Buffer message (aka debug):
+      hb_buffer_message_func_t
+      hb_buffer_set_message_func()
+    Actual message protocol to be fleshed out later.
+
+
+Overview of changes leading to 1.1.2
+Wednesday, November 26, 2015
+====================================
+
+- Fix badly-broken fallback shaper that affected terminology.
+  https://github.com/harfbuzz/harfbuzz/issues/187
+- Fix y_scaling in Graphite shaper.
+- API changes:
+  * An unset glyph_h_origin() function in font-funcs now (sensibly)
+    implies horizontal origin at 0,0.  Ie, the nil callback returns
+    true instead of false.  As such, implementations that have a
+    glyph_h_origin() that simply returns true, can remove that function
+    with HarfBuzz >= 1.1.2.  This results in a tiny speedup.
+
+
+Overview of changes leading to 1.1.1
+Wednesday, November 24, 2015
+====================================
+
+- Build fixes, specially for hb-coretext.
+
+
+Overview of changes leading to 1.1.0
+Wednesday, November 18, 2015
+====================================
+
+- Implement 'stch' stretch feature for Syriac Abbreviation Mark.
+  https://github.com/harfbuzz/harfbuzz/issues/141
+- Disable use of decompose_compatibility() callback.
+- Implement "shaping" of various Unicode space characters, even
+  if the font does not support them.
+  https://github.com/harfbuzz/harfbuzz/issues/153
+- If font does not support U+2011 NO-BREAK HYPHEN, fallback to
+  U+2010 HYPHEN.
+- Changes resulting from libFuzzer continuous fuzzing:
+  * Reject font tables that need more than 8 edits,
+  * Bound buffer growth during shaping to 32x,
+  * Fix assertions and other issues at OOM / buffer max-growth.
+- Misc fixes and optimizations.
+- API changes:
+  * All fonts created with hb_font_create() now inherit from
+    (ie. have parent) hb_font_get_empty().
+
+
+Overview of changes leading to 1.0.6
+Thursday, October 15, 2015
+====================================
+
+- Reduce max nesting level in OT lookups from 8 to 6.
+  Should not affect any real font as far as I know.
+- Fix memory access issue in ot-font.
+- Revert default load-flags of fonts created using hb_ft_font_create()
+  back to FT_LOAD_DEFAULT|FT_LOAD_NO_HINTING.  This was changed in
+  last release (1.0.5), but caused major issues, so revert.
+  https://github.com/harfbuzz/harfbuzz/issues/143
+
+
+Overview of changes leading to 1.0.5
+Tuesday, October 13, 2015
+====================================
+
+- Fix multiple memory access bugs discovered using libFuzzer.
+  https://github.com/harfbuzz/harfbuzz/issues/139
+  Everyone should upgrade to this version as soon as possible.
+  We now have continuous fuzzing set up, to avoid issues like
+  these creeping in again.
+- Misc fixes.
+
+- New API:
+  * hb_font_set_parent().
+  * hb_ft_font_[sg]et_load_flags()
+    The default flags for fonts created using hb_ft_font_create()
+    has changed to default to FT_LOAD_DEFAULT now.  Previously it
+    was defaulting to FT_LOAD_DFEAULT|FT_LOAD_NO_HINTING.
+
+- API changes:
+  * Fonts now default to units-per-EM as their scale, instead of 0.
+  * hb_font_create_sub_font() does NOT make parent font immutable
+    anymore.  hb_font_make_immutable() does.
+
+
+Overview of changes leading to 1.0.4
+Wednesday, September 30, 2015
+====================================
+
+- Fix minor out-of-bounds read error.
+
+
+Overview of changes leading to 1.0.3
+Tuesday, September 1, 2015
+====================================
+
+- Start of user documentation, from Simon Cozens!
+- Implement glyph_extents() for TrueType fonts in hb-ot-font.
+- Improve GPOS cursive attachments with conflicting lookups.
+- More fixes for cluster-level = 1.
+- Uniscribe positioning fix.
+
+
+Overview of changes leading to 1.0.2
+Wednesday, August 19, 2015
+====================================
+
+- Fix shaping with cluster-level > 0.
+- Fix Uniscribe backend font-size scaling.
+- Declare dependencies in harfbuzz.pc.
+  FreeType is not declared though, to avoid bugs in pkg-config
+  0.26 with recursive dependencies.
+- Slightly improved debug infrastructure.  More to come later.
+- Misc build fixes.
+
+
+Overview of changes leading to 1.0.1
+Monday, July 27, 2015
+====================================
+
+- Fix out-of-bounds access in USE shaper.
+
+
+Overview of changes leading to 1.0.0
+Sunday, July 26, 2015
+====================================
+
+- Implement Universal Shaping Engine:
+  https://www.microsoft.com/typography/OpenTypeDev/USE/intro.htm
+  http://blogs.windows.com/bloggingwindows/2015/02/23/windows-shapes-the-worlds-languages/
+- Bump version to 1.0.0.  The soname was NOT bumped.
+
+
+Overview of changes leading to 0.9.42
+Thursday, July 26, 2015
+=====================================
+
+- New API to allow for retrieving finer-grained cluster
+  mappings if the client desires to handle them.  Default
+  behavior is unchanged.
+- Fix cluster merging when removing default-ignorables.
+- Update to Unicode 8.0
+- hb-graphite2 fixes.
+- Misc fixes.
+- Removed HB_NO_MERGE_CLUSTERS hack.
+- New API:
+  hb_buffer_cluster_level_t enum
+  hb_buffer_get_cluster_level()
+  hb_buffer_set_cluster_level()
+  hb-shape / hb-view --cluster-level
+
+
+Overview of changes leading to 0.9.41
+Thursday, June 18, 2015
+=====================================
+
+- Fix hb-coretext with trailing whitespace in right-to-left.
+- New API: hb_buffer_reverse_range().
+- Allow implementing atomic ops in config.h.
+- Fix hb_language_t in language bindings.
+- Misc fixes.
+
+
+Overview of changes leading to 0.9.40
+Friday, March 20, 2015
+=====================================
+
+- Another hb-coretext crasher fix.  Ouch!
+- Happy Norouz!
+
+
+Overview of changes leading to 0.9.39
+Wednesday, March 4, 2015
+=====================================
+
+- Critical hb-coretext fixes.
+- Optimizations and refactoring; no functional change
+  expected.
+- Misc build fixes.
+
+
+Overview of changes leading to 0.9.38
+Friday, January 23, 2015
+=====================================
+
+- Fix minor out-of-bounds access in Indic shaper.
+- Change New Tai Lue shaping engine from South-East Asian to default,
+  reflecting change in Unicode encoding model.
+- Add hb-shape --font-size.  Can take up to two numbers for separate
+  x / y size.
+- Fix CoreText and FreeType scale issues with negative scales.
+- Reject blobs larger than 2GB.  This might break some icu-le-hb clients
+  that need security fixes.  See:
+  http://www.icu-project.org/trac/ticket/11450
+- Avoid accessing font tables during face destruction, in casce rogue
+  clients released face data already.
+- Fix up gobject-introspection a bit.  Python bindings kinda working.
+  See README.python.
+- Misc fixes.
+- API additions:
+  hb_ft_face_create_referenced()
+  hb_ft_font_create_referenced()
+
+
+Overview of changes leading to 0.9.37
+Wednesday, December 17, 2014
+=====================================
+
+- Fix out-of-bounds access in Context lookup format 3.
+- Indic: Allow ZWJ/ZWNJ before syllable modifiers.
+
+
+Overview of changes leading to 0.9.36
+Thursday, November 20, 2014
+=====================================
+
+- First time that three months went by without a release since
+  0.9.2 was released on August 10, 2012!
+- Fix performance bug in hb_ot_collect_glyphs():
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1090869
+- Add basic vertical-text support to hb-ot-font.
+- Misc build fixes.
+
+
+Overview of changes leading to 0.9.35
+Saturday, August 13, 2014
+=====================================
+
+- Fix major shape-plan caching bug when more than one shaper were
+  provided to hb_shape_full() (as exercised by XeTeX).
+  http://www.mail-archive.com/[email protected]/msg1246370.html
+- Fix Arabic fallback shaping regression.  This was broken in 0.9.32.
+- Major hb-coretext fixes.  That backend is complete now, including
+  respecing buffer direction and language, down to vertical writing.
+- Build fixes for Windows CE.  Should build fine now.
+- Misc fixes:
+  Use atexit() only if it's safe to call from shared library
+  https://bugs.freedesktop.org/show_bug.cgi?id=82246
+  Mandaic had errors in its Unicode Joining_Type
+  https://bugs.freedesktop.org/show_bug.cgi?id=82306
+- API changes:
+
+  * hb_buffer_clear_contents() does not reset buffer flags now.
+
+    After 763e5466c0a03a7c27020e1e2598e488612529a7, one doesn't
+    need to set flags for different pieces of text.  The flags now
+    are something the client sets up once, depending on how it
+    actually uses the buffer.  As such, don't clear it in
+    clear_contents().
+
+    I don't expect any changes to be needed to any existing client.
+
+
+Overview of changes leading to 0.9.34
+Saturday, August 2, 2014
+=====================================
+
+- hb_feature_from_string() now accepts CSS font-feature-settings format.
+- As a result, hb-shape / hb-view --features also accept CSS-style strings.
+  Eg, "'liga' off" is accepted now.
+- Add old-spec Myanmar shaper:
+  https://bugs.freedesktop.org/show_bug.cgi?id=81775
+- Don't apply 'calt' in Hangul shaper.
+- Fix mark advance zeroing for Hebrew shaper:
+  https://bugs.freedesktop.org/show_bug.cgi?id=76767
+- Implement Windows-1256 custom Arabic shaping.  Only built on Windows,
+  and requires help from get_glyph().  Used by Firefox.
+  https://bugzilla.mozilla.org/show_bug.cgi?id=1045139
+- Disable 'liga' in vertical text.
+- Build fixes.
+- API changes:
+
+  * Make HB_BUFFER_FLAG_BOT/EOT easier to use.
+
+    Previously, we expected users to provide BOT/EOT flags when the
+    text *segment* was at paragraph boundaries.  This meant that for
+    clients that provide full paragraph to HarfBuzz (eg. Pango), they
+    had code like this:
+
+      hb_buffer_set_flags (hb_buffer,
+                           (item_offset == 0 ? HB_BUFFER_FLAG_BOT : 0) |
+                           (item_offset + item_length == paragraph_length ?
+                            HB_BUFFER_FLAG_EOT : 0));
+
+      hb_buffer_add_utf8 (hb_buffer,
+                          paragraph_text, paragraph_length,
+                          item_offset, item_length);
+
+    After this change such clients can simply say:
+
+      hb_buffer_set_flags (hb_buffer,
+                           HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
+
+      hb_buffer_add_utf8 (hb_buffer,
+                          paragraph_text, paragraph_length,
+                          item_offset, item_length);
+
+    Ie, HarfBuzz itself checks whether the segment is at the beginning/end
+    of the paragraph.  Clients that only pass item-at-a-time to HarfBuzz
+    continue not setting any flags whatsoever.
+
+    Another way to put it is: if there's pre-context text in the buffer,
+    HarfBuzz ignores the BOT flag.  If there's post-context, it ignores
+    EOT flag.
+
+
+Overview of changes leading to 0.9.33
+Tuesday, July 22, 2014
+=====================================
+
+- Turn off ARabic 'cswh' feature that was accidentally turned on.
+- Add HB_TAG_MAX_SIGNED.
+- Make hb_face_make_immutable() really make face immutable!
+- Windows build fixes.
+
+
+Overview of changes leading to 0.9.32
+Thursday, July 17, 2014
+=====================================
+
+- Apply Arabic shaping features in spec order exactly.
+- Another fix for Mongolian free variation selectors.
+- For non-Arabic scripts in Arabic shaper apply 'rlig' and 'calt'
+  together.
+- Minor adjustment to U+FFFD logic.
+- Fix hb-coretext build.
+
+
+Overview of changes leading to 0.9.31
+Wednesday, July 16, 2014
+=====================================
+
+- Only accept valid UTF-8/16/32; we missed many cases before.
+- Better shaping of invalid UTF-8/16/32.  Falls back to
+  U+FFFD REPLACEMENT CHARACTER now.
+- With all changes in this release, the buffer will contain fully
+  valid Unicode after hb_buffer_add_utf8/16/32 no matter how
+  broken the input is.  This can be overridden though.  See below.
+- Fix Mongolian Variation Selectors for fonts without GDEF.
+- Fix minor invalid buffer access.
+- Accept zh-Hant and zh-Hans language tags.  hb_ot_tag_to_language()
+  now uses these instead of private tags.
+- Build fixes.
+- New API:
+  * hb_buffer_add_codepoints().  This does what hb_buffer_add_utf32()
+    used to do, ie. no validity check on the input at all.  add_utf32
+    now replaces invalid Unicode codepoints with the replacement
+    character (see below).
+  * hb_buffer_set_replacement_codepoint()
+  * hb_buffer_get_replacement_codepoint()
+    Previously, in hb_buffer_add_utf8 and hb_buffer_add_utf16, when
+    we detected broken input, we replaced that with (hb_codepoint_t)-1.
+    This has changed to use U+FFFD now, but can be changed using these
+    new API.
+
+
+Overview of changes leading to 0.9.30
+Wednesday, July 9, 2014
+=====================================
+
+- Update to Unicode 7.0.0:
+  * New scripts Manichaean and Psalter Pahlavi are shaped using
+    Arabic shaper.
+  * All the other new scripts to through the generic shaper for
+    now.
+- Minor Indic improvements.
+- Fix graphite2 backend cluster mapping [crasher!]
+- API changes:
+  * New HB_SCRIPT_* values for Unicode 7.0 scripts.
+  * New function hb_ot_layout_language_get_required_feature().
+- Build fixes.
+
+
+Overview of changes leading to 0.9.29
+Thursday, May 29, 2014
+=====================================
+
+- Implement cmap in hb-ot-font.h.  No variation-selectors yet.
+- Myanmar: Allow MedialYa+Asat.
+- Various Indic fixes:
+  * Support most characters in Extended Devanagary and Vedic
+    Unicode blocks.
+  * Allow digits and a some punctuation as consonant placeholders.
+- Build fixes.
+
+
+Overview of changes leading to 0.9.28
+Monday, April 28, 2014
+=====================================
+
+- Unbreak old-spec Indic shaping. (bug 76705)
+- Fix shaping of U+17DD and U+0FC6.
+- Add HB_NO_MERGE_CLUSTERS build option.  NOT to be enabled by default
+  for shipping libraries.  It's an option for further experimentation
+  right now.  When we are sure how to do it properly, we will add
+  public run-time API for the functionality.
+- Build fixes.
+
+
+Overview of changes leading to 0.9.27
+Tuesday, March 18, 2014
+=====================================
+
+- Don't use "register" storage class specifier
+- Wrap definition of free_langs() with HAVE_ATEXIT
+- Add coretext_aat shaper and hb_coretext_face_create() constructor
+- If HAVE_ICU_BUILTIN is defined, use hb-icu Unicode callbacks
+- Add Myanmar test case from OpenType Myanmar spec
+- Only do fallback Hebrew composition if no GPOS 'mark' available
+- Allow bootstrapping without gtk-doc
+- Use AM_MISSING_PROG for ragel and git
+- Typo in ucdn's Makefile.am
+- Improve MemoryBarrier() implementation
+
+
+Overview of changes leading to 0.9.26
+Thursday, January 30, 2014
+=====================================
+
+- Misc fixes.
+- Fix application of 'rtlm' feature.
+- Automatically apply frac/numr/dnom around U+2044 FRACTION SLASH.
+- New header: hb-ot-shape.h
+- Uniscribe: fix scratch-buffer accounting.
+- Reorder Tai Tham SAKOT to after tone-marks.
+- Add Hangul shaper.
+- New files:
+  hb-ot-shape-complex-hangul.cc
+  hb-ot-shape-complex-hebrew.cc
+  hb-ot-shape-complex-tibetan.cc
+- Disable 'cswh' feature in Arabic shaper.
+- Coretext: better handle surrogate pairs.
+- Add HB_TAG_MAX and _HB_SCRIPT_MAX_VALUE.
+
+
+Overview of changes leading to 0.9.25
+Wednesday, December 4, 2013
+=====================================
+
+- Myanmar shaper improvements.
+- Avoid font fallback in CoreText backend.
+- Additional OpenType language tag mappiongs.
+- More aggressive shape-plan caching.
+- Build with / require automake 1.13.
+- Build with libtool 2.4.2.418 alpha to support ppc64le.
+
+
+Overview of changes leading to 0.9.24
+Tuesday, November 13, 2013
+=====================================
+
+- Misc compiler warning fixes with clang.
+- No functional changes.
+
+
+Overview of changes leading to 0.9.23
+Monday, October 28, 2013
+=====================================
+
+- "Udupi HarfBuzz Hackfest", Paris, October 14..18 2013.
+- Fix (Chain)Context recursion with non-monotone lookup positions.
+- Misc Indic bug fixes.
+- New Javanese / Buginese shaping, similar to Windows 8.1.
+
+
+Overview of changes leading to 0.9.22
+Thursday, October 3, 2013
+=====================================
+
+- Fix use-after-end-of-scope in hb_language_from_string().
+- Fix hiding of default_ignorables if font doesn't have space glyph.
+- Protect against out-of-range lookup indices.
+
+- API Changes:
+
+  * Added hb_ot_layout_table_get_lookup_count()
+
+
+Overview of changes leading to 0.9.21
+Monday, September 16, 2013
+=====================================
+
+- Rename gobject-introspection library name from harfbuzz to HarfBuzz.
+- Remove (long disabled) hb-old and hb-icu-le test shapers.
+- Misc gtk-doc and gobject-introspection annotations.
+- Misc fixes.
+- API changes:
+
+  * Add HB_SET_VALUE_INVALID
+
+Overview of changes leading to 0.9.20
+Thursday, August 29, 2013
+=====================================
+
+General:
+- Misc substitute_closure() fixes.
+- Build fixes.
+
+Documentation:
+- gtk-doc boilerplate integrated.  Docs are built now, but
+  contain no contents.  By next release hopefully we have
+  some content in.  Enable using --enable-gtk-doc.
+
+GObject and Introspection:
+- Added harfbuzz-gobject library (hb-gobject.h) that has type
+  bindings for all HarfBuzz objects and enums.  Enable using
+  --with-gobject.
+- Added gobject-introspection boilerplate.  Nothing useful
+  right now.  Work in progress.  Gets enabled automatically if
+  --with-gobject is used.  Override with --disable-introspection.
+
+OpenType shaper:
+- Apply 'mark' in Myanmar shaper.
+- Don't apply 'dlig' by default.
+
+Uniscribe shaper:
+- Support user features.
+- Fix loading of fonts that are also installed on the system.
+- Fix shaping of Arabic Presentation Forms.
+- Fix build with wide chars.
+
+CoreText shaper:
+- Support user features.
+
+Source changes:
+- hb_face_t code moved to hb-face.h / hb-face.cc.
+- Added hb-deprecated.h.
+
+API changes:
+- Added HB_DISABLE_DEPRECATED.
+- Deprecated HB_SCRIPT_CANADIAN_ABORIGINAL; replaced by
+  HB_SCRIPT_CANADIAN_SYLLABICS.
+- Deprecated HB_BUFFER_FLAGS_DEFAULT; replaced by
+  HB_BUFFER_FLAG_DEFAULT.
+- Deprecated HB_BUFFER_SERIALIZE_FLAGS_DEFAULT; replaced by
+  HB_BUFFER_SERIALIZE_FLAG_DEFAULT.
+
+
+Overview of changes leading to 0.9.19
+Tuesday, July 16, 2013
+=====================================
+
+- Build fixes.
+- Better handling of multiple variation selectors in a row.
+- Pass on variation selector to GSUB if not consumed by cmap.
+- Fix undefined memory access.
+- Add Javanese config to Indic shaper.
+- Misc bug fixes.
+
+Overview of changes leading to 0.9.18
+Tuesday, May 28, 2013
+=====================================
+
+New build system:
+
+- All unneeded code is all disabled by default,
+
+- Uniscribe and CoreText shapers can be enabled with their --with options,
+
+- icu_le and old shapers cannot be enabled for now,
+
+- glib, freetype, and cairo will be detected automatically.
+  They can be force on/off'ed with their --with options,
+
+- icu and graphite2 are default off, can be enabled with their --with
+  options,
+
+Moreover, ICU support is now build into a separate library:
+libharfbuzz-icu.so, and a new harfbuzz-icu.pc is shipped for it.
+Distros can enable ICU now without every application on earth
+getting linked to via libharfbuzz.so.
+
+For distros I recommend that they make sure they are building --with-glib
+--with-freetype --with-cairo, --with-icu, and optionally --with-graphite2;
+And package harfbuzz and harfbuzz-icu separately.
+
+
+Overview of changes leading to 0.9.17
+Monday, May 20, 2013
+=====================================
+
+- Build fixes.
+- Fix bug in hb_set_get_min().
+- Fix regression with Arabic mark positioning / width-zeroing.
+
+Overview of changes leading to 0.9.16
+Friday, April 19, 2013
+=====================================
+
+- Major speedup in OpenType lookup processing.  With the Amiri
+  Arabic font, this release is over 3x faster than previous
+  release.  All scripts / languages should see this speedup.
+
+- New --num-iterations option for hb-shape / hb-view; useful for
+  profiling.
+
+Overview of changes leading to 0.9.15
+Friday, April 05, 2013
+=====================================
+
+- Build fixes.
+- Fix crasher in graphite2 shaper.
+- Fix Arabic mark width zeroing regression.
+- Don't compose Hangul jamo into Unicode syllables.
+
+
+Overview of changes leading to 0.9.14
+Thursday, March 21, 2013
+=====================================
+
+- Build fixes.
+- Fix time-consuming sanitize with malicious fonts.
+- Implement hb_buffer_deserialize_glyphs() for both json and text.
+- Do not ignore Hangul filler characters.
+- Indic fixes:
+  * Fix Malayalam pre-base reordering interaction with post-forms.
+  * Further adjust ZWJ handling.  Should fix known regressions from
+    0.9.13.
+
+
+Overview of changes leading to 0.9.13
+Thursday, February 25, 2013
+=====================================
+
+- Build fixes.
+- Ngapi HarfBuzz Hackfest in London (February 2013):
+  * Fixed all known Indic bugs,
+  * New Win8-style Myanmar shaper,
+  * New South-East Asian shaper for Tai Tham, Cham, and New Tai Lue,
+  * Smartly ignore Default_Ignorable characters (joiners, etc) wheb
+    matching GSUB/GPOS lookups,
+  * Fix 'Phags-Pa U+A872 shaping,
+  * Fix partial disabling of default-on features,
+  * Allow disabling of TrueType kerning.
+- Fix possible crasher with broken fonts with overlapping tables.
+- Removed generated files from git again.  So, one needs ragel to
+  bootstrap from the git tree.
+
+API changes:
+- hb_shape() and related APIs now abort if buffer direction is
+  HB_DIRECTION_INVALID.  Previously, hb_shape() was calling
+  hb_buffer_guess_segment_properties() on the buffer before
+  shaping.  The heuristics in that function are fragile.  If the
+  user really wants the old behvaior, they can call that function
+  right before calling hb_shape() to get the old behavior.
+- hb_blob_create_sub_blob() always creates sub-blob with
+  HB_MEMORY_MODE_READONLY.  See comments for the reason.
+
+
+Overview of changes leading to 0.9.12
+Thursday, January 18, 2013
+=====================================
+
+- Build fixes for Sun compiler.
+- Minor bug fix.
+
+Overview of changes leading to 0.9.11
+Thursday, January 10, 2013
+=====================================
+
+- Build fixes.
+- Fix GPOS mark attachment with null Anchor offsets.
+- [Indic] Fix old-spec reordering of viramas if sequence ends in one.
+- Fix multi-threaded shaper data creation crash.
+- Add atomic ops for Solaris.
+
+API changes:
+- Rename hb_buffer_clear() to hb_buffer_clear_contents().
+
+
+Overview of changes leading to 0.9.10
+Thursday, January 3, 2013
+=====================================
+
+- [Indic] Fixed rendering of Malayalam dot-reph
+- Updated OT language tags.
+- Updated graphite2 backend.
+- Improved hb_ot_layout_get_size_params() logic.
+- Improve hb-shape/hb-view help output.
+- Fixed hb-set.h implementation to not crash.
+- Fixed various issues with hb_ot_layout_collect_lookups().
+- Various build fixes.
+
+New API:
+
+hb_graphite2_face_get_gr_face()
+hb_graphite2_font_get_gr_font()
+hb_coretext_face_get_cg_font()
+
+Modified API:
+
+hb_ot_layout_get_size_params()
+
+
+Overview of changes leading to 0.9.9
+Wednesday, December 5, 2012
+====================================
+
+- Fix build on Windows.
+- Minor improvements.
+
+
+Overview of changes leading to 0.9.8
+Tuesday, December 4, 2012
+====================================
+
+
+- Actually implement hb_shape_plan_get_shaper ().
+- Make UCDB data tables const.
+- Lots of internal refactoring in OTLayout tables.
+- Flesh out hb_ot_layout_lookup_collect_glyphs().
+
+New API:
+
+hb_ot_layout_collect_lookups()
+hb_ot_layout_get_size_params()
+
+
+Overview of changes leading to 0.9.7
+Sunday, November 21, 2012
+====================================
+
+
+HarfBuzz "All-You-Can-Eat-Sushi" (aka Vancouver) Hackfest and follow-on fixes.
+
+- Fix Arabic contextual joining using pre-context text.
+- Fix Sinhala "split matra" mess.
+- Fix Khmer shaping with broken fonts.
+- Implement Thai "PUA" shaping for old fonts.
+- Do NOT route Kharoshthi script through the Indic shaper.
+- Disable fallback positioning for Indic and Thai shapers.
+- Misc fixes.
+
+
+hb-shape / hb-view changes:
+
+- Add --text-before and --text-after
+- Add --bot / --eot / --preserve-default-ignorables
+- hb-shape --output-format=json
+
+
+New API:
+
+hb_buffer_clear()
+
+hb_buffer_flags_t
+
+HB_BUFFER_FLAGS_DEFAULT
+HB_BUFFER_FLAG_BOT
+HB_BUFFER_FLAG_EOT
+HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES
+
+hb_buffer_set_flags()
+hb_buffer_get_flags()
+
+HB_BUFFER_SERIALIZE_FLAGS
+hb_buffer_serialize_glyphs()
+hb_buffer_deserialize_glyphs()
+hb_buffer_serialize_list_formats()
+
+hb_set_add_range()
+hb_set_del_range()
+hb_set_get_population()
+hb_set_next_range()
+
+hb_face_[sg]et_glyph_count()
+
+hb_segment_properties_t
+HB_SEGMENT_PROPERTIES_DEFAULT
+hb_segment_properties_equal()
+hb_segment_properties_hash()
+
+hb_buffer_set_segment_properties()
+hb_buffer_get_segment_properties()
+
+hb_ot_layout_glyph_class_t
+hb_ot_layout_get_glyph_class()
+hb_ot_layout_get_glyphs_in_class()
+
+hb_shape_plan_t
+hb_shape_plan_create()
+hb_shape_plan_create_cached()
+hb_shape_plan_get_empty()
+hb_shape_plan_reference()
+hb_shape_plan_destroy()
+hb_shape_plan_set_user_data()
+hb_shape_plan_get_user_data()
+hb_shape_plan_execute()
+hb_shape_plan_get_shaper()
+
+hb_ot_shape_plan_collect_lookups()
+
+
+API changes:
+
+- Remove "mask" parameter from hb_buffer_add().
+- Rename hb_ot_layout_would_substitute_lookup() and hb_ot_layout_substitute_closure_lookup().
+- hb-set.h API const correction.
+- Renamed hb_set_min/max() to hb_set_get_min/max().
+- Rename hb_ot_layout_feature_get_lookup_indexes() to hb_ot_layout_feature_get_lookups().
+- Rename hb_buffer_guess_properties() to hb_buffer_guess_segment_properties().
+
+
+
+Overview of changes leading to 0.9.6
+Sunday, November 13, 2012
+====================================
+
+- Don't clear pre-context text if no new context is provided.
+- Fix ReverseChainingSubstLookup, which was totally borked.
+- Adjust output format of hb-shape a bit.
+- Include config.h.in in-tree.  Makes it easier for alternate build systems.
+- Fix hb_buffer_set_length(buffer, 0) invalid memory allocation.
+- Use ICU LayoutEngine's C API instead of C++.  Avoids much headache.
+- Drop glyphs for all of Unicode Default_Ignorable characters.
+- Misc build fixes.
+
+Arabic shaper:
+- Enable 'dlig' and 'mset' features in Arabic shaper.
+- Implement 'Phags-pa shaping, improve Mongolian.
+
+Indic shaper:
+- Decompose Sinhala split matras the way old HarfBuzz / Pango did.
+- Initial support for Consonant Medials.
+- Start adding new-style Myanmar shaping.
+- Make reph and 'pref' logic introspect the font.
+- Route Meetei-Mayek through the Indic shaper.
+- Don't apply 'liga' in Indic shaper.
+- Improve Malayalam pre-base reordering Ra interaction with Chillus.
+
+
+
+Overview of changes leading to 0.9.5
+Sunday, October 14, 2012
+====================================
+
+- Synthetic-GSUB Arabic fallback shaping.
+
+- Misc Indic improvements.
+
+- Add build system support for pthread.
+
+- Imported UCDN for in-tree Unicode callbacks implementation.
+
+- Context-aware Arabic joining.
+
+- Misc other fixes.
+
+- New API:
+
+  hb_feature_to/from-string()
+  hb_buffer_[sg]et_content_type()
+
+
+
+Overview of changes leading to 0.9.4
+Tuesday, Sep 03, 2012
+====================================
+
+- Indic improvements with old-spec Malayalam.
+
+- Better fallback glyph positioning, specially with Thai / Lao marks.
+
+- Implement dotted-circle insertion.
+
+- Better Arabic fallback shaping / ligation.
+
+- Added ICU LayoutEngine backend for testing.  Call it by the 'icu_le' name.
+
+- Misc fixes.
+
+
+
+Overview of changes leading to 0.9.3
+Friday, Aug 18, 2012
+====================================
+
+- Fixed fallback mark positioning for left-to-right text.
+
+- Improve mark positioning for the remaining combining classes.
+
+- Unbreak Thai and fallback Arabic shaping.
+
+- Port Arabic shaper to shape-plan caching.
+
+- Use new ICU normalizer functions.
+
+
+
+Overview of changes leading to 0.9.2
+Friday, Aug 10, 2012
+====================================
+
+- Over a thousand commits!  This is the first major release of HarfBuzz.
+
+- HarfBuzz is feature-complete now!  It should be in par, or better, than
+  both Pango's shapers and old HarfBuzz / Qt shapers.
+
+- New Indic shaper, supporting main Indic scripts, Sinhala, and Khmer.
+
+- Improved Arabic shaper, with fallback Arabic shaping, supporting Arabic,
+  Sinhala, N'ko, Mongolian, and Mandaic.
+
+- New Thai / Lao shaper.
+
+- Tibetan / Hangul support in the generic shaper.
+
+- Synthetic GDEF support for fonts without a GDEF table.
+
+- Fallback mark positioning for fonts without a GPOS table.
+
+- Unicode normalization shaping heuristic during glyph mapping.
+
+- New experimental Graphite2 backend.
+
+- New Uniscribe backend (primarily for testing).
+
+- New CoreText backend (primarily for testing).
+
+- Major optimization and speedup.
+
+- Test suites and testing infrastructure (work in progress).
+
+- Greatly improved hb-view cmdline tool.
+
+- hb-shape cmdline tool.
+
+- Unicode 6.1 support.
+
+Summary of API changes:
+
+o Changed API:
+
+  - Users are expected to only include main header files now (ie. hb.h,
+    hb-glib.h, hb-ft.h, ...)
+
+  - All struct tag names had their initial underscore removed.
+    Ie. "struct _hb_buffer_t" is "struct hb_buffer_t" now.
+
+  - All set_user_data() functions now take a "replace" boolean parameter.
+
+  - hb_buffer_create() takes zero arguments now.
+    Use hb_buffer_pre_allocate() to pre-allocate.
+
+  - hb_buffer_add_utf*() now accept -1 for length parameteres,
+    meaning "nul-terminated".
+
+  - hb_direction_t enum values changed.
+
+  - All *_from_string() APIs now take a length parameter to allow for
+    non-nul-terminated strings. A -1 length means "nul-terminated".
+
+  - Typedef for hb_language_t changed.
+
+  - hb_get_table_func_t renamed to hb_reference_table_func_t.
+
+  - hb_ot_layout_table_choose_script()
+
+  - Various renames in hb-unicode.h.
+
+o New API:
+
+  - hb_buffer_guess_properties()
+    Automatically called by hb_shape().
+
+  - hb_buffer_normalize_glyphs()
+
+  - hb_tag_from_string()
+
+  - hb-coretext.h
+
+  - hb-uniscribe.h
+
+  - hb_face_reference_blob()
+  - hb_face_[sg]et_index()
+  - hb_face_set_upem()
+
+  - hb_font_get_glyph_name_func_t
+    hb_font_get_glyph_from_name_func_t
+    hb_font_funcs_set_glyph_name_func()
+    hb_font_funcs_set_glyph_from_name_func()
+    hb_font_get_glyph_name()
+    hb_font_get_glyph_from_name()
+    hb_font_glyph_to_string()
+    hb_font_glyph_from_string()
+
+  - hb_font_set_funcs_data()
+
+  - hb_ft_font_set_funcs()
+  - hb_ft_font_get_face()
+
+  - hb-gobject.h (work in progress)
+
+  - hb_ot_shape_glyphs_closure()
+    hb_ot_layout_substitute_closure_lookup()
+
+  - hb-set.h
+
+  - hb_shape_full()
+
+  - hb_unicode_combining_class_t
+
+  - hb_unicode_compose_func_t
+    hb_unicode_decompose_func_t
+    hb_unicode_decompose_compatibility_func_t
+    hb_unicode_funcs_set_compose_func()
+    hb_unicode_funcs_set_decompose_func()
+    hb_unicode_funcs_set_decompose_compatibility_func()
+    hb_unicode_compose()
+    hb_unicode_decompose()
+    hb_unicode_decompose_compatibility()
+
+o Removed API:
+
+  - hb_ft_get_font_funcs()
+
+  - hb_ot_layout_substitute_start()
+    hb_ot_layout_substitute_lookup()
+    hb_ot_layout_substitute_finish()
+    hb_ot_layout_position_start()
+    hb_ot_layout_position_lookup()
+    hb_ot_layout_position_finish()
+
+
+
+Overview of changes leading to 0.6.0
+Friday, May 27, 2011
+====================================
+
+- Vertical text support in GPOS
+- Almost all API entries have unit tests now, under test/
+- All thread-safety issues are fixed
+
+Summary of API changes follows.
+
+
+* Simple Types API:
+
+  o New API:
+    HB_LANGUAGE_INVALID
+    hb_language_get_default()
+    hb_direction_to_string()
+    hb_direction_from_string()
+    hb_script_get_horizontal_direction()
+    HB_UNTAG()
+
+  o Renamed API:
+    hb_category_t renamed to hb_unicode_general_category_t
+
+  o Changed API:
+    hb_language_t is a typed pointers now
+
+  o Removed API:
+    HB_TAG_STR()
+
+
+* Use ISO 15924 tags for hb_script_t:
+
+  o New API:
+    hb_script_from_iso15924_tag()
+    hb_script_to_iso15924_tag()
+    hb_script_from_string()
+
+  o Changed API:
+    HB_SCRIPT_* enum members changed value.
+
+
+* Buffer API streamlined:
+
+  o New API:
+    hb_buffer_reset()
+    hb_buffer_set_length()
+    hb_buffer_allocation_successful()
+
+  o Renamed API:
+    hb_buffer_ensure() renamed to hb_buffer_pre_allocate()
+    hb_buffer_add_glyph() renamed to hb_buffer_add()
+
+  o Removed API:
+    hb_buffer_clear()
+    hb_buffer_clear_positions()
+
+  o Changed API:
+    hb_buffer_get_glyph_infos() takes an out length parameter now
+    hb_buffer_get_glyph_positions() takes an out length parameter now
+
+
+* Blob API streamlined:
+
+  o New API:
+    hb_blob_get_data()
+    hb_blob_get_data_writable()
+
+  o Renamed API:
+    hb_blob_create_empty() renamed to hb_blob_get_empty()
+
+  o Removed API:
+    hb_blob_lock()
+    hb_blob_unlock()
+    hb_blob_is_writable()
+    hb_blob_try_writable()
+
+  o Changed API:
+    hb_blob_create() takes user_data before destroy now
+
+
+* Unicode functions API:
+
+  o Unicode function vectors can subclass other unicode function vectors now.
+    Unimplemented callbacks in the subclass automatically chainup to the parent.
+
+  o All hb_unicode_funcs_t callbacks take a user_data now.  Their setters
+    take a user_data and its respective destroy callback.
+
+  o New API:
+    hb_unicode_funcs_get_empty()
+    hb_unicode_funcs_get_default()
+    hb_unicode_funcs_get_parent()
+
+  o Changed API:
+    hb_unicode_funcs_create() now takes a parent_funcs.
+
+  o Removed func getter functions:
+    hb_unicode_funcs_get_mirroring_func()
+    hb_unicode_funcs_get_general_category_func()
+    hb_unicode_funcs_get_script_func()
+    hb_unicode_funcs_get_combining_class_func()
+    hb_unicode_funcs_get_eastasian_width_func()
+
+
+* Face API:
+
+  o Renamed API:
+    hb_face_get_table() renamed to hb_face_reference_table()
+    hb_face_create_for_data() renamed to hb_face_create()
+
+  o Changed API:
+    hb_face_create_for_tables() takes user_data before destroy now
+    hb_face_reference_table() returns empty blob instead of NULL
+    hb_get_table_func_t accepts the face as first parameter now
+
+* Font API:
+
+  o Fonts can subclass other fonts now.  Unimplemented callbacks in the
+    subclass automatically chainup to the parent.  When chaining up,
+    scale is adjusted if the parent font has a different scale.
+
+  o All hb_font_funcs_t callbacks take a user_data now.  Their setters
+    take a user_data and its respective destroy callback.
+
+  o New API:
+    hb_font_get_parent()
+    hb_font_funcs_get_empty()
+    hb_font_create_sub_font()
+
+  o Removed API:
+    hb_font_funcs_copy()
+    hb_font_unset_funcs()
+
+  o Removed func getter functions:
+    hb_font_funcs_get_glyph_func()
+    hb_font_funcs_get_glyph_advance_func()
+    hb_font_funcs_get_glyph_extents_func()
+    hb_font_funcs_get_contour_point_func()
+    hb_font_funcs_get_kerning_func()
+
+  o Changed API:
+    hb_font_create() takes a face and references it now
+    hb_font_set_funcs() takes user_data before destroy now
+    hb_font_set_scale() accepts signed integers now
+    hb_font_get_contour_point_func_t now takes glyph first, then point_index
+    hb_font_get_glyph_func_t returns a success boolean now
+
+
+* Changed object model:
+
+  o All object types have a _get_empty() now:
+    hb_blob_get_empty()
+    hb_buffer_get_empty()
+    hb_face_get_empty()
+    hb_font_get_empty()
+    hb_font_funcs_get_empty()
+    hb_unicode_funcs_get_empty()
+
+  o Added _set_user_data() and _get_user_data() for all object types:
+    hb_blob_get_user_data()
+    hb_blob_set_user_data()
+    hb_buffer_get_user_data()
+    hb_buffer_set_user_data()
+    hb_face_get_user_data()
+    hb_face_set_user_data()
+    hb_font_funcs_get_user_data()
+    hb_font_funcs_set_user_data()
+    hb_font_get_user_data()
+    hb_font_set_user_data()
+    hb_unicode_funcs_get_user_data()
+    hb_unicode_funcs_set_user_data()
+
+  o Removed the _get_reference_count() from all object types:
+    hb_blob_get_reference_count()
+    hb_buffer_get_reference_count()
+    hb_face_get_reference_count()
+    hb_font_funcs_get_reference_count()
+    hb_font_get_reference_count()
+    hb_unicode_funcs_get_reference_count()
+
+  o Added _make_immutable() and _is_immutable() for all object types except for buffer:
+    hb_blob_make_immutable()
+    hb_blob_is_immutable()
+    hb_face_make_immutable()
+    hb_face_is_immutable()
+
+
+* Changed API for vertical text support
+
+  o The following callbacks where removed:
+    hb_font_get_glyph_advance_func_t
+    hb_font_get_kerning_func_t
+
+  o The following new callbacks added instead:
+    hb_font_get_glyph_h_advance_func_t
+    hb_font_get_glyph_v_advance_func_t
+    hb_font_get_glyph_h_origin_func_t
+    hb_font_get_glyph_v_origin_func_t
+    hb_font_get_glyph_h_kerning_func_t
+    hb_font_get_glyph_v_kerning_func_t
+
+  o The following API removed as such:
+    hb_font_funcs_set_glyph_advance_func()
+    hb_font_funcs_set_kerning_func()
+    hb_font_get_glyph_advance()
+    hb_font_get_kerning()
+
+  o New API added instead:
+    hb_font_funcs_set_glyph_h_advance_func()
+    hb_font_funcs_set_glyph_v_advance_func()
+    hb_font_funcs_set_glyph_h_origin_func()
+    hb_font_funcs_set_glyph_v_origin_func()
+    hb_font_funcs_set_glyph_h_kerning_func()
+    hb_font_funcs_set_glyph_v_kerning_func()
+    hb_font_get_glyph_h_advance()
+    hb_font_get_glyph_v_advance()
+    hb_font_get_glyph_h_origin()
+    hb_font_get_glyph_v_origin()
+    hb_font_get_glyph_h_kerning()
+    hb_font_get_glyph_v_kerning()
+
+  o The following higher-leve API added for convenience:
+    hb_font_get_glyph_advance_for_direction()
+    hb_font_get_glyph_origin_for_direction()
+    hb_font_add_glyph_origin_for_direction()
+    hb_font_subtract_glyph_origin_for_direction()
+    hb_font_get_glyph_kerning_for_direction()
+    hb_font_get_glyph_extents_for_origin()
+    hb_font_get_glyph_contour_point_for_origin()
+
+
+* OpenType Layout API:
+
+  o New API:
+    hb_ot_layout_position_start()
+    hb_ot_layout_substitute_start()
+    hb_ot_layout_substitute_finish()
+
+
+* Glue code:
+
+  o New API:
+    hb_glib_script_to_script()
+    hb_glib_script_from_script()
+    hb_icu_script_to_script()
+    hb_icu_script_from_script()
+
+
+* Version API added:
+
+  o New API:
+    HB_VERSION_MAJOR
+    HB_VERSION_MINOR
+    HB_VERSION_MICRO
+    HB_VERSION_STRING
+    HB_VERSION_CHECK()
+    hb_version()
+    hb_version_string()
+    hb_version_check()
+
+

+ 7 - 0
thirdparty/harfbuzz/THANKS

@@ -0,0 +1,7 @@
+Bradley Grainger
+Kenichi Ishibashi
+Ivan Kuckir <https://photopea.com/>
+Ryan Lortie
+Jeff Muizelaar
+suzuki toshiya
+Philip Withnall

+ 98 - 0
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 */

+ 158 - 0
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 */

+ 840 - 0
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 */

+ 222 - 0
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 */

+ 417 - 0
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 */

+ 999 - 0
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 */

+ 1157 - 0
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 */

+ 173 - 0
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 */

+ 230 - 0
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 */

+ 382 - 0
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

+ 486 - 0
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 */

+ 75 - 0
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 */

+ 92 - 0
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 */

Некоторые файлы не были показаны из-за большого количества измененных файлов