Pārlūkot izejas kodu

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

bruvzg 5 gadi atpakaļ
vecāks
revīzija
b9f441e81e
100 mainītis faili ar 27035 papildinājumiem un 4 dzēšanām
  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.
   2002, NVIDIA Corporation.
 License: glslang
 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/
 Files: ./thirdparty/jpeg_compressor/
 Comment: jpeg-compressor
 Comment: jpeg-compressor
 Copyright: 2012, Rich Geldreich
 Copyright: 2012, Rich Geldreich
@@ -1180,6 +1207,46 @@ License: FTL
    Robert Wilhelm    <[email protected]>
    Robert Wilhelm    <[email protected]>
    Werner Lemberg    <[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
 License: MPL-2.0
  Mozilla Public License Version 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
  In no event will the author be held liable for damages arising from the use
  of this font.
  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
 License: Zlib
  This software is provided 'as-is', without any express or implied
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  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_enet", "Use the built-in ENet library", True))
 opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType 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_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_libogg", "Use the built-in libogg library", True))
 opts.Add(BoolVariable("builtin_libpng", "Use the built-in libpng 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))
 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"])
     env.Append(CPPFLAGS=["-isystem", env["ANDROID_NDK_ROOT"] + "/sources/cxx-stl/llvm-libc++abi/include"])
 
 
     # Disable exceptions and rtti on non-tools (template) builds
     # Disable exceptions and rtti on non-tools (template) builds
-    if env["tools"]:
+    if env["tools"] or env["builtin_icu"]:
         env.Append(CXXFLAGS=["-frtti"])
         env.Append(CXXFLAGS=["-frtti"])
     else:
     else:
         env.Append(CXXFLAGS=["-fno-rtti", "-fno-exceptions"])
         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).
         # 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"])
         env.Append(LINKFLAGS=["-s", "TOTAL_MEMORY=33554432"])
+    elif env["builtin_icu"]:
+        env.Append(CCFLAGS=["-frtti"])
     else:
     else:
         # Disable exceptions and rtti on non-tools (template) builds
         # Disable exceptions and rtti on non-tools (template) builds
         # These flags help keep the file size down.
         # 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
     # freetype depends on libpng and zlib, so bundling one of them while keeping others
     # as shared libraries leads to weird issues
     # 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_freetype"] = True
         env["builtin_libpng"] = True
         env["builtin_libpng"] = True
         env["builtin_zlib"] = True
         env["builtin_zlib"] = True
+        env["builtin_graphite"] = True
+        env["builtin_harfbuzz"] = True
 
 
     if not env["builtin_freetype"]:
     if not env["builtin_freetype"]:
         env.ParseConfig("pkg-config freetype2 --cflags --libs")
         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"]:
     if not env["builtin_libpng"]:
         env.ParseConfig("pkg-config libpng16 --cflags --libs")
         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
     # freetype depends on libpng and zlib, so bundling one of them while keeping others
     # as shared libraries leads to weird issues
     # 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_freetype"] = True
         env["builtin_libpng"] = True
         env["builtin_libpng"] = True
         env["builtin_zlib"] = True
         env["builtin_zlib"] = True
+        env["builtin_graphite"] = True
+        env["builtin_harfbuzz"] = True
 
 
     if not env["builtin_freetype"]:
     if not env["builtin_freetype"]:
         env.ParseConfig("pkg-config freetype2 --cflags --libs")
         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"]:
     if not env["builtin_libpng"]:
         env.ParseConfig("pkg-config libpng16 --cflags --libs")
         env.ParseConfig("pkg-config libpng16 --cflags --libs")
 
 
@@ -233,7 +250,17 @@ def configure(env):
     env.Append(CPPDEFINES=["SERVER_ENABLED", "UNIX_ENABLED"])
     env.Append(CPPDEFINES=["SERVER_ENABLED", "UNIX_ENABLED"])
 
 
     if platform.system() == "Darwin":
     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"])
     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.
 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
 ## 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 */

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels