text:
# Base and default-included sources and headers
hb_base_sources = [
  'hb-aat-fdsc-table.hh',
  'hb-aat-layout-ankr-table.hh',
  'hb-aat-layout-bsln-table.hh',
  'hb-aat-layout-common.hh',
  'hb-aat-layout-feat-table.hh',
  'hb-aat-layout-just-table.hh',
  'hb-aat-layout-kerx-table.hh',
  'hb-aat-layout-lcar-table.hh',
  'hb-aat-layout-morx-table.hh',
  'hb-aat-layout-opbd-table.hh',
  'hb-aat-layout-trak-table.hh',
  'hb-aat-layout.cc',
  'hb-aat-layout.hh',
  'hb-aat-ltag-table.hh',
  'hb-aat-map.cc',
  'hb-aat-map.hh',
  'hb-algs.hh',
  'hb-array.hh',
  'hb-atomic.hh',
  'hb-blob.cc',
  'hb-blob.hh',
  'hb-buffer-serialize.cc',
  'hb-buffer.cc',
  'hb-buffer.hh',
  'hb-cache.hh',
  'hb-cff-interp-common.hh',
  'hb-cff-interp-cs-common.hh',
  'hb-cff-interp-dict-common.hh',
  'hb-cff1-interp-cs.hh',
  'hb-cff2-interp-cs.hh',
  'hb-common.cc',
  'hb-config.hh',
  'hb-debug.hh',
  'hb-dispatch.hh',
  'hb-draw.cc',
  'hb-draw.hh',
  'hb-face.cc',
  'hb-face.hh',
  'hb-fallback-shape.cc',
  'hb-font.cc',
  'hb-font.hh',
  'hb-iter.hh',
  'hb-kern.hh',
  'hb-machinery.hh',
  'hb-map.cc',
  'hb-map.hh',
  'hb-bimap.hh',
  'hb-meta.hh',
  'hb-mutex.hh',
  'hb-null.hh',
  'hb-number.cc',
  'hb-number.hh',
  'hb-object.hh',
  'hb-open-file.hh',
  'hb-open-type.hh',
  'hb-ot-cff-common.hh',
  'hb-ot-cff1-table.cc',
  'hb-ot-cff1-table.hh',
  'hb-ot-cff1-std-str.hh',
  'hb-ot-cff2-table.cc',
  'hb-ot-cff2-table.hh',
  'hb-ot-cmap-table.hh',
  'hb-ot-color-cbdt-table.hh',
  'hb-ot-color-colr-table.hh',
  'hb-ot-color-cpal-table.hh',
  'hb-ot-color-sbix-table.hh',
  'hb-ot-color-svg-table.hh',
  'hb-ot-color.cc',
  'hb-ot-face.cc',
  'hb-ot-face.hh',
  'hb-ot-face-table-list.hh',
  'hb-ot-font.cc',
  'hb-ot-gasp-table.hh',
  'hb-ot-glyf-table.hh',
  'hb-ot-hdmx-table.hh',
  'hb-ot-head-table.hh',
  'hb-ot-hhea-table.hh',
  'hb-ot-hmtx-table.hh',
  'hb-ot-kern-table.hh',
  'hb-ot-layout-base-table.hh',
  'hb-ot-layout-common.hh',
  'hb-ot-layout-gdef-table.hh',
  'hb-ot-layout-gpos-table.hh',
  'hb-ot-layout-gsub-table.hh',
  'hb-ot-layout-gsubgpos.hh',
  'hb-ot-layout-jstf-table.hh',
  'hb-ot-layout.cc',
  'hb-ot-layout.hh',
  'hb-ot-map.cc',
  'hb-ot-map.hh',
  'hb-ot-math-table.hh',
  'hb-ot-math.cc',
  'hb-ot-maxp-table.hh',
  'hb-ot-meta-table.hh',
  'hb-ot-meta.cc',
  'hb-ot-metrics.cc',
  'hb-ot-metrics.hh',
  'hb-ot-name-language-static.hh',
  'hb-ot-name-language.hh',
  'hb-ot-name-table.hh',
  'hb-ot-name.cc',
  'hb-ot-os2-table.hh',
  'hb-ot-os2-unicode-ranges.hh',
  'hb-ot-post-macroman.hh',
  'hb-ot-post-table.hh',
  'hb-ot-shape-complex-arabic-fallback.hh',
  'hb-ot-shape-complex-arabic-table.hh',
  'hb-ot-shape-complex-arabic-win1256.hh',
  'hb-ot-shape-complex-arabic.cc',
  'hb-ot-shape-complex-arabic.hh',
  'hb-ot-shape-complex-default.cc',
  'hb-ot-shape-complex-hangul.cc',
  'hb-ot-shape-complex-hebrew.cc',
  'hb-ot-shape-complex-indic-table.cc',
  'hb-ot-shape-complex-indic.cc',
  'hb-ot-shape-complex-indic.hh',
  'hb-ot-shape-complex-khmer.cc',
  'hb-ot-shape-complex-khmer.hh',
  'hb-ot-shape-complex-myanmar.cc',
  'hb-ot-shape-complex-myanmar.hh',
  'hb-ot-shape-complex-thai.cc',
  'hb-ot-shape-complex-use-table.cc',
  'hb-ot-shape-complex-use.cc',
  'hb-ot-shape-complex-use.hh',
  'hb-ot-shape-complex-vowel-constraints.cc',
  'hb-ot-shape-complex-vowel-constraints.hh',
  'hb-ot-shape-complex.hh',
  'hb-ot-shape-fallback.cc',
  'hb-ot-shape-fallback.hh',
  'hb-ot-shape-normalize.cc',
  'hb-ot-shape-normalize.hh',
  'hb-ot-shape.cc',
  'hb-ot-shape.hh',
  'hb-ot-stat-table.hh',
  'hb-ot-tag-table.hh',
  'hb-ot-tag.cc',
  'hb-ot-var-avar-table.hh',
  'hb-ot-var-fvar-table.hh',
  'hb-ot-var-gvar-table.hh',
  'hb-ot-var-hvar-table.hh',
  'hb-ot-var-mvar-table.hh',
  'hb-ot-var.cc',
  'hb-ot-vorg-table.hh',
  'hb-pool.hh',
  'hb-sanitize.hh',
  'hb-serialize.hh',
  'hb-set-digest.hh',
  'hb-set.cc',
  'hb-set.hh',
  'hb-shape-plan.cc',
  'hb-shape-plan.hh',
  'hb-shape.cc',
  'hb-shaper-impl.hh',
  'hb-shaper-list.hh',
  'hb-shaper.cc',
  'hb-shaper.hh',
  'hb-static.cc',
  'hb-string-array.hh',
  'hb-ucd-table.hh',
  'hb-ucd.cc',
  'hb-unicode-emoji-table.hh',
  'hb-unicode.cc',
  'hb-unicode.hh',
  'hb-utf.hh',
  'hb-vector.hh',
  'hb.hh',
]

hb_base_ragel_generated_sources = [
  'hb-buffer-deserialize-json.hh',
  'hb-buffer-deserialize-text.hh',
  'hb-number-parser.hh',
  'hb-ot-shape-complex-indic-machine.hh',
  'hb-ot-shape-complex-khmer-machine.hh',
  'hb-ot-shape-complex-myanmar-machine.hh',
  'hb-ot-shape-complex-use-machine.hh',
]
hb_base_ragel_sources = [
  'hb-buffer-deserialize-json.rl',
  'hb-buffer-deserialize-text.rl',
  'hb-number-parser.rl',
  'hb-ot-shape-complex-indic-machine.rl',
  'hb-ot-shape-complex-khmer-machine.rl',
  'hb-ot-shape-complex-myanmar-machine.rl',
  'hb-ot-shape-complex-use-machine.rl',
]

hb_base_headers = [
  'hb-aat-layout.h',
  'hb-aat.h',
  'hb-blob.h',
  'hb-buffer.h',
  'hb-common.h',
  'hb-deprecated.h',
  'hb-draw.h',
  'hb-face.h',
  'hb-font.h',
  'hb-map.h',
  'hb-ot-color.h',
  'hb-ot-deprecated.h',
  'hb-ot-font.h',
  'hb-ot-layout.h',
  'hb-ot-math.h',
  'hb-ot-meta.h',
  'hb-ot-metrics.h',
  'hb-ot-name.h',
  'hb-ot-shape.h',
  'hb-ot-var.h',
  'hb-ot.h',
  'hb-set.h',
  'hb-shape-plan.h',
  'hb-shape.h',
  'hb-unicode.h',
  'hb-version.h',
  'hb.h',
]

# Optional Sources and Headers with external deps

hb_ft_sources = ['hb-ft.cc']
hb_ft_headers = ['hb-ft.h']

hb_glib_sources = ['hb-glib.cc']
hb_glib_headers = ['hb-glib.h']

hb_graphite2_sources = ['hb-graphite2.cc']
hb_graphite2_headers = ['hb-graphite2.h']

# System-dependent sources and headers

hb_coretext_sources = ['hb-coretext.cc']
hb_coretext_headers = ['hb-coretext.h']

hb_directwrite_sources = ['hb-directwrite.cc']
hb_directwrite_headers = ['hb-directwrite.h']

hb_gdi_sources = ['hb-gdi.cc']
hb_gdi_headers = ['hb-gdi.h']

hb_uniscribe_sources = ['hb-uniscribe.cc']
hb_uniscribe_headers = ['hb-uniscribe.h']

# Sources for libharfbuzz-gobject and libharfbuzz-icu
hb_icu_sources = ['hb-icu.cc']
hb_icu_headers = ['hb-icu.h']

# Sources for libharfbuzz-subset
hb_subset_sources = [
  'hb-number.cc',
  'hb-number.hh',
  'hb-ot-cff1-table.cc',
  'hb-ot-cff2-table.cc',
  'hb-static.cc',
  'hb-subset-cff-common.cc',
  'hb-subset-cff-common.hh',
  'hb-subset-cff1.cc',
  'hb-subset-cff1.hh',
  'hb-subset-cff2.cc',
  'hb-subset-cff2.hh',
  'hb-subset-input.cc',
  'hb-subset-input.hh',
  'hb-subset-plan.cc',
  'hb-subset-plan.hh',
  'hb-subset-plan.hh',
  'hb-subset.cc',
  'hb-subset.hh',
  'hb-subset.hh',
]

hb_subset_headers = ['hb-subset.h']

hb_gobject_sources = [
  'hb-gobject-structs.cc'
]

hb_gobject_headers = [
  'hb-gobject.h',
  'hb-gobject-structs.h',
]

incsrc = include_directories('.')

hb_sources = hb_base_sources + hb_base_ragel_generated_sources
hb_headers = hb_base_headers

if conf.get('HAVE_FREETYPE', 0) == 1
  hb_sources += hb_ft_sources
  hb_headers += hb_ft_headers
endif

if conf.get('HAVE_GDI', 0) == 1
  hb_sources += hb_gdi_sources
  hb_headers += hb_gdi_headers
endif

if conf.get('HAVE_GRAPHITE2', 0) == 1
  hb_sources += hb_graphite2_sources
  hb_headers += hb_graphite2_headers
endif

if conf.get('HAVE_GLIB', 0) == 1
  hb_sources += hb_glib_sources
  hb_headers += hb_glib_headers
endif

if conf.get('HAVE_UNISCRIBE', 0) == 1
  hb_sources += hb_uniscribe_sources
  hb_headers += hb_uniscribe_headers
endif

if conf.get('HAVE_DIRECTWRITE', 0) == 1
  hb_sources += hb_directwrite_sources
  hb_headers += hb_directwrite_headers
endif

if conf.get('HAVE_CORETEXT', 0) == 1
  hb_sources += hb_coretext_sources
  hb_headers += hb_coretext_headers
endif

if get_option('amalgam')
  # replace the array if is amalgam build
  hb_sources = ['harfbuzz.cc']
endif

have_icu = conf.get('HAVE_ICU', 0) == 1
have_icu_builtin = conf.get('HAVE_ICU_BUILTIN', 0) == 1

if have_icu and have_icu_builtin
  hb_sources += hb_icu_sources
  hb_headers += hb_icu_headers
  deps += [icu_dep]
endif

if host_machine.system() == 'windows' or get_option('with-libstdcxx')
  chosen_linker = 'cpp'
else
  # our autotools port was limiting this to HAVE_GCC as https://github.com/harfbuzz/harfbuzz/commit/e784632
  # let's see if that is needed anymore

  # Use a C linker, not C++; Don't link to libstdc++
  chosen_linker = 'c'
endif

# harfbuzz
gen_def = find_program('gen-def.py')

harfbuzz_def_command_args = [gen_def, '@OUTPUT@', '@INPUT@']
if get_option('experimental-api')
  harfbuzz_def_command_args += '--experimental-api'
endif

harfbuzz_def = custom_target('harfbuzz.def',
    command: harfbuzz_def_command_args,
    input: hb_headers,
    output: 'harfbuzz.def')
defs_list = [harfbuzz_def]

version = '0.' + '0'.join(meson.project_version().split('.')) + '.0'

extra_hb_cpp_args = []
if cpp.get_id() == 'msvc'
  if get_option('default_library') == 'shared'
    extra_hb_cpp_args += '-DHB_DLL_EXPORT'
  endif
  hb_so_version = ''
else
  hb_so_version = '0'
endif

libharfbuzz = library('harfbuzz', hb_sources,
  include_directories: incconfig,
  dependencies: deps,
  cpp_args: cpp_args + extra_hb_cpp_args,
  soversion: hb_so_version,
  version: version,
  install: true,
  link_language: chosen_linker,
)

libharfbuzz_dep = declare_dependency(
  link_with: libharfbuzz,
  include_directories: incsrc,
  dependencies: deps)

# harfbuzz-subset
harfbuzz_subset_def = custom_target('harfbuzz-subset.def',
    command: [gen_def, '@OUTPUT@', '@INPUT@'],
    input: hb_subset_headers,
    output: 'harfbuzz-subset.def')
defs_list += [harfbuzz_subset_def]

libharfbuzz_subset = library('harfbuzz-subset', hb_subset_sources,
  include_directories: incconfig,
  dependencies: deps,
  link_with: [libharfbuzz],
  cpp_args: cpp_args + extra_hb_cpp_args,
  soversion: hb_so_version,
  version: version,
  install: true,
  link_language: chosen_linker,
)

libharfbuzz_subset_dep = declare_dependency(
  link_with: libharfbuzz_subset,
  include_directories: incsrc,
  dependencies: deps)

if get_option('tests').enabled()
  # TODO: MSVC gives the following,
  # error LNK2019: unresolved external symbol "unsigned __int64 const * const _hb_NullPool"
  if cpp.get_id() != 'msvc'
    noinst_programs = {
      'main': 'main.cc',
      'test-basics': 'test.cc',
      'test-buffer-serialize': 'test-buffer-serialize.cc',
      'test-ot-meta': 'test-ot-meta.cc',
      'test-ot-name': 'test-ot-name.cc',
      'test-ot-glyphname': 'test-ot-glyphname.cc',
      'test-ot-gpos-size-params': 'test-gpos-size-params.cc',
      'test-ot-gsub-would-substitute': 'test-gsub-would-substitute.cc',
    }
    foreach name, source : noinst_programs
      executable(name, source,
        include_directories: incconfig,
        cpp_args: cpp_args,
        dependencies: libharfbuzz_dep,
        install: false,
      )
    endforeach
  endif

  check_programs = {
    'dump-indic-data': ['dump-indic-data.cc', 'hb-ot-shape-complex-indic-table.cc'],
    'dump-khmer-data': ['dump-khmer-data.cc', 'hb-ot-shape-complex-indic-table.cc'],
    'dump-myanmar-data': ['dump-myanmar-data.cc', 'hb-ot-shape-complex-indic-table.cc'],
    'dump-use-data': ['dump-use-data.cc', 'hb-ot-shape-complex-use-table.cc'],
  }
  foreach name, source : check_programs
    executable(name, source,
      include_directories: incconfig,
      cpp_args: cpp_args,
      dependencies: libharfbuzz_dep,
      install: false,
    )
  endforeach

  compiled_tests = {
    'test-array': 'test-array.cc',
    'test-number': ['test-number.cc', 'hb-number.cc'],
    'test-ot-tag': 'hb-ot-tag.cc',
    'test-unicode-ranges': 'test-unicode-ranges.cc',
  }
  if cpp.get_id() != 'msvc'
    # TODO: MSVC doesn't like these, fix them
    compiled_tests += {
      'test-algs': ['test-algs.cc', 'hb-static.cc'],
      'test-bimap': ['test-bimap.cc', 'hb-static.cc'],
      'test-iter': ['test-iter.cc', 'hb-static.cc'],
      'test-meta': ['test-meta.cc', 'hb-static.cc'],
    }
  endif
  foreach name, source : compiled_tests
    test(name, executable(name, source,
      include_directories: incconfig,
      cpp_args: cpp_args + ['-DMAIN', '-UNDEBUG'],
      dependencies: libharfbuzz_dep,
      install: false,
    ))
  endforeach

  if host_machine.system() != 'windows' and not meson.is_cross_build()
    # Some of them should be ported to python
    dist_check_script = [
      'check-c-linkage-decls.sh',
      'check-externs.sh',
      'check-header-guards.sh',
      'check-static-inits.sh',
    ]
    if not get_option('amalgam')
      dist_check_script += 'check-includes.sh'
    endif
    if false and not get_option('with-libstdcxx')
      # enable this once https://github.com/mesonbuild/meson/pull/6838 hits a release
      # and make that version (i.e. 0.55) our minimum build requirement
      dist_check_script += 'check-libstdc++.sh' # See https://github.com/harfbuzz/harfbuzz/issues/2276
    endif

    env = environment()
    env.set('srcdir', meson.current_source_dir())
    env.set('builddir', meson.current_build_dir())
    env.set('libs', meson.current_build_dir()) # TODO: Merge this with builddir after autotools removal
    env.set('HBSOURCES', ' '.join(hb_sources))
    env.set('HBHEADERS', ' '.join(hb_headers))

    foreach name : dist_check_script
      test(name, find_program(name), env: env)
    endforeach
  endif
endif

pkgmod.generate(libharfbuzz,
  description: 'HarfBuzz text shaping library',
  subdirs: [meson.project_name()],
  version: meson.project_version(),
)

pkgmod.generate(libharfbuzz_subset,
  description: 'HarfBuzz font subsetter',
  subdirs: [meson.project_name()],
  version: meson.project_version(),
)

if have_icu and not have_icu_builtin
  harfbuzz_icu_def = custom_target('harfbuzz-icu.def',
    command: [gen_def, '@OUTPUT@', '@INPUT@'],
    input: [hb_icu_headers],
    output: 'harfbuzz-icu.def')
  defs_list += [harfbuzz_icu_def]

  libharfbuzz_icu = library('harfbuzz-icu', [hb_icu_sources, hb_icu_headers],
    include_directories: incconfig,
    dependencies: icu_dep,
    link_with: [libharfbuzz],
    cpp_args: cpp_args + extra_hb_cpp_args,
    soversion: hb_so_version,
    version: version,
    install: true,
    # ICU links to stdc++ anyway so the default linker is good
    # link_language: chosen_linker,
  )

  libharfbuzz_icu_dep = declare_dependency(
    link_with: libharfbuzz_icu,
    include_directories: incsrc,
    dependencies: deps)

  pkgmod.generate(libharfbuzz_icu,
    description: 'HarfBuzz text shaping library ICU integration',
    subdirs: [meson.project_name()],
    version: meson.project_version(),
  )

  install_headers(hb_icu_headers, subdir: meson.project_name())
else
  libharfbuzz_icu_dep = dependency('', required: false)
endif

have_gobject = conf.get('HAVE_GOBJECT', 0) == 1

cmake_config = configuration_data()
cmake_config.set('libdir', '${prefix}/@0@'.format(get_option('libdir')))
cmake_config.set('includedir', '${prefix}/@0@'.format(get_option('includedir')))
cmake_config.set('HB_LIBTOOL_VERSION_INFO', hb_libtool_version_info)
cmake_config.set('have_gobject', have_gobject ? 'true' : 'false')
configure_file(input: 'harfbuzz-config.cmake.in',
  output: 'harfbuzz-config.cmake',
  configuration: cmake_config,
  install_dir: join_paths(get_option('libdir'), 'cmake', 'harfbuzz'))

if have_gobject
  gnome = import('gnome')

  h_templ = configure_file(
    input: 'hb-gobject-enums.h.tmpl',
    output: 'hb-gobject-enums-tmp.h.tmpl',
    configuration: configuration_data(),
    format: 'cmake')

  cc_templ = configure_file(
    input: 'hb-gobject-enums.cc.tmpl',
    output: 'hb-gobject-enums-tmp.cc.tmpl',
    configuration: configuration_data(),
    format: 'cmake')

  enums = gnome.mkenums('hb-gobject',
    sources: hb_headers,
    h_template: h_templ,
    c_template: cc_templ,
    identifier_prefix: 'hb_',
    symbol_prefix: 'hb_gobject',
  )

  enum_c = custom_target('hb-gobject-enums.cc',
    input: enums[0],
    output: 'hb-gobject-enums.cc',
    command: [python3, files('fix_get_types.py')[0], '@INPUT@', '@OUTPUT@']
  )

  enum_h = custom_target('hb-gobject-enums.h',
    input: enums[1],
    output: 'hb-gobject-enums.h',
    command: [python3, files('fix_get_types.py')[0], '@INPUT@', '@OUTPUT@'],
    install: true,
    install_dir: join_paths(get_option('prefix'), get_option('includedir'), meson.project_name()),
  )

  hb_gobject_sources += [enum_c]

  harfbuzz_gobject_def = custom_target('harfbuzz-gobject.def',
    command: [gen_def, '@OUTPUT@', '@INPUT@'],
    input: [hb_gobject_headers, enum_h],
    output: 'harfbuzz-gobject.def')
  defs_list += [harfbuzz_gobject_def]

  libharfbuzz_gobject = library('harfbuzz-gobject', [hb_gobject_sources, enum_c, enum_h],
    include_directories: incconfig,
    dependencies: deps,
    link_with: [libharfbuzz],
    cpp_args: cpp_args + extra_hb_cpp_args,
    soversion: hb_so_version,
    version: version,
    install: true,
    link_language: chosen_linker,
  )

  gir = find_program('g-ir-scanner', required: get_option('introspection'))
  build_gir = gir.found() and not meson.is_cross_build()

  if build_gir
    hb_gen_files_gir = gnome.generate_gir(libharfbuzz, libharfbuzz_gobject,
      sources: [hb_headers, hb_sources, hb_gobject_headers, hb_gobject_sources, enum_h],
      namespace: 'HarfBuzz',
      nsversion: '0.0',
      identifier_prefix: 'hb_',
      symbol_prefix: ['hb', 'hb_gobject'],
      includes: ['GObject-2.0'],
      export_packages: ['harfbuzz-gobject'],
      header: 'hb-gobject.h',
      install: true,
      extra_args:  ['--cflags-begin',
                    '-DHB_H',
                    '-DHB_H_IN',
                    '-DHB_OT_H',
                    '-DHB_OT_H_IN',
                    '-DHB_AAT_H',
                    '-DHB_AAT_H_IN',
                    '-DHB_GOBJECT_H',
                    '-DHB_GOBJECT_H_IN',
                    '-DHB_EXTERN=',
                    '--cflags-end'])
  endif

  libharfbuzz_gobject_dep = declare_dependency(
    link_with: libharfbuzz_gobject,
    include_directories: incsrc,
    sources: build_gir ? hb_gen_files_gir : hb_gobject_sources,
    dependencies: deps)

  pkgmod.generate(libharfbuzz_gobject,
    description: 'HarfBuzz text shaping library GObject integration',
    subdirs: [meson.project_name()],
    version: meson.project_version(),
  )

  install_headers(hb_gobject_headers, subdir: meson.project_name())
else
  libharfbuzz_gobject_dep = dependency('', required: false)
endif

if get_option('tests').enabled() and host_machine.system() != 'windows' and not meson.is_cross_build()
  test('check-symbols.sh', find_program('check-symbols.sh'),
    depends: defs_list,
    env: env)
endif

install_headers(hb_headers + hb_subset_headers, subdir: meson.project_name())


text:
/*
 * Copyright © 2018  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#include "hb-map.hh"

/**
 * SECTION:hb-map
 * @title: hb-map
 * @short_description: Object representing integer to integer mapping
 * @include: hb.h
 *
 * Map objects are integer-to-integer hash-maps.  Currently they are
 * not used in the HarfBuzz public API, but are provided for client's
 * use if desired.
 **/

/**
 * hb_map_create: (Xconstructor)
 *
 * Return value: (transfer full):
 *
 * Since: 1.7.7
 **/
hb_map_t *
hb_map_create ()
{
  hb_map_t *map;

  if (!(map = hb_object_create<hb_map_t> ()))
    return hb_map_get_empty ();

  map->init_shallow ();

  return map;
}

/**
 * hb_map_get_empty:
 *
 * Return value: (transfer full):
 *
 * Since: 1.7.7
 **/
hb_map_t *
hb_map_get_empty ()
{
  return const_cast<hb_map_t *> (&Null (hb_map_t));
}

/**
 * hb_map_reference: (skip)
 * @map: a map.
 *
 * Return value: (transfer full):
 *
 * Since: 1.7.7
 **/
hb_map_t *
hb_map_reference (hb_map_t *map)
{
  return hb_object_reference (map);
}

/**
 * hb_map_destroy: (skip)
 * @map: a map.
 *
 * Since: 1.7.7
 **/
void
hb_map_destroy (hb_map_t *map)
{
  if (!hb_object_destroy (map)) return;

  map->fini_shallow ();

  free (map);
}

/**
 * hb_map_set_user_data: (skip)
 * @map: a map.
 * @key:
 * @data:
 * @destroy:
 * @replace:
 *
 * Return value:
 *
 * Since: 1.7.7
 **/
hb_bool_t
hb_map_set_user_data (hb_map_t           *map,
		      hb_user_data_key_t *key,
		      void *              data,
		      hb_destroy_func_t   destroy,
		      hb_bool_t           replace)
{
  return hb_object_set_user_data (map, key, data, destroy, replace);
}

/**
 * hb_map_get_user_data: (skip)
 * @map: a map.
 * @key:
 *
 * Return value: (transfer none):
 *
 * Since: 1.7.7
 **/
void *
hb_map_get_user_data (hb_map_t           *map,
		      hb_user_data_key_t *key)
{
  return hb_object_get_user_data (map, key);
}

/**
 * hb_map_allocation_successful:
 * @map: a map.
 *
 *
 *
 * Return value:
 *
 * Since: 1.7.7
 **/
hb_bool_t
hb_map_allocation_successful (const hb_map_t  *map)
{
  return map->successful;
}

/**
 * hb_map_set:
 * @map: a map.
 * @key:
 * @value:
 *
 *
 *
 * Since: 1.7.7
 **/
void
hb_map_set (hb_map_t       *map,
	    hb_codepoint_t  key,
	    hb_codepoint_t  value)
{
  map->set (key, value);
}

/**
 * hb_map_get:
 * @map: a map.
 * @key:
 *
 *
 *
 * Since: 1.7.7
 **/
hb_codepoint_t
hb_map_get (const hb_map_t *map,
	    hb_codepoint_t  key)
{
  return map->get (key);
}

/**
 * hb_map_del:
 * @map: a map.
 * @key:
 *
 *
 *
 * Since: 1.7.7
 **/
void
hb_map_del (hb_map_t       *map,
	    hb_codepoint_t  key)
{
  map->del (key);
}

/**
 * hb_map_has:
 * @map: a map.
 * @key:
 *
 *
 *
 * Since: 1.7.7
 **/
hb_bool_t
hb_map_has (const hb_map_t *map,
	    hb_codepoint_t  key)
{
  return map->has (key);
}

/**
 * hb_map_clear:
 * @map: a map.
 *
 *
 *
 * Since: 1.7.7
 **/
void
hb_map_clear (hb_map_t *map)
{
  return map->clear ();
}

/**
 * hb_map_is_empty:
 * @map: a map.
 *
 *
 *
 * Since: 1.7.7
 **/
hb_bool_t
hb_map_is_empty (const hb_map_t *map)
{
  return map->is_empty ();
}

/**
 * hb_map_get_population:
 * @map: a map.
 *
 *
 *
 * Since: 1.7.7
 **/
unsigned int
hb_map_get_population (const hb_map_t *map)
{
  return map->get_population ();
}

repo_language: C

mime_type: text/x-c


text:
/*
 * Copyright © 2010,2012  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#include "hb.hh"

#ifndef HB_NO_OT_SHAPE

#include "hb-ot-shape-complex.hh"

/* Thai / Lao shaper */

/* PUA shaping */

enum thai_consonant_type_t
{
  NC,
  AC,
  RC,
  DC,
  NOT_CONSONANT,
  NUM_CONSONANT_TYPES = NOT_CONSONANT
};

static thai_consonant_type_t
get_consonant_type (hb_codepoint_t u)
{
  if (u == 0x0E1Bu || u == 0x0E1Du || u == 0x0E1Fu/* || u == 0x0E2Cu*/)
    return AC;
  if (u == 0x0E0Du || u == 0x0E10u)
    return RC;
  if (u == 0x0E0Eu || u == 0x0E0Fu)
    return DC;
  if (hb_in_range<hb_codepoint_t> (u, 0x0E01u, 0x0E2Eu))
    return NC;
  return NOT_CONSONANT;
}

enum thai_mark_type_t
{
  AV,
  BV,
  T,
  NOT_MARK,
  NUM_MARK_TYPES = NOT_MARK
};

static thai_mark_type_t
get_mark_type (hb_codepoint_t u)
{
  if (u == 0x0E31u || hb_in_range<hb_codepoint_t> (u, 0x0E34u, 0x0E37u) ||
      u == 0x0E47u || hb_in_range<hb_codepoint_t> (u, 0x0E4Du, 0x0E4Eu))
    return AV;
  if (hb_in_range<hb_codepoint_t> (u, 0x0E38u, 0x0E3Au))
    return BV;
  if (hb_in_range<hb_codepoint_t> (u, 0x0E48u, 0x0E4Cu))
    return T;
  return NOT_MARK;
}

enum thai_action_t
{
  NOP,
  SD,  /* Shift combining-mark down */
  SL,  /* Shift combining-mark left */
  SDL, /* Shift combining-mark down-left */
  RD   /* Remove descender from base */
};

static hb_codepoint_t
thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font)
{
  struct thai_pua_mapping_t {
    hb_codepoint_t u;
    hb_codepoint_t win_pua;
    hb_codepoint_t mac_pua;
  } const *pua_mappings = nullptr;
  static const thai_pua_mapping_t SD_mappings[] = {
    {0x0E48u, 0xF70Au, 0xF88Bu}, /* MAI EK */
    {0x0E49u, 0xF70Bu, 0xF88Eu}, /* MAI THO */
    {0x0E4Au, 0xF70Cu, 0xF891u}, /* MAI TRI */
    {0x0E4Bu, 0xF70Du, 0xF894u}, /* MAI CHATTAWA */
    {0x0E4Cu, 0xF70Eu, 0xF897u}, /* THANTHAKHAT */
    {0x0E38u, 0xF718u, 0xF89Bu}, /* SARA U */
    {0x0E39u, 0xF719u, 0xF89Cu}, /* SARA UU */
    {0x0E3Au, 0xF71Au, 0xF89Du}, /* PHINTHU */
    {0x0000u, 0x0000u, 0x0000u}
  };
  static const thai_pua_mapping_t SDL_mappings[] = {
    {0x0E48u, 0xF705u, 0xF88Cu}, /* MAI EK */
    {0x0E49u, 0xF706u, 0xF88Fu}, /* MAI THO */
    {0x0E4Au, 0xF707u, 0xF892u}, /* MAI TRI */
    {0x0E4Bu, 0xF708u, 0xF895u}, /* MAI CHATTAWA */
    {0x0E4Cu, 0xF709u, 0xF898u}, /* THANTHAKHAT */
    {0x0000u, 0x0000u, 0x0000u}
  };
  static const thai_pua_mapping_t SL_mappings[] = {
    {0x0E48u, 0xF713u, 0xF88Au}, /* MAI EK */
    {0x0E49u, 0xF714u, 0xF88Du}, /* MAI THO */
    {0x0E4Au, 0xF715u, 0xF890u}, /* MAI TRI */
    {0x0E4Bu, 0xF716u, 0xF893u}, /* MAI CHATTAWA */
    {0x0E4Cu, 0xF717u, 0xF896u}, /* THANTHAKHAT */
    {0x0E31u, 0xF710u, 0xF884u}, /* MAI HAN-AKAT */
    {0x0E34u, 0xF701u, 0xF885u}, /* SARA I */
    {0x0E35u, 0xF702u, 0xF886u}, /* SARA II */
    {0x0E36u, 0xF703u, 0xF887u}, /* SARA UE */
    {0x0E37u, 0xF704u, 0xF888u}, /* SARA UEE */
    {0x0E47u, 0xF712u, 0xF889u}, /* MAITAIKHU */
    {0x0E4Du, 0xF711u, 0xF899u}, /* NIKHAHIT */
    {0x0000u, 0x0000u, 0x0000u}
  };
  static const thai_pua_mapping_t RD_mappings[] = {
    {0x0E0Du, 0xF70Fu, 0xF89Au}, /* YO YING */
    {0x0E10u, 0xF700u, 0xF89Eu}, /* THO THAN */
    {0x0000u, 0x0000u, 0x0000u}
  };

  switch (action) {
    case NOP: return u;
    case SD:  pua_mappings = SD_mappings; break;
    case SDL: pua_mappings = SDL_mappings; break;
    case SL:  pua_mappings = SL_mappings; break;
    case RD:  pua_mappings = RD_mappings; break;
  }
  for (; pua_mappings->u; pua_mappings++)
    if (pua_mappings->u == u)
    {
      hb_codepoint_t glyph;
      if (hb_font_get_glyph (font, pua_mappings->win_pua, 0, &glyph))
	return pua_mappings->win_pua;
      if (hb_font_get_glyph (font, pua_mappings->mac_pua, 0, &glyph))
	return pua_mappings->mac_pua;
      break;
    }
  return u;
}

static enum thai_above_state_t
{     /* Cluster above looks like: */
  T0, /*  ⣤                      */
  T1, /*     ⣼                   */
  T2, /*        ⣾                */
  T3, /*           ⣿             */
  NUM_ABOVE_STATES
} thai_above_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] =
{
  T0, /* NC */
  T1, /* AC */
  T0, /* RC */
  T0, /* DC */
  T3, /* NOT_CONSONANT */
};

static const struct thai_above_state_machine_edge_t {
  thai_action_t action;
  thai_above_state_t next_state;
} thai_above_state_machine[NUM_ABOVE_STATES][NUM_MARK_TYPES] =
{        /*AV*/    /*BV*/    /*T*/
/*T0*/ {{NOP,T3}, {NOP,T0}, {SD, T3}},
/*T1*/ {{SL, T2}, {NOP,T1}, {SDL,T2}},
/*T2*/ {{NOP,T3}, {NOP,T2}, {SL, T3}},
/*T3*/ {{NOP,T3}, {NOP,T3}, {NOP,T3}},
};

static enum thai_below_state_t
{
  B0, /* No descender */
  B1, /* Removable descender */
  B2, /* Strict descender */
  NUM_BELOW_STATES
} thai_below_start_state[NUM_CONSONANT_TYPES + 1/* For NOT_CONSONANT */] =
{
  B0, /* NC */
  B0, /* AC */
  B1, /* RC */
  B2, /* DC */
  B2, /* NOT_CONSONANT */
};

static const struct thai_below_state_machine_edge_t {
  thai_action_t action;
  thai_below_state_t next_state;
} thai_below_state_machine[NUM_BELOW_STATES][NUM_MARK_TYPES] =
{        /*AV*/    /*BV*/    /*T*/
/*B0*/ {{NOP,B0}, {NOP,B2}, {NOP, B0}},
/*B1*/ {{NOP,B1}, {RD, B2}, {NOP, B1}},
/*B2*/ {{NOP,B2}, {SD, B2}, {NOP, B2}},
};

static void
do_thai_pua_shaping (const hb_ot_shape_plan_t *plan HB_UNUSED,
		     hb_buffer_t              *buffer,
		     hb_font_t                *font)
{
#ifdef HB_NO_OT_SHAPE_COMPLEX_THAI_FALLBACK
  return;
#endif

  thai_above_state_t above_state = thai_above_start_state[NOT_CONSONANT];
  thai_below_state_t below_state = thai_below_start_state[NOT_CONSONANT];
  unsigned int base = 0;

  hb_glyph_info_t *info = buffer->info;
  unsigned int count = buffer->len;
  for (unsigned int i = 0; i < count; i++)
  {
    thai_mark_type_t mt = get_mark_type (info[i].codepoint);

    if (mt == NOT_MARK) {
      thai_consonant_type_t ct = get_consonant_type (info[i].codepoint);
      above_state = thai_above_start_state[ct];
      below_state = thai_below_start_state[ct];
      base = i;
      continue;
    }

    const thai_above_state_machine_edge_t &above_edge = thai_above_state_machine[above_state][mt];
    const thai_below_state_machine_edge_t &below_edge = thai_below_state_machine[below_state][mt];
    above_state = above_edge.next_state;
    below_state = below_edge.next_state;

    /* At least one of the above/below actions is NOP. */
    thai_action_t action = above_edge.action != NOP ? above_edge.action : below_edge.action;

    buffer->unsafe_to_break (base, i);
    if (action == RD)
      info[base].codepoint = thai_pua_shape (info[base].codepoint, action, font);
    else
      info[i].codepoint = thai_pua_shape (info[i].codepoint, action, font);
  }
}

static void
preprocess_text_thai (const hb_ot_shape_plan_t *plan,
		      hb_buffer_t              *buffer,
		      hb_font_t                *font)
{
  /* This function implements the shaping logic documented here:
   *
   *   https://linux.thai.net/~thep/th-otf/shaping.html
   *
   * The first shaping rule listed there is needed even if the font has Thai
   * OpenType tables.  The rest do fallback positioning based on PUA codepoints.
   * We implement that only if there exist no Thai GSUB in the font.
   */

  /* The following is NOT specified in the MS OT Thai spec, however, it seems
   * to be what Uniscribe and other engines implement.  According to Eric Muller:
   *
   * When you have a SARA AM, decompose it in NIKHAHIT + SARA AA, *and* move the
   * NIKHAHIT backwards over any tone mark (0E48-0E4B).
   *
   * <0E14, 0E4B, 0E33> -> <0E14, 0E4D, 0E4B, 0E32>
   *
   * This reordering is legit only when the NIKHAHIT comes from a SARA AM, not
   * when it's there to start with. The string <0E14, 0E4B, 0E4D> is probably
   * not what a user wanted, but the rendering is nevertheless nikhahit above
   * chattawa.
   *
   * Same for Lao.
   *
   * Note:
   *
   * Uniscribe also does some below-marks reordering.  Namely, it positions U+0E3A
   * after U+0E38 and U+0E39.  We do that by modifying the ccc for U+0E3A.
   * See unicode->modified_combining_class ().  Lao does NOT have a U+0E3A
   * equivalent.
   */

  /*
   * Here are the characters of significance:
   *
   *			Thai	Lao
   * SARA AM:		U+0E33	U+0EB3
   * SARA AA:		U+0E32	U+0EB2
   * Nikhahit:		U+0E4D	U+0ECD
   *
   * Testing shows that Uniscribe reorder the following marks:
   * Thai:	<0E31,0E34..0E37,0E47..0E4E>
   * Lao:	<0EB1,0EB4..0EB7,0EC7..0ECE>
   *
   * Note how the Lao versions are the same as Thai + 0x80.
   */

  /* We only get one script at a time, so a script-agnostic implementation
   * is adequate here. */
#define IS_SARA_AM(x) (((x) & ~0x0080u) == 0x0E33u)
#define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0x0E33u + 0x0E4Du)
#define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
#define IS_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u))

  buffer->clear_output ();
  unsigned int count = buffer->len;
  for (buffer->idx = 0; buffer->idx < count && buffer->successful;)
  {
    hb_codepoint_t u = buffer->cur().codepoint;
    if (likely (!IS_SARA_AM (u))) {
      buffer->next_glyph ();
      continue;
    }

    /* Is SARA AM. Decompose and reorder. */
    hb_glyph_info_t &nikhahit = buffer->output_glyph (NIKHAHIT_FROM_SARA_AM (u));
    _hb_glyph_info_set_continuation (&nikhahit);
    buffer->replace_glyph (SARA_AA_FROM_SARA_AM (u));
    if (unlikely (!buffer->successful))
      return;

    /* Make Nikhahit be recognized as a ccc=0 mark when zeroing widths. */
    unsigned int end = buffer->out_len;
    _hb_glyph_info_set_general_category (&buffer->out_info[end - 2], HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK);

    /* Ok, let's see... */
    unsigned int start = end - 2;
    while (start > 0 && IS_TONE_MARK (buffer->out_info[start - 1].codepoint))
      start--;

    if (start + 2 < end)
    {
      /* Move Nikhahit (end-2) to the beginning */
      buffer->merge_out_clusters (start, end);
      hb_glyph_info_t t = buffer->out_info[end - 2];
      memmove (buffer->out_info + start + 1,
	       buffer->out_info + start,
	       sizeof (buffer->out_info[0]) * (end - start - 2));
      buffer->out_info[start] = t;
    }
    else
    {
      /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the
       * previous cluster. */
      if (start && buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
	buffer->merge_out_clusters (start - 1, end);
    }
  }
  buffer->swap_buffers ();

  /* If font has Thai GSUB, we are done. */
  if (plan->props.script == HB_SCRIPT_THAI && !plan->map.found_script[0])
    do_thai_pua_shaping (plan, buffer, font);
}

const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
{
  nullptr, /* collect_features */
  nullptr, /* override_features */
  nullptr, /* data_create */
  nullptr, /* data_destroy */
  preprocess_text_thai,
  nullptr, /* postprocess_glyphs */
  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
  nullptr, /* decompose */
  nullptr, /* compose */
  nullptr, /* setup_masks */
  HB_TAG_NONE, /* gpos_tag */
  nullptr, /* reorder_marks */
  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
  false,/* fallback_position */
};

#endif


text:
prefix=%prefix%
exec_prefix=%exec_prefix%
libdir=%libdir%
includedir=%includedir%

Name: harfbuzz
Description: HarfBuzz text shaping library GObject integration
Version: %VERSION%

Requires: harfbuzz gobject-2.0 glib-2.0
Libs: -L${libdir} -lharfbuzz-gobject
Cflags: -I${includedir}/harfbuzz


text:
/*
 * Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "hb.hh"
#include "hb-unicode.hh"
#include "hb-machinery.hh"

#include "hb-ucd-table.hh"

static hb_unicode_combining_class_t
hb_ucd_combining_class (hb_unicode_funcs_t *ufuncs HB_UNUSED,
			hb_codepoint_t unicode,
			void *user_data HB_UNUSED)
{
  return (hb_unicode_combining_class_t) _hb_ucd_ccc (unicode);
}

static hb_unicode_general_category_t
hb_ucd_general_category (hb_unicode_funcs_t *ufuncs HB_UNUSED,
			 hb_codepoint_t unicode,
			 void *user_data HB_UNUSED)
{
  return (hb_unicode_general_category_t) _hb_ucd_gc (unicode);
}

static hb_codepoint_t
hb_ucd_mirroring (hb_unicode_funcs_t *ufuncs HB_UNUSED,
		  hb_codepoint_t unicode,
		  void *user_data HB_UNUSED)
{
  return unicode + _hb_ucd_bmg (unicode);
}

static hb_script_t
hb_ucd_script (hb_unicode_funcs_t *ufuncs HB_UNUSED,
	       hb_codepoint_t unicode,
	       void *user_data HB_UNUSED)
{
  return _hb_ucd_sc_map[_hb_ucd_sc (unicode)];
}

#define SBASE 0xAC00u
#define LBASE 0x1100u
#define VBASE 0x1161u
#define TBASE 0x11A7u
#define SCOUNT 11172u
#define LCOUNT 19u
#define VCOUNT 21u
#define TCOUNT 28u
#define NCOUNT (VCOUNT * TCOUNT)

static inline bool
_hb_ucd_decompose_hangul (hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b)
{
  unsigned si = ab - SBASE;

  if (si >= SCOUNT)
    return false;

  if (si % TCOUNT)
  {
    /* LV,T */
    *a = SBASE + (si / TCOUNT) * TCOUNT;
    *b = TBASE + (si % TCOUNT);
    return true;
  } else {
    /* L,V */
    *a = LBASE + (si / NCOUNT);
    *b = VBASE + (si % NCOUNT) / TCOUNT;
    return true;
  }
}

static inline bool
_hb_ucd_compose_hangul (hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab)
{
  if (a >= SBASE && a < (SBASE + SCOUNT) && b > TBASE && b < (TBASE + TCOUNT) &&
    !((a - SBASE) % TCOUNT))
  {
    /* LV,T */
    *ab = a + (b - TBASE);
    return true;
  }
  else if (a >= LBASE && a < (LBASE + LCOUNT) && b >= VBASE && b < (VBASE + VCOUNT))
  {
    /* L,V */
    int li = a - LBASE;
    int vi = b - VBASE;
    *ab = SBASE + li * NCOUNT + vi * TCOUNT;
    return true;
  }
  else
    return false;
}

static int
_cmp_pair (const void *_key, const void *_item)
{
  uint64_t& a = * (uint64_t*) _key;
  uint64_t b = (* (uint64_t*) _item) & HB_CODEPOINT_ENCODE3(0x1FFFFFu, 0x1FFFFFu, 0);

  return a < b ? -1 : a > b ? +1 : 0;
}
static int
_cmp_pair_11_7_14 (const void *_key, const void *_item)
{
  uint32_t& a = * (uint32_t*) _key;
  uint32_t b = (* (uint32_t*) _item) & HB_CODEPOINT_ENCODE3_11_7_14(0x1FFFFFu, 0x1FFFFFu, 0);

  return a < b ? -1 : a > b ? +1 : 0;
}

static hb_bool_t
hb_ucd_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
		hb_codepoint_t a, hb_codepoint_t b, hb_codepoint_t *ab,
		void *user_data HB_UNUSED)
{
  if (_hb_ucd_compose_hangul (a, b, ab)) return true;

  hb_codepoint_t u = 0;

  if ((a & 0xFFFFF800u) == 0x0000u && (b & 0xFFFFFF80) == 0x0300u)
  {
    uint32_t k = HB_CODEPOINT_ENCODE3_11_7_14 (a, b, 0);
    const uint32_t *v = hb_bsearch (k,
				    _hb_ucd_dm2_u32_map,
				    ARRAY_LENGTH (_hb_ucd_dm2_u32_map),
				    sizeof (*_hb_ucd_dm2_u32_map),
				    _cmp_pair_11_7_14);
    if (likely (!v)) return false;
    u = HB_CODEPOINT_DECODE3_11_7_14_3 (*v);
  }
  else
  {
    uint64_t k = HB_CODEPOINT_ENCODE3 (a, b, 0);
    const uint64_t *v = hb_bsearch (k,
				    _hb_ucd_dm2_u64_map,
				    ARRAY_LENGTH (_hb_ucd_dm2_u64_map),
				    sizeof (*_hb_ucd_dm2_u64_map),
				    _cmp_pair);
    if (likely (!v)) return false;
    u = HB_CODEPOINT_DECODE3_3 (*v);
  }

  if (unlikely (!u)) return false;
  *ab = u;
  return true;
}

static hb_bool_t
hb_ucd_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
		  hb_codepoint_t ab, hb_codepoint_t *a, hb_codepoint_t *b,
		  void *user_data HB_UNUSED)
{
  if (_hb_ucd_decompose_hangul (ab, a, b)) return true;

  unsigned i = _hb_ucd_dm (ab);

  if (likely (!i)) return false;
  i--;

  if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map))
  {
    if (i < ARRAY_LENGTH (_hb_ucd_dm1_p0_map))
      *a = _hb_ucd_dm1_p0_map[i];
    else
    {
      i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map);
      *a = 0x20000 | _hb_ucd_dm1_p2_map[i];
    }
    *b = 0;
    return true;
  }
  i -= ARRAY_LENGTH (_hb_ucd_dm1_p0_map) + ARRAY_LENGTH (_hb_ucd_dm1_p2_map);

  if (i < ARRAY_LENGTH (_hb_ucd_dm2_u32_map))
  {
    uint32_t v = _hb_ucd_dm2_u32_map[i];
    *a = HB_CODEPOINT_DECODE3_11_7_14_1 (v);
    *b = HB_CODEPOINT_DECODE3_11_7_14_2 (v);
    return true;
  }
  i -= ARRAY_LENGTH (_hb_ucd_dm2_u32_map);

  uint64_t v = _hb_ucd_dm2_u64_map[i];
  *a = HB_CODEPOINT_DECODE3_1 (v);
  *b = HB_CODEPOINT_DECODE3_2 (v);
  return true;
}

#if HB_USE_ATEXIT
static void free_static_ucd_funcs ();
#endif

static struct hb_ucd_unicode_funcs_lazy_loader_t : hb_unicode_funcs_lazy_loader_t<hb_ucd_unicode_funcs_lazy_loader_t>
{
  static hb_unicode_funcs_t *create ()
  {
    hb_unicode_funcs_t *funcs = hb_unicode_funcs_create (nullptr);

    hb_unicode_funcs_set_combining_class_func (funcs, hb_ucd_combining_class, nullptr, nullptr);
    hb_unicode_funcs_set_general_category_func (funcs, hb_ucd_general_category, nullptr, nullptr);
    hb_unicode_funcs_set_mirroring_func (funcs, hb_ucd_mirroring, nullptr, nullptr);
    hb_unicode_funcs_set_script_func (funcs, hb_ucd_script, nullptr, nullptr);
    hb_unicode_funcs_set_compose_func (funcs, hb_ucd_compose, nullptr, nullptr);
    hb_unicode_funcs_set_decompose_func (funcs, hb_ucd_decompose, nullptr, nullptr);

    hb_unicode_funcs_make_immutable (funcs);

#if HB_USE_ATEXIT
    atexit (free_static_ucd_funcs);
#endif

    return funcs;
  }
} static_ucd_funcs;

#if HB_USE_ATEXIT
static
void free_static_ucd_funcs ()
{
  static_ucd_funcs.free_instance ();
}
#endif

hb_unicode_funcs_t *
hb_ucd_get_unicode_funcs ()
{
#ifdef HB_NO_UCD
  return hb_unicode_funcs_get_empty ();
#endif
  return static_ucd_funcs.get_unconst ();
}


text:
/*
 * 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 */


text:
/*
 * Copyright © 2018  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_MAP_HH
#define HB_MAP_HH

#include "hb.hh"

/*
 * hb_hashmap_t
 */

template <typename K, typename V,
	  K kINVALID = hb_is_pointer (K) ? 0 : hb_is_signed (K) ? hb_int_min (K) : (K) -1,
	  V vINVALID = hb_is_pointer (V) ? 0 : hb_is_signed (V) ? hb_int_min (V) : (V) -1>
struct hb_hashmap_t
{
  HB_DELETE_COPY_ASSIGN (hb_hashmap_t);
  hb_hashmap_t ()  { init (); }
  ~hb_hashmap_t () { fini (); }

  static_assert (hb_is_integral (K) || hb_is_pointer (K), "");
  static_assert (hb_is_integral (V) || hb_is_pointer (V), "");

  struct item_t
  {
    K key;
    V value;
    uint32_t hash;

    void clear () { key = kINVALID; value = vINVALID; hash = 0; }

    bool operator == (K o) { return hb_deref (key) == hb_deref (o); }
    bool operator == (const item_t &o) { return *this == o.key; }
    bool is_unused () const    { return key == kINVALID; }
    bool is_tombstone () const { return key != kINVALID && value == vINVALID; }
    bool is_real () const { return key != kINVALID && value != vINVALID; }
    hb_pair_t<K, V> get_pair() const { return hb_pair_t<K, V> (key, value); }
  };

  hb_object_header_t header;
  bool successful; /* Allocations successful */
  unsigned int population; /* Not including tombstones. */
  unsigned int occupancy; /* Including tombstones. */
  unsigned int mask;
  unsigned int prime;
  item_t *items;

  void init_shallow ()
  {
    successful = true;
    population = occupancy = 0;
    mask = 0;
    prime = 0;
    items = nullptr;
  }
  void init ()
  {
    hb_object_init (this);
    init_shallow ();
  }
  void fini_shallow ()
  {
    free (items);
    items = nullptr;
    population = occupancy = 0;
  }
  void fini ()
  {
    hb_object_fini (this);
    fini_shallow ();
  }

  void reset ()
  {
    if (unlikely (hb_object_is_immutable (this)))
      return;
    successful = true;
    clear ();
  }

  bool in_error () const { return !successful; }

  bool resize ()
  {
    if (unlikely (!successful)) return false;

    unsigned int power = hb_bit_storage (population * 2 + 8);
    unsigned int new_size = 1u << power;
    item_t *new_items = (item_t *) malloc ((size_t) new_size * sizeof (item_t));
    if (unlikely (!new_items))
    {
      successful = false;
      return false;
    }
    + hb_iter (new_items, new_size)
    | hb_apply (&item_t::clear)
    ;

    unsigned int old_size = mask + 1;
    item_t *old_items = items;

    /* Switch to new, empty, array. */
    population = occupancy = 0;
    mask = new_size - 1;
    prime = prime_for (power);
    items = new_items;

    /* Insert back old items. */
    if (old_items)
      for (unsigned int i = 0; i < old_size; i++)
	if (old_items[i].is_real ())
	  set_with_hash (old_items[i].key,
			 old_items[i].hash,
			 old_items[i].value);

    free (old_items);

    return true;
  }

  void set (K key, V value)
  {
    set_with_hash (key, hb_hash (key), value);
  }

  V get (K key) const
  {
    if (unlikely (!items)) return vINVALID;
    unsigned int i = bucket_for (key);
    return items[i].is_real () && items[i] == key ? items[i].value : vINVALID;
  }

  void del (K key) { set (key, vINVALID); }

  /* Has interface. */
  static constexpr V SENTINEL = vINVALID;
  typedef V value_t;
  value_t operator [] (K k) const { return get (k); }
  bool has (K k, V *vp = nullptr) const
  {
    V v = (*this)[k];
    if (vp) *vp = v;
    return v != SENTINEL;
  }
  /* Projection. */
  V operator () (K k) const { return get (k); }

  void clear ()
  {
    if (unlikely (hb_object_is_immutable (this)))
      return;
    if (items)
      + hb_iter (items, mask + 1)
      | hb_apply (&item_t::clear)
      ;

    population = occupancy = 0;
  }

  bool is_empty () const { return population == 0; }

  unsigned int get_population () const { return population; }

  /*
   * Iterator
   */
  auto iter () const HB_AUTO_RETURN
  (
    + hb_array (items, mask ? mask + 1 : 0)
    | hb_filter (&item_t::is_real)
    | hb_map (&item_t::get_pair)
  )
  auto keys () const HB_AUTO_RETURN
  (
    + hb_array (items, mask ? mask + 1 : 0)
    | hb_filter (&item_t::is_real)
    | hb_map (&item_t::key)
    | hb_map (hb_ridentity)
  )
  auto values () const HB_AUTO_RETURN
  (
    + hb_array (items, mask ? mask + 1 : 0)
    | hb_filter (&item_t::is_real)
    | hb_map (&item_t::value)
    | hb_map (hb_ridentity)
  )

  /* Sink interface. */
  hb_hashmap_t& operator << (const hb_pair_t<K, V>& v)
  { set (v.first, v.second); return *this; }

  protected:

  void set_with_hash (K key, uint32_t hash, V value)
  {
    if (unlikely (!successful)) return;
    if (unlikely (key == kINVALID)) return;
    if ((occupancy + occupancy / 2) >= mask && !resize ()) return;
    unsigned int i = bucket_for_hash (key, hash);

    if (value == vINVALID && items[i].key != key)
      return; /* Trying to delete non-existent key. */

    if (!items[i].is_unused ())
    {
      occupancy--;
      if (items[i].is_tombstone ())
	population--;
    }

    items[i].key = key;
    items[i].value = value;
    items[i].hash = hash;

    occupancy++;
    if (!items[i].is_tombstone ())
      population++;
  }

  unsigned int bucket_for (K key) const
  {
    return bucket_for_hash (key, hb_hash (key));
  }

  unsigned int bucket_for_hash (K key, uint32_t hash) const
  {
    unsigned int i = hash % prime;
    unsigned int step = 0;
    unsigned int tombstone = (unsigned) -1;
    while (!items[i].is_unused ())
    {
      if (items[i].hash == hash && items[i] == key)
	return i;
      if (tombstone == (unsigned) -1 && items[i].is_tombstone ())
	tombstone = i;
      i = (i + ++step) & mask;
    }
    return tombstone == (unsigned) -1 ? i : tombstone;
  }

  static unsigned int prime_for (unsigned int shift)
  {
    /* Following comment and table copied from glib. */
    /* Each table size has an associated prime modulo (the first prime
     * lower than the table size) used to find the initial bucket. Probing
     * then works modulo 2^n. The prime modulo is necessary to get a
     * good distribution with poor hash functions.
     */
    /* Not declaring static to make all kinds of compilers happy... */
    /*static*/ const unsigned int prime_mod [32] =
    {
      1,          /* For 1 << 0 */
      2,
      3,
      7,
      13,
      31,
      61,
      127,
      251,
      509,
      1021,
      2039,
      4093,
      8191,
      16381,
      32749,
      65521,      /* For 1 << 16 */
      131071,
      262139,
      524287,
      1048573,
      2097143,
      4194301,
      8388593,
      16777213,
      33554393,
      67108859,
      134217689,
      268435399,
      536870909,
      1073741789,
      2147483647  /* For 1 << 31 */
    };

    if (unlikely (shift >= ARRAY_LENGTH (prime_mod)))
      return prime_mod[ARRAY_LENGTH (prime_mod) - 1];

    return prime_mod[shift];
  }
};

/*
 * hb_map_t
 */

struct hb_map_t : hb_hashmap_t<hb_codepoint_t,
			       hb_codepoint_t,
			       HB_MAP_VALUE_INVALID,
			       HB_MAP_VALUE_INVALID> {};

#endif /* HB_MAP_HH */


text:
/*
 * Copyright © 2010  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_SHAPE_HH
#define HB_OT_SHAPE_HH

#include "hb.hh"

#include "hb-ot-map.hh"
#include "hb-aat-map.hh"

struct hb_ot_shape_plan_key_t
{
  unsigned int variations_index[2];

  void init (hb_face_t *face,
	     const int *coords,
	     unsigned   num_coords)
  {
    for (unsigned int table_index = 0; table_index < 2; table_index++)
      hb_ot_layout_table_find_feature_variations (face,
						  table_tags[table_index],
						  coords,
						  num_coords,
						  &variations_index[table_index]);
  }

  bool equal (const hb_ot_shape_plan_key_t *other)
  {
    return 0 == memcmp (this, other, sizeof (*this));
  }
};

struct hb_shape_plan_key_t;

struct hb_ot_shape_plan_t
{
  hb_segment_properties_t props;
  const struct hb_ot_complex_shaper_t *shaper;
  hb_ot_map_t map;
  hb_aat_map_t aat_map;
  const void *data;
#ifndef HB_NO_OT_SHAPE_FRACTIONS
  hb_mask_t frac_mask, numr_mask, dnom_mask;
#else
  static constexpr hb_mask_t frac_mask = 0;
  static constexpr hb_mask_t numr_mask = 0;
  static constexpr hb_mask_t dnom_mask = 0;
#endif
  hb_mask_t rtlm_mask;
#ifndef HB_NO_OT_KERN
  hb_mask_t kern_mask;
#else
  static constexpr hb_mask_t kern_mask = 0;
#endif
#ifndef HB_NO_AAT_SHAPE
  hb_mask_t trak_mask;
#else
  static constexpr hb_mask_t trak_mask = 0;
#endif

#ifndef HB_NO_OT_KERN
  bool requested_kerning : 1;
#else
  static constexpr bool requested_kerning = false;
#endif
#ifndef HB_NO_AAT_SHAPE
  bool requested_tracking : 1;
#else
  static constexpr bool requested_tracking = false;
#endif
#ifndef HB_NO_OT_SHAPE_FRACTIONS
  bool has_frac : 1;
#else
  static constexpr bool has_frac = false;
#endif
  bool has_vert : 1;
  bool has_gpos_mark : 1;
  bool zero_marks : 1;
  bool fallback_glyph_classes : 1;
  bool fallback_mark_positioning : 1;
  bool adjust_mark_positioning_when_zeroing : 1;

  bool apply_gpos : 1;
#ifndef HB_NO_OT_KERN
  bool apply_kern : 1;
#else
  static constexpr bool apply_kern = false;
#endif
#ifndef HB_NO_AAT_SHAPE
  bool apply_kerx : 1;
  bool apply_morx : 1;
  bool apply_trak : 1;
#else
  static constexpr bool apply_kerx = false;
  static constexpr bool apply_morx = false;
  static constexpr bool apply_trak = false;
#endif

  void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
  {
    unsigned int table_index;
    switch (table_tag) {
      case HB_OT_TAG_GSUB: table_index = 0; break;
      case HB_OT_TAG_GPOS: table_index = 1; break;
      default: return;
    }
    map.collect_lookups (table_index, lookups);
  }

  HB_INTERNAL bool init0 (hb_face_t                     *face,
			  const hb_shape_plan_key_t     *key);
  HB_INTERNAL void fini ();

  HB_INTERNAL void substitute (hb_font_t *font, hb_buffer_t *buffer) const;
  HB_INTERNAL void position (hb_font_t *font, hb_buffer_t *buffer) const;
};

struct hb_shape_plan_t;

struct hb_ot_shape_planner_t
{
  /* In the order that they are filled in. */
  hb_face_t *face;
  hb_segment_properties_t props;
  hb_ot_map_builder_t map;
  hb_aat_map_builder_t aat_map;
#ifndef HB_NO_AAT_SHAPE
  bool apply_morx : 1;
#else
  static constexpr bool apply_morx = false;
#endif
  bool script_zero_marks : 1;
  bool script_fallback_mark_positioning : 1;
  const struct hb_ot_complex_shaper_t *shaper;

  HB_INTERNAL hb_ot_shape_planner_t (hb_face_t                     *face,
				     const hb_segment_properties_t *props);

  HB_INTERNAL void compile (hb_ot_shape_plan_t           &plan,
			    const hb_ot_shape_plan_key_t &key);
};

#endif /* HB_OT_SHAPE_HH */


text:
/*
 * Copyright © 2017  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_POST_MACROMAN_HH
#if 0 /* Make checks happy. */
#define HB_OT_POST_MACROMAN_HH
#include "hb.hh"
#endif

_S(".notdef")
_S(".null")
_S("nonmarkingreturn")
_S("space")
_S("exclam")
_S("quotedbl")
_S("numbersign")
_S("dollar")
_S("percent")
_S("ampersand")
_S("quotesingle")
_S("parenleft")
_S("parenright")
_S("asterisk")
_S("plus")
_S("comma")
_S("hyphen")
_S("period")
_S("slash")
_S("zero")
_S("one")
_S("two")
_S("three")
_S("four")
_S("five")
_S("six")
_S("seven")
_S("eight")
_S("nine")
_S("colon")
_S("semicolon")
_S("less")
_S("equal")
_S("greater")
_S("question")
_S("at")
_S("A")
_S("B")
_S("C")
_S("D")
_S("E")
_S("F")
_S("G")
_S("H")
_S("I")
_S("J")
_S("K")
_S("L")
_S("M")
_S("N")
_S("O")
_S("P")
_S("Q")
_S("R")
_S("S")
_S("T")
_S("U")
_S("V")
_S("W")
_S("X")
_S("Y")
_S("Z")
_S("bracketleft")
_S("backslash")
_S("bracketright")
_S("asciicircum")
_S("underscore")
_S("grave")
_S("a")
_S("b")
_S("c")
_S("d")
_S("e")
_S("f")
_S("g")
_S("h")
_S("i")
_S("j")
_S("k")
_S("l")
_S("m")
_S("n")
_S("o")
_S("p")
_S("q")
_S("r")
_S("s")
_S("t")
_S("u")
_S("v")
_S("w")
_S("x")
_S("y")
_S("z")
_S("braceleft")
_S("bar")
_S("braceright")
_S("asciitilde")
_S("Adieresis")
_S("Aring")
_S("Ccedilla")
_S("Eacute")
_S("Ntilde")
_S("Odieresis")
_S("Udieresis")
_S("aacute")
_S("agrave")
_S("acircumflex")
_S("adieresis")
_S("atilde")
_S("aring")
_S("ccedilla")
_S("eacute")
_S("egrave")
_S("ecircumflex")
_S("edieresis")
_S("iacute")
_S("igrave")
_S("icircumflex")
_S("idieresis")
_S("ntilde")
_S("oacute")
_S("ograve")
_S("ocircumflex")
_S("odieresis")
_S("otilde")
_S("uacute")
_S("ugrave")
_S("ucircumflex")
_S("udieresis")
_S("dagger")
_S("degree")
_S("cent")
_S("sterling")
_S("section")
_S("bullet")
_S("paragraph")
_S("germandbls")
_S("registered")
_S("copyright")
_S("trademark")
_S("acute")
_S("dieresis")
_S("notequal")
_S("AE")
_S("Oslash")
_S("infinity")
_S("plusminus")
_S("lessequal")
_S("greaterequal")
_S("yen")
_S("mu")
_S("partialdiff")
_S("summation")
_S("product")
_S("pi")
_S("integral")
_S("ordfeminine")
_S("ordmasculine")
_S("Omega")
_S("ae")
_S("oslash")
_S("questiondown")
_S("exclamdown")
_S("logicalnot")
_S("radical")
_S("florin")
_S("approxequal")
_S("Delta")
_S("guillemotleft")
_S("guillemotright")
_S("ellipsis")
_S("nonbreakingspace")
_S("Agrave")
_S("Atilde")
_S("Otilde")
_S("OE")
_S("oe")
_S("endash")
_S("emdash")
_S("quotedblleft")
_S("quotedblright")
_S("quoteleft")
_S("quoteright")
_S("divide")
_S("lozenge")
_S("ydieresis")
_S("Ydieresis")
_S("fraction")
_S("currency")
_S("guilsinglleft")
_S("guilsinglright")
_S("fi")
_S("fl")
_S("daggerdbl")
_S("periodcentered")
_S("quotesinglbase")
_S("quotedblbase")
_S("perthousand")
_S("Acircumflex")
_S("Ecircumflex")
_S("Aacute")
_S("Edieresis")
_S("Egrave")
_S("Iacute")
_S("Icircumflex")
_S("Idieresis")
_S("Igrave")
_S("Oacute")
_S("Ocircumflex")
_S("apple")
_S("Ograve")
_S("Uacute")
_S("Ucircumflex")
_S("Ugrave")
_S("dotlessi")
_S("circumflex")
_S("tilde")
_S("macron")
_S("breve")
_S("dotaccent")
_S("ring")
_S("cedilla")
_S("hungarumlaut")
_S("ogonek")
_S("caron")
_S("Lslash")
_S("lslash")
_S("Scaron")
_S("scaron")
_S("Zcaron")
_S("zcaron")
_S("brokenbar")
_S("Eth")
_S("eth")
_S("Yacute")
_S("yacute")
_S("Thorn")
_S("thorn")
_S("minus")
_S("multiply")
_S("onesuperior")
_S("twosuperior")
_S("threesuperior")
_S("onehalf")
_S("onequarter")
_S("threequarters")
_S("franc")
_S("Gbreve")
_S("gbreve")
_S("Idotaccent")
_S("Scedilla")
_S("scedilla")
_S("Cacute")
_S("cacute")
_S("Ccaron")
_S("ccaron")
_S("dcroat")

#endif /* HB_OT_POST_MACROMAN_HH */


text:
/*
 * Copyright © 2018 Adobe Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Adobe Author(s): Michiharu Ariza
 */

#include "hb.hh"

#ifndef HB_NO_SUBSET_CFF

#include "hb-open-type.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-set.h"
#include "hb-subset-cff2.hh"
#include "hb-subset-plan.hh"
#include "hb-subset-cff-common.hh"
#include "hb-cff2-interp-cs.hh"

using namespace CFF;

struct cff2_sub_table_info_t : cff_sub_table_info_t
{
  cff2_sub_table_info_t ()
    : cff_sub_table_info_t (),
      var_store_link (0)
  {}

  objidx_t  var_store_link;
};

struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
{
  bool serialize (hb_serialize_context_t *c,
		  const op_str_t &opstr,
		  const cff2_sub_table_info_t &info) const
  {
    TRACE_SERIALIZE (this);

    switch (opstr.op)
    {
      case OpCode_vstore:
	return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link));

      default:
	return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info));
    }
  }
};

struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t>
{
  static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
  {
    switch (op)
    {
      case OpCode_return:
      case OpCode_endchar:
	/* dummy opcodes in CFF2. ignore */
	break;

      case OpCode_hstem:
      case OpCode_hstemhm:
      case OpCode_vstem:
      case OpCode_vstemhm:
      case OpCode_hintmask:
      case OpCode_cntrmask:
	if (param.drop_hints)
	{
	  env.clear_args ();
	  return;
	}
	HB_FALLTHROUGH;

      default:
	SUPER::flush_args_and_op (op, env, param);
	break;
    }
  }

  static void flush_args (cff2_cs_interp_env_t &env, flatten_param_t& param)
  {
    for (unsigned int i = 0; i < env.argStack.get_count ();)
    {
      const blend_arg_t &arg = env.argStack[i];
      if (arg.blending ())
      {
	if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
	{
	  env.set_error ();
	  return;
	}
	flatten_blends (arg, i, env, param);
	i += arg.numValues;
      }
      else
      {
	str_encoder_t  encoder (param.flatStr);
	encoder.encode_num (arg);
	i++;
      }
    }
    SUPER::flush_args (env, param);
  }

  static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t &env, flatten_param_t& param)
  {
    /* flatten the default values */
    str_encoder_t  encoder (param.flatStr);
    for (unsigned int j = 0; j < arg.numValues; j++)
    {
      const blend_arg_t &arg1 = env.argStack[i + j];
      if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
	      (arg1.deltas.length == env.get_region_count ())))))
      {
	env.set_error ();
	return;
      }
      encoder.encode_num (arg1);
    }
    /* flatten deltas for each value */
    for (unsigned int j = 0; j < arg.numValues; j++)
    {
      const blend_arg_t &arg1 = env.argStack[i + j];
      for (unsigned int k = 0; k < arg1.deltas.length; k++)
	encoder.encode_num (arg1.deltas[k]);
    }
    /* flatten the number of values followed by blend operator */
    encoder.encode_int (arg.numValues);
    encoder.encode_op (OpCode_blendcs);
  }

  static void flush_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
  {
    switch (op)
    {
      case OpCode_return:
      case OpCode_endchar:
	return;
      default:
	str_encoder_t  encoder (param.flatStr);
	encoder.encode_op (op);
    }
  }

  private:
  typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> SUPER;
  typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t, flatten_param_t> CSOPSET;
};

struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t>
{
  static void process_op (op_code_t op, cff2_cs_interp_env_t &env, subr_subset_param_t& param)
  {
    switch (op) {

      case OpCode_return:
	param.current_parsed_str->set_parsed ();
	env.return_from_subr ();
	param.set_current_str (env, false);
	break;

      case OpCode_endchar:
	param.current_parsed_str->set_parsed ();
	SUPER::process_op (op, env, param);
	break;

      case OpCode_callsubr:
	process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
	break;

      case OpCode_callgsubr:
	process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
	break;

      default:
	SUPER::process_op (op, env, param);
	param.current_parsed_str->add_op (op, env.str_ref);
	break;
    }
  }

  protected:
  static void process_call_subr (op_code_t op, cs_type_t type,
				 cff2_cs_interp_env_t &env, subr_subset_param_t& param,
				 cff2_biased_subrs_t& subrs, hb_set_t *closure)
  {
    byte_str_ref_t    str_ref = env.str_ref;
    env.call_subr (subrs, type);
    param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num);
    closure->add (env.context.subr_num);
    param.set_current_str (env, true);
  }

  private:
  typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> SUPER;
};

struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_subr_subset_t>
{
  cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
    : subr_subsetter_t (acc_, plan_) {}

  static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
  {
    /* vsindex is inserted at the beginning of the charstring as necessary */
    if (env.seen_vsindex ())
    {
      number_t  ivs;
      ivs.set_int ((int)env.get_ivs ());
      charstring.set_prefix (ivs, OpCode_vsindexcs);
    }
  }
};

struct cff2_subset_plan {
  cff2_subset_plan ()
    : orig_fdcount (0),
      subset_fdcount(1),
      subset_fdselect_size (0),
      subset_fdselect_format (0),
      drop_hints (false),
      desubroutinize (false)
  {
    subset_fdselect_ranges.init ();
    fdmap.init ();
    subset_charstrings.init ();
    subset_globalsubrs.init ();
    subset_localsubrs.init ();
  }

  ~cff2_subset_plan ()
  {
    subset_fdselect_ranges.fini ();
    fdmap.fini ();
    subset_charstrings.fini_deep ();
    subset_globalsubrs.fini_deep ();
    subset_localsubrs.fini_deep ();
  }

  bool create (const OT::cff2::accelerator_subset_t &acc,
	      hb_subset_plan_t *plan)
  {
    orig_fdcount = acc.fdArray->count;

    drop_hints = plan->drop_hints;
    desubroutinize = plan->desubroutinize;

    if (desubroutinize)
    {
      /* Flatten global & local subrs */
      subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t>
		    flattener(acc, plan);
      if (!flattener.flatten (subset_charstrings))
	return false;
    }
    else
    {
      cff2_subr_subsetter_t	subr_subsetter (acc, plan);

      /* Subset subrs: collect used subroutines, leaving all unused ones behind */
      if (!subr_subsetter.subset ())
	return false;

      /* encode charstrings, global subrs, local subrs with new subroutine numbers */
      if (!subr_subsetter.encode_charstrings (subset_charstrings))
	return false;

      if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
	return false;

      /* local subrs */
      if (!subset_localsubrs.resize (orig_fdcount))
	return false;
      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
      {
	subset_localsubrs[fd].init ();
	if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
	  return false;
      }
    }

    /* FDSelect */
    if (acc.fdSelect != &Null (CFF2FDSelect))
    {
      if (unlikely (!hb_plan_subset_cff_fdselect (plan,
				  orig_fdcount,
				  *(const FDSelect *)acc.fdSelect,
				  subset_fdcount,
				  subset_fdselect_size,
				  subset_fdselect_format,
				  subset_fdselect_ranges,
				  fdmap)))
	return false;
    }
    else
      fdmap.identity (1);

    return true;
  }

  cff2_sub_table_info_t info;

  unsigned int    orig_fdcount;
  unsigned int    subset_fdcount;
  unsigned int	  subset_fdselect_size;
  unsigned int    subset_fdselect_format;
  hb_vector_t<code_pair_t>   subset_fdselect_ranges;

  hb_inc_bimap_t   fdmap;

  str_buff_vec_t	    subset_charstrings;
  str_buff_vec_t	    subset_globalsubrs;
  hb_vector_t<str_buff_vec_t> subset_localsubrs;

  bool	    drop_hints;
  bool	    desubroutinize;
};

static bool _serialize_cff2 (hb_serialize_context_t *c,
			     cff2_subset_plan &plan,
			     const OT::cff2::accelerator_subset_t  &acc,
			     unsigned int num_glyphs)
{
  /* private dicts & local subrs */
  hb_vector_t<table_info_t>  private_dict_infos;
  if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false;

  for (int i = (int)acc.privateDicts.length; --i >= 0 ;)
  {
    if (plan.fdmap.has (i))
    {
      objidx_t	subrs_link = 0;

      if (plan.subset_localsubrs[i].length > 0)
      {
	CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
	if (unlikely (!dest)) return false;
	c->push ();
	if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
	  subrs_link = c->pop_pack ();
	else
	{
	  c->pop_discard ();
	  return false;
	}
      }
      PrivateDict *pd = c->start_embed<PrivateDict> ();
      if (unlikely (!pd)) return false;
      c->push ();
      cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
      if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link)))
      {
	unsigned fd = plan.fdmap[i];
	private_dict_infos[fd].size = c->length ();
	private_dict_infos[fd].link = c->pop_pack ();
      }
      else
      {
	c->pop_discard ();
	return false;
      }
    }
  }

  /* CharStrings */
  {
    CFF2CharStrings  *cs = c->start_embed<CFF2CharStrings> ();
    if (unlikely (!cs)) return false;
    c->push ();
    if (likely (cs->serialize (c, plan.subset_charstrings)))
      plan.info.char_strings_link = c->pop_pack ();
    else
    {
      c->pop_discard ();
      return false;
    }
  }

  /* FDSelect */
  if (acc.fdSelect != &Null (CFF2FDSelect))
  {
    c->push ();
    if (likely (hb_serialize_cff_fdselect (c, num_glyphs, *(const FDSelect *)acc.fdSelect, 					      plan.orig_fdcount,
					    plan.subset_fdselect_format, plan.subset_fdselect_size,
					    plan.subset_fdselect_ranges)))
      plan.info.fd_select.link = c->pop_pack ();
    else
    {
      c->pop_discard ();
      return false;
    }
  }

  /* FDArray (FD Index) */
  {
    c->push ();
    CFF2FDArray *fda = c->start_embed<CFF2FDArray> ();
    if (unlikely (!fda)) return false;
    cff_font_dict_op_serializer_t fontSzr;
    auto it =
    + hb_zip (+ hb_iter (acc.fontDicts)
	      | hb_filter ([&] (const cff2_font_dict_values_t &_)
		{ return plan.fdmap.has (&_ - &acc.fontDicts[0]); }),
	      hb_iter (private_dict_infos))
    ;
    if (unlikely (!fda->serialize (c, it, fontSzr))) return false;
    plan.info.fd_array_link = c->pop_pack ();
  }

  /* variation store */
  if (acc.varStore != &Null (CFF2VariationStore))
  {
    c->push ();
    CFF2VariationStore *dest = c->start_embed<CFF2VariationStore> ();
    if (unlikely (!dest || !dest->serialize (c, acc.varStore))) return false;
    plan.info.var_store_link = c->pop_pack ();
  }

  OT::cff2 *cff2 = c->allocate_min<OT::cff2> ();
  if (unlikely (!cff2)) return false;

  /* header */
  cff2->version.major = 0x02;
  cff2->version.minor = 0x00;
  cff2->topDict = OT::cff2::static_size;

  /* top dict */
  {
    TopDict &dict = cff2 + cff2->topDict;
    cff2_top_dict_op_serializer_t topSzr;
    if (unlikely (!dict.serialize (c, acc.topDict, topSzr, plan.info))) return false;
    cff2->topDictSize = c->head - (const char *)&dict;
  }

  /* global subrs */
  {
    CFF2Subrs *dest = c->start_embed <CFF2Subrs> ();
    if (unlikely (!dest)) return false;
    return dest->serialize (c, plan.subset_globalsubrs);
  }
}

static bool
_hb_subset_cff2 (const OT::cff2::accelerator_subset_t  &acc,
		 hb_subset_context_t	*c)
{
  cff2_subset_plan cff2_plan;

  if (unlikely (!cff2_plan.create (acc, c->plan))) return false;
  return _serialize_cff2 (c->serializer, cff2_plan, acc, c->plan->num_output_glyphs ());
}

/**
 * hb_subset_cff2:
 * Subsets the CFF2 table according to a provided subset context.
 **/
bool
hb_subset_cff2 (hb_subset_context_t *c)
{
  OT::cff2::accelerator_subset_t acc;
  acc.init (c->plan->source);
  bool result = likely (acc.is_valid ()) && _hb_subset_cff2 (acc, c);
  acc.fini ();

  return result;
}

#endif


text:
/*
 * Copyright © 2011,2012  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Google Author(s): Behdad Esfahbod
 */

#ifndef HB_OT_MAXP_TABLE_HH
#define HB_OT_MAXP_TABLE_HH

#include "hb-open-type.hh"

namespace OT {

/*
 * maxp -- Maximum Profile
 * https://docs.microsoft.com/en-us/typography/opentype/spec/maxp
 */

#define HB_OT_TAG_maxp HB_TAG('m','a','x','p')

struct maxpV1Tail
{
  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    return_trace (c->check_struct (this));
  }

  HBUINT16 maxPoints;		  /* Maximum points in a non-composite glyph. */
  HBUINT16 maxContours;		  /* Maximum contours in a non-composite glyph. */
  HBUINT16 maxCompositePoints;	  /* Maximum points in a composite glyph. */
  HBUINT16 maxCompositeContours;  /* Maximum contours in a composite glyph. */
  HBUINT16 maxZones;		  /* 1 if instructions do not use the twilight zone (Z0),
				   * or 2 if instructions do use Z0; should be set to 2 in
				   * most cases. */
  HBUINT16 maxTwilightPoints;	  /* Maximum points used in Z0. */
  HBUINT16 maxStorage;		  /* Number of Storage Area locations. */
  HBUINT16 maxFunctionDefs;	  /* Number of FDEFs, equal to the highest function number + 1. */
  HBUINT16 maxInstructionDefs;	  /* Number of IDEFs. */
  HBUINT16 maxStackElements;	  /* Maximum stack depth. (This includes Font and CVT
				   * Programs, as well as the instructions for each glyph.) */
  HBUINT16 maxSizeOfInstructions; /* Maximum byte count for glyph instructions. */
  HBUINT16 maxComponentElements;  /* Maximum number of components referenced at
				   * "top level" for any composite glyph. */
  HBUINT16 maxComponentDepth;	  /* Maximum levels of recursion; 1 for simple components. */
 public:
  DEFINE_SIZE_STATIC (26);
};

struct maxp
{
  static constexpr hb_tag_t tableTag = HB_OT_TAG_maxp;

  unsigned int get_num_glyphs () const { return numGlyphs; }

  void set_num_glyphs (unsigned int count)
  {
    numGlyphs = count;
  }

  bool sanitize (hb_sanitize_context_t *c) const
  {
    TRACE_SANITIZE (this);
    if (unlikely (!c->check_struct (this)))
      return_trace (false);

    if (version.major == 1)
    {
      const maxpV1Tail &v1 = StructAfter<maxpV1Tail> (*this);
      return_trace (v1.sanitize (c));
    }
    return_trace (likely (version.major == 0 && version.minor == 0x5000u));
  }

  bool subset (hb_subset_context_t *c) const
  {
    TRACE_SUBSET (this);
    maxp *maxp_prime = c->serializer->embed (this);
    if (unlikely (!maxp_prime)) return_trace (false);

    maxp_prime->numGlyphs = c->plan->num_output_glyphs ();
    if (maxp_prime->version.major == 1)
    {
      const maxpV1Tail *src_v1 = &StructAfter<maxpV1Tail> (*this);
      maxpV1Tail *dest_v1 = c->serializer->embed<maxpV1Tail> (src_v1);
      if (unlikely (!dest_v1)) return_trace (false);

      if (c->plan->drop_hints)
	drop_hint_fields (dest_v1);
    }

    return_trace (true);
  }

  static void drop_hint_fields (maxpV1Tail* dest_v1)
  {
    dest_v1->maxZones = 1;
    dest_v1->maxTwilightPoints = 0;
    dest_v1->maxStorage = 0;
    dest_v1->maxFunctionDefs = 0;
    dest_v1->maxInstructionDefs = 0;
    dest_v1->maxStackElements = 0;
    dest_v1->maxSizeOfInstructions = 0;
  }

  protected:
  FixedVersion<>version;/* Version of the maxp table (0.5 or 1.0),
			 * 0x00005000u or 0x00010000u. */
  HBUINT16	numGlyphs;
			/* The number of glyphs in the font. */
/*maxpV1Tail	v1Tail[HB_VAR_ARRAY]; */
  public:
  DEFINE_SIZE_STATIC (6);
};

} /* namespace OT */

#endif /* HB_OT_MAXP_TABLE_HH */


text:
/*
 * Copyright © 2018  Ebrahim Byagowi
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */

#ifndef HB_AAT_H
#define HB_AAT_H
#define HB_AAT_H_IN

#include "hb.h"

#include "hb-aat-layout.h"

HB_BEGIN_DECLS

HB_END_DECLS

#undef HB_AAT_H_IN
#endif /* HB_AAT_H */