diff options
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.105/otftotfm/metrics.cc')
-rw-r--r-- | Build/source/texk/lcdf-typetools/lcdf-typetools-2.105/otftotfm/metrics.cc | 1569 |
1 files changed, 1569 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.105/otftotfm/metrics.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.105/otftotfm/metrics.cc new file mode 100644 index 00000000000..f73bb9d01f9 --- /dev/null +++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.105/otftotfm/metrics.cc @@ -0,0 +1,1569 @@ +/* metrics.{cc,hh} -- an encoding during and after OpenType features + * + * Copyright (c) 2003-2014 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); +} |