/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.

    Copyright (C) 2002-2020 by Jin-Hwan Cho and Shunsaku Hirata,
    the dvipdfmx project team.
    
    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.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "system.h"

#include "numbers.h"
#include "error.h"
#include "mem.h"

#include "dpxconf.h"
#include "dpxfile.h"
#include "dpxutil.h"

#include "pdfobj.h"
#include "pdfresource.h"
#include "pdffont.h"

#include "pdfencoding.h"
#include "unicode.h"
#include "agl.h"

/* TrueType */
#include "sfnt.h"
#include "tt_cmap.h"
#include "tt_table.h"
#include "tt_glyf.h"
#include "tt_post.h"
#include "tt_gsub.h"
#include "tt_aux.h"

#include "truetype.h"

/* Modifying this has no effect :P */
#ifdef ENABLE_NOEMBED
#  undef ENABLE_NOEMBED
#endif

int
pdf_font_open_truetype (pdf_font *font, const char *ident, int index, int encoding_id, int embedding)
{
  pdf_obj  *fontdict, *descriptor;
  sfnt     *sfont;
  FILE     *fp = NULL;
  int       length, error = 0;

  ASSERT(font);
  ASSERT(ident);

  fp = DPXFOPEN(ident, DPX_RES_TYPE_TTFONT);
  if (!fp) {
    fp = DPXFOPEN(ident, DPX_RES_TYPE_DFONT);
    if (!fp)
      return  -1;
    sfont = dfont_open(fp, index);
  } else {
    sfont = sfnt_open(fp);
  }

  if (!sfont) {
    WARN("Could not open TrueType font: %s", ident);
    if (fp)
      DPXFCLOSE(fp);
    return  -1;
  }

  if (sfont->type == SFNT_TYPE_TTC) {
    ULONG offset;
    
    offset = ttc_read_offset(sfont, index);
    if (offset == 0) {
      WARN("Invalid TTC index in %s.", ident);
      error = -1;
    } else {
      error = sfnt_read_table_directory(sfont, offset);
    }
  } else {
    error = sfnt_read_table_directory(sfont, sfont->offset);
  }
  if (error) {
    sfnt_close(sfont);
    if (fp)
      DPXFCLOSE(fp);
    return  -1; /* Silently */
  }

  /* Reading fontdict before checking fonttype conflicts with PKFONT
   * because pdf_font_get_resource() always makes a dictionary.
   */
  fontdict   = pdf_font_get_resource(font);
  descriptor = pdf_font_get_descriptor(font);
#ifndef  ENABLE_NOEMBED
  if (!embedding) {
    WARN("No-embed option not supported for TrueType font: %s", ident);
    embedding = 1;
    font->flags &= ~PDF_FONT_FLAG_NOEMBED;
  }
#endif /* ENABLE_NOEMBED */

  ASSERT( fontdict && descriptor );

  {
    char     fontname[256];
    int      n;
    pdf_obj *tmp;

    memset(fontname, 0, 256);
    length = tt_get_ps_fontname(sfont, fontname, 255);
    if (length < 1) {
      length = MIN(strlen(ident), 255);
      memcpy(fontname, ident, length);
    }
    fontname[length] = '\0';
    for (n = 0; n < length; n++) {
      if (fontname[n] == 0) {
        memmove(fontname + n, fontname + n + 1, length - n - 1);
      }
    }
    if (strlen(fontname) == 0) {
      WARN("Can't find valid fontname for \"%s\".", ident);
      error = -1;
    } else {
      font->fontname = NEW(strlen(fontname)+1, char);
      strcpy(font->fontname, fontname);
      tmp = tt_get_fontdesc(sfont, &embedding, -1, 1, fontname);
      if (!tmp) {
        WARN("Could not obtain necessary font info: %s", ident);
        error = -1;
      } else {
        ASSERT(pdf_obj_typeof(tmp) == PDF_DICT);
        pdf_merge_dict(descriptor, tmp);
        pdf_release_obj(tmp);
      }
    }
  }
  if (error) {
    sfnt_close(sfont);
    if (fp)
      DPXFCLOSE(fp);
    return  -1;
  }

  if (!embedding) {
#ifndef ENABLE_NOEMBED
    WARN("Font file=\"%s\" can't be embedded due to liscence restrictions.", ident);
    error = -1;
#else
    if (encoding_id >= 0 && !pdf_encoding_is_predefined(encoding_id)) {
      WARN("Custom encoding not allowed for non-embedded TrueType font: %s", ident);
      error = -1;
    } else {
      /* There are basically no guarantee for font substitution
       * can work with "symblic" fonts. At least all glyphs
       * contained in the font must be identified; glyphs covers
       * by this instance of font should contain glyphs only from
       * Adobe Standard Latin Set. We allow non-embedded font
       * only to predefined encodings for this reason. Note that
       * "builtin" encoding means "MacRoman" here.
       */
      pdf_obj *tmp;
      int      flags;

      font->flags |= PDF_FONT_FLAG_NOEMBED;
      tmp = pdf_lookup_dict(descriptor, "Flags");
      if (tmp && pdf_obj_typeof(tmp) == PDF_NUMBER) {
        flags  = (int) pdf_number_value(tmp);
        flags &= (1 << 2); /* clear Symbolic */
        flags |= (1 << 5); /* set Nonsymbolic */
        pdf_add_dict(descriptor, pdf_new_name("Flags"), pdf_new_number(flags));
      }
    }
#endif /* ENABLE_NOEMBED */
  }

  sfnt_close(sfont);
  if (fp)
    DPXFCLOSE(fp);

  if (!error) {
    pdf_add_dict(fontdict,
                pdf_new_name("Type"),    pdf_new_name("Font"));
    pdf_add_dict(fontdict,
                pdf_new_name("Subtype"), pdf_new_name("TrueType"));
    font->subtype = PDF_FONT_FONTTYPE_TRUETYPE;
  }

  return error;
}

/*
 * The 'name' table should be preserved since it contains copyright
 * information, but it might cause problem when there are invalid
 * table entries (wrongly encoded text which is often the case in
 * CJK fonts). Acrobat does not use 'name' table. Unicode TrueType
 * fonts may have 10K bytes 'name' table...
 *
 * We preserve the 'OS/2' table too, since it contains the license
 * information. PDF applications should use this table to decide
 * whether the font is embedded only for the purpose of preview &
 * printing. Otherwise, we must encrypt the document. Acrobat does
 * not use 'OS/2' table, though...
 */
static struct
{
  const char *name;
  int   must_exist;
} required_table[] = {
  {"OS/2", 0}, {"head", 1}, {"hhea", 1}, {"loca", 1}, {"maxp", 1},
  {"name", 1}, {"glyf", 1}, {"hmtx", 1}, {"fpgm", 0}, {"cvt ", 0},
  {"prep", 0}, {"cmap", 1}, {NULL, 0}
};

static void
do_widths (pdf_font *font, double *widths)
{
  pdf_obj  *fontdict;
  pdf_obj  *array;
  int       code, firstchar, lastchar;
  char     *usedchars;

  fontdict   = font->resource;
  usedchars  = font->usedchars;

  for (firstchar = 255, lastchar = 0, code = 0; code < 256; code++) {
    if (usedchars[code]) {
      if (code < firstchar) firstchar = code;
      if (code > lastchar)  lastchar  = code;
    }
  }
  if (firstchar > lastchar) {
    WARN("No glyphs actually used???");
    return;
  }

  pdf_check_tfm_widths(font->ident, widths, firstchar, lastchar, usedchars);

  array = pdf_new_array();
  for (code = firstchar; code <= lastchar; code++) {
    if (usedchars[code]) {
      pdf_add_array(array, pdf_new_number(ROUND(widths[code], 0.1)));
    } else {
      pdf_add_array(array, pdf_new_number(0.0));
    }
  }
  if (pdf_array_length(array) > 0) {
    pdf_add_dict(fontdict, pdf_new_name("Widths"), pdf_ref_obj(array));
  }
  pdf_release_obj(array);

  pdf_add_dict(fontdict,
               pdf_new_name("FirstChar"), pdf_new_number(firstchar));
  pdf_add_dict(fontdict,
               pdf_new_name("LastChar"),  pdf_new_number(lastchar));

  return;
}

#define PDFUNIT(v) ((double) (ROUND(1000.0*(v)/(glyphs->emsize), 1)))

/*
 * There are several issues in TrueType font support in PDF.
 * How PDF viewers select TrueType cmap table is not so clear.
 * Most reliable way seem to reencode font and sort glyphs as
 * charcode == gid and to use Mac-Roman format 0 subtable.
 * It does not work with encodings that uses full 256 range since
 * GID = 0 is reserved for .notdef, so GID = 256 is not accessible.
 */
static int
do_builtin_encoding (pdf_font *font, const char *usedchars, sfnt *sfont)
{
  struct tt_glyphs *glyphs;
  char             *cmap_table;
  tt_cmap          *ttcm;
  USHORT            gid, idx;
  int               code, count;
  double            widths[256];

  ttcm = tt_cmap_read(sfont, TT_MAC, TT_MAC_ROMAN);
  if (!ttcm) {
    WARN("Could not read Mac-Roman TrueType cmap table...");
    return  -1;
  }

  cmap_table = NEW(274, char);
  memset(cmap_table, 0, 274);
  sfnt_put_ushort(cmap_table,    0);            /* Version  */
  sfnt_put_ushort(cmap_table+2,  1);            /* Number of subtables */
  sfnt_put_ushort(cmap_table+4,  TT_MAC);       /* Platform ID */
  sfnt_put_ushort(cmap_table+6,  TT_MAC_ROMAN); /* Encoding ID */
  sfnt_put_ulong (cmap_table+8,  12);           /* Offset   */
  sfnt_put_ushort(cmap_table+12, 0);            /* Format   */
  sfnt_put_ushort(cmap_table+14, 262);          /* Length   */
  sfnt_put_ushort(cmap_table+16, 0);            /* Language */

  glyphs = tt_build_init();

  if (dpx_conf.verbose_level > 2)
    MESG("[glyphs:/.notdef");

  count = 1; /* .notdef */
  for (code = 0; code < 256; code++) {
    if (!usedchars[code])
      continue;

    if (dpx_conf.verbose_level > 2)
      MESG("/.c0x%02x", code);

    gid = tt_cmap_lookup(ttcm, code);
    if (gid == 0) {
      WARN("Glyph for character code=0x%02x missing in font font-file=\"%s\".",
           code, font->filename);
      idx = 0;
    } else {
      idx = tt_find_glyph(glyphs, gid);
      if (idx == 0)
        idx  = tt_add_glyph(glyphs, (USHORT)gid, (USHORT)count); /* count returned. */
    }
    cmap_table[18+code] = idx & 0xff; /* bug here */
    count++;
  }
  tt_cmap_release(ttcm);

  if (dpx_conf.verbose_level > 2)
    MESG("]");

  if (tt_build_tables(sfont, glyphs) < 0) {
    WARN("Packing TrueType font into SFNT failed!");
    tt_build_finish(glyphs);
    RELEASE(cmap_table);
    return  -1;
  }

  for (code = 0; code < 256; code++) {
    if (usedchars[code]) {
      idx = tt_get_index(glyphs, (USHORT) cmap_table[18+code]);
      widths[code] = PDFUNIT(glyphs->gd[idx].advw);
    } else {
      widths[code] = 0.0;
    }
  }
  do_widths(font, widths);

  if (dpx_conf.verbose_level > 1) 
    MESG("[%d glyphs]", glyphs->num_glyphs);

  tt_build_finish(glyphs);

  sfnt_set_table(sfont, "cmap", cmap_table, 274);

  return  0;
}

/* Order of lookup should be
 *  post, unicode+otl
 */
struct glyph_mapper
{
  tt_cmap  *codetogid;
  otl_gsub *gsub;
  sfnt     *sfont;
  struct tt_post_table *nametogid;
};


/* WARNING: This modifies glyphname itself */
static int
agl_decompose_glyphname (char *glyphname, char **nptrs, int size, char **suffix)
{
  char  *q, *p = glyphname;
  int    n;

  q = strchr(p, '.'); /* chop every thing after *first* dot */
  if (!q)
    *suffix = NULL;
  else {
    *q = '\0'; q++;
    *suffix = q;
  }

  nptrs[0] = p;
  for (n = 1; p && *p; n++) {
    p = strchr(p, '_');
    if (!p || p[1] == '\0')
      break;
    if (n >= size)
      ERROR("Uh ah..."); /* _FIXME_ */
    *p = '\0'; p++;
    nptrs[n] = p;
  }

  return  n;
}

static int
select_gsub (const char *feat, struct glyph_mapper *gm)
{
  int    idx, error = 0;

  if (!feat || *feat == 0 || !gm || !gm->gsub)
    return  -1;

  /* First treat as is */
  idx = otl_gsub_select(gm->gsub, "*", "*", feat);
  if (idx >= 0)
    return  0;

  if (dpx_conf.verbose_level > 1)
    MESG("\ntrutype>> Try loading OTL GSUB for \"*.*.%s\"...", feat);
  error = otl_gsub_add_feat(gm->gsub, "*", "*", feat, gm->sfont);
  if (!error) {
    idx = otl_gsub_select(gm->gsub, "*", "*", feat);
    return  (idx >= 0 ? 0 : -1);
  }

  return  -1;
}

static int findparanoiac (const char *glyph_name, USHORT *gid, struct glyph_mapper *gm);
static int resolve_glyph (const char *glyph_name, USHORT *gid, struct glyph_mapper *gm);

/* Apply GSUB. This is a bit tricky... */
static int
selectglyph (USHORT in, const char *suffix, struct glyph_mapper *gm, USHORT *out)
{
  char  *s, *q, t[5];
  const char *r;
  int    n, error = 0;

  ASSERT(suffix && gm && out);
  ASSERT(suffix && *suffix != 0);

  s = NEW(strlen(suffix) + 1, char);
  strcpy(s, suffix);

  /* First try converting suffix to feature tag.
   * agl.c currently only knows less ambiguos cases;
   * e.g., 'sc', 'superior', etc.
   */
  r = agl_suffix_to_otltag(s);
  if (r) { /* We found feature tag for 'suffix'. */
    error = select_gsub(r, gm); /* no fallback for this */
    if (!error)
      error = otl_gsub_apply(gm->gsub, &in);
  } else { /* 'suffix' may represent feature tag. */
    /* Try loading GSUB only when length of 'suffix' is less
     * than or equal to 4. tt_gsub give a warning otherwise.
     */
    if (strlen(s) > 4)
      error = -1; /* Uh */
    else if (strlen(s) == 4)
      error = select_gsub(s, gm);
    else { /* less than 4. pad ' '. */
      memset(t, ' ', 4); t[4] = '\0';
      memcpy(t, s, strlen(s));
      error = select_gsub(t, gm);
    }
    if (!error) /* 'suffix' represents feature tag. */
      error = otl_gsub_apply(gm->gsub, &in);
    else { /* other case: alt1, nalt10... (alternates) */
      for (q = s + strlen(s) - 1; q > s && *q >= '0' && *q <= '9'; q--);
      if (q == s)
        error = -1;
      else { /* starting at 1 */
        n = atoi(q + 1) - 1; q[1] = '\0';
        if (strlen(s) > 4)
          error = -1;
        else { /* This may be alternate substitution. */
          memset(t, ' ', 4); t[4] = '\0';
          memcpy(t, s, strlen(s));
          error = select_gsub(s, gm);
          if (!error)
            error = otl_gsub_apply_alt(gm->gsub, (USHORT)n, (USHORT *)&in);
        }
      }
    }
  }
  RELEASE(s);

  *out = in;
  return  error;
}


/* Compose glyphs via ligature substitution. */
static int
composeglyph (USHORT *glyphs, int n_glyphs,
              const char *feat, struct glyph_mapper *gm, USHORT *gid)
{
  int   error = 0;
  char  t[5] = {' ', ' ', ' ', ' ', 0};

  ASSERT(glyphs && n_glyphs > 0 && gm && gid);

  if (!feat || feat[0] == '\0') /* meaning "Unknown" */
      error = select_gsub("(?lig|lig?|?cmp|cmp?|frac|afrc)", gm);
  else {
    if (strlen(feat) > 4)
      error = -1;
    else {
      memcpy(t, feat, strlen(feat));
      error = select_gsub(t, gm);
    }
  }

  if (!error)
    error = otl_gsub_apply_lig(gm->gsub, (USHORT *)glyphs, (USHORT)n_glyphs,
                               (USHORT *)gid);

  return  error;
}

/* This may be called by findparanoiac(). */
static int
composeuchar (int32_t *unicodes, int n_unicodes,
              const char *feat, struct glyph_mapper *gm, USHORT *gid)
{
  USHORT  *gids;
  int      i, error = 0;

  if (!gm->codetogid)
    return  -1;

  gids = NEW(n_unicodes, USHORT);
  for (i = 0;
       !error && i < n_unicodes; i++) {
    gids[i] = tt_cmap_lookup(gm->codetogid, unicodes[i]);
    error   = (gids[i] == 0) ? -1 : 0;
  }

  if (!error)
    error = composeglyph(gids, n_unicodes, feat, gm, gid);

  RELEASE(gids);

  return  error;
}

/* Search 'post' table. */
static int
findposttable (const char *glyph_name, USHORT *gid, struct glyph_mapper *gm)
{
  if (!gm->nametogid)
    return -1;

  *gid = tt_lookup_post_table(gm->nametogid, glyph_name);
#if  0
  if (dpx_conf.verbose_level > 1)
  {
    if (*gid > 0)
      MESG("%s =post=> 0x%04X\n", glyph_name, *gid);
  }
#endif

  return (*gid == 0 ? -1 : 0);
}

/* This is wrong. We must care about '.'. */
#define is_comp(n) (strchr((n), '_') != NULL)

/* Glyph names are concatinated with '_'. */
static int
findcomposite (const char *glyphname, USHORT *gid, struct glyph_mapper *gm)
{
  char     *gname, *suffix = NULL;
  USHORT    gids[32];
  char     *nptrs[32];
  int       i, n_comp;
  int       error = 0;

  error = findposttable(glyphname, gid, gm);
  if (!error)
    return  0;

  gname = NEW(strlen(glyphname) + 1, char);
  strcpy(gname, glyphname);

  memset(gids, 0, 32 * sizeof(USHORT));
  n_comp = agl_decompose_glyphname(gname, nptrs, 32, &suffix);
  for (error = 0, i = 0; !error && i < n_comp; i++) {
    error = resolve_glyph(nptrs[i], &gids[i], gm);
    if (error)
      WARN("Could not resolve glyph \"%s\" (%dth component of glyph \"%s\").",
           nptrs[i], i, glyphname);
  }

  if (!error) {
    if (suffix &&
        (!strcmp(suffix, "liga") || !strcmp(suffix, "dlig") ||
         !strcmp(suffix, "hlig") || !strcmp(suffix, "frac") ||
         !strcmp(suffix, "ccmp") || !strcmp(suffix, "afrc")
        )
       ) {
      error = composeglyph(gids, n_comp, suffix, gm, gid);
    } else { /* first try composing glyph */
      error = composeglyph(gids, n_comp, NULL, gm, gid);
      if (!error && suffix) /* a_b_c.vert */
        error = selectglyph(*gid, suffix, gm, gid);
    }
  }
  RELEASE(gname);

  return  error;
}

/* glyphname should not have suffix here */
static int
findparanoiac (const char *glyphname, USHORT *gid, struct glyph_mapper *gm)
{
  agl_name  *agln;
  USHORT     idx   = 0U;
  int        error = 0;

  agln = agl_lookup_list(glyphname);
  while (agln && idx == 0) {
    if (agln->suffix) {
      error = findparanoiac(agln->name, &idx, gm);
      if (error)
        return error;

      error = selectglyph(idx, agln->suffix, gm, &idx);
      if (error) {
        WARN("Variant \"%s\" for glyph \"%s\" might not be found.",
             agln->suffix, agln->name);
        WARN("Using glyph name without suffix instead...");
        error = 0; /* ignore */
      }
    } else {
      if (agln->n_components == 1)
        idx = tt_cmap_lookup(gm->codetogid, agln->unicodes[0]);
      else if (agln->n_components > 1) {
        if (dpx_conf.verbose_level >= 0) /* give warning */
          WARN("Glyph \"%s\" looks like a composite glyph...",
               agln->name);
        error = composeuchar(agln->unicodes, agln->n_components, NULL, gm, &idx);
        if (dpx_conf.verbose_level >= 0) {
          if (error)
            WARN("Not found...");
          else {
            int   _i, _n = 0;
            char *_p, _buf[256];
            WARN(">> Composite glyph glyph-name=\"%s\" found at glyph-id=\"%u\".",
                  agln->name, idx);
            for (_p = _buf, _i = 0; _i < agln->n_components && _n < 245; _i++) {
              _p[_n++] = _i == 0 ? '<' : ' ';
              if (agln->unicodes[_i] >= 0x10000)
                _n += sprintf(_p+_n, "U+%06X", agln->unicodes[_i]);
              else
                _n += sprintf(_p+_n, "U+%04X", agln->unicodes[_i]);
              _p[_n++] = _i == agln->n_components - 1 ? '>' : ',';
            }
            _p[_n++] = '\0';
            WARN(">> Input Unicode seq.=\"%s\" ==> glyph-id=\"%u\" in font-file=\"_please_try_-v_\".", _buf, idx);
          }
        }
      } else ASSERT(0); /* Boooo */
    }
    agln = agln->alternate;
  }

  *gid = idx;
  return (idx == 0 ? -1 : 0);
}

static int
resolve_glyph (const char *glyphname, USHORT *gid, struct glyph_mapper *gm)
{
  int    error = 0;
  char  *name, *suffix = NULL;
  int32_t ucv;

  ASSERT(glyphname);

  /*
   * First we try glyph name to GID mapping using post table if post table
   * is available. If post table is not available or glyph is not listed 
   * in the post table, then we try Unicode if Windows-Unicode TrueType
   * cmap is available.
   */
  error = findposttable(glyphname, gid, gm);
  if (!error)
    return  0;

  if (!gm->codetogid)
    return  -1;

  name = agl_chop_suffix(glyphname, &suffix);
  if (!name) /* .notdef, .foo */
    error = -1;
  else if (agl_name_is_unicode(name)) {
    ucv  = agl_name_convert_unicode(name);
    *gid = tt_cmap_lookup(gm->codetogid, ucv);
    error = (*gid == 0) ? -1 : 0;
  } else {
    error = findparanoiac(name, gid, gm);
  }
  if (!error && suffix) {
    error = selectglyph(*gid, suffix, gm, gid);
    if (error) {
      WARN("Variant \"%s\" for glyph \"%s\" might not be found.",
           suffix, name);
      WARN("Using glyph name without suffix instead...");
      error = 0; /* ignore */
    }
  }
  if (suffix)
    RELEASE(suffix);
  if (name)
    RELEASE(name);

  return  error;
}

/* Things are complicated. We still need to use PostScript
 * glyph names. But OpenType fonts may not have PS name to
 * glyph mapping. We use Unicode plus OTL GSUB for finding
 * glyphs in this case.
 */
static int
setup_glyph_mapper (struct glyph_mapper *gm, sfnt *sfont)
{
  gm->sfont     = sfont;
  gm->nametogid = tt_read_post_table(sfont);
  gm->codetogid = tt_cmap_read(sfont, TT_WIN, TT_WIN_UCS4);
  if (!gm->codetogid)
    gm->codetogid = tt_cmap_read(sfont, TT_WIN, TT_WIN_UNICODE);

  if (!gm->nametogid && !gm->codetogid)
    return -1;

  gm->gsub = otl_gsub_new();

  return 0;
}

static void
clean_glyph_mapper (struct glyph_mapper *gm)
{
  if (gm->gsub)
    otl_gsub_release(gm->gsub);
  if (gm->codetogid)
    tt_cmap_release (gm->codetogid);
  if (gm->nametogid)
    tt_release_post_table(gm->nametogid);

  gm->gsub = NULL;
  gm->codetogid = NULL;
  gm->nametogid = NULL;
  gm->sfont = NULL;

  return;
}

static int
do_custom_encoding (pdf_font *font,
                    char **encoding, const char *usedchars, sfnt *sfont)
{
  struct tt_glyphs      *glyphs;
  char                  *cmap_table;
  int                    code, count;
  double                 widths[256];
  struct glyph_mapper    gm;
  USHORT                 idx, gid;
  int                    error = 0;

  ASSERT(font && encoding && usedchars && sfont);

  error = setup_glyph_mapper(&gm, sfont);
  if (error) {
    WARN("No post table nor Unicode cmap found in font: %s", font->filename);
    WARN(">> I can't find glyphs without this!");
    return  -1;
  }

  cmap_table = NEW(274, char);
  memset(cmap_table, 0, 274);
  sfnt_put_ushort(cmap_table,    0);            /* Version  */
  sfnt_put_ushort(cmap_table+2,  1);            /* Number of subtables */
  sfnt_put_ushort(cmap_table+4,  TT_MAC);       /* Platform ID */
  sfnt_put_ushort(cmap_table+6,  TT_MAC_ROMAN); /* Encoding ID */
  sfnt_put_ulong (cmap_table+8,  12);           /* Offset   */
  sfnt_put_ushort(cmap_table+12, 0);            /* Format   */
  sfnt_put_ushort(cmap_table+14, 262);          /* Length   */
  sfnt_put_ushort(cmap_table+16, 0);            /* Language */

  glyphs = tt_build_init();

  count = 1; /* +1 for .notdef */
  for (code = 0; code < 256; code++) {
    if (!usedchars[code])
      continue;

    if (!encoding[code] || !strcmp(encoding[code], ".notdef")) {
      WARN("Character code=\"0x%02X\" mapped to \".notdef\" glyph used in font font-file=\"%s\"",
           code, font->filename);
      WARN(">> Maybe incorrect encoding specified?");
      idx = 0;
    } else {
      if (is_comp(encoding[code]))
        error = findcomposite(encoding[code], &gid, &gm);
      else
        error = resolve_glyph(encoding[code], &gid, &gm);

      /*
       * Older versions of gs had problem with glyphs (other than .notdef)
       * mapped to gid = 0.
       */
      if (error) {
        WARN("Glyph \"%s\" not available in font \"%s\".",
             encoding[code], font->filename);
      } else {
        if (dpx_conf.verbose_level > 1)
          MESG("truetype>> Glyph glyph-name=\"%s\" found at glyph-id=\"%u\".\n", encoding[code], gid);
      }
      idx = tt_find_glyph(glyphs, gid);
      if (idx == 0) {
        idx = tt_add_glyph(glyphs, (USHORT)gid, (USHORT)count); /* count returned. */
        count++;
      }
    }
    cmap_table[18 + code] = idx & 0xff; /* bug here */
  }
  clean_glyph_mapper(&gm);

  if (tt_build_tables(sfont, glyphs) < 0) {
    WARN("Packing TrueType font into SFNT file faild..."); /* _FIXME_: wrong message */
    tt_build_finish(glyphs);
    RELEASE(cmap_table);
    return  -1;
  }

  for (code = 0; code < 256; code++) {
    if (usedchars[code]) {
      idx = tt_get_index(glyphs, (USHORT) cmap_table[18+code]);
      widths[code] = PDFUNIT(glyphs->gd[idx].advw);
    } else {
      widths[code] = 0.0;
    }
  }
  do_widths(font, widths);

  if (dpx_conf.verbose_level > 1) 
    MESG("[%d glyphs]", glyphs->num_glyphs);

  tt_build_finish(glyphs);

  sfnt_set_table(sfont, "cmap", cmap_table, 274);

  return  0;
}

int
pdf_font_load_truetype (pdf_font *font)
{
  pdf_obj   *descriptor  = font->descriptor;
  char      *ident       = font->filename;
  int        encoding_id = font->encoding_id;
  char      *usedchars   = font->usedchars;
#ifdef  ENABLE_NOEMBED
  int        embedding   = font->flags & PDF_FONT_FLAG_NOEMBED ? 0 : 1;
#endif /* ENABLE_NOEMBED */
  int        index       = font->index;
  char     **enc_vec;
  pdf_obj   *fontfile;
  FILE      *fp = NULL;
  sfnt      *sfont;
  int        i, error = 0;

  if (!font->reference)
    return  0;

  fp = DPXFOPEN(ident, DPX_RES_TYPE_TTFONT);
  if (!fp) {
    fp = DPXFOPEN(ident, DPX_RES_TYPE_DFONT);
    if (!fp) ERROR("Unable to open TrueType/dfont font file: %s", ident); /* Should find *truetype* here */
    sfont = dfont_open(fp, index);
  } else {
    sfont = sfnt_open(fp);
  }

  if (!sfont) {
    ERROR("Unable to open TrueType/dfont file: %s", ident);
    if (fp)
      DPXFCLOSE(fp);
    return  -1;
  } else if (sfont->type != SFNT_TYPE_TRUETYPE &&
             sfont->type != SFNT_TYPE_TTC &&
             sfont->type != SFNT_TYPE_DFONT) { 
    ERROR("Font \"%s\" not a TrueType/dfont font?", ident);
    sfnt_close(sfont);
    if (fp)
      DPXFCLOSE(fp);
    return  -1;
  }

  if (sfont->type == SFNT_TYPE_TTC) {
    ULONG offset;
    offset = ttc_read_offset(sfont, index);
    if (offset == 0) ERROR("Invalid TTC index in %s.", ident);
    error = sfnt_read_table_directory(sfont, offset);
  } else {
    error = sfnt_read_table_directory(sfont, sfont->offset);
  }

  if (error) {
    ERROR("Reading SFND table dir failed for font-file=\"%s\"... Not a TrueType font?", ident);
    sfnt_close(sfont);
    if (fp)
      DPXFCLOSE(fp);
    return  -1;
  }

  /*
   * Create new TrueType cmap table with MacRoman encoding.
   */
  if (encoding_id < 0)
    error = do_builtin_encoding(font, usedchars, sfont);
  else {
    enc_vec  = pdf_encoding_get_encoding(encoding_id);
    error = do_custom_encoding(font, enc_vec, usedchars, sfont);
  }
  if (error) {
    ERROR("Error occured while creating font subfont for \"%s\"", ident);
    sfnt_close(sfont);
    if (fp)
      DPXFCLOSE(fp);
    return  -1;
  }

#if  ENABLE_NOEMBED
  if (!embedding) {
    sfnt_close(sfont);
    if (fp)
      DPXFCLOSE(fp);
    return  0;
  }
#endif /* ENABLE_NOEMBED */

  /*
   * TODO: post table?
   */

  for (i = 0; required_table[i].name != NULL; i++) {
    if (sfnt_require_table(sfont,
                           required_table[i].name,
                           required_table[i].must_exist) < 0) {
      ERROR("Required TrueType table \"%s\" does not exist in font: %s",
            required_table[i].name, ident);
      sfnt_close(sfont);
      if (fp)
        DPXFCLOSE(fp);
      return  -1;
    }
  }

  /*
   * FontFile2
   */
  fontfile = sfnt_create_FontFile_stream(sfont);
  if (!fontfile)
    ERROR("Could not created FontFile stream for \"%s\".", ident);

  sfnt_close(sfont);
  if (fp)
    DPXFCLOSE(fp);

  if (dpx_conf.verbose_level > 1)
    MESG("[%ld bytes]", pdf_stream_length(fontfile));

  pdf_add_dict(descriptor,
               pdf_new_name("FontFile2"), pdf_ref_obj(fontfile)); /* XXX */
  pdf_release_obj(fontfile);

  return  0;
}
