#include "libpdftex.h"

typedef signed char     TTFCHAR;
typedef unsigned char   TTFBYTE;
typedef signed short    TTFSHORT;
typedef unsigned short  TTFUSHORT;
typedef signed long     TTFLONG;
typedef unsigned long   TTFULONG;
typedef unsigned long   TTFFIXED;
typedef unsigned short  TTFFUNIT;
typedef signed short    TTFFWORD;
typedef unsigned short  TTFUFWORD;
typedef unsigned short  TTFF2DOT14;

#define TTFCHAR_SIZE    1
#define TTFBYTE_SIZE    1
#define TTFSHORT_SIZE   2
#define TTFUSHORT_SIZE  2
#define TTFLONG_SIZE    4
#define TTFULONG_SIZE   4
#define TTFFIXED_SIZE   4
#define TTFFWORD_SIZE   2
#define TTFUFWORD_SIZE  2
#define TTFF2DOT14_SIZE 2

#define ARG_1_AND_2_ARE_WORDS       (1<<0)
#define ARGS_ARE_XY_VALUES          (1<<1)
#define ROUND_XY_TO_GRID            (1<<2)
#define WE_HAVE_A_SCALE             (1<<3)
#define RESERVED                    (1<<4)
#define MORE_COMPONENTS             (1<<5)
#define WE_HAVE_AN_X_AND_Y_SCALE    (1<<6)
#define WE_HAVE_A_TWO_BY_TWO        (1<<7)
#define WE_HAVE_INSTRUCTIONS        (1<<8)
#define USE_MY_METRICS              (1<<9)

#define GET_NUM(t)      ((t)getnum(t##_SIZE))
#define SKIP(n)         getnum(n)

#define GET_CHAR()      GET_NUM(TTFCHAR)
#define GET_BYTE()      GET_NUM(TTFBYTE)
#define GET_SHORT()     GET_NUM(TTFSHORT)
#define GET_USHORT()    GET_NUM(TTFUSHORT)
#define GET_LONG()      GET_NUM(TTFLONG)
#define GET_ULONG()     GET_NUM(TTFULONG)
#define GET_FIXED()     GET_NUM(TTFFIXED)
#define GET_FUNIT()     GET_NUM(TTFFUNIT)
#define GET_FWORD()     GET_NUM(TTFFWORD)
#define GET_UFWORD()    GET_NUM(TTFUFWORD)
#define GET_F2DOT14()   GET_NUM(TTFF2DOT14)

#define PUT_NUM(t,n)    ((t)putnum(t##_SIZE, n))

#define PUT_CHAR(n)     PUT_NUM(TTFCHAR, n)
#define PUT_BYTE(n)     PUT_NUM(TTFBYTE, n)
#define PUT_SHORT(n)    PUT_NUM(TTFSHORT, n)
#define PUT_USHORT(n)   PUT_NUM(TTFUSHORT, n)
#define PUT_LONG(n)     PUT_NUM(TTFLONG, n)
#define PUT_ULONG(n)    PUT_NUM(TTFULONG, n)
#define PUT_FIXED(n)    PUT_NUM(TTFFIXED, n)
#define PUT_FUNIT(n)    PUT_NUM(TTFFUNIT, n)
#define PUT_FWORD(n)    PUT_NUM(TTFFWORD, n)
#define PUT_UFWORD(n)   PUT_NUM(TTFUFWORD, n)
#define PUT_F2DOT14(n)  PUT_NUM(TTFF2DOT14, n)

#define COPY_BYTE()     PUT_BYTE(GET_BYTE())
#define COPY_CHAR()     PUT_CHAR(GET_CHAR())
#define COPY_USHORT()   PUT_USHORT(GET_USHORT())
#define COPY_SHORT()    PUT_SHORT(GET_SHORT())
#define COPY_ULONG()    PUT_ULONG(GET_ULONG())
#define COPY_LONG()     PUT_LONG(GET_LONG())
#define COPY_FIXED()    PUT_FIXED(GET_FIXED())
#define COPY_FUNIT()    PUT_FUNIT(GET_FUNIT())
#define COPY_FWORD()    PUT_FWORD(GET_FWORD())
#define COPY_UFWORD()   PUT_UFWORD(GET_UFWORD())
#define COPY_F2DOT14()  PUT_F2DOT14(GET_F2DOT14())

#define MAC_GLYPH_MAX   258
#define TABDIR_OFF      12

typedef struct {
    char tag[4];
    TTFULONG checksum;
    TTFULONG offset;
    TTFULONG length;
} tabdir_entry;

typedef struct {
    TTFLONG offset;
    TTFLONG newoffset;
    TTFUFWORD advWidth;
    TTFFWORD lsb;
    char *name;             /* name of glyph */
    TTFSHORT newindex;      /* new index of glyph in output file */
    TTFUSHORT name_index;   /* index of name as read from font file */
} glyph_entry;

typedef struct {
    char *name;             /* name of glyph */
    short newindex;         /* new index of glyph in output file */
} ttfenc_entry;

static TTFUSHORT ntabs;
static TTFULONG checksum;
static TTFUSHORT upem;
static TTFFIXED post_format;
static TTFSHORT loca_format;
static TTFULONG ttf_offset;
static TTFUSHORT glyphs_count;
static TTFUSHORT new_glyphs_count;
static TTFUSHORT nhmtxs;
static TTFUSHORT max_glyph_index;
static TTFUSHORT new_ntabs;

/* 
 * reindexing glyphs is a bit unclear: `glyph_tab' contains glyphs in
 * original order in font file, `ttfenc_tab' is the new encoding vector and
 * `glyph_index' is the new order of glyphs. So n-th glyph in new order is
 * located at `glyph_tab[glyph_index[n]]'. The field `newindex' of entries in
 * both `glyph_tab' and `ttfenc_tab' contains the index of the corresponding
 * glyph...
 *
 */

static glyph_entry *glyph_tab;
static ttfenc_entry *ttfenc_tab;
static short *glyph_index;

static tabdir_entry *dir_tab;
static char *glyph_names_buf;
static int chkcount;

static FILE *ttf_file;
integer ttf_length;

#define INFILE ttf_file
#define OUTFILE pdffile

#define TTF_OPEN()      truetypebopenin(ttf_file)
#define TTF_CLOSE()     xfclose(ttf_file)
#define TTF_GETCHAR()   getc(ttf_file)
#define TTF_EOF()       feof(ttf_file)


char *mac_glyph_names[MAC_GLYPH_MAX] = {
notdef, ".null", "CR", "space", "exclam", "quotedbl", "numbersign", "dollar",
"percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk",
"plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three",
"four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less",
"equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
"underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"braceleft", "bar", "braceright", "asciitilde", "Adieresis", "Aring",
"Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute", "agrave",
"acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute", "egrave",
"ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis",
"ntilde", "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute",
"ugrave", "ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling",
"section", "bullet", "paragraph", "germandbls", "registered", "copyright",
"trademark", "acute", "dieresis", "notequal", "AE", "Oslash", "infinity",
"plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff", "Sigma",
"Pi", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae",
"oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin",
"approxequal", "Delta", "guillemotleft", "guillemotright", "ellipsis",
"nbspace", "Agrave", "Atilde", "Otilde", "OE", "oe", "endash", "emdash",
"quotedblleft", "quotedblright", "quoteleft", "quoteright", "divide",
"lozenge", "ydieresis", "Ydieresis", "fraction", "currency", "guilsinglleft",
"guilsinglright", "fi", "fl", "daggerdbl", "middot", "quotesinglbase",
"quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute",
"Edieresis", "Egrave", "Iacute", "Icircumflex", "Idieresis", "Igrave",
"Oacute", "Ocircumflex", "applelogo", "Ograve", "Uacute", "Ucircumflex",
"Ugrave", "dotlessi", "circumflex", "tilde", "overscore", "breve",
"dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash",
"lslash", "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth",
"Yacute", "yacute", "Thorn", "thorn", "minus", "multiply", "onesuperior",
"twosuperior", "threesuperior", "onehalf", "onequarter", "threequarters",
"franc", "Gbreve", "gbreve", "Idot", "Scedilla", "scedilla", "Cacute",
"cacute", "Ccaron", "ccaron", "dmacron"
};

static char *newtabnames[] = {
    "OS/2",
    "PCLT",
    "cmap",
    "cvt ",
    "fpgm",
    "glyf",
    "head",
    "hhea",
    "hmtx",
    "loca",
    "maxp",
    "name",
    "post",
    "prep"
};

#define DEFAULT_NTABS       14

static unsigned char addchksm(unsigned char b)
{
    checksum += (b << (8*chkcount--));
    if (chkcount < 0)
        chkcount = 3;
    return b;
}

static TTFLONG putnum(int s, TTFLONG n)
{
    TTFULONG i = n;
    char buf[TTFLONG_SIZE + 1], *p = buf;
    while (s-- > 0) {
        *p++ = i & 0xFF;
        i >>= 8;
    }
    p--;
    while (p >= buf)
        xputc(addchksm(*p--), OUTFILE);
    return n;
}

static TTFLONG getnum(int s)
{
    TTFLONG i = 0;
    int c;
    while (s > 0) {
        if ((c = TTF_GETCHAR()) < 0) {
            if (c == EOF)
                FAIL("unexpected EOF while reading font file");
            else
                FAIL("getc() failed while reading font file");
        }
        i = (i << 8) + c;
        s--;
    }
    return i;
}

static long funit(long n)
{
    if (n < 0)
        return -((-n/upem)*1000 + ((-n%upem)*1000)/upem);
    else
        return (n/upem)*1000 + ((n%upem)*1000)/upem;
}

static void ncopy(int n)
{
    while (n-- > 0)
        COPY_BYTE();
}

static tabdir_entry *name_lookup(char *s, boolean required)
{
    tabdir_entry *tab;
    for (tab = dir_tab; tab - dir_tab < ntabs; tab++)
        if (strncmp(tab->tag, s, 4) == 0)
            break;
    if (tab - dir_tab == ntabs)
        if (required)
            FAIL("can't find table `%s'" AND s);
        else
            tab = 0;
    return tab;
}

static tabdir_entry *seek_tab(char *name, TTFLONG offset)
{
    tabdir_entry *tab = name_lookup(name, true);
    sprintf(printf_buf, "%s while reading table `%s'", filename, name);
    xfseek(INFILE, tab->offset + offset, SEEK_SET, printf_buf);
    return tab;
}

static void seek_off(char *name, TTFLONG offset)
{
    sprintf(printf_buf, "%s while reading table %s", filename, name);
    xfseek(INFILE, offset, SEEK_SET, printf_buf);
}

static void copy_encoding()
{
    int i;
    char **n = cur_glyph_names = enc_tab[fm_cur->encoding].glyph_names;
    ttfenc_entry *e = ttfenc_tab = XTALLOC(MAX_CHAR_NUM, ttfenc_entry);
    for (i = 0; i < MAX_CHAR_NUM; i++) {
        if (pdfischarused(tex_font, i))
            e->name = *n;
        else
            e->name = notdef;
        n++;
        e++;
    }
}

static void read_font()
{
    long i, j, k, l, n;
    char *p;
    glyph_entry *glyph;
    tabdir_entry *tab;
    TTFUFWORD last_advWidth;
    if ((i= GET_FIXED()) != 0x00010000)
        FAIL("unsupport version %8lx; can handle only version 1.0" AND i);
    dir_tab = XTALLOC(ntabs = GET_USHORT(), tabdir_entry);
    SKIP(3*TTFUSHORT_SIZE);
    for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
        for (i = 0; i < 4; i++)
            tab->tag[i] = GET_CHAR();
        tab->checksum = GET_ULONG();
        tab->offset = GET_ULONG();
        tab->length = GET_ULONG();
    }
    if (name_lookup("PCLT", false) == 0)
        new_ntabs--;
    if (name_lookup("fpgm", false) == 0)
        new_ntabs--;
    if (name_lookup("cvt ", false) == 0)
        new_ntabs--;
    if (name_lookup("prep", false) == 0)
        new_ntabs--;
    seek_tab("maxp", TTFFIXED_SIZE);
    glyph_tab = XTALLOC(1 + (glyphs_count = GET_USHORT()), glyph_entry);
    for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++)
        glyph->newindex = -1;
    glyph_index = XTALLOC(glyphs_count, short);
    glyph_index[0] = 0; /* index of ".notdef" glyph */
    glyph_index[1] = 1; /* index of ".null" glyph */
    for (i = 0; i < MAX_KEY_CODE; i++)
        font_keys[i].valid = true;
    seek_tab("head", 2*TTFFIXED_SIZE + 2*TTFULONG_SIZE + TTFUSHORT_SIZE);
    upem = GET_USHORT();
    SKIP(16);
    font_keys[FONTBBOX1_CODE].value.i = funit(GET_FWORD());
    font_keys[FONTBBOX2_CODE].value.i = funit(GET_FWORD());
    font_keys[FONTBBOX3_CODE].value.i = funit(GET_FWORD());
    font_keys[FONTBBOX4_CODE].value.i = funit(GET_FWORD());
    SKIP(2*TTFUSHORT_SIZE + TTFSHORT_SIZE);
    loca_format = GET_SHORT();
    seek_tab("hhea", TTFFIXED_SIZE);
    font_keys[ASCENT_CODE].value.i = funit(GET_FWORD());
    font_keys[DESCENT_CODE].value.i = funit(GET_FWORD());
    SKIP(TTFFWORD_SIZE + TTFUFWORD_SIZE + 3*TTFFWORD_SIZE + 8*TTFSHORT_SIZE);
    nhmtxs = GET_USHORT();
    if (name_lookup("PCLT", false) != 0) {
        seek_tab("PCLT", TTFFIXED_SIZE + TTFULONG_SIZE + TTFUSHORT_SIZE);
        font_keys[XHEIGHT_CODE].value.i = funit(GET_USHORT());
        SKIP(2*TTFUSHORT_SIZE);
        font_keys[CAPHEIGHT_CODE].value.i = funit(GET_USHORT());
    }
    else {
        font_keys[XHEIGHT_CODE].valid = false;
        font_keys[CAPHEIGHT_CODE].valid = false;
    }
    seek_tab("hmtx", 0);
    for (glyph = glyph_tab; glyph - glyph_tab < nhmtxs; glyph++) {
        glyph->advWidth = GET_UFWORD();
        glyph->lsb = GET_UFWORD();
    }
    if (nhmtxs < glyphs_count) {
        last_advWidth = glyph[-1].advWidth;
        for (;glyph - glyph_tab <  glyphs_count; glyph++) {
            glyph->advWidth = last_advWidth;
            glyph->lsb = GET_UFWORD();
        }
    }
    tab = seek_tab("post", 0);
    post_format = GET_FIXED();
    font_keys[ITALIC_ANGLE_CODE].value.i = GET_FIXED() >> 16;
    SKIP(2*TTFFWORD_SIZE + 5*TTFULONG_SIZE);
    switch (post_format) {
    case 0x10000:
        for (glyph = glyph_tab; glyph - glyph_tab < MAC_GLYPH_MAX; glyph++) {
            glyph->name = mac_glyph_names[glyph - glyph_tab];
            glyph->name_index = glyph - glyph_tab;
        }
        for (;glyph - glyph_tab < glyphs_count; glyph++) {
            glyph->name = notdef;
            glyph->name_index = 0;
        }
        break;
    case 0x20000:
        SKIP(TTFUSHORT_SIZE);
        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++)
            glyph->name_index = GET_USHORT();
        i = tab->length - (xftell(INFILE, filename) - tab->offset);
        glyph_names_buf = XTALLOC(i, char);
        for (p = glyph_names_buf; p - glyph_names_buf < i;) {
            for (k = GET_BYTE(); k > 0; k--)
                *p++ = GET_CHAR();
            *p++ = 0;
        }
        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++) {
            if (glyph->name_index < MAC_GLYPH_MAX)
                glyph->name = mac_glyph_names[glyph->name_index];
            else {
                p = glyph_names_buf;
                k = glyph->name_index - MAC_GLYPH_MAX;
                for (; k > 0; k--)
                    p = (char *)strchr(p, 0) + 1;
                glyph->name = p;
            }
        }
        break;
    default:
        FAIL("unsupported format (%8X) of `post' table" AND post_format);
    }
    seek_tab("loca", 0);
    if (loca_format != 0)
        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count + 1; glyph++)
            glyph->offset = GET_ULONG();
    else
        for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count + 1; glyph++)
            glyph->offset = GET_USHORT() << 1;
    seek_tab("name", TTFUSHORT_SIZE);
    i = xftell(INFILE, filename);
    n = GET_USHORT();
    j = GET_USHORT() + i - TTFUSHORT_SIZE;
    i += 2*TTFUSHORT_SIZE;
    while (n-- > 0) {
        seek_off("name", i + 3*TTFUSHORT_SIZE);
        k = GET_USHORT();
        l = GET_USHORT();
        if (k == 6) {
            seek_off("name", j + GET_USHORT());
            CHECK_BUF(l, PRINTF_BUF_SIZE);
            for (p = printf_buf; l > 0; l-- )
                *p++ = GET_CHAR();
            *p++ = 0;
            font_keys[FONTNAME_CODE].value.s = xstrdup(printf_buf);
            break;
        }
        i += 6*TTFUSHORT_SIZE;
    }
}

#define RESET_CHKSM() do {                                 \
    checksum = 0L;                                         \
    chkcount = 3;                                          \
    tab->offset = xftell(OUTFILE, filename) - ttf_offset;  \
} while (0)

#define SET_CHKSM() do {                                   \
    tab->checksum = checksum;                               \
    tab->length = xftell(OUTFILE, filename) - tab->offset - ttf_offset; \
} while (0)

static void copytab(char *name)
{
    tabdir_entry *tab = seek_tab(name, 0);
    long i;
    RESET_CHKSM();
    for (i = tab->length; i > 0; i--)
        COPY_CHAR();
    SET_CHKSM();
}

static void reenc_font()
{
    ttfenc_entry *e;
    tabdir_entry *tab;
    tab = name_lookup("cmap", true);
    RESET_CHKSM();
    PUT_USHORT(0);  /* table version number (0) */
    PUT_USHORT(1);  /* number of encoding tables (1) */
    PUT_USHORT(1);  /* platform ID */
    PUT_USHORT(0);  /* encoding ID */
    PUT_ULONG(4*TTFUSHORT_SIZE + TTFULONG_SIZE); /* byte offset from beginning of table */
    if (max_glyph_index < 256) {
        PUT_USHORT(0);  /* format number (0: byte encoding table) */
        PUT_USHORT(256 + 3*TTFUSHORT_SIZE); /* length of table */
        PUT_USHORT(0);  /* version number */
        for (e = ttfenc_tab; e - ttfenc_tab < MAX_CHAR_NUM; e++)
            PUT_BYTE(e->newindex);
    }
    else {
        PUT_USHORT(6);  /* format number (6): trimmed table mapping */
        PUT_USHORT(TTFUSHORT_SIZE*(5 + MAX_CHAR_NUM));
        PUT_USHORT(0);  /* version number (0) */
        PUT_USHORT(0);  /* first character code */
        PUT_USHORT(MAX_CHAR_NUM);  /* number of character code in table */
        for (e = ttfenc_tab; e - ttfenc_tab < MAX_CHAR_NUM; e++)
            PUT_USHORT(e->newindex);
    }
    SET_CHKSM();
}

static void copy_dirtab()
{
    tabdir_entry *tab;
    int i;
    fflush(OUTFILE);
    pdfgone += xftell(OUTFILE, filename) - ttf_offset;
    xfseek(OUTFILE, ttf_offset + TABDIR_OFF, SEEK_SET, filename);
    if (is_subsetted()) 
        for (i = 0; i < DEFAULT_NTABS; i++) {
            tab = name_lookup(newtabnames[i], false);
            if (tab == 0)
                continue;
            for (k = 0; k < 4; k++)
               PUT_CHAR(tab->tag[k]);
            PUT_ULONG(tab->checksum);
            PUT_ULONG(tab->offset);
            PUT_ULONG(tab->length);
        }
    else
        for (tab = dir_tab; tab - dir_tab < ntabs; tab++) {
            for (k = 0; k < 4; k++)
               PUT_CHAR(tab->tag[k]);
            PUT_ULONG(tab->checksum);
            PUT_ULONG(tab->offset);
            PUT_ULONG(tab->length);
        }
}


static void subset_font()
{
    long i, k;
    char *p;
    TTFUSHORT flags;
    TTFUSHORT idx;
    tabdir_entry *tab;
    short *index;
    ttfenc_entry *e;
    glyph_entry *glyph;
    ttf_offset = xftell(OUTFILE, filename);
    /* 
     * reindexing glyphs: we append index of used glyphs to `glyph_index'
     * while going through `ttfenc_tab'. After appending a new entry to
     * `glyph_index' we set field `newindex' of corresponding entries in both
     * `glyph_tab' and `ttfenc_tab' to the newly created index
     * 
     */
    for (e = ttfenc_tab; e - ttfenc_tab < MAX_CHAR_NUM; e++) {
        e->newindex = 0; /* index of ".notdef" glyph */
        if (e->name != notdef) {
            for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++)
                if (!strcmp(e->name, glyph->name))
                    break;
            if (glyph - glyph_tab < glyphs_count) {
                glyph_index[new_glyphs_count] = glyph - glyph_tab;
                glyph->newindex = e->newindex = new_glyphs_count;
                new_glyphs_count++;
            }
            else
                WARN("glyph `%s' undefined" AND e->name);
        }
    }
    max_glyph_index = new_glyphs_count - 1;
    for (i = 1, k = 0; i <= new_ntabs; i <<= 1, k++);
    PUT_FIXED(0x00010000); /* font version */
    PUT_USHORT(new_ntabs); /* number of tables */
    PUT_USHORT(i << 3); /* search range */
    PUT_USHORT(k - 1); /* entry selector */
    PUT_USHORT((new_ntabs<<4) - (i<<3)); /* range shift */
    xfseek(OUTFILE, ttf_offset + TABDIR_OFF + new_ntabs*4*TTFULONG_SIZE,
        SEEK_SET, filename);
    copytab("OS/2");
    if (name_lookup("PCLT", false) != 0)
        copytab("PCLT");
    if (name_lookup("fpgm", false) != 0)
        copytab("fpgm");
    if (name_lookup("cvt ", false) != 0)
        copytab("cvt ");
    if (name_lookup("prep", false) != 0)
        copytab("prep");
    reenc_font();
    i = (tab = name_lookup("glyf", true))->offset;
    RESET_CHKSM();
    loca_format = 0;
    for (index = glyph_index; index - glyph_index < new_glyphs_count; index++) {
        if ((glyph_tab[*index].newoffset = xftell(OUTFILE, filename) - tab->offset - ttf_offset) > 0x00020000 ||
            (glyph_tab[*index].newoffset & 1))
            loca_format = 1;
        if (glyph_tab[*index].offset != glyph_tab[*index + 1].offset) {
            seek_off("glyph", i + glyph_tab[*index].offset);
            k = COPY_SHORT();
            ncopy(4*TTFFWORD_SIZE);
            if (k < 0) {
                do {
                    flags = COPY_USHORT();
                    idx = GET_USHORT();
                    if (glyph_tab[idx].newindex < 0) {
                        glyph_tab[idx].newindex = new_glyphs_count;
                        glyph_index[new_glyphs_count++] = idx;
                        /* 
                            NOTICE: Here we change `new_glyphs_count',
                            which appears in the condition of the `for' loop
                        */
                    }
                    PUT_USHORT(glyph_tab[idx].newindex);
                    if (flags & ARG_1_AND_2_ARE_WORDS)
                        ncopy(2*TTFSHORT_SIZE);
                    else
                        ncopy(TTFUSHORT_SIZE);
                    if (flags & WE_HAVE_A_SCALE)
                        ncopy(TTFF2DOT14_SIZE);
                    else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
                        ncopy(2*TTFF2DOT14_SIZE);
                    else if (flags & WE_HAVE_A_TWO_BY_TWO)
                        ncopy(4*TTFF2DOT14_SIZE);
                } while (flags & MORE_COMPONENTS);
                if (flags & WE_HAVE_INSTRUCTIONS)
                    ncopy(COPY_USHORT());
            }
            else
                ncopy(glyph_tab[*index + 1].offset - glyph_tab[*index].offset - 
                    TTFUSHORT_SIZE - 4*TTFFWORD_SIZE);
        }
    }
    SET_CHKSM();
    if ((i = tab->length) > 0x00020000 || i & 1)
        loca_format = 1;
    tab = seek_tab("head", 0);
    RESET_CHKSM();
    ncopy(2*TTFFIXED_SIZE + 2*TTFULONG_SIZE + 2*TTFUSHORT_SIZE + 16 + 
        4*TTFFWORD_SIZE + 2*TTFUSHORT_SIZE + TTFSHORT_SIZE);
    if (loca_format)
        PUT_SHORT(1);
    else
        PUT_SHORT(0);
    PUT_SHORT(0);
    SET_CHKSM();
    tab = seek_tab("hhea", 0);
    RESET_CHKSM();
    ncopy(TTFFIXED_SIZE + 3*TTFFWORD_SIZE + TTFUFWORD_SIZE + 3*TTFFWORD_SIZE + 8*TTFSHORT_SIZE);
    PUT_USHORT(new_glyphs_count);
    SET_CHKSM();
    tab = seek_tab("hmtx", 0);
    RESET_CHKSM();
    for (index = glyph_index; index - glyph_index < new_glyphs_count; index++) {
        PUT_UFWORD(glyph_tab[*index].advWidth);
        PUT_UFWORD(glyph_tab[*index].lsb);
    }
    SET_CHKSM();
    tab = seek_tab("loca", 0);
    RESET_CHKSM();
    if (loca_format != 0) {
        for (index = glyph_index; index - glyph_index < new_glyphs_count; index++)
            PUT_ULONG(glyph_tab[*index].newoffset);
        PUT_ULONG(i);
    }
    else {
        for (index = glyph_index; index - glyph_index < new_glyphs_count; index++)
            PUT_USHORT(glyph_tab[*index].newoffset/2);
        PUT_USHORT(i/2);
    }
    SET_CHKSM();
    tab = seek_tab("maxp", TTFFIXED_SIZE + TTFUSHORT_SIZE);
    RESET_CHKSM();
    PUT_FIXED(0x00010000);
    PUT_USHORT(new_glyphs_count);
    ncopy(13*TTFUSHORT_SIZE);
    SET_CHKSM();
    copytab("name");
    tab = seek_tab("post", TTFFIXED_SIZE);
    RESET_CHKSM();
    PUT_FIXED(0x00020000);
    ncopy(TTFFIXED_SIZE + 2*TTFFWORD_SIZE + 5*TTFULONG_SIZE);
    PUT_USHORT(new_glyphs_count);
    for (k = 0, index = glyph_index; index - glyph_index < new_glyphs_count; index++)
        if (glyph_tab[*index].name_index < MAC_GLYPH_MAX)
            PUT_USHORT(glyph_tab[*index].name_index);
        else
            PUT_USHORT(MAC_GLYPH_MAX + k++);
    for (index = glyph_index; index - glyph_index < new_glyphs_count; index++)
        if (glyph_tab[*index].name_index >= MAC_GLYPH_MAX) {
            PUT_BYTE(k = strlen(p = glyph_tab[*index].name));
            while (k-- > 0)
                PUT_CHAR(*p++);
        }
    SET_CHKSM();
    copy_dirtab();
}

static void copy_font()
{
    ttfenc_entry *e;
    tabdir_entry *tab;
    glyph_entry *glyph;
    int i;
    ttf_offset = xftell(OUTFILE, filename);
    for (i = 1, k = 0; i <= ntabs; i <<= 1, k++);
    PUT_FIXED(0x00010000); /* font version */
    PUT_USHORT(ntabs); /* number of tables */
    PUT_USHORT(i << 3); /* search range */
    PUT_USHORT(k - 1); /* entry selector */
    PUT_USHORT((ntabs<<4) - (i<<3)); /* range shift */
    xfseek(OUTFILE, ttf_offset + TABDIR_OFF + ntabs*4*TTFULONG_SIZE,
        SEEK_SET, filename);
    for (i = 0; i < ntabs; i++) {
        if (!is_reencoded() || (strncmp(dir_tab[i].tag, "cmap", 4) != 0)) {
            copytab(dir_tab[i].tag);
            continue;
        }
        tab = name_lookup("cmap", true);
        max_glyph_index = 0;
        for (e = ttfenc_tab; e - ttfenc_tab < MAX_CHAR_NUM; e++) {
            e->newindex = 0; /* index of ".notdef" glyph */
            if (e->name != notdef) {
                for (glyph = glyph_tab; glyph - glyph_tab < glyphs_count; glyph++)
                    if (strcmp(e->name, glyph->name) == 0)
                        break;
                if (glyph - glyph_tab < glyphs_count)
                    e->newindex = glyph - glyph_tab;
                else
                    WARN("glyph `%s' undefined" AND e->name);
                if (e->newindex > max_glyph_index)
                    max_glyph_index = e->newindex;
            }
        }
        reenc_font();
    }
    copy_dirtab();
}

void writettf()
{
    filename = fm_cur->ff_name;
    packfilename(maketexstring(filename), getnullstr(), getnullstr());
    if (!TTF_OPEN()) {
        WARN("cannot open TrueType font file for reading");
        font_file_not_found = true;
        return;
    }
    if (is_subsetted() && !is_reencoded()) {
        WARN("encoding vector required for TrueType font subsetting");
        return;
    }
    TEX_PRINTF("<%s" AND fm_cur->ff_name);
    glyph_names_buf = 0;
    new_glyphs_count = 2;
    new_ntabs = DEFAULT_NTABS;
    read_font();
    if (is_included()) {
        pdfsaveoffset = pdfoffset;
        pdfflush();
        copy_encoding();
        if (!is_subsetted())
            copy_font();
        else
            subset_font();
        xfflush(OUTFILE);
        xfseek(OUTFILE, pdfgone, SEEK_SET, filename);
        ttf_length = pdfoffset - pdfsaveoffset;
        XFREE(ttfenc_tab);
    }
    XFREE(dir_tab);
    XFREE(glyph_tab);
    XFREE(glyph_index);
    XFREE(glyph_names_buf);
    TEX_PRINTF(">");
}
