/*
Copyright (c) 1996-2005 Han The Thanh, <thanh@pdftex.org>

This file is part of pdfTeX.

pdfTeX 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.

pdfTeX 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 pdfTeX; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/ttf2afm.c#15 $
*/

/*
#include <kpathsea/c-auto.h>
#include <kpathsea/c-fopen.h>
#include <kpathsea/c-limits.h>
#include <kpathsea/c-memstr.h>
#include <kpathsea/c-proto.h>
#include <kpathsea/c-std.h>
#include <kpathsea/c-unistd.h>
#include <kpathsea/c-vararg.h>
#include <kpathsea/getopt.h>
*/
#include <kpathsea/kpathsea.h>
#include <time.h>
#include <libgen.h>
#include <pdftexdir/ptexmac.h>
#include <pdftexdir/writettf.h>

static const char perforce_id[] = 
    "$Id: //depot/Build/source.development/TeX/texk/web2c/pdftexdir/ttf2afm.c#15 $";

/* constants used for print_glyph */
#define AS_NAME         0
#define AS_INDEX        1
#define AS_UNICODE      2

#define VERSION         "1.0"

#define enc_getchar()   xgetc(encfile)
#define enc_eof()       feof(encfile)
#define pdftex_fail     ttf_fail

#define print_str(S)    if (S != NULL) fprintf(outfile, #S " %s\n", S)
#define print_dimen(N)  if (N != 0) fprintf(outfile, #N " %i\n", (int)get_ttf_funit(N))

#define get_ttf_funit(n) \
    (n < 0 ? -((-n/upem)*1000 + ((-n%upem)*1000)/upem) :\
    ((n/upem)*1000 + ((n%upem)*1000)/upem))

typedef struct _unicode_entry {
    TTF_USHORT code;
    struct _unicode_entry *next;
} unicode_entry;

typedef struct {
    TTF_ULONG wx;
    const char *name;
    TTF_USHORT index;
    TTF_LONG bbox[4];
    TTF_LONG offset;
    char found;
    unicode_entry *unicode_list;
} mtx_entry;

typedef struct _kern_entry {
    TTF_FWORD value;
    TTF_USHORT adjacent;
    struct _kern_entry *next;
} kern_entry;


char *FontName = NULL;
char *FullName = NULL;
char *FamilyName = NULL;
char *Notice = NULL;
char *Version = NULL;
char *Weight = NULL;
TTF_LONG ItalicAngle = 0;
TTF_LONG IsFixedPitch = 0;
TTF_LONG FontBBox1 = 0;
TTF_LONG FontBBox2 = 0;
TTF_LONG FontBBox3 = 0;
TTF_LONG FontBBox4 = 0;
TTF_LONG UnderlinePosition = 0;
TTF_LONG UnderlineThickness = 0;
TTF_LONG CapHeight = 0;
TTF_LONG XHeight = 0;
TTF_LONG Ascender = 0;
TTF_LONG Descender = 0;

char *cur_file_name = NULL;
char *bname = NULL;
FILE *fontfile, *encfile, *outfile = NULL;
char enc_line[ENC_BUF_SIZE];
int print_glyph = AS_NAME;    /* print glyph as names by default*/
int print_cmap = 0;
int use_ext_enc = 0;    /* use external encoding? */
int select_unicode = 1; /* use the first unicode mapping by default */
int printing_enc = 0; /* set to 1 while printing encodings */


TTF_USHORT upem;
TTF_USHORT ntabs;
int nhmtx;
int post_format;
int loca_format;
int nglyphs;
int nkernpairs = 0;
int names_count = 0;
char *ps_glyphs_buf = NULL;
dirtab_entry *dir_tab;
mtx_entry *mtx_tab;
kern_entry *kern_tab;
char *enc_names[MAX_CHAR_CODE + 1];

cmap_entry *cmap_tab;
TTF_USHORT ncmapsubtabs;
long cmap_offset;

TTF_USHORT unicode_map[0xFFFF];

#include "macnames.c"

void ttf_fail(char *fmt,...)
{
    va_list args;
    va_start(args, fmt);
    fprintf(stderr, "\nError: ttf2afm");
    if (cur_file_name)
        fprintf(stderr, " (file %s)", cur_file_name);
    fprintf(stderr, ": ");
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
    va_end(args);
    exit(-1);
}

void ttf_warn(char *fmt,...)
{
    va_list args;
    va_start(args, fmt);
    fprintf(stderr, "\nWarning: ttf2afm");
    if (cur_file_name)
        fprintf(stderr, " (file %s)", cur_file_name);
    fprintf(stderr, ": ");
    vfprintf(stderr, fmt, args);
    fprintf(stderr, "\n");
    va_end(args);
}

int xgetc(FILE *stream)
{
    int c = getc(stream);
    if (c < 0 && c != EOF)
        ttf_fail("getc() failed");
    return c;
}

long ttf_getnum(int s)
{
    long i = 0;
    int c;
    while (s > 0) {
        if ((c = xgetc(fontfile)) < 0)
            ttf_fail("unexpected EOF");
        i = (i << 8) + c;
        s--;
    }
    return i;
}

dirtab_entry *name_lookup(char *s)
{
    dirtab_entry *p;
    for (p = dir_tab; p - dir_tab < ntabs; p++)
        if (strncmp(p->tag, s, 4) == 0)
            break;
    if (p - dir_tab == ntabs)
        p = NULL;
    return p;
}

void ttf_seek_tab(char *name, TTF_LONG offset)
{
    dirtab_entry *p = name_lookup(name);
    if (p == NULL)
        ttf_fail("can't find table `%s'", name);
    if (fseek(fontfile, p->offset + offset, SEEK_SET) < 0)
        ttf_fail("fseek() failed while reading `%s' table", name);
}

void ttf_seek_off(char *name, TTF_LONG offset)
{
    if (fseek(fontfile, offset, SEEK_SET) < 0)
        ttf_fail("fseek() failed while reading `%s' table", name);
}

void store_kern_value(TTF_USHORT i, TTF_USHORT j, TTF_FWORD v)
{
    kern_entry *pk;
    for (pk = kern_tab + i; pk->next != NULL; pk = pk->next);
    pk->next = xtalloc(1, kern_entry);
    pk = pk->next;
    pk->next = NULL;
    pk->adjacent = j;
    pk->value = v;
}

TTF_FWORD get_kern_value(TTF_USHORT i, TTF_USHORT j)
{
    kern_entry *pk;
    for (pk = kern_tab + i; pk->next != NULL; pk = pk->next)
        if (pk->adjacent == j)
            return pk->value;
    return 0;
}

void free_tabs()
{
    int i;
    kern_entry *p, *q, *r;
    unicode_entry *u, *v;
    mtx_entry *pm;
    xfree(ps_glyphs_buf);
    xfree(dir_tab);
    xfree(cmap_tab);
    for (pm = mtx_tab; pm - mtx_tab < nglyphs; pm++)
        if (pm->unicode_list != NULL) {
            for (u = pm->unicode_list; u != NULL; u = v) {
                v = u->next;
                xfree(u);
            }
        }
    xfree(mtx_tab);
    for (i = 0; i <= MAX_CHAR_CODE; i++)
        if (enc_names[i] != notdef)
            free(enc_names[i]);
    if (kern_tab == NULL)
        return;
    for (p = kern_tab; p - kern_tab < nglyphs; p++)
        if (p->next != NULL) {
            for (q = p->next; q != NULL; q = r) {
                r = q->next;
                xfree(q);
            }
        }
    xfree(kern_tab);
}

void enc_getline()
{
    char *p;
    int c;
restart:
    if (enc_eof())
        ttf_fail("unexpected end of file");
    p = enc_line;
    do {
        c = enc_getchar();
        append_char_to_buf(c, p, enc_line, ENC_BUF_SIZE);
    } while (c != 10);
    append_eol(p, enc_line, ENC_BUF_SIZE);
    if (p - enc_line <= 2 || *enc_line == '%')
        goto restart;
}

void read_encoding(char *encname)
{
    char buf[ENC_BUF_SIZE], *q, *r;
    int i;
    cur_file_name = encname;
    if ((encfile = kpse_open_file(encname, kpse_enc_format)) == NULL)
        ttf_fail("can't open encoding file for reading");
    enc_getline();
    if (*enc_line != '/' || (r = strchr(enc_line, '[')) == 0)
        ttf_fail("invalid encoding vector: name or `[' missing:\n%s", enc_line);
    for (i = 0; i <= MAX_CHAR_CODE; i++)
        enc_names[i] = (char*) notdef;
    if (r[1] == 32)
        r += 2;
    else
        r++;
    for (;;) {
        while (*r == '/') {
            for (q = buf, r++; *r != ' ' && *r != 10 && *r != ']' && *r != '/'; *q++ = *r++);
            *q = 0;
            if (*r == ' ')
                r++;
            if (strcmp(buf, notdef) != 0)
                enc_names[names_count] = xstrdup(buf);
            if (names_count++ > MAX_CHAR_CODE)
                ttf_fail("encoding vector contains more than %i names",
                     (int)(MAX_CHAR_CODE + 1));
        }
        if (*r != 10 && *r != '%')
            if (strncmp(r, "] def", strlen("] def")) == 0)
                goto done;
            else
                ttf_fail("invalid encoding vector: a name or `] def' expected:\n%s", enc_line);
        enc_getline();
        r = enc_line;
    }
done:
    xfclose(encfile, cur_file_name);
    if (names_count != MAX_CHAR_CODE + 1)
        ttf_warn("encoding vector contains only %i names (expected %i)", 
                 names_count, MAX_CHAR_CODE + 1);
}

void append_unicode(long glyph_index, TTF_USHORT code)
{
    mtx_entry *m;
    unicode_entry *u, *v;
    assert(glyph_index >= 0 && glyph_index < nglyphs);
    u = xtalloc(1, unicode_entry), *v;
    m = mtx_tab + glyph_index;
    u->next = NULL;
    u->code = code;
    if (m->unicode_list == NULL)
        m->unicode_list = u;
    else {
        for (v = m->unicode_list; v->next != NULL; v = v->next);
        v->next = u;
    }
}

void read_cmap()
{
    cmap_entry *e;
    seg_entry *seg_tab, *s;
    TTF_USHORT *glyphId, format, segCount;
    long int n, i, j, k, first_code, length, last_sep, index;
    int unicode_map_count = 0;
    ttf_seek_tab("cmap", TTF_USHORT_SIZE); /* skip the table vesrion number (=0) */
    ncmapsubtabs = get_ushort();
    cmap_offset = xftell(fontfile, cur_file_name) - 2*TTF_USHORT_SIZE;
    cmap_tab = xtalloc(ncmapsubtabs, cmap_entry);
    for (e = cmap_tab; e - cmap_tab < ncmapsubtabs; e++) {
        e->platform_id = get_ushort();
        e->encoding_id = get_ushort();
        e->offset = get_ulong();
    }
    for (i = 0; i < 0xFFFF; ++i)
        unicode_map[i] = NOGLYPH_ASSIGNED_YET;
    for (e = cmap_tab; e - cmap_tab < ncmapsubtabs; e++) {
        ttf_seek_off("cmap", cmap_offset + e->offset);
        format = get_ushort();
        if (is_unicode_mapping(e) && format == 4) {
            ++unicode_map_count;
            if (unicode_map_count == select_unicode)
                goto read_unicode_mapping;
        }
        continue;
read_unicode_mapping:
        length = get_ushort(); /* length of subtable */
        get_ushort(); /* skip the version number */
        segCount = get_ushort()/2;
        get_ushort(); /* skip searchRange */
        get_ushort(); /* skip entrySelector */
        get_ushort(); /* skip rangeShift */
        seg_tab = xtalloc(segCount, seg_entry);
        for (s = seg_tab; s - seg_tab < segCount; s++)
            s->endCode = get_ushort();
        get_ushort(); /* skip reversedPad */
        for (s = seg_tab; s - seg_tab < segCount; s++)
            s->startCode = get_ushort();
        for (s = seg_tab; s - seg_tab < segCount; s++)
            s->idDelta = get_ushort();
        for (s = seg_tab; s - seg_tab < segCount; s++)
            s->idRangeOffset = get_ushort();
        length -= 8*TTF_USHORT_SIZE + 4*segCount*TTF_USHORT_SIZE;
        n = length/TTF_USHORT_SIZE; /* number of glyphID's */
        glyphId = xtalloc(n, TTF_USHORT);
        for (i = 0; i < n; i++)
            glyphId[i] = get_ushort();
        for (s = seg_tab; s - seg_tab < segCount; s++) {
            for (i = s->startCode; i <= s->endCode; i++) {
                if (i == 0xFFFF)
                    break;
                if (s->idRangeOffset != 0xFFFF) {
                    if (s->idRangeOffset == 0)
                        index = (s->idDelta + i) & 0xFFFF;
                    else {
                        k = (i - s->startCode) + s->idRangeOffset/2 + 
                            (s - seg_tab) - segCount ;
                        assert(k >= 0 && k < n);
                        index = glyphId[k];
                        if (index != 0)
                            index = (index + s->idDelta) & 0xFFFF;
                    }
                    if (index < 0 || index >= nglyphs)
                        ttf_fail("cmap: glyph index out of range [0..%i)", nglyphs);
                    if (unicode_map[i] != NOGLYPH_ASSIGNED_YET)
                        ttf_fail("cmap: unicode %.4X is mapped to multiple glyphs", i);
                    if (unicode_map[i] == 0)
                        ttf_warn("unicode %.4X is mapped to glyph 0", i);
                    unicode_map[i] = index;
                    append_unicode(index, i);
                }
            }
        }
        xfree(seg_tab);
        xfree(glyphId);
        break;
    }
    if (e - cmap_tab == ncmapsubtabs)
        ttf_fail("Invalid argument `-m %i': out of range [1..%i]",
                 select_unicode, unicode_map_count);
}

void read_font()
{
    long i, j, k, l, n, m, platform_id, encoding_id;
    TTF_FWORD kern_value;
    char buf[1024], *p;
    dirtab_entry *pd;
    kern_entry *pk;
    mtx_entry *pm;
    ttf_skip(TTF_FIXED_SIZE);
    ntabs = get_ushort();
    ttf_skip(3*TTF_USHORT_SIZE);
    dir_tab = xtalloc(ntabs, dirtab_entry);
    for (pd = dir_tab; pd - dir_tab < ntabs; pd++) {
        pd->tag[0] = get_char();
        pd->tag[1] = get_char();
        pd->tag[2] = get_char();
        pd->tag[3] = get_char();
        ttf_skip(TTF_ULONG_SIZE);
        pd->offset = get_ulong();
        pd->length = get_ulong();
    }
    ttf_seek_tab("head", 2*TTF_FIXED_SIZE + 2*TTF_ULONG_SIZE + TTF_USHORT_SIZE);
    upem = get_ushort();
    ttf_skip(16);
    FontBBox1 = get_fword();
    FontBBox2 = get_fword();
    FontBBox3 = get_fword();
    FontBBox4 = get_fword();
    ttf_skip(TTF_USHORT_SIZE);
    ttf_skip(TTF_USHORT_SIZE + TTF_SHORT_SIZE);
    loca_format = get_short();
    ttf_seek_tab("maxp", TTF_FIXED_SIZE);
    nglyphs = get_ushort();
    mtx_tab = xtalloc(nglyphs + 1, mtx_entry);
    for (pm = mtx_tab; pm - mtx_tab < nglyphs + 1; pm++) {
        pm->name = NULL; /* notdef */
        pm->found = 0;
        pm->unicode_list = NULL;
    }
    ttf_seek_tab("hhea", TTF_FIXED_SIZE);
    Ascender = get_fword();
    Descender = get_fword();
    ttf_skip(TTF_FWORD_SIZE + TTF_UFWORD_SIZE + 3*TTF_FWORD_SIZE + 8*TTF_SHORT_SIZE);
    nhmtx = get_ushort();
    ttf_seek_tab("hmtx", 0);
    for (pm = mtx_tab; pm - mtx_tab < nhmtx; pm++) {
        pm->wx = get_ufword();
        ttf_skip(TTF_FWORD_SIZE);
    }
    i = pm[-1].wx;
    for (; pm - mtx_tab < nglyphs; pm++)
        pm->wx = i;
    ttf_seek_tab("post", 0);
    post_format = get_fixed();
    ItalicAngle = get_fixed();
    UnderlinePosition = get_fword();
    UnderlineThickness = get_fword();
    IsFixedPitch = get_ulong();
    ttf_skip(4*TTF_ULONG_SIZE);
    switch (post_format) {
    case 0x00010000:
        for (pm = mtx_tab; pm - mtx_tab < NMACGLYPHS; pm++)
            pm->name = mac_glyph_names[pm - mtx_tab];
        break;
    case 0x00020000:
        l = get_ushort(); /* some fonts have this value different from nglyphs */
        for (pm = mtx_tab; pm - mtx_tab < l; pm++)
            pm->index = get_ushort();
        if ((pd = name_lookup("post")) == NULL)
            ttf_fail("can't find table `post'");
        n = pd->length - (xftell(fontfile, cur_file_name) - pd->offset);
        ps_glyphs_buf = xtalloc(n + 1, char);
        for (m = 0, p = ps_glyphs_buf; p - ps_glyphs_buf < n;) {
            for (i = get_byte(); i > 0; i--)
                *p++ = get_char();
            *p++ = 0;
            m++;
        }
        for (pm = mtx_tab; pm - mtx_tab < l; pm++) {
            if (pm->index < NMACGLYPHS)
                pm->name = mac_glyph_names[pm->index];
            else {
                k = pm->index - NMACGLYPHS;
                if (k < m) {
                    for (p = ps_glyphs_buf; k > 0; k--)
                        p = (char *)strend(p) + 1;
                    pm->name = p;
                }
                else {
                    pm->name = NULL; /* index out of valid range, fix name to notdef */
                }
            }
        }
        break;
    case 0x00030000:
        if (print_glyph == AS_NAME) {
            ttf_warn("no names available in `post' table, print glyph names as indices");
            print_glyph = AS_INDEX;
        }
        break;
    default:
        ttf_fail("unsupported format (%.8X) of `post' table", post_format);
    }
    ttf_seek_tab("loca", 0);
    for (pm = mtx_tab; pm - mtx_tab < nglyphs + 1; pm++)
        pm->offset = (loca_format == 1 ? get_ulong() : get_ushort() << 1);
    if ((pd = name_lookup("glyf")) == NULL)
        ttf_fail("can't find table `glyf'");
    for (n = pd->offset, pm = mtx_tab; pm - mtx_tab < nglyphs; pm++) {
        ttf_seek_off("glyf", n + pm->offset);
        ttf_skip(TTF_SHORT_SIZE);
        pm->bbox[0] = get_fword();
        pm->bbox[1] = get_fword();
        pm->bbox[2] = get_fword();
        pm->bbox[3] = get_fword();
    }

    ttf_seek_tab("name", 0);
    i = ftell(fontfile);
    get_ushort();           /* skip Format selector (=0) */
    n = get_ushort();       /* number of name records */
    j = get_ushort() + i;   /* start of string storage */
    i += 3*TTF_USHORT_SIZE; /* update the current offset */
    while (n-- > 0) {
        ttf_seek_off("name", i);
        platform_id = get_ushort();
        encoding_id = get_ushort();
        get_ushort(); /* skip language_id */
        k = get_ushort(); /* name_id */
        l = get_ushort(); /* string length */
        if ((platform_id == 1 && encoding_id == 0) &&
            (k == 0 || k == 1 || k == 4 || k == 5 || 
             k == 6)) {
            ttf_seek_off("name", j + get_ushort());
            for (p = buf; l-- > 0; p++)
                *p = get_char();
            *p++ = 0;
            p = xstrdup(buf);
            switch (k) {
            case 0:  Notice = p; break;
            case 1:  FamilyName = p; break;
            case 4:  FullName = p; break;
            case 5:  Version = p; break;
            case 6:  FontName = p; break;
            }
            if (Notice != NULL && FamilyName != NULL && 
                FullName != NULL && FontName != NULL && 
                Version != NULL)
                break;
        }
        i += 6*TTF_USHORT_SIZE;
    }
    if ((pd = name_lookup("PCLT")) != NULL) {
        ttf_seek_off("PCLT", pd->offset + TTF_FIXED_SIZE + TTF_ULONG_SIZE + TTF_USHORT_SIZE);
        XHeight = get_ushort();
        if (XHeight > FontBBox3) {
            ttf_warn("XHeight is too big (%i)\n"
                     "This is likely a font bug, so I discarded this parameter.", 
                     (int)get_ttf_funit(XHeight));
            XHeight = 0;
        }
        ttf_skip(2*TTF_USHORT_SIZE);
        CapHeight = get_ushort();
        if (CapHeight > FontBBox3) {
            ttf_warn("CapHeight is too big (%i)\n"
                     "This is likely a font bug, so I discarded this parameter.", 
                     (int)get_ttf_funit(CapHeight));
            CapHeight = 0;
        }
    }
    if ((pd = name_lookup("OS/2")) != NULL) {
        ttf_seek_off("OS/2", pd->offset + TTF_USHORT_SIZE + TTF_SHORT_SIZE);
        switch (get_ushort()) {
        case 100: Weight = xstrdup("Thin"); break;
        case 200: Weight = xstrdup("ExtraLight"); break;
        case 300: Weight = xstrdup("Light"); break;
        case 400: Weight = xstrdup("Normal"); break;
        case 500: Weight = xstrdup("Medium"); break;
        case 600: Weight = xstrdup("SemiBold"); break;
        case 700: Weight = xstrdup("Bold"); break;
        case 800: Weight = xstrdup("ExtraBold"); break;
        case 900: Weight = xstrdup("Black"); break;
        }
    }
    read_cmap();
    if ((pd = name_lookup("kern")) == NULL)
        return;
    kern_tab = xtalloc(nglyphs, kern_entry);
    for (pk = kern_tab; pk - kern_tab < nglyphs; pk++) {
        pk->next = NULL;
        pk->value = 0;
    }
    ttf_seek_off("kern", pd->offset + TTF_USHORT_SIZE);
    for (n = get_ushort(); n > 0; n--) {
        ttf_skip(2*TTF_USHORT_SIZE);
        k = get_ushort();
        if (!(k & 1) || (k & 2) || (k & 4))
            return;
        if (k >> 8 != 0) {
            ttf_warn("warning: only format 0 supported of `kern' \
                 subtables, others are ignored\n");
            continue;
        }
        k = get_ushort();
        ttf_skip(3*TTF_USHORT_SIZE);
        while (k-- > 0) {
            i = get_ushort();
            j = get_ushort();
            kern_value = get_fword();
            if (kern_value != 0) {
                store_kern_value(i, j, kern_value);
                nkernpairs++;
            }
        }
    }
}

int null_glyph(const char *s)
{
    return (s == NULL || s == notdef);
    /*
        strcmp(s, ".null") == 0 ||
        strcmp(s, ".notdef") == 0 ||
        strcmp(s, "CR") == 0 ||
        strcmp(s, "nonmarkingreturn") == 0
    */
}

void print_glyph_name(FILE *f, long glyph_index, int convention)
{
    unicode_entry *u;
    static char buf[1024];
    const char *n;
    assert(glyph_index >= 0 && glyph_index < nglyphs);
    n = mtx_tab[glyph_index].name;
    if (printing_enc && (n == notdef || glyph_index == 0)) {
        fputs(notdef, f);
        return;
    }
    switch (convention) {
    case AS_NAME:
        if (!null_glyph(n))
            fprintf(f, "%s", mtx_tab[glyph_index].name);
        else if (n == notdef && glyph_index == 0)
            fputs(notdef, f);
        else
            fprintf(f, "%s%i", GLYPH_PREFIX_INDEX, glyph_index);
        break;
    case AS_INDEX:
        fprintf(f, "%s%i", GLYPH_PREFIX_INDEX, glyph_index);
        break;
    case AS_UNICODE:
        u = mtx_tab[glyph_index].unicode_list;
        if (glyph_index == 0 || u == NULL)
            fprintf(f, "%s%i", GLYPH_PREFIX_INDEX, glyph_index);
        else {
            fprintf(f, "%s%.4X", GLYPH_PREFIX_UNICODE, u->code);
            if (u->next != NULL) {
                *buf = 0;
                for (; u != NULL; u = u->next) {
                    assert(strlen(buf) + strlen(GLYPH_PREFIX_UNICODE) + 4 < sizeof(buf));
                    sprintf(strend(buf), "%s%.4X ", GLYPH_PREFIX_UNICODE, u->code);
                }
                ttf_warn("glyph %i have multiple encodings: %s", 
                         glyph_index, buf);
            }
        }
        break;
    }
}

void print_char_metric(FILE *f, int charcode, long glyph_index)
{
    assert(glyph_index >= 0 && glyph_index < nglyphs);
    fprintf(f, "C %i ; WX %i ; N ", (int) charcode, 
            (int)get_ttf_funit(mtx_tab[glyph_index].wx));
    print_glyph_name(f, glyph_index, print_glyph);
    fprintf(f, " ; B %i %i %i %i ;\n", 
            (int)get_ttf_funit(mtx_tab[glyph_index].bbox[0]),
            (int)get_ttf_funit(mtx_tab[glyph_index].bbox[1]),
            (int)get_ttf_funit(mtx_tab[glyph_index].bbox[2]),
            (int)get_ttf_funit(mtx_tab[glyph_index].bbox[3]));
}

void print_afm(char *date, char *fontname)
{
    int i, ncharmetrics;
    mtx_entry *pm;
    short mtx_index[MAX_CHAR_CODE + 1], *idx;
    unsigned int index;
    char **pe;
    kern_entry *pk, *qk;
    unicode_entry *u;
    char buf[20];
    fputs("StartFontMetrics 2.0\n", outfile);
    fprintf(outfile, "Comment Converted at %s by ttf2afm from font file `%s'\n", date, fontname);
    print_str(FontName);
    print_str(FullName);
    print_str(FamilyName);
    print_str(Weight);
    fprintf(outfile, "ItalicAngle %i", (int)(ItalicAngle/0x10000));
    if (ItalicAngle%0x10000 > 0)
        fprintf(outfile, ".%i", (int)((ItalicAngle%0x10000)*1000)/0x10000);
    fputs("\n", outfile);
    fprintf(outfile, "IsFixedPitch %s\n", IsFixedPitch ? "true" : "false");
    fprintf(outfile, "FontBBox %i %i %i %i\n", 
            (int)get_ttf_funit(FontBBox1),
            (int)get_ttf_funit(FontBBox2),
            (int)get_ttf_funit(FontBBox3),
            (int)get_ttf_funit(FontBBox4));
    print_dimen(UnderlinePosition);
    print_dimen(UnderlineThickness);
    print_str(Version);
    print_str(Notice);
    fputs("EncodingScheme FontSpecific\n", outfile);
    print_dimen(CapHeight);
    print_dimen(XHeight);
    print_dimen(Ascender);
    print_dimen(Descender);
    ncharmetrics = nglyphs;
    if (use_ext_enc == 0) { /* external encoding vector not given */
        fprintf(outfile, "StartCharMetrics %u\n", ncharmetrics);
        for (pm = mtx_tab; pm - mtx_tab < nglyphs; pm++) {
            pm->found = 1;
            print_char_metric(outfile, -1, pm - mtx_tab);
        }
    }
    else { /* external encoding vector given */
        for (idx = mtx_index; idx - mtx_index <= MAX_CHAR_CODE; *idx++ = 0);
        for (pe = enc_names; pe - enc_names < names_count; pe++) {
            if (*pe == notdef)
                continue;
            /* scan form `uniABCD' */
            if (sscanf(*pe, GLYPH_PREFIX_UNICODE "%X", &index) == 1) {
                if (unicode_map[index] != NOGLYPH_ASSIGNED_YET) {
                    pm = mtx_tab + unicode_map[index];
                    pm->found = 1;
                }
                else
                    ttf_warn("`unicode %s%.4X' is not mapped to any glyph", 
                             GLYPH_PREFIX_UNICODE, index);
                continue;
            }
            /* scan form `index123' */
            if (sscanf(*pe, GLYPH_PREFIX_INDEX "%u", &index) == 1) {
                if (index >= nglyphs)
                    ttf_fail("`%s' out of valid range [0..%i)",
                         *pe, nglyphs);
                pm = mtx_tab + index;
                pm->found = 1;
                continue;
            }
            for (pm = mtx_tab; pm - mtx_tab < nglyphs; pm++)
                if (pm->name != NULL && strcmp(*pe, pm->name) == 0)
                    break; 
            if (pm - mtx_tab < nglyphs) {
                mtx_index[pe - enc_names] = pm - mtx_tab;
                pm->found = 1;
                continue;
            }
            else
                ttf_warn("glyph `%s' not found", *pe);
        }
        fprintf(outfile, "StartCharMetrics %u\n", ncharmetrics);
        for (idx = mtx_index; idx - mtx_index <= MAX_CHAR_CODE; idx++) {
            if (*idx != 0 && mtx_tab[*idx].found == 1)
                print_char_metric(outfile, idx - mtx_index, *idx);
        }
        for (pm = mtx_tab; pm - mtx_tab < nglyphs; pm++) {
            if (pm->found == 0)
                print_char_metric(outfile, -1, pm - mtx_tab);
        }
    }
end_metrics:
    fputs("EndCharMetrics\n", outfile);
    if (nkernpairs == 0)
        goto end_kerns;
    fprintf(outfile, "StartKernData\nStartKernPairs %i\n", nkernpairs);
    for (pk = kern_tab; pk - kern_tab < nglyphs; pk++)
        for (qk = pk; qk != NULL; qk = qk->next)
            if (qk->value != 0) {
                fputs("KPX ", outfile);
                print_glyph_name(outfile, pk - kern_tab, print_glyph);
                fputs(" ", outfile);
                print_glyph_name(outfile, qk->adjacent, print_glyph);
                fprintf(outfile, " %i\n", get_ttf_funit(qk->value));
            }
    fputs("EndKernPairs\nEndKernData\n", outfile);
end_kerns:
    fputs("EndFontMetrics\n", outfile);
}

void print_encoding(char *fontname)
{
    long int i, k, first_code, length, last_sep;
    FILE *file;
    TTF_USHORT format;
    char *enc_name, *end_enc_name;
    cmap_entry *e;
    const char *n;
    printing_enc = 1;
    enc_name = xtalloc(strlen(bname) + 20, char);
    strcpy(enc_name, bname);
    end_enc_name = strend(enc_name);
    for (e = cmap_tab; e - cmap_tab < ncmapsubtabs; e++) {
        ttf_seek_off("cmap", cmap_offset + e->offset);
        format = get_ushort();
        if (format != 0 && format != 4 && format != 6) {
            ttf_warn("format %i of encoding subtable unsupported", (int)format);
            continue;
        }
        sprintf(end_enc_name, ".e%i%i", 
                (int)e->platform_id, (int)e->encoding_id);
        if ((file = xfopen(enc_name, FOPEN_W_MODE)) == NULL)
            ttf_fail("cannot open file for writing (%s)\n", enc_name);
        fprintf(file, "%% Encoding table from font file %s\n", fontname);
        fprintf(file, "%% Platform ID %i", (int)e->platform_id);
        switch (e->platform_id) {
        case 0:  fprintf(file, " (Apple Unicode)"); break;
        case 1:  fprintf(file, " (Macintosh)"); break;
        case 2:  fprintf(file, " (ISO)"); break;
        case 3:  fprintf(file, " (Microsoft)"); break;
        }
        fprintf(file, "\n");
        fprintf(file, "%% Encoding ID %i", (int)e->encoding_id);
        if (e->platform_id == 1 &&  e->encoding_id == 0)
            fprintf(file, " (Roman)");
        if (e->platform_id == 3)
            switch (e->encoding_id) {
            case 0:  fprintf(file, " (Symbol)"); break;
            case 1:  fprintf(file, " (Unicode)"); break;
            }
        fprintf(file, "\n");
        fprintf(file, "%% Format %i", (int)(format));
        switch (format) {
        case 0:  fprintf(file, " (byte encoding table)"); break;
        case 4:  fprintf(file, " (segment mapping to delta values)"); break;
        case 6:  fprintf(file, " (trimmed table mapping)"); break;
        }
        fprintf(file, "\n");
        fprintf(file, "/Encoding%i [\n", (int)(e - cmap_tab + 1));
        switch (format) {
        case 0:
            get_ushort(); /* skip length */
            get_ushort(); /* skip version number */
            for (i = 0; i <= MAX_CHAR_CODE; i++) {
                fputs("/", file);
                print_glyph_name(file, get_byte(), print_glyph);
                fputs("\n", file);
            }
            break;
        case 4:
            for (i = 0; i < nglyphs; ++i) {
                fprintf(file, "%% Glyph %i -> ", i);
                print_glyph_name(file, i, AS_UNICODE);
                fputs("\n", file);
            }
            break;
        case 6:
            get_ushort(); /* skip table length */
            get_ushort(); /* skip version number */
            first_code = get_ushort(); /* first character code */
            for (i = 0; i < first_code; ++i)
                fprintf(file, "/%s\n", notdef);
            length = get_ushort(); /* number of character codes */
            for (i = first_code; i < first_code + length; i++) {
                k = get_ushort();
                if (i > MAX_CHAR_CODE)
                    fputs("% ", file);
                fputs("/", file);
                print_glyph_name(file, k, print_glyph);
                fputs("\n", file);
            }
            for (i =  first_code + length; i <= MAX_CHAR_CODE; i++)
                fprintf(file, "/%s\n", notdef);
            break;
        default:
            ttf_warn("format %i of encoding subtable unsupported", (int)format);
        }
        fprintf(file, "] def\n");
    }
    xfree(enc_name);
}

void usage()
{
    cur_file_name = NULL;
    fprintf(stderr,
        "Usage: ttf2afm [-i|-u|-c|-v] [-e enc] [-o filename] [-m NUM] fontfile\n"
        "    -i:          force printing glyph names in form 'index123'\n"
        "    -u:          force printing glyph names in form 'uniABCD'\n"
        "    -c:          print encoding tables to `basename.e<platformID><encodingID>'\n"
        "    -v:          print version\n"
        "    -e enc:      encode the AFM output using encoding vector from `enc'\n"
        "    -o filename: write output to file `filename' instead of stdout\n"
        "    -m NUM:      select unicode mapping (default = 1, ie the first)\n"
        "    fontfile:    the TrueType fontfile\n"
        "\nPlease send bug reports or feature requests to <pdftex@tug.org>\n"
    );
    _exit(-1);
}

int main(int argc, char **argv)
{
    char date[128], *s, *buf = NULL;
    time_t t = time(&t);
    int c;
    kpse_set_progname(argv[0]);
    kpse_init_prog("ttf2afm", 0, 0, 0) ;
    while ((c = getopt(argc, argv, "iucve:o:m:")) != -1)
        switch (c) {
        case 'i':
            print_glyph = AS_INDEX;
            break;
        case 'u':
            print_glyph = AS_UNICODE;
            break;
        case 'c':
            print_cmap = 1;
            break;
        case 'e':
            cur_file_name = optarg;
            read_encoding(cur_file_name);
            use_ext_enc = 1;
            break;
        case 'o':
            cur_file_name = optarg;
            outfile = xfopen(cur_file_name, FOPEN_W_MODE);
            if (outfile == NULL)
                ttf_fail("cannot open file for writing");
            break;
        case 'm':
            select_unicode = atoi(optarg);
            break;
        case 'v':
            fprintf(stderr, 
                    "ttf2afm version " VERSION "\n"
                    "Copyright (C) 1997-2005 Han The Thanh.\n"
                    "There is NO warranty.  Redistribution of this software is\n"
                    "covered by the terms of both the pdfTeX copyright and\n"
                    "the GNU General Public License.\n"
                    "For more information about these matters, see the files\n"
                    "named COPYING and the pdfTeX source.\n"
                    "Primary author of ttf2afm: Han The Thanh.\n"
                   );
            _exit(0);
        default:
            usage();
        }
    if (argc - optind != 1)
        usage();
    sprintf(date, "%s\n", ctime(&t));
    *(char *)strchr(date, '\n') = 0;
    cur_file_name = argv[optind];
    if (print_cmap) {
        buf = xstrdup(cur_file_name);
        bname = xbasename(buf);
        if ((s = rindex(bname, '.')) != NULL)
            *s = 0;
    }
    if ((fontfile = kpse_open_file(cur_file_name, kpse_truetype_format)) == NULL)
        ttf_fail("can't open font file for reading");
    read_font();
    if (outfile == NULL)
        outfile = stdout;
    print_afm(date, cur_file_name);
    if (print_cmap)
        print_encoding(cur_file_name);
    xfree(FontName);
    xfree(FullName);
    xfree(FamilyName);
    xfree(Notice);
    xfree(Version);
    xfree(Weight);
    xfree(buf);
    free_tabs();
    xfclose(fontfile, cur_file_name);
    return 0;
}
