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 \
	$(NULL)

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 \
	$(NULL)
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 \
	$(NULL)

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 \
	$(NULL)

# 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 \
	$(NULL)

HB_SUBSET_headers = \
	hb-subset.h \
	$(NULL)

HB_GOBJECT_DIST_sources = hb-gobject-structs.cc
HB_GOBJECT_DIST_headers = hb-gobject.h hb-gobject-structs.h
HB_GOBJECT_ENUM_sources = hb-gobject-enums.cc
HB_GOBJECT_ENUM_headers = hb-gobject-enums.h
HB_GOBJECT_NODIST_sources = $(HB_GOBJECT_ENUM_sources)
HB_GOBJECT_NODIST_headers = $(HB_GOBJECT_ENUM_headers)
HB_GOBJECT_sources = $(HB_GOBJECT_DIST_sources) $(HB_GOBJECT_NODIST_sources)
HB_GOBJECT_headers = $(HB_GOBJECT_DIST_headers) $(HB_GOBJECT_NODIST_headers)


text:
project('graphite2', 'cpp')
library_version = '1.3.14'

src = [
	'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/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'
]

cpp_args = [
	'-DGRAPHITE2_STATIC',
	'-DGRAPHITE2_NTRACING',
	'-DGRAPHITE2_NFILEFACE'
]

if meson.get_compiler('cpp').get_id() != 'msvc'
	cpp_args += [
		'-std=c++11',
		'-Wno-unknown-pragmas',
		'-fno-rtti',
		'-fno-exceptions'
	]
	src += ['src/direct_machine.cpp']
else
	src += ['src/call_machine.cpp']
endif

graphite2 = static_library(
	'graphite2',
	src,
	cpp_args : cpp_args,
	include_directories : include_directories('src', 'include'),
	pic : true
)

graphite2_dep = declare_dependency(
	include_directories : include_directories('include'),
	link_with : graphite2
)

repo_language: C

mime_type: text/x-c


text:
#!/usr/bin/env python

Import('env')
Import('env_modules')

from compat import isbasestring

env_graphite2 = env_modules.Clone()

# Thirdparty source files
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/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'
]

env_graphite2.Append(CPPPATH=['src', 'include'])

env_graphite2.Append(CCFLAGS=[
    '-DGRAPHITE2_STATIC',
    '-DGRAPHITE2_NTRACING',
    '-DGRAPHITE2_NFILEFACE'
])

if (not env_graphite2.msvc):
    env_graphite2.Append(CXXFLAGS=[
        '-std=c++11',
        '-fno-rtti',
        '-fno-exceptions'
    ])
    sources += ['src/direct_machine.cpp']
else:
    sources += ['src/call_machine.cpp']

env_graphite2.disable_warnings()

env_thirdparty = env_graphite2.Clone()
env_thirdparty.disable_warnings()
lib = env_thirdparty.add_library("graphite2_builtin", 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 isbasestring(linklib): # 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])


text:
def can_build(env, platform):
    return True

def configure(env):
    pass


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


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


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


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


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


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


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


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


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


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


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


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