Logo Search packages:      
Sourcecode: lcdf-typetools version File versions

otfgsub.cc

// -*- related-file-name: "../include/efont/otfgsub.hh" -*-

/* otfgsub.{cc,hh} -- OpenType GSUB table
 *
 * Copyright (c) 2003-2004 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 <efont/otfgsub.hh>
#include <efont/otfname.hh>
#include <lcdf/error.hh>
#include <lcdf/straccum.hh>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

namespace Efont { namespace OpenType {

void
Substitution::clear(Substitute &s, uint8_t &t)
{
    switch (t) {
      case T_GLYPHS:
      delete[] s.gids;
      break;
      case T_COVERAGE:
      delete s.coverage;
      break;
    }
    t = T_NONE;
}

void
Substitution::assign_space(Substitute &s, uint8_t &t, int n)
{
    clear(s, t);
    if (n == 1)
      t = T_GLYPH;
    else if (n > 1) {
      s.gids = new Glyph[n + 1];
      s.gids[0] = n;
      t = T_GLYPHS;
    }
}

void
Substitution::assign(Substitute &s, uint8_t &t, Glyph gid)
{
    clear(s, t);
    s.gid = gid;
    t = T_GLYPH;
}

void
Substitution::assign(Substitute &s, uint8_t &t, int ngids, const Glyph *gids)
{
    clear(s, t);
    assert(ngids > 0);
    if (ngids == 1) {
      s.gid = gids[0];
      t = T_GLYPH;
    } else {
      s.gids = new Glyph[ngids + 1];
      s.gids[0] = ngids;
      memcpy(s.gids + 1, gids, ngids * sizeof(Glyph));
      t = T_GLYPHS;
    }
}

void
Substitution::assign(Substitute &s, uint8_t &t, const Coverage &coverage)
{
    clear(s, t);
    s.coverage = new Coverage(coverage);
    t = T_COVERAGE;
}

void
Substitution::assign(Substitute &s, uint8_t &t, const Substitute &os, uint8_t ot)
{
    if (&s == &os)
      return;
    switch (ot) {
      case T_NONE:
      clear(s, t);
      break;
      case T_GLYPH:
      assign(s, t, os.gid);
      break;
      case T_GLYPHS:
      assign(s, t, os.gids[0], os.gids + 1);
      break;
      case T_COVERAGE:
      assign(s, t, *os.coverage);
      break;
      default:
      assert(0);
    }
}

Substitution::Substitution(const Substitution &o)
    : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_NONE), _right_is(T_NONE),
      _alternate(o._alternate)
{
    assign(_left, _left_is, o._left, o._left_is);
    assign(_in, _in_is, o._in, o._in_is);
    assign(_out, _out_is, o._out, o._out_is);
    assign(_right, _right_is, o._right, o._right_is);
}

Substitution::Substitution(Glyph in, Glyph out)
    : _left_is(T_NONE), _in_is(T_GLYPH), _out_is(T_GLYPH), _right_is(T_NONE)
{
    _in.gid = in;
    _out.gid = out;
}

Substitution::Substitution(Glyph in, const Vector<Glyph> &out, bool is_alternate)
    : _left_is(T_NONE), _in_is(T_GLYPH), _out_is(T_NONE), _right_is(T_NONE),
      _alternate(is_alternate)
{
    assert(out.size() > 0);
    _in.gid = in;
    assign(_out, _out_is, out.size(), &out[0]);
}

Substitution::Substitution(Glyph in1, Glyph in2, Glyph out)
    : _left_is(T_NONE), _in_is(T_GLYPHS), _out_is(T_GLYPH), _right_is(T_NONE)
{
    _in.gids = new Glyph[3];
    _in.gids[0] = 2;
    _in.gids[1] = in1;
    _in.gids[2] = in2;
    _out.gid = out;
}

Substitution::Substitution(const Vector<Glyph> &in, Glyph out)
    : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_GLYPH), _right_is(T_NONE)
{
    assert(in.size() > 0);
    assign(_in, _in_is, in.size(), &in[0]);
    _out.gid = out;
}

Substitution::Substitution(int nin, const Glyph *in, Glyph out)
    : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_GLYPH), _right_is(T_NONE)
{
    assert(nin > 0);
    assign(_in, _in_is, nin, in);
    _out.gid = out;
}

Substitution::Substitution(int nleft, int nin, int nout, int nright)
    : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_NONE), _right_is(T_NONE)
{
    if (nleft)
      assign_space(_left, _left_is, nleft);
    if (nin)
      assign_space(_in, _in_is, nin);
    if (nout)
      assign_space(_out, _out_is, nout);
    if (nright)
      assign_space(_right, _right_is, nright);
}

Substitution::~Substitution()
{
    clear(_left, _left_is);
    clear(_in, _in_is);
    clear(_out, _out_is);
    clear(_right, _right_is);
}

Substitution &
Substitution::operator=(const Substitution &o)
{
    assign(_left, _left_is, o._left, o._left_is);
    assign(_in, _in_is, o._in, o._in_is);
    assign(_out, _out_is, o._out, o._out_is);
    assign(_right, _right_is, o._right, o._right_is);
    _alternate = o._alternate;
    return *this;
}

bool
Substitution::substitute_in(const Substitute &s, uint8_t t, const Coverage &c)
{
    switch (t) {
      case T_NONE:
      return true;
      case T_GLYPH:
      return c.covers(s.gid);
      case T_GLYPHS:
      for (int i = 1; i <= s.gids[0]; i++)
          if (!c.covers(s.gids[i]))
            return false;
      return true;
      case T_COVERAGE:
      return *s.coverage <= c;
      default:
      assert(0);
      return false;
    }
}

bool
Substitution::substitute_in(const Substitute &s, uint8_t t, const GlyphSet &gs)
{
    switch (t) {
      case T_NONE:
      return true;
      case T_GLYPH:
      return gs.covers(s.gid);
      case T_GLYPHS:
      for (int i = 1; i <= s.gids[0]; i++)
          if (!gs.covers(s.gids[i]))
            return false;
      return true;
      case T_COVERAGE:
      for (Coverage::iterator i = s.coverage->begin(); i; i++)
          if (!gs.covers(*i))
            return false;
      return true;
      default:
      assert(0);
      return false;
    }
}

bool
Substitution::context_in(const Coverage &c) const
{
    return substitute_in(_left, _left_is, c)
      && substitute_in(_in, _in_is, c)
      && substitute_in(_right, _right_is, c);
}

bool
Substitution::context_in(const GlyphSet &gs) const
{
    return substitute_in(_left, _left_is, gs)
      && substitute_in(_in, _in_is, gs)
      && substitute_in(_right, _right_is, gs);
}

Glyph
Substitution::extract_glyph(const Substitute &s, uint8_t t) throw ()
{
    return (t == T_GLYPH ? s.gid : 0);
}

Glyph
Substitution::extract_glyph(const Substitute &s, int which, uint8_t t) throw ()
{
    switch (t) {
      case T_GLYPH:
      return (which == 0 ? s.gid : 0);
      case T_GLYPHS:
      return (s.gids[0] > which ? s.gids[which + 1] : 0);
      case T_COVERAGE:
      for (Coverage::iterator ci = s.coverage->begin(); ci; ci++, which--)
          if (which == 0)
            return *ci;
      return 0;
      default:
      return 0;
    }
}

bool
Substitution::extract_glyphs(const Substitute &s, uint8_t t, Vector<Glyph> &v, bool coverage_ok) throw ()
{
    switch (t) {
      case T_GLYPH:
      v.push_back(s.gid);
      return true;
      case T_GLYPHS:
      for (int i = 1; i <= s.gids[0]; i++)
          v.push_back(s.gids[i]);
      return true;
      case T_COVERAGE:
      if (coverage_ok) {
          for (Coverage::iterator i = s.coverage->begin(); i; i++)
            v.push_back(*i);
          return true;
      } else
          return false;
      default:
      return false;
    }
}

Glyph *
Substitution::extract_glyphptr(const Substitute &s, uint8_t t) throw ()
{
    switch (t) {
      case T_GLYPH:
      return const_cast<Glyph *>(&s.gid);
      case T_GLYPHS:
      return &s.gids[1];
      default:
      return 0;
    }
}

int
Substitution::extract_nglyphs(const Substitute &s, uint8_t t, bool coverage_ok) throw ()
{
    switch (t) {
      case T_GLYPH:
      return 1;
      case T_GLYPHS:
      return s.gids[0];
      case T_COVERAGE:
      return (coverage_ok ? 1 : 0);
      default:
      return 0;
    }
}

bool
Substitution::matches(const Substitute &s, uint8_t t, int pos, Glyph g) throw ()
{
    switch (t) {
      case T_GLYPH:
      return (pos == 0 && s.gid == g);
      case T_GLYPHS:
      return (pos >= 0 && pos < s.gids[0] && s.gids[1 + pos] == g);
      case T_COVERAGE:
      return (pos == 0 && s.coverage->covers(g));
      default:
      return false;
    }
}

bool
Substitution::is_noop() const
{
    return (_in_is == T_GLYPH && _out_is == T_GLYPH && _in.gid == _out.gid)
      || (_in_is == T_GLYPHS && _out_is == T_GLYPHS
          && _in.gids[0] == _out.gids[0]
          && memcmp(_in.gids, _out.gids, (_in.gids[0] + 1) * sizeof(Glyph)) == 0);
}

bool
Substitution::all_in_glyphs(Vector<Glyph> &v) const
{
    bool ok = true;
    if (_left_is != T_NONE)
      ok &= extract_glyphs(_left, _left_is, v, false);
    ok &= extract_glyphs(_in, _in_is, v, false);
    if (_right_is != T_NONE)
      ok &= extract_glyphs(_right, _right_is, v, false);
    return ok;
}

bool
Substitution::all_out_glyphs(Vector<Glyph> &v) const
{
    bool ok = true;
    if (_left_is != T_NONE)
      ok &= extract_glyphs(_left, _left_is, v, false);
    ok &= extract_glyphs(_out, _out_is, v, false);
    if (_right_is != T_NONE)
      ok &= extract_glyphs(_right, _right_is, v, false);
    return ok;
}

void
Substitution::assign_append(Substitute &s, uint8_t &t, const Substitute &ls, uint8_t lt, const Substitute &rs, uint8_t rt)
{
    if (lt == T_NONE)
      assign(s, t, rs, rt);
    else if (rt == T_NONE)
      assign(s, t, ls, lt);
    else if (lt != T_COVERAGE && rt != T_COVERAGE) {
      int nl = extract_nglyphs(ls, lt, false);
      int nr = extract_nglyphs(rs, rt, false);
      Glyph *gids = new Glyph[nl + nr + 1];
      gids[0] = nl + nr;
      memcpy(&gids[1], extract_glyphptr(ls, lt), nl * sizeof(Glyph));
      memcpy(&gids[1 + nl], extract_glyphptr(rs, rt), nr * sizeof(Glyph));
      clear(s, t);
      s.gids = gids;
      t = T_GLYPHS;
    } else
      throw Error();
}

void
Substitution::assign_append(Substitute &s, uint8_t &t, const Substitute &ls, uint8_t lt, Glyph rg)
{
    Substitute rs;
    rs.gid = rg;
    assign_append(s, t, ls, lt, rs, T_GLYPH);
}

Substitution
Substitution::in_out_append_glyph(Glyph g) const
{
    Substitution s;
    assign(s._left, s._left_is, _left, _left_is);
    assign(s._right, s._right_is, _right, _right_is);
    assign_append(s._in, s._in_is, _in, _in_is, g);
    assign_append(s._out, s._out_is, _out, _out_is, g);
    return s;
}

void
Substitution::add_outer_left(Glyph g)
{
    Substitute ls;
    ls.gid = g;
    assign_append(_left, _left_is, ls, T_GLYPH, _left, _left_is);
}

void
Substitution::remove_outer_left()
{
    if (_left_is == T_GLYPH)
      _left_is = T_NONE;
    else if (_left_is == T_GLYPHS) {
      if (_left.gids[0] == 2)
          assign(_left, _left_is, _left.gids[2]);
      else {
          _left.gids[0]--;
          memmove(_left.gids + 1, _left.gids + 2, _left.gids[0] * sizeof(Glyph));
      }
    }
}

void
Substitution::add_outer_right(Glyph g)
{
    assign_append(_right, _right_is, _right, _right_is, g);
}

void
Substitution::remove_outer_right()
{
    if (_right_is == T_GLYPH)
      _right_is = T_NONE;
    else if (_right_is == T_GLYPHS) {
      if (_right.gids[0] == 2)
          assign(_right, _right_is, _right.gids[1]);
      else
          _right.gids[0]--;
    }
}

bool
Substitution::out_alter(const Substitution &o, int pos) throw ()
{
    const Glyph *g = out_glyphptr();
    int ng = out_nglyphs();
    const Glyph *out_g = o.out_glyphptr();
    int out_ng = o.out_nglyphs();
    int in_ng = o.in_nglyphs();
    if (pos + in_ng > ng || out_ng == 0)
      return false;
    
    // check that input substitution actually matches us
    for (int i = 0; i < in_ng; i++)
      if (!o.in_matches(i, g[pos+i]))
          return false;

    // actually change output
    Vector<Glyph> new_g;
    for (int i = 0; i < pos; i++)
      new_g.push_back(g[i]);
    for (int i = 0; i < out_ng; i++)
      new_g.push_back(out_g[i]);
    for (int i = pos + in_ng; i < ng; i++)
      new_g.push_back(g[i]);
    assign(_out, _out_is, new_g.size(), &new_g[0]);
    
    return true;
}

static void
unparse_glyphid(StringAccum &sa, Glyph gid, const Vector<PermString> *gns) throw ()
{
    if (gid > 0 && gns && gns->size() > gid && (*gns)[gid])
      sa << (*gns)[gid];
    else
      sa << "g" << gid;
}

void
Substitution::unparse_glyphids(StringAccum &sa, const Substitute &s, uint8_t t, const Vector<PermString> *gns) throw ()
{
    if (t == T_GLYPH)
      unparse_glyphid(sa, s.gid, gns);
    else if (t == T_GLYPHS) {
      for (int i = 1; i <= s.gids[0]; i++) {
          if (i != 1)
            sa << ' ';
          unparse_glyphid(sa, s.gids[i], gns);
      }
    } else if (t == T_COVERAGE)
      sa << "<coverage>";
    else
      sa << "-";
}

void
Substitution::unparse(StringAccum &sa, const Vector<PermString> *gns) const
{
    if (!*this)
      sa << "NULL[]";
    else {
      if (is_single())
          sa << "SINGLE[";
      else if (is_ligature())
          sa << "LIGATURE[";
      else if (is_multiple())
          sa << "MULTIPLE[";
      else if (is_alternate())
          sa << "ALTERNATE[";
      else if (is_simple_context())
          sa << "SIMPLECONTEXT[";
      else
          sa << "UNKNOWN[";

      if (_left_is != T_NONE) {
          unparse_glyphids(sa, _left, _left_is, gns);
          sa << " | ";
      }
      unparse_glyphids(sa, _in, _in_is, gns);
      sa << " => ";
      unparse_glyphids(sa, _out, _out_is, gns);
      if (_right_is != T_NONE) {
          sa << " | ";
          unparse_glyphids(sa, _right, _right_is, gns);
      }

      sa << ']';
    }
}

String
Substitution::unparse(const Vector<PermString> *gns) const
{
    StringAccum sa;
    unparse(sa, gns);
    return sa.take_string();
}



/**************************
 * Gsub                   *
 *                        *
 **************************/

Gsub::Gsub(const Data &d, const Font *otf, ErrorHandler *errh) throw (Error)
    : _chaincontext_reverse_backtrack(false)
{
    // Fixed      Version
    // Offset     ScriptList
    // Offset     FeatureList
    // Offset     LookupList
    if (d.length() == 0)
      throw BlankTable("GSUB");
    if (d.u16(0) != 1)
      throw Format("GSUB");
    if (_script_list.assign(d.offset_subtable(4), errh) < 0)
      throw Format("GSUB script list");
    if (_feature_list.assign(d.offset_subtable(6), errh) < 0)
      throw Format("GSUB feature list");
    _lookup_list = d.offset_subtable(8);

    if (!otf)
      return;

    // Check for "correct" chaining context rules, as suggested by Adobe's
    // OpenType FDK
    try {
      Name nametable(otf->table("name"), ErrorHandler::silent_handler());
      String vstr = nametable.name(std::find_if(nametable.begin(), nametable.end(), Name::PlatformPred(Name::N_VERSION, 1, 0, 0)));
      const char *v = vstr.begin(), *endv = vstr.end();
      if (v + 20 <= endv) {
          if (v[0] != 'O' || v[1] != 'T' || v[2] != 'F' || v[3] == ';')
            goto try_core;
          for (v += 4; v < endv && *v != ';'; v++)
            /* do nothing */;
          if (v + 3 >= endv || v[1] != 'P' || v[2] != 'S' || v[3] == ';')
            goto try_core;
          for (v += 4; v < endv && *v != ';'; v++)
            /* do nothing */;
          if (v + 11 >= endv || memcmp(v + 1, "Core 1.0.", 9) != 0
            || (v[10] != '2' && v[10] != '3')
            || (v[11] < '0' || v[11] > '9'))
            goto try_core;
          _chaincontext_reverse_backtrack = true;
      }
      try_core:
      v = vstr.begin();
      if (v + 16 <= endv) {
          if (v[0] != 'C' || v[1] != 'o' || v[2] != 'r' || v[3] != 'e')
            goto done;
          for (v += 4; v < endv && *v != ';'; v++)
            /* do nothing */;
          if (v + 12 > endv || memcmp(v, ";makeotf.lib", 12) != 0)
            goto done;
          _chaincontext_reverse_backtrack = true;
      }
      done: ;
    } catch (Error) {
    }
}

int
Gsub::nlookups() const
{
    return _lookup_list.u16(0);
}

GsubLookup
Gsub::lookup(unsigned i) const
{
    if (i >= _lookup_list.u16(0))
      throw Error("GSUB lookup out of range");
    else
      return GsubLookup(_lookup_list.offset_subtable(2 + i*2));
}


/**************************
 * GsubLookup             *
 *                        *
 **************************/

GsubLookup::GsubLookup(const Data &d) throw (Error)
    : _d(d)
{
    if (_d.length() < 6)
      throw Format("GSUB Lookup table");
}

bool
GsubLookup::unparse_automatics(const Gsub &gsub, Vector<Substitution> &v) const
{
    int nlookup = _d.u16(4);
    switch (_d.u16(0)) {
      case L_SINGLE:
      for (int i = 0; i < nlookup; i++)
          GsubSingle(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).unparse(v);
      return true;
      case L_MULTIPLE:
      for (int i = 0; i < nlookup; i++)
          GsubMultiple(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).unparse(v);
      return true;
      case L_ALTERNATE:
      for (int i = 0; i < nlookup; i++)
          GsubMultiple(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).unparse(v, true);
      return true;
      case L_LIGATURE:
      for (int i = 0; i < nlookup; i++)
          GsubLigature(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).unparse(v);
      return true;
      case L_CONTEXT: {
        bool understood = true;
        for (int i = 0; i < nlookup; i++)
            understood &= GsubContext(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).unparse(gsub, v);
        return understood;
      }
      case L_CHAIN: {
        bool understood = true;
        for (int i = 0; i < nlookup; i++)
            understood &= GsubChainContext(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).unparse(gsub, v);
        return understood;
      }
      default:
      return false;
    }
}

bool
GsubLookup::apply(const Glyph *g, int pos, int n, Substitution &s) const
{
    int nlookup = _d.u16(4);
    switch (_d.u16(0)) {
      case L_SINGLE:
      for (int i = 0; i < nlookup; i++)
          if (GsubSingle(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).apply(g, pos, n, s))
            return true;
      return false;
      case L_MULTIPLE:
      for (int i = 0; i < nlookup; i++)
          if (GsubMultiple(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).apply(g, pos, n, s))
            return true;
      return false;
      case L_ALTERNATE:
      for (int i = 0; i < nlookup; i++)
          if (GsubMultiple(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).apply(g, pos, n, s, true))
            return true;
      return false;
      case L_LIGATURE:
      for (int i = 0; i < nlookup; i++)
          if (GsubLigature(_d.offset_subtable(HEADERSIZE + i*RECSIZE)).apply(g, pos, n, s))
            return true;
      return false;
      default:                // XXX
      return false;
    }
}


/**************************
 * GsubSingle             *
 *                        *
 **************************/

GsubSingle::GsubSingle(const Data &d) throw (Error)
    : _d(d)
{
    if (_d[0] != 0
      || (_d[1] != 1 && _d[1] != 2))
      throw Format("GSUB Single Substitution");
    Coverage coverage(_d.offset_subtable(2));
    if (!coverage.ok()
      || (_d[1] == 2 && coverage.size() > _d.u16(4)))
      throw Format("GSUB Single Substitution coverage");
}

Coverage
GsubSingle::coverage() const throw ()
{
    return Coverage(_d.offset_subtable(2), 0, false);
}

Glyph
GsubSingle::map(Glyph g) const
{
    int ci = coverage().coverage_index(g);
    if (ci < 0)
      return g;
    else if (_d[1] == 1)
      return g + _d.s16(4);
    else
      return _d.u16(HEADERSIZE + FORMAT2_RECSIZE*ci);
}

void
GsubSingle::unparse(Vector<Substitution> &v) const
{
    if (_d[1] == 1) {
      int delta = _d.s16(4);
      for (Coverage::iterator i = coverage().begin(); i; i++)
          v.push_back(Substitution(*i, *i + delta));
    } else {
      for (Coverage::iterator i = coverage().begin(); i; i++)
          v.push_back(Substitution(*i, _d.u16(HEADERSIZE + i.coverage_index()*FORMAT2_RECSIZE)));
    }
}

bool
GsubSingle::apply(const Glyph *g, int pos, int n, Substitution &s) const
{
    int ci;
    if (pos < n   && (ci = coverage().coverage_index(g[pos])) >= 0) {
      if (_d[1] == 1)
          s = Substitution(g[pos], g[pos] + _d.s16(4));
      else
          s = Substitution(g[pos], _d.u16(HEADERSIZE + ci*FORMAT2_RECSIZE));
      return true;
    } else
      return false;
}


/**************************
 * GsubMultiple           *
 *                        *
 **************************/

GsubMultiple::GsubMultiple(const Data &d) throw (Error)
    : _d(d)
{
    if (_d[0] != 0 || _d[1] != 1)
      throw Format("GSUB Multiple Substitution");
    Coverage coverage(_d.offset_subtable(2));
    if (!coverage.ok()
      || coverage.size() > _d.u16(4))
      throw Format("GSUB Multiple Substitution coverage");
}

Coverage
GsubMultiple::coverage() const throw ()
{
    return Coverage(_d.offset_subtable(2), 0, false);
}

bool
GsubMultiple::map(Glyph g, Vector<Glyph> &v) const
{
    v.clear();
    int ci = coverage().coverage_index(g);
    if (ci < 0) {
      v.push_back(g);
      return false;
    } else {
      Data seq = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
      for (int i = 0; i < seq.u16(0); i++)
          v.push_back(seq.u16(SEQ_HEADERSIZE + i*SEQ_RECSIZE));
      return true;
    }
}

void
GsubMultiple::unparse(Vector<Substitution> &v, bool is_alternate) const
{
    Vector<Glyph> result;
    for (Coverage::iterator i = coverage().begin(); i; i++) {
      Data seq = _d.offset_subtable(HEADERSIZE + i.coverage_index()*RECSIZE);
      result.clear();
      for (int j = 0; j < seq.u16(0); j++)
          result.push_back(seq.u16(SEQ_HEADERSIZE + j*SEQ_RECSIZE));
      v.push_back(Substitution(*i, result, is_alternate));
    }
}

bool
GsubMultiple::apply(const Glyph *g, int pos, int n, Substitution &s, bool is_alternate) const
{
    int ci;
    if (pos < n   && (ci = coverage().coverage_index(g[pos])) >= 0) {
      Vector<Glyph> result;
      Data seq = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
      for (int j = 0; j < seq.u16(0); j++)
          result.push_back(seq.u16(SEQ_HEADERSIZE + j*SEQ_RECSIZE));
      s = Substitution(g[pos], result, is_alternate);
      return true;
    } else
      return false;
}


/**************************
 * GsubLigature           *
 *                        *
 **************************/

GsubLigature::GsubLigature(const Data &d) throw (Error)
    : _d(d)
{
    if (_d[0] != 0
      || _d[1] != 1)
      throw Format("GSUB Ligature Substitution");
    Coverage coverage(_d.offset_subtable(2));
    if (!coverage.ok()
      || coverage.size() > _d.u16(4))
      throw Format("GSUB Ligature Substitution coverage");
}

Coverage
GsubLigature::coverage() const throw ()
{
    return Coverage(_d.offset_subtable(2), 0, false);
}

bool
GsubLigature::map(const Vector<Glyph> &gs, Glyph &result, int &consumed) const
{
    assert(gs.size() > 0);
    result = gs[0];
    consumed = 1;
    int ci = coverage().coverage_index(gs[0]);
    if (ci < 0)
      return false;
    Data ligset = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
    int nligset = ligset.u16(0);
    for (int i = 0; i < nligset; i++) {
      Data lig = ligset.offset_subtable(SET_HEADERSIZE + i*SET_RECSIZE);
      int nlig = lig.u16(2);
      if (nlig > gs.size() - 1)
          goto bad;
      for (int j = 0; j < nlig - 1; j++)
          if (lig.u16(LIG_HEADERSIZE + j*LIG_RECSIZE) != gs[j + 1])
            goto bad;
      result = lig.u16(0);
      consumed = nlig + 1;
      return true;
      bad: ;
    }
    return false;
}

void
GsubLigature::unparse(Vector<Substitution> &v) const
{
    for (Coverage::iterator i = coverage().begin(); i; i++) {
      Data ligset = _d.offset_subtable(HEADERSIZE + i.coverage_index()*RECSIZE);
      int nligset = ligset.u16(0);
      Vector<Glyph> components(1, *i);
      for (int j = 0; j < nligset; j++) {
          Data lig = ligset.offset_subtable(SET_HEADERSIZE + j*SET_RECSIZE);
          int nlig = lig.u16(2);
          components.resize(1);
          for (int k = 0; k < nlig - 1; k++)
            components.push_back(lig.u16(LIG_HEADERSIZE + k*LIG_RECSIZE));
          v.push_back(Substitution(components, lig.u16(0)));
      }
    }
}

bool
GsubLigature::apply(const Glyph *g, int pos, int n, Substitution &s) const
{
    int ci;
    if (pos < n   && (ci = coverage().coverage_index(g[pos])) >= 0) {
      Data ligset = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
      int nligset = ligset.u16(0);
      for (int j = 0; j < nligset; j++) {
          Data lig = ligset.offset_subtable(SET_HEADERSIZE + j*SET_RECSIZE);
          int nlig = lig.u16(2);
          if (pos + nlig <= n) {
            for (int k = 0; k < nlig - 1; k++)
                if (lig.u16(LIG_HEADERSIZE + k*LIG_RECSIZE) != g[pos + k + 1])
                  goto ligature_failed;
            s = Substitution(nlig, &g[pos], lig.u16(0));
            return true;
          }
        ligature_failed: ;
      }
    }
    return false;
}


/**************************
 * GsubContext            *
 *                        *
 **************************/

GsubContext::GsubContext(const Data &d) throw (Error)
    : _d(d)
{
    switch (_d.u16(0)) {
      case 1:
      case 2:
      break;
      case 3: {
        int ninput = _d.u16(2);
        if (ninput < 1)
            throw Format("GSUB Context Substitution input sequence");
        Coverage coverage(_d.offset_subtable(F3_HSIZE));
        if (!coverage.ok())
            throw Format("GSUB Context Substitution coverage");
        break;
      }
      default:
      throw Format("GSUB Context Substitution");
    }
}

Coverage
GsubContext::coverage() const throw ()
{
    if (_d[1] == 3)
      return Coverage(_d.offset_subtable(F3_HSIZE), 0, false);
    else
      return Coverage();
}

bool
GsubContext::f3_unparse(const Data &data, int nglyph, int glyphtab_offset, int nsub, int subtab_offset, const Gsub &gsub, Vector<Substitution> &outsubs, const Substitution &prototype_sub)
{
    Vector<Substitution> subs;
    subs.push_back(prototype_sub);
    Vector<Substitution> work_subs;

    // get array of possible substitutions including contexts
    for (int i = 0; i < nglyph; i++) {
      assert(!work_subs.size());
      Coverage c(data.offset_subtable(glyphtab_offset + i*2));
      for (Coverage::iterator ci = c.begin(); ci; ci++)
          for (int j = 0; j < subs.size(); j++)
            work_subs.push_back(subs[j].in_out_append_glyph(*ci));
      subs.clear();
      subs.swap(work_subs);
    }

    // now, apply referred lookups to the resulting substitution array
    Vector<Glyph> in_glyphs;
    Vector<int> pos_map;
    Substitution subtab_sub;
    for (int i = 0; i < subs.size(); i++) {
      Substitution &s = subs[i];
      int napplied = 0;
      for (int j = 0; j < nsub; j++) {
          int seq_index = data.u16(subtab_offset + SUBRECSIZE*j);
          int lookup_index = data.u16(subtab_offset + SUBRECSIZE*j + 2);
          // XXX check seq_index against size of output glyphs?
          if (gsub.lookup(lookup_index).apply(s.out_glyphptr(), seq_index, s.out_nglyphs(), subtab_sub)) {
            napplied++;
            s.out_alter(subtab_sub, seq_index);
          }
      }
      // 26.Jun.2003 -- always push substitution back, since the no-op might
      // override a following substitution
      outsubs.push_back(s);
    }

    return true;        // XXX
}

bool
GsubContext::unparse(const Gsub &gsub, Vector<Substitution> &v) const
{
    if (_d.u16(0) != 3)       // XXX
      return false;
    int nglyph = _d.u16(2);
    int nsubst = _d.u16(4);
    return f3_unparse(_d, nglyph, F3_HSIZE, nsubst, F3_HSIZE + nglyph*2, gsub, v, Substitution());
}


/**************************
 * GsubChainContext       *
 *                        *
 **************************/

GsubChainContext::GsubChainContext(const Data &d) throw (Error)
    : _d(d)
{
    switch (_d.u16(0)) {
      case 1:
      case 2:
      break;
      case 3: {
        int nbacktrack = _d.u16(2);
        int input_offset = F3_HSIZE + nbacktrack*2;
        int ninput = _d.u16(input_offset);
        if (ninput < 1)
            throw Format("GSUB ChainContext Substitution input sequence");
        Coverage coverage(_d.offset_subtable(input_offset + F3_INPUT_HSIZE));
        if (!coverage.ok())
            throw Format("GSUB ChainContext Substitution coverage");
        break;
      }
      default:
      throw Format("GSUB ChainContext Substitution");
    }
}

Coverage
GsubChainContext::coverage() const throw ()
{
    if (_d.u16(0) != 3)
      return Coverage();
    int nbacktrack = _d.u16(2);
    int input_offset = F3_HSIZE + nbacktrack*2;
    return Coverage(_d.offset_subtable(input_offset + F3_INPUT_HSIZE), 0, false);
}

bool
GsubChainContext::unparse(const Gsub &gsub, Vector<Substitution> &v) const
{
    if (_d.u16(0) != 3)
      return false;
    
    int nbacktrack = _d.u16(2);
    int input_offset = F3_HSIZE + nbacktrack*2;
    int ninput = _d.u16(input_offset);
    int lookahead_offset = input_offset + F3_INPUT_HSIZE + ninput*2;
    int nlookahead = _d.u16(lookahead_offset);
    int subst_offset = lookahead_offset + F3_LOOKAHEAD_HSIZE + nlookahead*2;
    int nsubst = _d.u16(subst_offset);

    Vector<Coverage> backtrackc;
    Vector<Coverage> lookaheadc;
    if (gsub.chaincontext_reverse_backtrack()) {
      for (int i = 0; i < nbacktrack; i++)
          backtrackc.push_back(Coverage(_d.offset_subtable(F3_HSIZE + i*2)));
    } else {
      for (int i = nbacktrack - 1; i >= 0; i--)
          backtrackc.push_back(Coverage(_d.offset_subtable(F3_HSIZE + i*2)));
    }
    for (int i = 0; i < nlookahead; i++)
      lookaheadc.push_back(Coverage(_d.offset_subtable(lookahead_offset + F3_LOOKAHEAD_HSIZE + i*2)));
    
    Vector<Coverage::iterator> backtracki;
    Vector<Coverage::iterator> lookaheadi;
    for (int i = 0; i < nbacktrack; i++)
      backtracki.push_back(backtrackc[i].begin());
    for (int i = 0; i < nlookahead; i++)
      lookaheadi.push_back(lookaheadc[i].begin());

    bool any = false;
    
    while (1) {

      // run GsubContext
      Substitution s(nbacktrack, 0, 0, nlookahead);
      Glyph *left_begin = s.left_glyphptr();
      for (int i = 0; i < nbacktrack; i++)
          left_begin[i] = *backtracki[i];
      Glyph *right_begin = s.right_glyphptr();
      for (int i = 0; i < nlookahead; i++)
          right_begin[i] = *lookaheadi[i];

      any |= GsubContext::f3_unparse(_d, ninput, input_offset + F3_INPUT_HSIZE, nsubst, subst_offset + F3_SUBST_HSIZE, gsub, v, s);
      
      // step iterators
      for (int i = nlookahead - 1; i >= 0; i--) {
          lookaheadi[i]++;
          if (lookaheadi[i])
            goto next;
          lookaheadi[i] = lookaheadc[i].begin();
      }
      for (int i = nbacktrack - 1; i >= 0; i--) {
          backtracki[i]++;
          if (backtracki[i])
            goto next;
          backtracki[i] = backtrackc[i].begin();
      }
      break;

      next: ;
    }

    return any;
}


}}

Generated by  Doxygen 1.6.0   Back to index