/* metrics.{cc,hh} -- an encoding during and after OpenType features
 *
 * Copyright (c) 2003-2018 Eddie Kohler
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of 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 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 General
 * Public License for more details.
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "metrics.hh"
#include "dvipsencoding.hh"
#include "util.hh"
#include "glyphfilter.hh"
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <lcdf/straccum.hh>

Metrics::Metrics(const Efont::CharstringProgram *font, int nglyphs)
    : _boundary_glyph(nglyphs), _emptyslot_glyph(nglyphs + 1),
      _design_units(1000), _units_per_em(font->units_per_em()),
      _liveness_marked(false)
{
    _encoding.assign(256, Char());
    add_mapped_font(font, String());
}

Metrics::~Metrics()
{
    for (Char *c = _encoding.begin(); c != _encoding.end(); c++)
        delete c->virtual_char;
}

int
Metrics::add_mapped_font(const Efont::CharstringProgram *font, const String &name)
{
    _mapped_fonts.push_back(font);
    _mapped_font_names.push_back(name);
    return _mapped_fonts.size() - 1;
}

void
Metrics::check() const
{
    // check invariants
    // 1. all 'ligatures' entries refer to valid characters
    // 2. all 'ligatures' entries with 'in1 == c' are in '_encoding[c].ligs'
    // 3. 'virtual_char' SHOW operations point to valid non-virtual chars
    for (int code = 0; code < _encoding.size(); code++) {
        const Char *ch = &_encoding[code];
        assert((ch->virtual_char != 0) == (ch->glyph == VIRTUAL_GLYPH));
        for (const Ligature *l = ch->ligatures.begin(); l != ch->ligatures.end(); l++)
            assert(valid_code(l->in2) && valid_code(l->out));
        for (const Kern *k = ch->kerns.begin(); k != ch->kerns.end(); k++)
            assert(valid_code(k->in2));
        if (const VirtualChar *vc = ch->virtual_char) {
            assert(vc->name);
            int font_number = 0;
            for (const Setting *s = vc->setting.begin(); s != vc->setting.end(); s++) {
                assert(s->valid_op());
                if (s->op == Setting::SHOW && font_number == 0)
                    assert(nonvirtual_code(s->x));
                else if (s->op == Setting::FONT)
                    font_number = s->x;
            }
        }
        assert(ch->built_in1 < 0 || valid_code(ch->built_in1));
        assert(ch->built_in2 < 0 || valid_code(ch->built_in2));
        assert((ch->built_in1 >= 0) == (ch->built_in2 >= 0));
        assert(ch->base_code < 0 || valid_code(ch->base_code));
        if (valid_code(ch->base_code)) {
            const Char *ch2 = &_encoding[ch->base_code];
            assert((!ch->virtual_char && ch->glyph)
                   || (!ch2->virtual_char && ch2->glyph));
        }
        if (ch->flag(Char::CONTEXT_ONLY))
            assert(ch->virtual_char && ch->built_in1 >= 0 && ch->built_in2 >= 0);
        if (ch->flag(Char::CONTEXT_ONLY))
            assert(ch->flag(Char::LIVE));
    }
}

PermString
Metrics::code_name(Code code) const
{
    if (code < 0 || code >= _encoding.size())
        return permprintf("<badcode%d>", code);
    else {
        const Char &ch = _encoding[code];
        if (ch.virtual_char)
            return ch.virtual_char->name;
        else if (ch.glyph == _boundary_glyph)
            return "<boundary>";
        else if (ch.glyph == _emptyslot_glyph)
            return "<emptyslot>";
        else if (ch.glyph >= 0 && ch.glyph < _mapped_fonts[0]->nglyphs())
            return _mapped_fonts[0]->glyph_name(ch.glyph);
        else
            return permprintf("<glyph%d>", ch.glyph);
    }
}


/*****************************************************************************/
/* encoding                                                                  */

Metrics::Code
Metrics::unicode_encoding(uint32_t uni) const
{
    for (const Char *ch = _encoding.begin(); ch < _encoding.end(); ch++)
        if (ch->unicode == uni)
            return ch - _encoding.begin();
    return -1;
}

Metrics::Code
Metrics::hard_encoding(Glyph g, Code after) const
{
    if (g < 0)
        return -1;
    int answer = -1, n = 0;
    for (int i = _encoding.size() - 1; i >= after; i--)
        if (_encoding[i].glyph == g)
            answer = i, n++;
    if (n < 2 && after == 0) {
        if (g >= _emap.size())
            _emap.resize(g + 1, -2);
        _emap[g] = answer;
    }
    return answer;
}

Metrics::Code
Metrics::force_encoding(Glyph g, int lookup_source)
{
    assert(g >= 0);
    int e = encoding(g, 0);
    if (e >= 0)
        return e;
    else {
        Char ch;
        ch.glyph = g;
        ch.base_code = _encoding.size();
        ch.lookup_source = lookup_source;
        _encoding.push_back(ch);
        assign_emap(g, ch.base_code);
        return ch.base_code;
    }
}

void
Metrics::encode(Code code, uint32_t uni, Glyph g)
{
    assert(code >= 0 && g >= 0 && g != VIRTUAL_GLYPH);
    if (code >= _encoding.size())
        _encoding.resize(code + 1, Char());
    _encoding[code].unicode = uni;
    _encoding[code].glyph = g;
    if (g > 0)
        _encoding[code].base_code = code;
    assert(!_encoding[code].virtual_char);
    assign_emap(g, code);
}

void
Metrics::encode_virtual(Code code, PermString name, uint32_t uni, const Vector<Setting> &v, bool base_char)
{
    assert(code >= 0 && v.size() > 0);
    if (code >= _encoding.size())
        _encoding.resize(code + 1, Char());
    _encoding[code].unicode = uni;
    _encoding[code].glyph = VIRTUAL_GLYPH;
    if (base_char)
        _encoding[code].flags |= Char::BASE_REP;
    assert(!_encoding[code].virtual_char);
    VirtualChar *vc = _encoding[code].virtual_char = new VirtualChar;
    vc->name = name;
    vc->setting = v;
    int font_number = 0;
    for (Setting *s = vc->setting.begin(); s != vc->setting.end(); s++) {
        assert(s->valid_op() && (s->op != Setting::SHOW || font_number != 0 || nonvirtual_code(s->x)));
        if (s->op == Setting::FONT)
            font_number = s->x;
    }
}

void
Metrics::apply_base_encoding(const String &font_name, const DvipsEncoding &dvipsenc, const Vector<int> &mapping)
{
    int font_number = -1;
    for (Char *c = _encoding.begin(); c != _encoding.end(); c++)
        if (c->glyph > 0 && !c->virtual_char && c->glyph < mapping.size()
            && mapping[c->glyph] >= 0) {
            if (font_number < 0)
                font_number = add_mapped_font(mapped_font(0), font_name);
            VirtualChar *vc = c->virtual_char = new VirtualChar;
            vc->name = dvipsenc.encoding(mapping[c->glyph]);
            vc->setting.push_back(Setting(Setting::FONT, font_number));
            vc->setting.push_back(Setting(Setting::SHOW, mapping[c->glyph], c->glyph));
            c->glyph = VIRTUAL_GLYPH;
            c->base_code = -1;
            c->flags = (c->flags & ~Char::BASE_LIVE) | Char::BASE_REP;
        }
}

void
Metrics::add_altselector_code(Code code, int altselector_type)
{
    for (Kern *k = _altselectors.begin(); k != _altselectors.end(); k++)
        if (k->in2 == code) {
            k->kern = altselector_type;
            return;
        }
    _altselectors.push_back(Kern(code, altselector_type));
}

bool
Metrics::base_glyphs(Vector<Glyph> &v, int size) const
{
    bool any = false;
    v.assign(_encoding.size(), 0);
    for (const Char *ch = _encoding.begin(); ch != _encoding.end(); ch++)
        if (ch->base_code >= 0 && ch->base_code < size) {
            v[ch->base_code] = ch->glyph;
            any = true;
        }
    return any;
}


/*****************************************************************************/
/* Char methods                                                              */

void
Metrics::Char::clear()
{
    glyph = 0;
    base_code = -1;
    unicode = 0;
    ligatures.clear();
    kerns.clear();
    delete virtual_char;
    virtual_char = 0;
    pdx = pdy = adx = 0;
    built_in1 = built_in2 = -1;
    lookup_source = -1;
    flags = 0;
}

void
Metrics::Char::swap(Char &c)
{
    std::swap(glyph, c.glyph);
    // NB: only a partial switch of base_code!!
    if (base_code < 0)
        base_code = c.base_code;
    c.base_code = -1;
    std::swap(unicode, c.unicode);
    ligatures.swap(c.ligatures);
    kerns.swap(c.kerns);
    std::swap(virtual_char, c.virtual_char);
    std::swap(pdx, c.pdx);
    std::swap(pdy, c.pdy);
    std::swap(adx, c.adx);
    std::swap(built_in1, c.built_in1);
    std::swap(built_in2, c.built_in2);
    std::swap(lookup_source, c.lookup_source);
    std::swap(flags, c.flags);
}


/*****************************************************************************/
/* manipulating ligature lists                                               */

Metrics::Ligature *
Metrics::ligature_obj(Code code1, Code code2)
{
    assert(valid_code(code1) && valid_code(code2));
    Char &ch = _encoding[code1];
    for (Ligature *l = ch.ligatures.begin(); l != ch.ligatures.end(); l++)
        if (l->in2 == code2)
            return l;
    return 0;
}

inline void
Metrics::new_ligature(Code in1, Code in2, Code out)
{
    assert(valid_code(in1) && valid_code(in2) && valid_code(out));
    _encoding[in1].ligatures.push_back(Ligature(in2, out));
}

inline void
Metrics::repoint_ligature(Code, Ligature *l, Code out)
{
    l->out = out;
}

void
Metrics::add_ligature(Code in1, Code in2, Code out)
{
    if (Ligature *l = ligature_obj(in1, in2)) {
        Char &ch = _encoding[l->out];
        if (ch.flags & Char::BUILT) {
            // move old ligatures to point to the true ligature
            for (Ligature *ll = ch.ligatures.begin(); ll != ch.ligatures.end(); ll++)
                add_ligature(out, ll->in2, ll->out);
            repoint_ligature(in1, l, out);
        }
    } else
        new_ligature(in1, in2, out);
}

Metrics::Code
Metrics::pair_code(Code in1, Code in2, int lookup_source)
{
    if (const Ligature *l = ligature_obj(in1, in2)) {
        if (lookup_source < 0)
            _encoding[l->out].flags &= ~Char::INTERMEDIATE;
        return l->out;
    } else {
        Char ch;
        ch.glyph = VIRTUAL_GLYPH;
        ch.flags = Char::BUILT | (lookup_source >= 0 ? Char::INTERMEDIATE : 0);
        VirtualChar *vc = ch.virtual_char = new VirtualChar;
        vc->name = permprintf("%s__%s", code_str(in1), code_str(in2));
        setting(in1, vc->setting, SET_INTERMEDIATE);
        vc->setting.push_back(Setting(Setting::KERN));
        setting(in2, vc->setting, SET_INTERMEDIATE);
        ch.built_in1 = in1;
        ch.built_in2 = in2;
        ch.lookup_source = lookup_source;
        _encoding.push_back(ch);
        new_ligature(in1, in2, _encoding.size() - 1);
        return _encoding.size() - 1;
    }
}

void
Metrics::remove_ligatures(Code in1, Code in2)
{
    if (in1 == CODE_ALL) {
        for (in1 = 0; in1 < _encoding.size(); in1++)
            remove_ligatures(in1, in2);
    } else {
        Char &ch = _encoding[in1];
        if (in2 == CODE_ALL)
            ch.ligatures.clear();
        else if (Ligature *l = ligature_obj(in1, in2)) {
            *l = ch.ligatures.back();
            ch.ligatures.pop_back();
        }
    }
}


/*****************************************************************************/
/* manipulating kern lists                                                   */

Metrics::Kern *
Metrics::kern_obj(Code in1, Code in2)
{
    assert(valid_code(in1) && valid_code(in2));
    Char &ch = _encoding[in1];
    for (Kern *k = ch.kerns.begin(); k != ch.kerns.end(); k++)
        if (k->in2 == in2)
            return k;
    return 0;
}

int
Metrics::kern(Code in1, Code in2) const
{
    assert(valid_code(in1) && valid_code(in2));
    const Char &ch = _encoding[in1];
    for (const Kern *k = ch.kerns.begin(); k != ch.kerns.end(); k++)
        if (k->in2 == in2)
            return k->kern;
    return 0;
}

void
Metrics::add_kern(Code in1, Code in2, int kern)
{
    if (Kern *k = kern_obj(in1, in2))
        k->kern += kern;
    else
        _encoding[in1].kerns.push_back(Kern(in2, kern));
}

void
Metrics::set_kern(Code in1, Code in2, int kern)
{
    if (in1 == CODE_ALL) {
        for (in1 = 0; in1 < _encoding.size(); in1++)
            set_kern(in1, in2, kern);
    } else {
        Char &ch = _encoding[in1];
        if (in2 == CODE_ALL) {
            assert(kern == 0);
            ch.kerns.clear();
        } else if (Kern *k = kern_obj(in1, in2)) {
            if (kern == 0) {
                *k = ch.kerns.back();
                ch.kerns.pop_back();
            } else
                k->kern = kern;
        } else if (kern != 0)
            ch.kerns.push_back(Kern(in2, kern));
    }
}

int
Metrics::reencode_right_ligkern(Code old_in2, Code new_in2)
{
    int nchanges = 0;
    for (Char *ch = _encoding.begin(); ch != _encoding.end(); ch++) {
        for (Ligature *l = ch->ligatures.begin(); l != ch->ligatures.end(); l++)
            if (l->in2 == old_in2) {
                if (new_in2 >= 0)
                    l->in2 = new_in2;
                else {
                    *l = ch->ligatures.back();
                    ch->ligatures.pop_back();
                    l--;
                }
                nchanges++;
            }
        for (Kern *k = ch->kerns.begin(); k != ch->kerns.end(); k++)
            if (k->in2 == old_in2) {
                if (new_in2 >= 0)
                    k->in2 = new_in2;
                else {
                    *k = ch->kerns.back();
                    ch->kerns.pop_back();
                    k--;
                }
                nchanges++;
            }
        // XXX?
        if (ch->context_setting(-1, old_in2) && new_in2 >= 0 && ch->built_in1 >= 0)
            ch->built_in2 = new_in2;
    }
    return nchanges;
}


/*****************************************************************************/
/* positioning                                                               */

void
Metrics::add_single_positioning(Code c, int pdx, int pdy, int adx)
{
    assert(valid_code(c));
    Char &ch = _encoding[c];
    ch.pdx += pdx;
    ch.pdy += pdy;
    ch.adx += adx;
}


/*****************************************************************************/
/* changed_context structure                                                 */

class Metrics::ChangedContext { public:
    ChangedContext(int ncodes);
    ~ChangedContext();
    typedef Metrics::Code Code;

    enum Context { CH_NONE = 0, CH_SOME = 1, CH_ALL = 2 };
    bool allowed(Code, bool left_context) const;
    bool pair_allowed(Code, Code) const;
    bool virgin(Code) const;
    void disallow(Code);
    void disallow_pair(Code, Code);
  private:
    Vector<Vector<uint32_t> *> _v;
    int _initial_ncodes;
    mutable Vector<uint32_t> _all_sentinel;
    ChangedContext(const ChangedContext &);
    ChangedContext &operator=(const ChangedContext &);
    static inline bool bit(const Vector<uint32_t> &, Code);
    inline void ensure_all(Code) const;
};

Metrics::ChangedContext::ChangedContext(int ncodes)
    : _v(ncodes, 0), _initial_ncodes(ncodes), _all_sentinel(((ncodes - 1) >> 5) + 1, 0xFFFFFFFFU)
{
}

Metrics::ChangedContext::~ChangedContext()
{
    for (Vector<uint32_t> **v = _v.begin(); v != _v.end(); v++)
        if (*v != &_all_sentinel)
            delete *v;
}

inline void
Metrics::ChangedContext::ensure_all(Code c) const
{
    if (c >= 0 && (c >> 5) >= _all_sentinel.size())
        _all_sentinel.resize((c >> 5) + 1, 0xFFFFFFFFU);
}

inline bool
Metrics::ChangedContext::bit(const Vector<uint32_t> &v, Code c)
{
    if (c < 0 || (c >> 5) >= v.size())
        return false;
    else
        return (v[c >> 5] & (1 << (c & 0x1F))) != 0;
}

bool
Metrics::ChangedContext::allowed(Code c, bool left_context) const
{
    if (c < 0)
        return false;
    else if (c >= _v.size())
        return left_context;
    else
        return (_v[c] != &_all_sentinel);
}

bool
Metrics::ChangedContext::pair_allowed(Code c1, Code c2) const
{
    ensure_all(c2);
    if (c1 < 0 || c2 < 0)
        return false;
    else if (c1 >= _v.size() || c2 >= _v.size() || !_v[c1])
        return true;
    else
        return !bit(*_v[c1], c2);
}

bool
Metrics::ChangedContext::virgin(Code c) const
{
    return (c >= 0 && (c >= _v.size() || _v[c] == 0));
}

void
Metrics::ChangedContext::disallow(Code c)
{
    assert(c >= 0);
    if (c >= _v.size())
        _v.resize(c + 1, 0);
    if (_v[c] != &_all_sentinel) {
        delete _v[c];
        _v[c] = &_all_sentinel;
    }
}

void
Metrics::ChangedContext::disallow_pair(Code c1, Code c2)
{
    assert(c1 >= 0 && c2 >= 0);
    if (c1 >= _v.size())
        _v.resize(c1 + 1, 0);
    if (!_v[c1])
        _v[c1] = new Vector<uint32_t>;
    if (_v[c1] != &_all_sentinel) {
        if ((c2 >> 5) >= _v[c1]->size())
            _v[c1]->resize((c2 >> 5) + 1, 0);
        (*_v[c1])[c2 >> 5] |= 1 << (c2 & 0x1F);
    }
}


/*****************************************************************************/
/* applying GSUB substitutions                                               */

void
Metrics::apply_single(Code cin, const Substitution *s, int lookup,
                ChangedContext &ctx, const GlyphFilter &glyph_filter,
                const Vector<PermString> &glyph_names)
{
    // check if encoded
    if (!ctx.allowed(cin, false))
        /* not encoded before this substitution began, or completely changed;
           ingore */
        return;

    // check if substitution of this code allowed
    if (!glyph_filter.allow_substitution(s->in_glyph(), glyph_names, unicode(cin)))
        return;

    // look for an allowed alternate
    Glyph out = -1;
    for (int i = 0; out < 0 && i < s->out_nglyphs(); i++)
        if (glyph_filter.allow_alternate(s->out_glyph(i), glyph_names, unicode(cin)))
            out = s->out_glyph(i);
    if (out < 0)                // no allowed alternate
        return;

    // apply substitution
    if (ctx.virgin(cin)) {
        // no one has changed this glyph yet, change it unilaterally
        assign_emap(s->in_glyph(), -2);
        assign_emap(out, cin);
        assert(!_encoding[cin].virtual_char);
        _encoding[cin].glyph = out;
    } else {
        // some contextual substitutions have changed this glyph, add
        // contextual substitutions for the remaining possibilities
        Code cout = force_encoding(out, lookup);
        for (Code right = 0; right < _encoding.size(); right++)
            if (_encoding[right].visible() && !_encoding[right].flag(Char::BUILT) && ctx.pair_allowed(cin, right)) {
                Code pair = pair_code(cout, right, lookup);
                _encoding[cout].flags &= ~Char::INTERMEDIATE;
                add_ligature(cin, right, pair);
            }
    }

    // no more substitutions for cin
    ctx.disallow(cin);
}

void
Metrics::apply_ligature(const Vector<Code> &in, const Substitution *s, int lookup)
{
    // build up the character pair
    int cin1 = in[0];
    for (const Code *inp = in.begin() + 1; inp < in.end() - 1; inp++)
        cin1 = pair_code(cin1, *inp, lookup);
    int cin2 = in.back();

    // build up the output character
    Vector<Code> out;
    s->all_out_glyphs(out);
    int cout = -1;
    for (Glyph *outp = out.begin(); outp < out.end(); outp++) {
        *outp = force_encoding(*outp, lookup);
        cout = (cout < 0 ? *outp : pair_code(cout, *outp, lookup));
    }
    _encoding[cout].flags &= ~Char::INTERMEDIATE;

    // check for replacing a fake ligature
    int old_out = -1;
    if (Ligature *l = ligature_obj(cin1, cin2)) {
        if (l->out == cout)     // already created this same ligature
            return;
        if (_encoding[l->out].flags & Char::BUILT)
            old_out = l->out;
    }

    // make the final ligature
    add_ligature(cin1, cin2, cout);

    //fprintf(stderr, "%s : %d/%s %d/%s => %d/%s [was %d/%s]\n", s->unparse().c_str(), cin1, code_str(cin1), cin2, code_str(cin2), cout, code_str(cout), old_out, code_str(old_out));

    // if appropriate, swap old ligatures to point to the new result
    if (old_out >= 0)
        for (Char *ch = _encoding.begin(); ch != _encoding.end(); ch++)
            for (Ligature *l = ch->ligatures.begin(); l != ch->ligatures.end(); l++)
                if (l->out == old_out)
                    repoint_ligature(ch - _encoding.begin(), l, cout);
}

void
Metrics::apply_simple_context_ligature(const Vector<Code> &codes,
                        const Substitution *s, int lookup, ChangedContext &ctx)
{
    int nleft = s->left_nglyphs(), nin = s->in_nglyphs();
    assert(codes.size() >= 2);

    // check if context allows substitutions
    for (const Code *inp = codes.begin(); inp < codes.end(); ++inp)
        if (!ctx.allowed(*inp, inp - codes.begin() < nleft))
            return;

    // check if any part of the combination has already changed
    int ncheck = nleft + (nin > 2 ? 2 : nin);
    if (ncheck == codes.size())
        --ncheck;
    for (const Code *inp = codes.begin(); inp < codes.begin() + ncheck; ++inp)
        if (!ctx.pair_allowed(inp[0], inp[1]))
            return;

    // mark this combination as changed if appropriate
    if (codes.size() == 2 && nin == 1)
        ctx.disallow_pair(codes[0], codes[1]);

    // actually apply ligature
    apply_ligature(codes, s, lookup);
}

bool
Metrics::next_encoding(Vector<Code> &codes, const Vector<Glyph> &glyphs) const
{
    if (!codes.size()) {
        codes.assign(glyphs.size(), 0);
        for (int i = 0; i < glyphs.size(); ++i)
            if ((codes[i] = encoding(glyphs[i], 0)) < 0)
                return false;
        return true;
    } else {
        for (int i = 0; i < glyphs.size(); ++i)
            if ((codes[i] = encoding(glyphs[i], codes[i] + 1)) >= 0)
                return true;
            else
                codes[i] = encoding(glyphs[i], 0);
        return false;
    }
}

int
Metrics::apply(const Vector<Substitution>& sv, bool allow_single, int lookup, const GlyphFilter& glyph_filter, const Vector<PermString>& glyph_names)
{
    Vector<Glyph> glyphs;
    Vector<Code> codes;

    // keep track of what substitutions we have performed
    ChangedContext ctx(_encoding.size());

    // loop over substitutions
    int failures = 0;
    for (const Substitution *s = sv.begin(); s != sv.end(); s++) {
        bool is_single = s->is_single() || s->is_alternate();
        bool is_apply_single = is_single && allow_single;
        bool is_apply_simple_context_ligature = !is_single && !s->is_multiple() && s->is_simple_context();

        if (is_apply_single || is_apply_simple_context_ligature) {
            s->all_in_glyphs(glyphs);
            for (codes.clear(); next_encoding(codes, glyphs); ) {
                if (is_apply_single)
                    apply_single(codes[0], s, lookup, ctx, glyph_filter, glyph_names);
                else
                    apply_simple_context_ligature(codes, s, lookup, ctx);
            }
        } else
            failures++;
    }

    return sv.size() - failures;
}

void
Metrics::apply_alternates_single(Code cin, const Substitution *s, int lookup,
                const GlyphFilter &glyph_filter,
                const Vector<PermString> &glyph_names)
{
    for (const Kern *as = _altselectors.begin(); as != _altselectors.end(); as++)
        if (as->kern == 0) {
            Code last = cin;
            uint32_t u = unicode(cin);
            for (int i = 0; i < s->out_nglyphs(); i++)
                if (glyph_filter.allow_alternate(s->out_glyph(i), glyph_names, u)) {
                    Code out = force_encoding(s->out_glyph(i), lookup);
                    add_ligature(last, as->in2, out);
                    last = out;
                }
        } else if (as->kern <= s->out_nglyphs()) {
            Code out = force_encoding(s->out_glyph(as->kern - 1), lookup);
            add_ligature(cin, as->in2, out);
        }
}

void
Metrics::apply_alternates_ligature(const Vector<Code> &codes,
                const Substitution *s, int lookup,
                const GlyphFilter &glyph_filter,
                const Vector<PermString> &glyph_names)
{
    // check whether the output character is allowed
    if (!glyph_filter.allow_alternate(s->out_glyph(), glyph_names, 0))
        return;

    // find alternate selector and apply ligature if appropriate
    for (const Kern *as = _altselectors.begin(); as != _altselectors.end(); as++)
        if (as->kern == 0) {
            Vector<Code> lig(codes);
            lig.insert(lig.begin() + 1, as->in2);
            apply_ligature(lig, s, lookup);
        }
}

void
Metrics::apply_alternates(const Vector<Substitution>& sv, int lookup, const GlyphFilter& glyph_filter, const Vector<PermString>& glyph_names)
{
    Vector<Glyph> glyphs;
    Vector<Code> codes;

    for (const Substitution *s = sv.begin(); s != sv.end(); s++) {
        bool is_single = s->is_single() || s->is_alternate();
        if (is_single || s->is_ligature()) {
            s->all_in_glyphs(glyphs);
            for (codes.clear(); next_encoding(codes, glyphs); ) {
                if (is_single)
                    apply_alternates_single(codes[0], s, lookup, glyph_filter, glyph_names);
                else
                    apply_alternates_ligature(codes, s, lookup, glyph_filter, glyph_names);
            }
        }
    }
}


/*****************************************************************************/
/* applying GPOS positionings                                                */

static bool                     // returns old value
assign_bitvec(int*& bitvec, int e, int n)
{
    if (e >= 0 && e < n) {
        if (!bitvec) {
            bitvec = new int[((n - 1) >> 5) + 1];
            memset(bitvec, 0, sizeof(int) * (((n - 1) >> 5) + 1));
        }
        bool result = (bitvec[e >> 5] & (1 << (e & 0x1F))) != 0;
        bitvec[e >> 5] |= (1 << (e & 0x1F));
        return result;
    } else
        return false;
}

int
Metrics::apply(const Vector<Positioning>& pv)
{
    // keep track of what substitutions we have performed
    int *single_changed = 0;
    Vector<int *> pair_changed(_encoding.size(), 0);
    Vector<Glyph> glyphs;
    Vector<Code> codes;

    // loop over substitutions
    int success = 0;
    for (const Positioning *p = pv.begin(); p != pv.end(); p++) {
        bool is_single = p->is_single();
        if (is_single || p->is_pairkern()) {
            p->all_in_glyphs(glyphs);
            for (codes.clear(); next_encoding(codes, glyphs); )
                if (is_single) {
                    if (!assign_bitvec(single_changed, codes[0], _encoding.size())) {
                        _encoding[codes[0]].pdx += p->left().pdx;
                        _encoding[codes[0]].pdy += p->left().pdy;
                        _encoding[codes[0]].adx += p->left().adx;
                    }
                } else {
                    if (!assign_bitvec(pair_changed[codes[0]], codes[1], _encoding.size()))
                        add_kern(codes[0], codes[1], p->left().adx);
                }
            success++;
        }
    }

    delete[] single_changed;
    for (int i = 0; i < pair_changed.size(); i++)
        delete[] pair_changed[i];
    return success;
}


/*****************************************************************************/
/* liveness marking, Ligature3s                                              */

String Metrics::Ligature3::unparse(const Metrics& m) const {
    StringAccum sa;
    sa << '[' << m.code_name(in1) << ' ' << m.code_name(in2)
       << " -> " << m.code_name(out) << ']';
    return sa.take_string();
}

inline bool
operator<(const Metrics::Ligature3 &l1, const Metrics::Ligature3 &l2)
{
    // topological < : is l1's output one of l2's inputs?
    if (l1.out == l2.in1 || l1.out == l2.in2)
        return true;
    else
        return l1.in1 < l2.in1
            || (l1.in1 == l2.in1 && (l1.in2 < l2.in2
                                     || (l1.in2 == l2.in2 && l1.out < l2.out)));
}

void
Metrics::all_ligatures(Vector<Ligature3> &all_ligs) const
{
    /* Develop a topologically-sorted ligature list. */
    all_ligs.clear();
    for (Code code = 0; code < _encoding.size(); code++)
        for (const Ligature *l = _encoding[code].ligatures.begin(); l != _encoding[code].ligatures.end(); l++)
            all_ligs.push_back(Ligature3(code, l->in2, l->out));
    std::sort(all_ligs.begin(), all_ligs.end());
}

void
Metrics::mark_liveness(int size, const Vector<Ligature3> *all_ligs)
{
    _liveness_marked = true;
    bool changed;

    // make sure we have ligatures
    Vector<Ligature3> my_ligs;
    if (!all_ligs) {
        all_ligatures(my_ligs);
        all_ligs = &my_ligs;
    }

    /* Characters below 'size' are in both virtual and base encodings. */
    for (Char *ch = _encoding.begin(); ch < _encoding.begin() + size; ch++)
        if (ch->visible())
            ch->flags |= Char::LIVE | (ch->virtual_char ? 0 : Char::BASE_LIVE);

    /* Characters reachable from live chars by live ligatures are live. */
  redo_live_reachable:
    for (const Ligature3 *l = all_ligs->begin(); l != all_ligs->end(); l++)
        if (_encoding[l->in1].flag(Char::LIVE) && _encoding[l->in2].flag(Char::LIVE)) {
            Char &ch = _encoding[l->out];
            if (!ch.flag(Char::LIVE))
                ch.flags |= Char::LIVE | Char::CONTEXT_ONLY | (ch.virtual_char ? 0 : Char::BASE_LIVE);
            if (ch.flag(Char::CONTEXT_ONLY) && !ch.context_setting(l->in1, l->in2))
                ch.flags &= ~Char::CONTEXT_ONLY;
        }

    /* Characters reachable from context-only ligatures are live. */
    changed = false;
    for (Char *ch = _encoding.begin(); ch != _encoding.end(); ch++)
        if (ch->flag(Char::CONTEXT_ONLY)) {
            Char &ch1 = _encoding[ch->built_in1];
            Char &ch2 = _encoding[ch->built_in2];
            if (!ch1.flag(Char::LIVE) || !ch2.flag(Char::LIVE)) {
                ch1.flags |= Char::LIVE;
                ch2.flags |= Char::LIVE;
                changed = true;
            }
        }
    if (changed)
        goto redo_live_reachable;

    /* Characters reachable from live settings are base-live. */
    for (Char *ch = _encoding.begin(); ch != _encoding.end(); ch++)
        if (ch->flag(Char::LIVE))
            if (VirtualChar *vc = ch->virtual_char) {
                int font_number = 0;
                for (Setting *s = vc->setting.begin(); s != vc->setting.end(); s++)
                    if (s->op == Setting::SHOW && font_number == 0
                        && _encoding[s->x].base_code >= 0)
                        _encoding[s->x].flags |= Char::BASE_LIVE;
                    else if (s->op == Setting::FONT)
                        font_number = s->x;
            }
}

void
Metrics::reencode(const Vector<Code> &reencoding)
{
    for (Char *ch = _encoding.begin(); ch != _encoding.end(); ch++) {
        for (Ligature *l = ch->ligatures.begin(); l != ch->ligatures.end(); l++) {
            l->in2 = reencoding[l->in2];
            l->out = reencoding[l->out];
        }
        for (Kern *k = ch->kerns.begin(); k != ch->kerns.end(); k++)
            k->in2 = reencoding[k->in2];
        if (VirtualChar *vc = ch->virtual_char) {
            int font_number = 0;
            for (Setting *s = vc->setting.begin(); s != vc->setting.end(); s++)
                if (s->op == Setting::SHOW && font_number == 0)
                    s->x = reencoding[s->x];
                else if (s->op == Setting::FONT)
                    font_number = s->x;
        }
        if (ch->built_in1 >= 0) {
            ch->built_in1 = reencoding[ch->built_in1];
            ch->built_in2 = reencoding[ch->built_in2];
        }
        if (ch->base_code >= 0)
            ch->base_code = reencoding[ch->base_code];
    }
    _emap.clear();
}


/*****************************************************************************/
/* shrinking the encoding                                                    */

bool
Metrics::Char::context_setting(Code in1, Code in2) const
{
    // return true iff this character could represent the context setting of
    // 'in1' and 'in2'
    if (!virtual_char || ligatures.size())
        return false;
    else
        return (in1 == built_in1 || in2 == built_in2);
}

void
Metrics::cut_encoding(int size)
{
    /* Function makes it so that characters below 'size' do not point to
       characters above 'size', except for context ligatures. */

    /* Change "emptyslot"s to ".notdef"s. */
    for (Char *ch = _encoding.begin(); ch != _encoding.end(); ch++)
        if (ch->glyph == emptyslot_glyph()) {
            ch->glyph = 0;
            ch->base_code = -1;
            // 21.Feb.2007: Character isn't live any more.
            ch->flags &= ~(Char::BASE_LIVE | Char::LIVE);
        }

    /* Maybe we don't need to do anything else. */
    if (_encoding.size() <= size) {
        _encoding.resize(size, Char());
        return;
    }

    /* Need liveness markings. */
    if (!_liveness_marked)
        mark_liveness(size);

    /* Characters below 'size' are 'good'.
       Characters above 'size' are not 'good'. */
    Vector<int> good(_encoding.size(), 1);
    for (Code c = size; c < _encoding.size(); c++)
        good[c] = 0;

    /* Characters encoded via base_code are 'good', though. */
    for (Char *ch = _encoding.begin(); ch < _encoding.begin() + size; ch++)
        if (ch->base_code >= size)
            good[ch->base_code] = 1;

    /* Some fake characters might point beyond 'size'; remove them too. No
       need for a multipass algorithm since virtual chars never point to
       virtual chars. */
    for (Code c = 0; c < _encoding.size(); c++) {
        if (VirtualChar *vc = _encoding[c].virtual_char) {
            int font_number = 0;
            for (Setting *s = vc->setting.begin(); s != vc->setting.end(); s++)
                if (s->op == Setting::SHOW && font_number == 0 && !good[s->x]) {
                    _encoding[c].clear();
                    goto bad_virtual_char;
                } else if (s->op == Setting::FONT)
                    font_number = s->x;
        }
        if (c < size)
            good[c] = 1;
      bad_virtual_char: ;
    }

    /* Certainly none of the later ligatures or kerns will be meaningful. */
    for (Code c = size; c < _encoding.size(); c++) {
        _encoding[c].ligatures.clear();
        _encoding[c].kerns.clear();
    }

    /* Remove ligatures and kerns that point beyond 'size', except for valid
       context ligatures. Also remove ligatures that have non-live
       components. */
    /* 30.May.2005 -- Kerns might point involve a too-high character; kill
       them. */
    for (Code c = 0; c < size; c++) {
        Char &ch = _encoding[c];
        for (Ligature *l = ch.ligatures.begin(); l != ch.ligatures.end(); l++)
            if (!good[l->in2] || l->in2 >= size
                || (!good[l->out] && !_encoding[l->out].context_setting(c, l->in2))) {
                *l = ch.ligatures.back();
                ch.ligatures.pop_back();
                l--;
            }
        for (Kern *k = ch.kerns.begin(); k != ch.kerns.end(); k++)
            if (!good[k->in2] || k->in2 >= size) {
                *k = ch.kerns.back();
                ch.kerns.pop_back();
                k--;
            }
    }

    /* We are done! */
}

namespace {
// preference-sorting extra characters
enum {
    CONVENTIONAL_F_LIGATURE_SCORE = 4,
    CONVENTIONAL_F_F_LIGATURE_SCORE = 5,
    BASIC_LATIN_LOWER_SCORE = 3,
    BASIC_LATIN_UPPER_SCORE = 4,
    BASIC_LATIN_OTHER_SCORE = 5,
    LATIN1_SUPPLEMENT_SCORE = 6,
    LOW_16_SCORE = 7,
    OTHER_SCORE = 8,
    NOCHAR_SCORE = 100000,
    CONTEXT_PENALTY = 4
};

static int
unicode_score(uint32_t u)
{
    if (u == 0)
        return NOCHAR_SCORE;
    else if (u >= 'a' && u <= 'z')
        return BASIC_LATIN_LOWER_SCORE;
    else if (u >= 'A' && u <= 'Z')
        return BASIC_LATIN_UPPER_SCORE;
    else if (u < 0x0080)
        return BASIC_LATIN_OTHER_SCORE;
    else if (u < 0x0100)
        return LATIN1_SUPPLEMENT_SCORE;
    else if (u < 0x8000)
        return LOW_16_SCORE;
    else
        return OTHER_SCORE;
}

struct Slot {
    Metrics::Code old_code;
    Metrics::Code new_code;
    Metrics::Glyph glyph;
    int score;
    int lookup_source;
};

inline bool
operator<(const Slot &a, const Slot &b)
{
    // note: will give real glyphs priority over virtual ones at a given
    // priority
    // 6.Jul.2014 -- Make sure you include an old_code comparison; this
    // is required to ensure that a ligature's inputs (which have smaller
    // old_codes) are included whenever its outputs are included.
    if (a.lookup_source != b.lookup_source)
        return a.lookup_source < b.lookup_source;
    if (a.score != b.score)
        return a.score < b.score;
    if (a.glyph != b.glyph)
        return a.glyph < b.glyph;
    return a.old_code < b.old_code;
}
}

void
Metrics::shrink_encoding(int size, const DvipsEncoding &dvipsenc, ErrorHandler *errh)
{
    /* Move characters around. */

    /* Maybe we don't need to do anything. */
    if (_encoding.size() <= size) {
        cut_encoding(size);
        return;
    }

    /* Need a list of all ligatures.. */
    Vector<Ligature3> all_ligs;
    all_ligatures(all_ligs);

    /* Need liveness markings. */
    if (!_liveness_marked)
        mark_liveness(size, &all_ligs);

    /* Score characters by importance. Importance relates first to Unicode
       values, and then recursively to the importances of characters that form
       a ligature. */

    /* Create an initial set of scores, based on Unicode values. */
    Vector<int> scores(_encoding.size(), NOCHAR_SCORE);
    for (int i = 0; i < _encoding.size(); i++)
        if (_encoding[i].unicode)
            scores[i] = unicode_score(_encoding[i].unicode);

    /* Prefer conventional f-ligatures. */
    bool has_ff = false;
    for (Ligature3* l = all_ligs.begin(); l != all_ligs.end(); ++l)
        if (_encoding[l->in1].unicode == 'f'
            && (_encoding[l->in2].unicode == 'f'
                || _encoding[l->in2].unicode == 'i'
                || _encoding[l->in2].unicode == 'l')) {
            if (scores[l->out] > CONVENTIONAL_F_LIGATURE_SCORE)
                scores[l->out] = CONVENTIONAL_F_LIGATURE_SCORE;
            if (_encoding[l->in2].unicode == 'f') {
                _encoding[l->out].flags |= Char::IS_FF;
                has_ff = true;
            }
        }
    if (has_ff)
        for (Ligature3* l = all_ligs.begin(); l != all_ligs.end(); ++l)
            if (_encoding[l->in1].flag(Char::IS_FF)
                && (_encoding[l->in2].unicode == 'i'
                    || _encoding[l->in2].unicode == 'l')
                && scores[l->out] > CONVENTIONAL_F_F_LIGATURE_SCORE)
                scores[l->out] = CONVENTIONAL_F_F_LIGATURE_SCORE;

    /* Repeat these steps until you reach a stable set of scores: Score
       ligatures (ligscore = SUM[char scores]), then score characters touched
       only by fakes. */
    bool changed = true;
    while (changed) {
        changed = false;
        for (Ligature3 *l = all_ligs.begin(); l != all_ligs.end(); l++) {
            int score = scores[l->in1] + scores[l->in2];
            if (scores[l->out] > score)
                scores[l->out] = score, changed = true;
        }

        for (Code c = 0; c < _encoding.size(); c++)
            if (VirtualChar *vc = _encoding[c].virtual_char) {
                /* Make sure that if this virtual character appears, its parts
                   will also appear, by scoring the parts less */
                int score = scores[c] - 1, font_number = 0;
                for (Setting *s = vc->setting.begin(); s != vc->setting.end(); s++)
                    if (s->op == Setting::SHOW && font_number == 0
                        && score < scores[s->x])
                        scores[s->x] = score, changed = true;
                    else if (s->op == Setting::FONT)
                        font_number = s->x;
            }
    }

    /* Rescore intermediates to not be better off than their endpoints. */
    /* XXX multiple layers of intermediate? */
    for (Code c = 0; c < _encoding.size(); c++) {
        Char &ch = _encoding[c];
        if (ch.flag(Char::INTERMEDIATE))
            for (Ligature *l = ch.ligatures.begin(); l != ch.ligatures.end(); l++)
                if (scores[c] < scores[l->out] && !_encoding[l->out].context_setting(c, l->in2))
                    scores[c] = scores[l->out];
    }

    /* Collect characters that want to be reassigned. */
    Vector<Slot> slots;
    for (Code c = size; c < _encoding.size(); c++)
        if (scores[c] < NOCHAR_SCORE
            && !(_encoding[c].flags & Char::CONTEXT_ONLY)
            && (_encoding[c].flags & (Char::LIVE | Char::BASE_LIVE))) {
            Slot slot = { c, -1, _encoding[c].glyph, scores[c], _encoding[c].lookup_source };
            slots.push_back(slot);
        }
    // Sort them by score, then by glyph.
    std::sort(slots.begin(), slots.end());

    /* Prefer their old slots, if available. */
    for (Slot *slot = slots.begin(); slot < slots.end(); slot++)
        if (PermString g = code_name(slot->old_code)) {
            int c = dvipsenc.encoding_of(g);
            if (c >= 0 && _encoding[c].glyph == 0) {
                _encoding[c].swap(_encoding[slot->old_code]);
                slot->new_code = c;
            }
        }

    /* List empty slots in two phases: Those not encoded by the input
       encoding, then those encoded by the input encoding (but that character
       wasn't available). */
    Vector<Code> empty_codes;
    for (int want_encoded = 0; want_encoded < 2; want_encoded++)
        for (Code c = 0; c < size; c++)
            if (_encoding[c].base_code < 0
                && dvipsenc.encoded(c) == (bool) want_encoded)
                empty_codes.push_back(c);

    /* Then, assign codes to the unencoded characters. */
    int nunencoded = 0;

    for (Slot *slot = slots.begin(); slot != slots.end(); slot++) {
        if (slot->new_code >= 0)
            continue;

        int needs = (_encoding[slot->old_code].visible_base() ? 1 : 0)
            + (_encoding[slot->old_code].flag(Char::LIVE) ? 2 : 0);
        assert(needs > 0);

        Code dest = -1;
        for (Code *h = empty_codes.begin(); h < empty_codes.end() && dest < 0; h++) {
            int haves = (_encoding[*h].base_code < 0 ? 1 : 0)
                + (!_encoding[*h].visible() ? 2 : 0);
            if ((needs & haves) == needs)
                dest = *h;
        }

        if (dest >= 0) {
            if (needs & 2) {
                assert(!_encoding[dest].visible());
                _encoding[dest].swap(_encoding[slot->old_code]);
                slot->new_code = dest;
            } else {
                _encoding[slot->old_code].base_code = dest;
                slot->new_code = slot->old_code;
            }
            if (needs & 1) {
                assert(_encoding[dest].base_code < 0 || _encoding[dest].base_code == slot->old_code);
                _encoding[dest].base_code = slot->old_code;
            }
        } else
            nunencoded++;
    }

    /* Complain if some characters can't fit. */
    if (nunencoded) {
        // collect names of unencoded glyphs
        Vector<String> unencoded;
        for (Slot *slot = slots.begin(); slot != slots.end(); slot++)
            if (slot->new_code < 0)
                unencoded.push_back(code_name(slot->old_code));
        std::sort(unencoded.begin(), unencoded.end());
        StringAccum sa;
        for (const String* a = unencoded.begin(); a < unencoded.end(); a++)
            sa << *a << ' ';
        sa.pop_back();
        sa.append_break_lines(sa.take_string(), 68, "  ");
        sa.pop_back();
        errh->lwarning(" ", (unencoded.size() == 1 ? "not enough room in encoding, ignoring %d glyph" : "not enough room in encoding, ignoring %d glyphs"), unencoded.size());
        errh->lmessage(" ", "(\
The font uses more glyphs than the encoding has available slots,\n\
so these glyphs have been left out:\n%s\n\
To select specific glyphs, add them to the input encoding.)", sa.c_str());
    }

    /* Reencode changed slots. */
    Vector<Code> reencoding;
    for (Code c = 0; c < _encoding.size(); c++)
        reencoding.push_back(c);
    for (Slot *s = slots.begin(); s != slots.end(); s++)
        if (s->new_code >= 0)
            reencoding[s->old_code] = s->new_code;
    reencode(reencoding);

    check();
}

void
Metrics::make_base(int size)
{
    Vector<Code> reencoding;
    for (Code c = 0; c < size && c < _encoding.size(); c++) {
        Char &ch = _encoding[c];
        if (ch.base_code >= 0 && ch.base_code != c) {
            if (!reencoding.size())
                for (Code cc = 0; cc < _encoding.size(); cc++)
                    reencoding.push_back(cc);
            reencoding[ch.base_code] = c;
            reencoding[c] = ch.base_code;
            _encoding[c].swap(_encoding[ch.base_code]);
        }
        if (ch.virtual_char)    // remove it
            ch.clear();
    }
    if (reencoding.size()) {
        reencode(reencoding);
        cut_encoding(size);
    }
    check();
}


/*****************************************************************************/
/* output                                                                    */

bool
Metrics::need_virtual(int size) const
{
    if (size > _encoding.size())
        size = _encoding.size();
    for (const Char *ch = _encoding.begin(); ch < _encoding.begin() + size; ch++)
        if (ch->glyph /* actually encoded */
            && (ch->pdx || ch->pdy || ch->adx || ch->virtual_char))
            return true;
    return false;
}

bool
Metrics::need_base()
{
    if (!_liveness_marked)
        mark_liveness(_encoding.size());
    for (const Char *ch = _encoding.begin(); ch < _encoding.end(); ch++)
        if ((ch->flags & Char::BASE_LIVE) && ch->glyph != _boundary_glyph)
            return true;
    return false;
}

bool
Metrics::setting(Code code, Vector<Setting> &v, SettingMode sm) const
{
    extern int letterspace;

    if (!(sm & SET_KEEP))
        v.clear();

    if (!valid_code(code) || _encoding[code].glyph == 0)
        return false;

    const Char &ch = _encoding[code];

    if (const VirtualChar *vc = ch.virtual_char) {
        bool good = true;
        int font_number = 0;

        if (ch.pdx != 0 || ch.pdy != 0)
            v.push_back(Setting(Setting::MOVE, ch.pdx, ch.pdy));

        for (const Setting *s = vc->setting.begin(); s != vc->setting.end(); s++)
            switch (s->op) {
              case Setting::MOVE:
              case Setting::RULE:
              case Setting::PUSH:
              case Setting::POP:
              case Setting::SPECIAL:
                v.push_back(*s);
                break;
              case Setting::FONT:
                v.push_back(*s);
                font_number = s->x;
                break;
              case Setting::SHOW:
                if (font_number == 0)
                    good &= setting(s->x, v, (SettingMode)(sm | SET_KEEP));
                else
                    v.push_back(*s);
                break;
              case Setting::KERN:
              case Setting::KERNX:
                if (sm & SET_INTERMEDIATE)
                    v.push_back(*s);
                else if (font_number == 0 && s > vc->setting.begin()
                         && s + 1 < vc->setting.end()
                         && s[-1].op == Setting::SHOW
                         && s[1].op == Setting::SHOW) {
                    int k = kern(s[-1].x, s[1].x);
                    if (s->op == Setting::KERNX)
                        k -= letterspace;
                    if (k)
                        v.push_back(Setting(Setting::MOVE, k, 0));
                }
                break;
            }

        if (ch.pdy != 0 || ch.adx - ch.pdx != 0)
            v.push_back(Setting(Setting::MOVE, ch.adx - ch.pdx, -ch.pdy));
        return good;

    } else if (ch.base_code >= 0) {
        if (ch.pdx != 0 || ch.pdy != 0)
            v.push_back(Setting(Setting::MOVE, ch.pdx, ch.pdy));

        v.push_back(Setting(Setting::SHOW, ch.base_code, ch.glyph));

        if (ch.pdy != 0 || ch.adx - ch.pdx != 0)
            v.push_back(Setting(Setting::MOVE, ch.adx - ch.pdx, -ch.pdy));
        return true;

    } else
        return false;
}

int
Metrics::ligatures(Code in1, Vector<Code> &in2, Vector<Code> &out, Vector<int> &context) const
{
    in2.clear();
    out.clear();
    context.clear();

    const Char &in1ch = _encoding[in1];
    for (const Ligature *l = in1ch.ligatures.begin(); l != in1ch.ligatures.end(); l++) {
        in2.push_back(l->in2);
        const Char &outch = _encoding[l->out];
        if (outch.context_setting(in1, l->in2)) {
            if (in1 == outch.built_in1 && l->in2 == outch.built_in2)
                in2.pop_back();
            else if (in1 == outch.built_in1) {
                out.push_back(outch.built_in2);
                context.push_back(-1);
            } else {
                out.push_back(outch.built_in1);
                context.push_back(1);
            }
        } else {
            out.push_back(l->out);
            context.push_back(0);
        }
    }

    return in2.size();
}

int
Metrics::kerns(Code in1, Vector<Code> &in2, Vector<int> &kern) const
{
    in2.clear();
    kern.clear();

    const Char &in1ch = _encoding[in1];
    for (const Kern *k = in1ch.kerns.begin(); k != in1ch.kerns.end(); k++)
        if (k->kern != 0) {
            in2.push_back(k->in2);
            kern.push_back(k->kern);
        }

    return in2.size();
}


/*****************************************************************************/
/* debugging                                                                 */

void
Metrics::unparse(const Char *ch) const
{
    Code c;
    if (ch >= _encoding.begin() && ch < _encoding.end())
        c = ch - _encoding.begin();
    else
        c = -1;
    fprintf(stderr, "%4d/%s%s%s%s%s%s\n", c, code_str(c),
            (ch->flag(Char::LIVE) ? " [L]" : ""),
            (ch->flag(Char::BASE_LIVE) ? " [B]" : ""),
            (ch->flag(Char::CONTEXT_ONLY) ? " [C]" : ""),
            (ch->flag(Char::BUILT) ? " [!]" : ""),
            (ch->base_code >= 0 ? " <BC>" : ""));
    if (ch->base_code >= 0 && ch->base_code != c)
        fprintf(stderr, "\tBASE %d/%s\n", ch->base_code, code_str(ch->base_code));
    if (const VirtualChar *vc = ch->virtual_char) {
        fprintf(stderr, "\t*");
        int curfont = 0;
        for (const Setting *s = vc->setting.begin(); s != vc->setting.end(); s++)
            switch (s->op) {
              case Setting::FONT:
                fprintf(stderr, " {F%d}", s->x);
                curfont = s->x;
                break;
              case Setting::SHOW:
                fprintf(stderr, " %d", s->x);
                if (curfont == 0)
                    fprintf(stderr, "/%s", code_str(s->x));
                break;
              case Setting::KERN:
                fprintf(stderr, " <>");
                break;
              case Setting::MOVE:
                fprintf(stderr, " <%+d,%+d>", s->x, s->y);
                break;
              case Setting::RULE:
                fprintf(stderr, " [%d,%d]", s->x, s->y);
                break;
              case Setting::PUSH:
                fprintf(stderr, " (");
                break;
              case Setting::POP:
                fprintf(stderr, " )");
                break;
              case Setting::SPECIAL:
                fprintf(stderr, " S{%s}", s->s.c_str());
                break;
            }
        fprintf(stderr, "  ((%d/%s, %d/%s))\n", ch->built_in1, code_str(ch->built_in1), ch->built_in2, code_str(ch->built_in2));
    }
    for (const Ligature *l = ch->ligatures.begin(); l != ch->ligatures.end(); l++)
        fprintf(stderr, "\t[%d/%s => %d/%s]%s\n", l->in2, code_str(l->in2), l->out, code_str(l->out), (_encoding[l->out].context_setting(c, l->in2) ? " [C]" : ""));
#if 0
    for (const Kern *k = ch->kerns.begin(); k != ch->kerns.end(); k++)
        fprintf(stderr, "\t{%d/%s %+d}\n", k->in2, code_str(k->in2), k->kern);
#endif
}

void
Metrics::unparse() const
{
    for (const Char *ch = _encoding.begin(); ch < _encoding.end(); ch++)
        if (ch->glyph)
            unparse(ch);
}
