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

   Copyright (C) 2007-2019 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.
*/

/*
 * CID-Keyed Font support:
 *
 *  Only CFF/OpenType CID-Keyed Font with Type 2 charstrings is supported.
 *
 */

#include "dpx-cidtype0.h"

#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "tectonic_bridge_core.h"
#include "dpx-agl.h"
#include "dpx-cff.h"
#include "dpx-cff_dict.h"
#include "dpx-cff_limits.h"
#include "dpx-cff_types.h"
#include "dpx-cid.h"
/* typedef CID in cmap.h */
#include "dpx-cmap.h"
#include "dpx-cmap_write.h"
#include "dpx-cs_type2.h"
#include "dpx-dpxconf.h"
#include "dpx-dpxfile.h"
#include "dpx-error.h"
#include "dpx-mem.h"
#include "dpx-mfileio.h"
#include "dpx-numbers.h"
/* pseudo unique tag */
#include "dpx-pdffont.h"
#include "dpx-pdfobj.h"
/* Font info. from OpenType tables */
#include "dpx-sfnt.h"
#include "dpx-t1_char.h"
#include "dpx-t1_load.h"
#include "dpx-tt_aux.h"
/* Metrics */
#include "dpx-tt_table.h"
#include "dpx-type0.h"

/*
 * PDF Reference 3rd. ed., p.340, "Glyph Metrics in CID Fonts".
 */
#ifndef PDFUNIT
#define PDFUNIT(v) (ROUND((1000.0*(v))/(head->unitsPerEm),1))
#endif

static void
add_CIDHMetrics (sfnt *sfont, pdf_obj *fontdict,
                 unsigned char *CIDToGIDMap, unsigned short last_cid,
                 struct tt_maxp_table *maxp,
                 struct tt_head_table *head, struct tt_longMetrics *hmtx)
{
    pdf_obj *w_array, *an_array = NULL;
    int    cid, start = 0, prev = 0;
    double defaultAdvanceWidth;
    int    empty = 1;

    (void) sfont; /* unused */
    defaultAdvanceWidth = PDFUNIT(hmtx[0].advance);
    /*
     * We alway use format:
     *  c [w_1 w_2 ... w_n]
     */
    w_array = pdf_new_array();
    for (cid = 0; cid <= last_cid; cid++) {
        USHORT gid;
        double advanceWidth;
        gid = CIDToGIDMap ? ((CIDToGIDMap[2*cid] << 8)|CIDToGIDMap[2*cid+1]) : cid;
        if (gid >= maxp->numGlyphs || (cid != 0 && gid == 0))
            continue;
        advanceWidth = PDFUNIT(hmtx[gid].advance);
        if (advanceWidth == defaultAdvanceWidth) {
            if (an_array) {
                pdf_add_array(w_array, pdf_new_number(start));
                pdf_add_array(w_array, an_array);
                an_array = NULL;
                empty = 0;
            }
        } else {
            if (cid != prev + 1 &&  an_array) {
                pdf_add_array(w_array, pdf_new_number(start));
                pdf_add_array(w_array, an_array);
                an_array = NULL;
                empty = 0;
            }
            if (an_array == NULL) {
                an_array = pdf_new_array();
                start = cid;
            }
            pdf_add_array(an_array, pdf_new_number(advanceWidth));
            prev = cid;
        }
    }

    if (an_array) {
        pdf_add_array(w_array, pdf_new_number(start));
        pdf_add_array(w_array, an_array);
        empty = 0;
    }

    /*
     * We always write DW for older MacOS X's preview app.
     * PDF Reference 2nd. ed, wrongly described default value of DW as 0, and
     * MacOS X's (up to 10.2.8) preview app. implements this wrong description.
     */
    pdf_add_dict(fontdict,
                 pdf_new_name("DW"),
                 pdf_new_number(defaultAdvanceWidth));
    if (!empty) {
        pdf_add_dict(fontdict,
                     pdf_new_name("W"),
                     pdf_ref_obj(w_array));
    }
    pdf_release_obj(w_array);

    return;
}

static void
add_CIDVMetrics (sfnt *sfont, pdf_obj *fontdict,
                 unsigned char *CIDToGIDMap, unsigned short last_cid,
                 struct tt_maxp_table *maxp,
                 struct tt_head_table *head, struct tt_longMetrics *hmtx)
{
    pdf_obj *w2_array, *an_array = NULL;
    int    cid;
    struct tt_VORG_table *vorg;
    struct tt_vhea_table *vhea  = NULL;
    struct tt_longMetrics *vmtx = NULL;
    double defaultAdvanceHeight, defaultVertOriginY;
    int    empty = 1;

    /*
     * No accurate vertical metrics can be obtained by simple way if the
     * font does not have VORG table. Only CJK fonts may have VORG.
     */
    if (sfnt_find_table_pos(sfont, "VORG") <= 0)
        return;

    vorg = tt_read_VORG_table(sfont);
    defaultVertOriginY = PDFUNIT(vorg->defaultVertOriginY);
    if (sfnt_find_table_pos(sfont, "vhea") > 0)
        vhea = tt_read_vhea_table(sfont);
    if (vhea && sfnt_find_table_pos(sfont, "vmtx") > 0) {
        sfnt_locate_table(sfont, "vmtx");
        vmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, vhea->numOfLongVerMetrics, vhea->numOfExSideBearings);
    }

    if (sfnt_find_table_pos(sfont, "OS/2") <= 0) {
        struct tt_os2__table *os2;
        /* OpenType font must have OS/2 table. */
        os2 = tt_read_os2__table(sfont);
        defaultVertOriginY   = PDFUNIT(os2->sTypoAscender);
        defaultAdvanceHeight = PDFUNIT(os2->sTypoAscender - os2->sTypoDescender);
        free(os2);
    } else {
        /* Some TrueType fonts used in Macintosh does not have OS/2 table. */
        defaultAdvanceHeight = 1000;
    }

    w2_array = pdf_new_array();
    for (cid = 0; cid <= last_cid; cid++) {
        USHORT i, gid;
        double advanceHeight, vertOriginX, vertOriginY;
        gid = CIDToGIDMap ? ((CIDToGIDMap[2*cid] << 8)|CIDToGIDMap[2*cid+1]) : cid;
        if (gid >= maxp->numGlyphs || (cid != 0 && gid == 0))
            continue;
        advanceHeight = vmtx ? PDFUNIT(vmtx[gid].advance) : defaultAdvanceHeight;
        vertOriginX   = PDFUNIT(hmtx[gid].advance*0.5);
        vertOriginY   = defaultVertOriginY;
        for (i = 0;
             i < vorg->numVertOriginYMetrics && gid >= vorg->vertOriginYMetrics[i].glyphIndex;
             i++) {
            if (gid == vorg->vertOriginYMetrics[i].glyphIndex)
                vertOriginY = PDFUNIT(vorg->vertOriginYMetrics[i].vertOriginY);
        }

        /*
         * c_first c_last w1_y v_x v_y
         * This form may hit Acrobat's implementation limit of array element size, 8192.
         * AFPL GhostScript 8.11 stops with rangecheck error with this. Maybe GS's bug?
         */
        if (vertOriginY != defaultVertOriginY ||
            advanceHeight != defaultAdvanceHeight) {
            pdf_add_array(w2_array, pdf_new_number(cid));
            pdf_add_array(w2_array, pdf_new_number(cid));
            pdf_add_array(w2_array, pdf_new_number(-advanceHeight));
            pdf_add_array(w2_array, pdf_new_number(vertOriginX));
            pdf_add_array(w2_array, pdf_new_number(vertOriginY));
            empty = 0;
        }
    }

    if (defaultVertOriginY != 880 || defaultAdvanceHeight != 1000) {
        an_array = pdf_new_array();
        pdf_add_array(an_array, pdf_new_number(defaultVertOriginY));
        pdf_add_array(an_array, pdf_new_number(-defaultAdvanceHeight));
        pdf_add_dict(fontdict, pdf_new_name ("DW2"), an_array);
    }
    if (!empty) {
        pdf_add_dict(fontdict,
                     pdf_new_name("W2"), pdf_ref_obj(w2_array));
    }
    pdf_release_obj(w2_array);

    free(vorg->vertOriginYMetrics);
    free(vorg);

    free(vmtx);
    free(vhea);

    return;
}

static void
add_CIDMetrics (sfnt *sfont, pdf_obj *fontdict,
                unsigned char *CIDToGIDMap, unsigned short last_cid, int need_vmetrics)
{
    struct tt_longMetrics *hmtx;
    struct tt_head_table  *head;
    struct tt_hhea_table  *hhea;
    struct tt_maxp_table  *maxp;

    /*
     * Read head, hhea, maxp:
     *
     *   unitsPerEm       --> head
     *   numHMetrics      --> hhea
     *   numGlyphs        --> maxp
     */
    head = tt_read_head_table(sfont);
    maxp = tt_read_maxp_table(sfont);
    hhea = tt_read_hhea_table(sfont);

    sfnt_locate_table(sfont, "hmtx");
    hmtx = tt_read_longMetrics(sfont, maxp->numGlyphs, hhea->numOfLongHorMetrics, hhea->numOfExSideBearings);

    add_CIDHMetrics(sfont, fontdict, CIDToGIDMap, last_cid, maxp, head, hmtx);
    if (need_vmetrics)
        add_CIDVMetrics(sfont, fontdict, CIDToGIDMap, last_cid, maxp, head, hmtx);

    free(hmtx);
    free(hhea);
    free(maxp);
    free(head);

    return;
}

/*
 * Create an instance of embeddable font.
 */
static int
write_fontfile (pdf_font *font, cff_font *cffont)
{
    cff_index *topdict, *fdarray, *private;
    unsigned char *dest;
    int destlen = 0, i, size;
    int offset, topdict_offset, fdarray_offset;

    /*  DICT sizes (offset set to long int) */
    topdict = cff_new_index(1);
    fdarray = cff_new_index(cffont->num_fds);
    private = cff_new_index(cffont->num_fds);

    cff_dict_remove(cffont->topdict, "UniqueID");
    cff_dict_remove(cffont->topdict, "XUID");
    cff_dict_remove(cffont->topdict, "Private");  /* some bad font may have */
    cff_dict_remove(cffont->topdict, "Encoding"); /* some bad font may have */

    topdict->offset[1] = cff_dict_pack(cffont->topdict,
                                       (card8 *) work_buffer,
                                       WORK_BUFFER_SIZE) + 1;
    for (i = 0;i < cffont->num_fds; i++) {
        size = 0;
        if (cffont->private && cffont->private[i]) {
            size = cff_dict_pack(cffont->private[i],
                                 (card8 *) work_buffer, WORK_BUFFER_SIZE);
            if (size < 1) { /* Private had contained only Subr */
                cff_dict_remove(cffont->fdarray[i], "Private");
            }
        }
        (private->offset)[i+1] = (private->offset)[i] + size;
        (fdarray->offset)[i+1] = (fdarray->offset)[i] +
            cff_dict_pack(cffont->fdarray[i],
                          (card8 *) work_buffer, WORK_BUFFER_SIZE);
    }

    destlen = 4; /* header size */
    destlen += cff_set_name(cffont, font->fontname);
    destlen += cff_index_size(topdict);
    destlen += cff_index_size(cffont->string);
    destlen += cff_index_size(cffont->gsubr);
    destlen += (cffont->charsets->num_entries) * 2 + 1;  /* charset format 0 */
    destlen += (cffont->fdselect->num_entries) * 3 + 5; /* fdselect format 3 */
    destlen += cff_index_size(cffont->cstrings);
    destlen += cff_index_size(fdarray);
    destlen += private->offset[private->count] - 1; /* Private is not INDEX */

    dest = NEW(destlen, card8);

    offset = 0;
    /* Header */
    offset += cff_put_header(cffont, dest + offset, destlen - offset);
    /* Name */
    offset += cff_pack_index(cffont->name, dest + offset, destlen - offset);
    /* Top DICT */
    topdict_offset = offset;
    offset += cff_index_size(topdict);
    /* Strings */
    offset += cff_pack_index(cffont->string, dest + offset, destlen - offset);
    /* Global Subrs */
    offset += cff_pack_index(cffont->gsubr, dest + offset, destlen - offset);

    /* charset */
    cff_dict_set(cffont->topdict, "charset", 0, offset);
    offset += cff_pack_charsets(cffont, dest + offset, destlen - offset);

    /* FDSelect */
    cff_dict_set(cffont->topdict, "FDSelect", 0, offset);
    offset += cff_pack_fdselect(cffont, dest + offset, destlen - offset);

    /* CharStrings */
    cff_dict_set(cffont->topdict, "CharStrings", 0, offset);
    offset += cff_pack_index(cffont->cstrings,
                             dest + offset, cff_index_size(cffont->cstrings));
    cff_release_index(cffont->cstrings);
    cffont->cstrings = NULL; /* Charstrings cosumes huge memory */

    /* FDArray and Private */
    cff_dict_set(cffont->topdict, "FDArray", 0, offset);
    fdarray_offset = offset;
    offset += cff_index_size(fdarray);

    fdarray->data = NEW(fdarray->offset[fdarray->count] - 1, card8);
    for (i = 0; i < cffont->num_fds; i++) {
        size = private->offset[i+1] - private->offset[i];
        if (cffont->private[i] && size > 0) {
            cff_dict_pack(cffont->private[i], dest + offset, size);
            cff_dict_set(cffont->fdarray[i], "Private", 0, size);
            cff_dict_set(cffont->fdarray[i], "Private", 1, offset);
        }
        cff_dict_pack(cffont->fdarray[i],
                      fdarray->data + (fdarray->offset)[i] - 1,
                      fdarray->offset[fdarray->count] - 1);
        offset += size;
    }

    cff_pack_index(fdarray, dest + fdarray_offset, cff_index_size(fdarray));
    cff_release_index(fdarray);
    cff_release_index(private);

    /* Finally Top DICT */
    topdict->data = NEW(topdict->offset[topdict->count] - 1, card8);
    cff_dict_pack(cffont->topdict,
                  topdict->data, topdict->offset[topdict->count] - 1);
    cff_pack_index(topdict, dest + topdict_offset, cff_index_size(topdict));
    cff_release_index(topdict);

    /*
     * FontFile
     */
    {
        pdf_obj *fontfile, *stream_dict;

        fontfile    = pdf_new_stream(STREAM_COMPRESS);
        stream_dict = pdf_stream_dict(fontfile);
        pdf_add_dict(font->descriptor,
                     pdf_new_name("FontFile3"),
                     pdf_ref_obj (fontfile));
        pdf_add_dict(stream_dict,
                     pdf_new_name("Subtype"),
                     pdf_new_name("CIDFontType0C"));
        pdf_add_stream(fontfile, (char *) dest, offset);
        pdf_release_obj(fontfile);
        free(dest);
    }

    return destlen;
}

static void
CIDFont_type0_add_CIDSet(pdf_font *font, char *used_chars, card16 last_cid) {
    /*
     * CIDSet:
     * Length of CIDSet stream is not clear. Must be 8192 bytes long?
     */
    pdf_obj *cidset;

    cidset = pdf_new_stream(STREAM_COMPRESS);
    pdf_add_stream(cidset, used_chars, (last_cid / 8) + 1);
    pdf_add_dict(font->descriptor,
                 pdf_new_name("CIDSet"), pdf_ref_obj(cidset));
    pdf_release_obj(cidset);
}

int
CIDFont_type0_dofont (pdf_font *font)
{
    rust_input_handle_t handle;
    sfnt *sfont;
    cff_font *cffont;
    cff_index    *charstrings, *idx;
    cff_charsets *charset = NULL;
    cff_fdselect *fdselect = NULL;
    int    charstring_len, max_len;
    int    destlen = 0;
    int    size, offset = 0;
    card8 *data;
    card16 num_glyphs = 0, gid;
    int    cid;
    card16 cs_count, last_cid = 0;
    int    fd, prev_fd;
    char  *used_chars;
    unsigned char *CIDToGIDMap = NULL;

    assert(font);

    if (!font->reference)
        return 0;

    pdf_add_dict(font->resource,
                 pdf_new_name("FontDescriptor"),
                 pdf_ref_obj (font->descriptor));

    if (font->flags & PDF_FONT_FLAG_BASEFONT)
        return 0;
    else if (!font->cid.options.embed &&
             (opt_flags_cidfont & CIDFONT_FORCE_FIXEDPITCH)) {
        /* No metrics needed. */
        pdf_add_dict(font->resource,
                     pdf_new_name("DW"), pdf_new_number(1000.0));
        return 0;
    }

    used_chars = font->usedchars;

    handle = dpx_open_opentype_file (font->filename);
    if (!handle) {
        handle = dpx_open_truetype_file (font->filename);

        if (!handle) {
            dpx_warning("Could not open file: %s", font->filename);
            return -1;
        }
    }

    sfont = sfnt_open(handle);
    if (!sfont) {
        dpx_warning("Failed to read font file: %s", font->filename);
        ttstub_input_close(handle);
        return -1;
    }

    if (sfont->type == SFNT_TYPE_TTC)
        offset = ttc_read_offset(sfont, font->index);

    if ((sfont->type != SFNT_TYPE_TTC &&
        sfont->type != SFNT_TYPE_POSTSCRIPT) ||
        sfnt_read_table_directory(sfont, offset) < 0 ||
        (offset = sfnt_find_table_pos(sfont, "CFF ")) == 0) {
        dpx_warning("Not a CFF/OpenType font: %s", font->filename);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    cffont = cff_open(sfont->handle, offset, 0);
    if (!cffont) {
        dpx_warning("Failed to read CFF font data: %s", font->filename);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    if (!(cffont->flag & FONTTYPE_CIDFONT)) {
        dpx_warning("Unexpected type (CIDFont expected): %s", font->filename);
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    cff_read_charsets(cffont);

    {
        int cid_count;

        if (cff_dict_known(cffont->topdict, "CIDCount")) {
            cid_count = (int) cff_dict_get(cffont->topdict, "CIDCount", 0);
        } else {
            cid_count = CID_MAX + 1;
        }

        CIDToGIDMap = NEW(2 * cid_count, unsigned char);
        memset(CIDToGIDMap, 0, 2 * cid_count);
        add_to_used_chars2(used_chars, 0); /* .notdef */
        for (cid = 0; cid <= CID_MAX; cid++) {
            if (is_used_char2(used_chars, cid)) {
                gid = cff_charsets_lookup(cffont, (card16)cid);
                if (cid != 0 && gid == 0) {
                    dpx_warning("Glyph for CID %u missing in font \"%s\".", (CID) cid, font->filename);
                    used_chars[cid/8] &= ~(1 << (7 - (cid % 8)));
                    continue;
                }
                CIDToGIDMap[2*cid]   = (gid >> 8) & 0xff;
                CIDToGIDMap[2*cid+1] = gid & 0xff;
                last_cid = cid;
                num_glyphs++;
            }
        }
    }

    /*
    * DW, W, DW2 and W2:
    * Those values are obtained from OpenType table (not TFM).
    */
    if (opt_flags_cidfont & CIDFONT_FORCE_FIXEDPITCH) {
        pdf_add_dict(font->resource,
                    pdf_new_name("DW"), pdf_new_number(1000.0));
    } else {
        add_CIDMetrics(sfont, font->resource, CIDToGIDMap, last_cid,
                    font->cid.need_vmetrics ? 1 : 0);
    }

    if (!font->cid.options.embed) {
        free(CIDToGIDMap);
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return 0;
    }

    /*
     * Embed font subset.
     */
    cff_read_fdselect(cffont);
    cff_read_fdarray(cffont);
    cff_read_private(cffont);

    cff_read_subrs(cffont);

    offset = (int) cff_dict_get(cffont->topdict, "CharStrings", 0);
    cff_seek_set(cffont, offset);
    idx = cff_get_index_header(cffont);
    /* offset is now absolute offset ... bad */
    offset = cff_tell(cffont);

    if ((cs_count = idx->count) < 2) {
        dpx_warning("No valid charstring data found: %s", font->filename);
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    /* New Charsets data */
    charset = NEW(1, cff_charsets);
    charset->format = 0;
    charset->num_entries = 0;
    charset->data.glyphs = NEW(num_glyphs, s_SID);

    /* New FDSelect data */
    fdselect = NEW(1, cff_fdselect);
    fdselect->format = 3;
    fdselect->num_entries = 0;
    fdselect->data.ranges = NEW(num_glyphs, cff_range3);

    /* New CharStrings INDEX */
    charstrings = cff_new_index((card16)(num_glyphs+1));
    max_len = 2 * CS_STR_LEN_MAX;
    charstrings->data = NEW(max_len, card8);
    charstring_len = 0;

    /*
     * TODO: Re-assign FD number.
     */
    prev_fd = -1; gid = 0;
    data = NEW(CS_STR_LEN_MAX, card8);
    for (cid = 0; cid <= last_cid; cid++) {
        unsigned short gid_org;

        if (!is_used_char2(used_chars, cid))
            continue;

        gid_org = (CIDToGIDMap[2*cid] << 8)|(CIDToGIDMap[2*cid+1]);
        if ((size = (idx->offset)[gid_org+1] - (idx->offset)[gid_org])
            > CS_STR_LEN_MAX) {
            dpx_warning("Charstring too long: %s (gid=%u)", font->filename, gid_org);
            free(data);
            free(charstrings);
            free(fdselect);
            free(charset);
            cff_release_index(idx);
            free(CIDToGIDMap);
            cff_close(cffont);
            sfnt_close(sfont);
            ttstub_input_close(handle);
            return -1;
        }

        if (charstring_len + CS_STR_LEN_MAX >= max_len) {
            max_len = charstring_len + 2 * CS_STR_LEN_MAX;
            charstrings->data = RENEW(charstrings->data, max_len, card8);
        }
        (charstrings->offset)[gid] = charstring_len + 1;
        cff_seek(cffont, offset + (idx->offset)[gid_org] - 1);
        cff_read_data(data, size, cffont);
        fd = cff_fdselect_lookup(cffont, gid_org);
        charstring_len += cs_copy_charstring(charstrings->data + charstring_len,
                                             max_len - charstring_len,
                                             data, size,
                                             cffont->gsubr, (cffont->subrs)[fd], 0, 0, NULL);
        if (cid > 0 && gid_org > 0) {
            charset->data.glyphs[charset->num_entries] = cid;
            charset->num_entries += 1;
        }
        if (fd != prev_fd) {
            fdselect->data.ranges[fdselect->num_entries].first = gid;
            fdselect->data.ranges[fdselect->num_entries].fd    = fd;
            fdselect->num_entries += 1;
            prev_fd = fd;
        }
        gid++;
    }
    if (gid != num_glyphs) {
        dpx_warning("Unexpected error: %s", font->filename);
        free(data);
        free(charstrings);
        free(fdselect);
        free(charset);
        cff_release_index(idx);
        free(CIDToGIDMap);
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }
    free(data);
    cff_release_index(idx);

    free(CIDToGIDMap);

    (charstrings->offset)[num_glyphs] = charstring_len + 1;
    charstrings->count = num_glyphs;
    cffont->num_glyphs    = num_glyphs;
    cffont->cstrings      = charstrings;

    /* discard old one, set new data */
    cff_release_charsets(cffont->charsets);
    cffont->charsets = charset;
    cff_release_fdselect(cffont->fdselect);
    cffont->fdselect = fdselect;

    /* no Global subr */
    if (cffont->gsubr)
        cff_release_index(cffont->gsubr);
    cffont->gsubr = cff_new_index(0);

    for (fd = 0; fd < cffont->num_fds; fd++) {
        if (cffont->subrs && cffont->subrs[fd]) {
            cff_release_index(cffont->subrs[fd]);
            cffont->subrs[fd] = NULL;
        }
        if (cffont->private && (cffont->private)[fd]) {
            cff_dict_remove((cffont->private)[fd], "Subrs"); /* no Subrs */
        }
    }

    destlen = write_fontfile(font, cffont);

    cff_close(cffont);
    sfnt_close(sfont);
    ttstub_input_close(handle);

    if (dpx_conf.verbose_level > 1)
        dpx_message("[%u/%u glyphs][%d bytes]", num_glyphs, cs_count, destlen);

    if (pdf_check_version(2, 0) < 0) {
        CIDFont_type0_add_CIDSet(font, used_chars, last_cid);
    }

    return 0;
}

int
CIDFont_type0_open_from_t1 (pdf_font *font, const char *name, int index, cid_opt *opt)
{
    CIDSysInfo  csi;
    char       *fontname;
    cff_font   *cffont;
    rust_input_handle_t handle;

    assert(font);

    handle = dpx_open_type1_file(name);
    if (!handle)
        return -1;

    cffont = t1_load_font(NULL, 1, handle);
    if (!cffont) {
        ttstub_input_close(handle);
        return -1;
    }

    {
        char *shortname;
        int fontname_len = 8;

        shortname = cff_get_name(cffont);
        if (!shortname) {
            dpx_warning("No valid FontName found: %s", name);
            cff_close(cffont);
            ttstub_input_close(handle);
        }
        /*
        * Mangled name requires more 7 bytes.
        */
        fontname = NEW(strlen(shortname) + fontname_len, char);
        memset(fontname, 0, strlen(shortname) + fontname_len);
        strcpy(fontname, shortname);
        free(shortname);
    }

    csi.registry   = NEW(strlen("Adobe") + 1, char);
    strcpy(csi.registry, "Adobe");
    csi.ordering   = NEW(strlen("Identity") + 1, char);
    strcpy(csi.ordering, "Identity");
    csi.supplement = 0;
    if (opt->style != FONT_STYLE_NONE) {
        dpx_warning(",Bold, ,Italic, ... not supported for this type of font...");
        opt->style = FONT_STYLE_NONE;
    }

    font->fontname = fontname;
    font->subtype  = PDF_FONT_FONTTYPE_CIDTYPE0;
    font->cid.csi  = csi;
    font->flags   |= CIDFONT_FLAG_TYPE1;

    font->resource = pdf_new_dict();
    pdf_add_dict(font->resource,
                pdf_new_name("Type"),
                pdf_new_name("Font"));
    pdf_add_dict(font->resource,
                pdf_new_name("Subtype"),
                pdf_new_name("CIDFontType0"));

    pdf_font_make_uniqueTag(font->uniqueID);

    font->descriptor = pdf_new_dict();
    {
        char *tmp;

        tmp = NEW(strlen(font->fontname)+8, char);
        sprintf(tmp, "%s+%s", font->uniqueID, font->fontname);
        pdf_add_dict(font->descriptor, pdf_new_name("FontName"), pdf_new_name(tmp));
        pdf_add_dict(font->resource,   pdf_new_name("BaseFont"), pdf_new_name(tmp));
        free(tmp);
    }
    {
        pdf_obj *csi_dict = pdf_new_dict();
        pdf_add_dict(csi_dict,
                    pdf_new_name("Registry"),
                    pdf_new_string(csi.registry, strlen(csi.registry)));
        pdf_add_dict(csi_dict,
                    pdf_new_name("Ordering"),
                    pdf_new_string(csi.ordering, strlen(csi.ordering)));
        pdf_add_dict(csi_dict,
                    pdf_new_name("Supplement"),
                    pdf_new_number(csi.supplement));
        pdf_add_dict(font->resource, pdf_new_name("CIDSystemInfo"), csi_dict);
    }

    return 0;
}

int
CIDFont_type0_open (pdf_font *font, const char *name, int index, cid_opt *opt)
{
    CIDSysInfo  csi;
    char       *fontname;
    sfnt       *sfont = NULL;
    cff_font   *cffont;
    rust_input_handle_t handle;
    ULONG       offset = 0;

    assert(font);

    handle = dpx_open_opentype_file(name);
    if (!handle) {
        handle = dpx_open_truetype_file(name);
        if (!handle)
            return -1;
    }

    sfont = sfnt_open(handle);
    if (!sfont) {
        ttstub_input_close(handle);
        return -1;
    }

    if (sfont->type == SFNT_TYPE_TTC)
        offset = ttc_read_offset(sfont, index);

    if ((sfont->type != SFNT_TYPE_TTC && sfont->type != SFNT_TYPE_POSTSCRIPT) ||
        sfnt_read_table_directory(sfont, offset) < 0 ||
        (offset = sfnt_find_table_pos(sfont, "CFF ")) == 0) {
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    cffont = cff_open(sfont->handle, offset, 0);
    if (!cffont) {
        dpx_warning("Cannot read CFF font data");
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    if (!(cffont->flag & FONTTYPE_CIDFONT)) {
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    {
        char *shortname;
        int fontname_len = 8;

        shortname = cff_get_name(cffont);
        if (!shortname) {
            dpx_warning("No valid FontName found.");
            cff_close(cffont);
            sfnt_close(sfont);
            ttstub_input_close(handle);
            return -1;
        }
        /*
        * Mangled name requires more 7 bytes.
        * Style requires more 11 bytes.
        */
        fontname_len += 11;
        fontname = NEW(strlen(shortname) + fontname_len, char);
        memset(fontname, 0, strlen(shortname) + fontname_len);
        strcpy(fontname, shortname);
        free(shortname);
    }
    csi.registry   = cff_get_string(cffont, (s_SID)cff_dict_get(cffont->topdict, "ROS", 0));
    csi.ordering   = cff_get_string(cffont, (s_SID)cff_dict_get(cffont->topdict, "ROS", 1));
    csi.supplement = (int)cff_dict_get(cffont->topdict, "ROS", 2);

    cff_close(cffont);

    if (opt->embed && opt->style != FONT_STYLE_NONE) {
        dpx_warning("Embedding disabled due to style option for %s.", name);
        opt->embed = 0;

        switch (opt->style) {
        case FONT_STYLE_BOLD:
            strcat(fontname, ",Bold");
            break;
        case FONT_STYLE_ITALIC:
            strcat(fontname, ",Italic");
            break;
        case FONT_STYLE_BOLDITALIC:
            strcat(fontname, ",BoldItalic");
            break;
        }
    }

    /* getting font info. from TrueType tables */
    font->descriptor = tt_get_fontdesc(sfont, &(opt->embed), opt->stemv, 0, name);
    if (!font->descriptor) {
        dpx_warning("Could not obtain necessary font info: %s", name);
        free(fontname);
        free(csi.registry);
        free(csi.ordering);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    font->fontname = fontname;
    font->subtype  = PDF_FONT_FONTTYPE_CIDTYPE0;
    font->cid.csi  = csi;

    font->resource = pdf_new_dict();
    pdf_add_dict(font->resource,
                pdf_new_name("Type"),
                pdf_new_name("Font"));
    pdf_add_dict(font->resource,
                pdf_new_name("Subtype"),
                pdf_new_name("CIDFontType0"));

    if (opt->embed) {
        char *tmp;

        tmp = NEW(strlen(font->fontname)+8, char);
        pdf_font_make_uniqueTag(font->uniqueID);
        sprintf(tmp, "%s+%s", font->uniqueID, font->fontname);
        pdf_add_dict(font->descriptor, pdf_new_name("FontName"), pdf_new_name(tmp));
        pdf_add_dict(font->resource,   pdf_new_name("BaseFont"), pdf_new_name(tmp));
        free(tmp);
    } else {
        pdf_add_dict(font->descriptor, pdf_new_name("FontName"), pdf_new_name(font->fontname));
        pdf_add_dict(font->resource,   pdf_new_name("BaseFont"), pdf_new_name(font->fontname));
    }
    {
        pdf_obj *csi_dict = pdf_new_dict();
        pdf_add_dict(csi_dict,
                    pdf_new_name("Registry"),
                    pdf_new_string(csi.registry, strlen(csi.registry)));
        pdf_add_dict(csi_dict,
                    pdf_new_name("Ordering"),
                    pdf_new_string(csi.ordering, strlen(csi.ordering)));
        pdf_add_dict(csi_dict,
                    pdf_new_name("Supplement"),
                    pdf_new_number(csi.supplement));
        pdf_add_dict(font->resource, pdf_new_name("CIDSystemInfo"), csi_dict);
    }
    pdf_add_dict(font->resource,
                pdf_new_name("DW"),
                pdf_new_number(1000)); /* not sure */

    sfnt_close(sfont);
    ttstub_input_close(handle);

    return 0;
}

int
CIDFont_type0_open_from_t1c (pdf_font *font, const char *name, int index, cid_opt *opt)
{
    CIDSysInfo  csi;
    char       *fontname;
    sfnt       *sfont = NULL;
    cff_font   *cffont;
    rust_input_handle_t handle;
    ULONG       offset = 0;

    assert(font);

    handle = dpx_open_opentype_file(name);
    if (!handle) {
        handle = dpx_open_truetype_file(name);
        if (!handle)
            return -1;
    }

    sfont = sfnt_open(handle);
    if (!sfont) {
        dpx_warning("Not a CFF/OpenType font: %s", name);
        ttstub_input_close(handle);
    }

    if (sfont->type == SFNT_TYPE_TTC)
        offset = ttc_read_offset(sfont, index);

    if ((sfont->type != SFNT_TYPE_TTC && sfont->type != SFNT_TYPE_POSTSCRIPT) ||
        sfnt_read_table_directory(sfont, offset) < 0 ||
        (offset = sfnt_find_table_pos(sfont, "CFF ")) == 0) {
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    cffont = cff_open(sfont->handle, offset, 0);
    if (!cffont) {
        dpx_warning("Cannot read CFF font data: %s", name);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    if (cffont->flag & FONTTYPE_CIDFONT) {
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    {
        char *shortname;
        int fontname_len = 8;

        shortname = cff_get_name(cffont);
        if (!shortname) {
            dpx_warning("No valid FontName found: %s", name);
            cff_close(cffont);
            sfnt_close(sfont);
            ttstub_input_close(handle);
            return -1;
        }
        /*
        * Mangled name requires more 7 bytes.
        */
        fontname = NEW(strlen(shortname) + fontname_len, char);
        memset(fontname, 0, strlen(shortname) + fontname_len);
        strcpy(fontname, shortname);
        free(shortname);
    }
    csi.registry   = NEW(strlen("Adobe") + 1, char);
    strcpy(csi.registry, "Adobe");
    csi.ordering   = NEW(strlen("Identity") + 1, char);
    strcpy(csi.ordering, "Identity");
    csi.supplement = 0;

    cff_close(cffont);

    opt->embed = 1;
    /* getting font info. from TrueType tables */
    font->descriptor = tt_get_fontdesc(sfont, &(opt->embed), opt->stemv, 0, name);
    if (!font->descriptor) {
        dpx_warning("Could not obtain necessary font info: %s", name);
        free(fontname);
        free(csi.registry);
        free(csi.ordering);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    } else if (!opt->embed) {
        dpx_warning("Can’t embed font due to font license: %s", name);
        free(fontname);
        free(csi.registry);
        free(csi.ordering);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    font->fontname = fontname;
    font->subtype  = PDF_FONT_FONTTYPE_CIDTYPE0;
    font->cid.csi  = csi;
    font->flags   |= CIDFONT_FLAG_TYPE1C;

    font->resource = pdf_new_dict();
    pdf_add_dict(font->resource,
                pdf_new_name("Type"),
                pdf_new_name("Font"));
    pdf_add_dict(font->resource,
                pdf_new_name("Subtype"),
                pdf_new_name("CIDFontType0"));

    if (opt->embed) {
        char *tmp;

        tmp = NEW(strlen(font->fontname)+8, char);
        pdf_font_make_uniqueTag(font->uniqueID);
        sprintf(tmp, "%s+%s", font->uniqueID, font->fontname);
        pdf_add_dict(font->descriptor, pdf_new_name("FontName"), pdf_new_name(tmp));
        pdf_add_dict(font->resource,   pdf_new_name("BaseFont"), pdf_new_name(tmp));
        free(tmp);
    } else {
        pdf_add_dict(font->descriptor, pdf_new_name("FontName"), pdf_new_name(font->fontname));
        pdf_add_dict(font->resource,   pdf_new_name("BaseFont"), pdf_new_name(font->fontname));
    }
    {
        pdf_obj *csi_dict = pdf_new_dict();
        pdf_add_dict(csi_dict,
                    pdf_new_name("Registry"),
                    pdf_new_string(csi.registry, strlen(csi.registry)));
        pdf_add_dict(csi_dict,
                    pdf_new_name("Ordering"),
                    pdf_new_string(csi.ordering, strlen(csi.ordering)));
        pdf_add_dict(csi_dict,
                    pdf_new_name("Supplement"),
                    pdf_new_number(csi.supplement));
        pdf_add_dict(font->resource, pdf_new_name("CIDSystemInfo"), csi_dict);
    }

    sfnt_close(sfont);
    ttstub_input_close(handle);

    return 0;
}

int
CIDFont_type0_t1cdofont (pdf_font *font)
{
    rust_input_handle_t handle;
    sfnt *sfont;
    cff_font  *cffont;
    cff_index *charstrings, *idx;
    int    charstring_len, max_len;
    int    destlen = 0;
    int    size, offset = 0;
    card8 *data;
    card16 num_glyphs, gid, last_cid;
    int    i, cid;
    char  *used_chars;
    double default_width, nominal_width;

    assert(font);

    if (!font->reference)
        return 0;

    pdf_add_dict(font->resource,
                 pdf_new_name("FontDescriptor"),
                 pdf_ref_obj (font->descriptor));

    used_chars = font->usedchars;

    handle = dpx_open_opentype_file(font->filename);
    if (!handle) {
        handle = dpx_open_truetype_file(font->filename);
        if (!handle) {
            dpx_warning("Could not open file: %s", font->filename);
            return -1;
        }
    }

    sfont = sfnt_open(handle);
    if (!sfont) {
        dpx_warning("Failed to read font data: %s", font->filename);
        ttstub_input_close(handle);
        return -1;
    }

    if (sfont->type == SFNT_TYPE_TTC)
        offset = ttc_read_offset(sfont, font->index);

    if ((sfont->type != SFNT_TYPE_TTC &&
        sfont->type != SFNT_TYPE_POSTSCRIPT) ||
        sfnt_read_table_directory(sfont, offset) < 0 ||
        (offset = sfnt_find_table_pos(sfont, "CFF ")) == 0) {
        dpx_warning("Not a CFF/OpenType font: %s", font->filename);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    cffont = cff_open(sfont->handle, offset, 0);
    if (!cffont) {
        dpx_warning("Failed to read CFF font data: %s", font->filename);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    if (cffont->flag & FONTTYPE_CIDFONT) {
        dpx_warning("Unxpected type (CIDFont) found: %s", font->filename);
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    cff_read_private(cffont);
    cff_read_subrs  (cffont);

    if (cffont->private[0] && cff_dict_known(cffont->private[0], "StdVW")) {
        double stemv;
        stemv = cff_dict_get(cffont->private[0], "StdVW", 0);
        pdf_add_dict(font->descriptor,
                     pdf_new_name("StemV"), pdf_new_number(stemv));
    }
    if (cffont->private[0] && cff_dict_known(cffont->private[0], "defaultWidthX")) {
        default_width = (double) cff_dict_get(cffont->private[0], "defaultWidthX", 0);
    } else {
        default_width = CFF_DEFAULTWIDTHX_DEFAULT;
    }
    if (cffont->private[0] && cff_dict_known(cffont->private[0], "nominalWidthX")) {
        nominal_width = (double) cff_dict_get(cffont->private[0], "nominalWidthX", 0);
    } else {
        nominal_width = CFF_NOMINALWIDTHX_DEFAULT;
    }

    num_glyphs = 0; last_cid = 0;
    add_to_used_chars2(used_chars, 0); /* .notdef */
    for (i = 0; i < (cffont->num_glyphs + 7)/8; i++) {
        int c, j;

        c = used_chars[i];
        for (j = 7; j >= 0; j--) {
            if (c & (1 << j)) {
                num_glyphs++;
                last_cid = (i + 1) * 8 - j - 1;
            }
        }
    }

    {
        cff_fdselect *fdselect;

        fdselect = NEW(1, cff_fdselect);
        fdselect->format = 3;
        fdselect->num_entries = 1;
        fdselect->data.ranges = NEW(1, cff_range3);
        fdselect->data.ranges[0].first = 0;
        fdselect->data.ranges[0].fd    = 0;
        cffont->fdselect = fdselect;
    }

    {
        cff_charsets *charset;

        charset  = NEW(1, cff_charsets);
        charset->format = 0;
        charset->num_entries = num_glyphs-1;
        charset->data.glyphs = NEW(num_glyphs-1, s_SID);

        for (gid = 0, cid = 0; cid <= last_cid; cid++) {
            if (is_used_char2(used_chars, cid)) {
                if (gid > 0)
                    charset->data.glyphs[gid-1] = cid;
                gid++;
            }
        }
        /* cff_release_charsets(cffont->charsets); */
        cffont->charsets = charset;
    }

    cff_dict_add(cffont->topdict, "CIDCount", 1);
    cff_dict_set(cffont->topdict, "CIDCount", 0, last_cid + 1);

    cffont->fdarray    = NEW(1, cff_dict *);
    cffont->fdarray[0] = cff_new_dict();
    cff_dict_add(cffont->fdarray[0], "FontName", 1);
    cff_dict_set(cffont->fdarray[0], "FontName", 0,
                 (double) cff_add_string(cffont, font->fontname + 7, 1)); /* FIXME: Skip XXXXXX+ */
    cff_dict_add(cffont->fdarray[0], "Private", 2);
    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);
    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);
    /* FDArray  - index offset, not known yet */
    cff_dict_add(cffont->topdict, "FDArray", 1);
    cff_dict_set(cffont->topdict, "FDArray", 0, 0.0);
    /* FDSelect - offset, not known yet */
    cff_dict_add(cffont->topdict, "FDSelect", 1);
    cff_dict_set(cffont->topdict, "FDSelect", 0, 0.0);

    cff_dict_remove(cffont->topdict, "UniqueID");
    cff_dict_remove(cffont->topdict, "XUID");
    cff_dict_remove(cffont->topdict, "Private");
    cff_dict_remove(cffont->topdict, "Encoding");


    /* */
    offset = (int) cff_dict_get(cffont->topdict, "CharStrings", 0);
    cff_seek_set(cffont, offset);
    idx = cff_get_index_header(cffont);
    /* offset is now absolute offset ... bad */
    offset = cff_tell(cffont);

    if (idx->count < 2) {
        dpx_warning("No valid charstring data found: %s", font->filename);
        cff_release_index(idx);
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }

    /* New CharStrings INDEX */
    charstrings = cff_new_index((card16)(num_glyphs+1));
    max_len = 2 * CS_STR_LEN_MAX;
    charstrings->data = NEW(max_len, card8);
    charstring_len = 0;

    gid  = 0;
    data = NEW(CS_STR_LEN_MAX, card8);
    for (cid = 0; cid <= last_cid; cid++) {
        if (!is_used_char2(used_chars, cid))
            continue;

        if ((size = (idx->offset)[cid+1] - (idx->offset)[cid])
            > CS_STR_LEN_MAX) {
            dpx_warning("Charstring too long: %s (gid=%u)", font->filename, cid);
            free(data);
            cff_release_index(charstrings);
            cff_release_index(idx);
            cff_close(cffont);
            sfnt_close(sfont);
            ttstub_input_close(handle);
            return -1;
        }

        if (charstring_len + CS_STR_LEN_MAX >= max_len) {
            max_len = charstring_len + 2 * CS_STR_LEN_MAX;
            charstrings->data = RENEW(charstrings->data, max_len, card8);
        }
        (charstrings->offset)[gid] = charstring_len + 1;
        cff_seek(cffont, offset + (idx->offset)[cid] - 1);
        cff_read_data(data, size, cffont);
        charstring_len += cs_copy_charstring(charstrings->data + charstring_len,
                                             max_len - charstring_len,
                                             data, size,
                                             cffont->gsubr, (cffont->subrs)[0],
                                             default_width, nominal_width, NULL);
        gid++;
    }
    if (gid != num_glyphs) {
        dpx_warning("Unexpected error: %s", font->filename);
        free(data);
        cff_release_index(charstrings);
        cff_release_index(idx);
        cff_close(cffont);
        sfnt_close(sfont);
        ttstub_input_close(handle);
        return -1;
    }
    free(data);
    cff_release_index(idx);

    (charstrings->offset)[num_glyphs] = charstring_len + 1;
    charstrings->count = num_glyphs;
    cffont->num_glyphs    = num_glyphs;
    cffont->cstrings      = charstrings;

    /* no Global subr */
    if (cffont->gsubr)
        cff_release_index(cffont->gsubr);
    cffont->gsubr = cff_new_index(0);

    if (cffont->subrs && cffont->subrs[0]) {
        cff_release_index(cffont->subrs[0]);
        cffont->subrs[0] = NULL;
    }
    if (cffont->private && (cffont->private)[0]) {
        cff_dict_remove((cffont->private)[0], "Subrs"); /* no Subrs */
    }

    cff_add_string(cffont, "Adobe", 1);
    cff_add_string(cffont, "Identity", 1);

    cff_dict_update(cffont->topdict, cffont);

    if (cffont->private && cffont->private[0]) {
        cff_dict_update(cffont->private[0], cffont);
    }

    cff_update_string(cffont);

    /* CFF code need to be rewrote... */
    cff_dict_add(cffont->topdict, "ROS", 3);
    cff_dict_set(cffont->topdict, "ROS", 0,
                 (double) cff_get_sid(cffont, "Adobe"));
    cff_dict_set(cffont->topdict, "ROS", 1,
                 (double) cff_get_sid(cffont, "Identity"));
    cff_dict_set(cffont->topdict, "ROS", 2, 0.0);

    destlen = write_fontfile(font, cffont);

    /*
     * DW, W, DW2 and W2:
     * Those values are obtained from OpenType table (not TFM).
     */
    {
        unsigned char *CIDToGIDMap;

        CIDToGIDMap = NEW(2 * (last_cid+1), unsigned char);
        memset(CIDToGIDMap, 0, 2 * (last_cid + 1));
        for (cid = 0; cid <= last_cid; cid++) {
            if (is_used_char2(used_chars, cid)) {
                CIDToGIDMap[2*cid  ] = (cid >> 8) & 0xff;
                CIDToGIDMap[2*cid+1] = cid & 0xff;
            }
        }
        add_CIDMetrics(sfont, font->resource, CIDToGIDMap, last_cid,
                       font->cid.need_vmetrics ? 1 : 0);
        free(CIDToGIDMap);
    }

    cff_close(cffont);
    sfnt_close(sfont);
    ttstub_input_close(handle);

    if (dpx_conf.verbose_level > 1)
        dpx_message("[%u glyphs][%d bytes]", num_glyphs, destlen);

    if (pdf_check_version(2, 0) < 0) {
        CIDFont_type0_add_CIDSet(font, used_chars, last_cid);
    }

    return 0;
}

static int
load_base_CMap (const char *font_name, int wmode, cff_font *cffont)
{
    int       cmap_id = -1;
    CMap     *cmap;
    char     *cmap_name;
    card16    gid;
    unsigned char range_min[4] = {0x00, 0x00, 0x00, 0x00};
    unsigned char range_max[4] = {0x7f, 0xff, 0xff, 0xff};

    cmap_name = NEW(strlen(font_name)+strlen("-UCS4-H")+1, char);
    if (wmode) {
        sprintf(cmap_name, "%s-UCS4-V", font_name);
    } else {
        sprintf(cmap_name, "%s-UCS4-H", font_name);
    }

    cmap_id = CMap_cache_find(cmap_name);
    if (cmap_id >= 0) {
        free(cmap_name);
        return cmap_id;
    }

    cmap = CMap_new();
    CMap_set_name (cmap, cmap_name);
    CMap_set_type (cmap, CMAP_TYPE_CODE_TO_CID);
    CMap_set_wmode(cmap, wmode);
    CMap_add_codespacerange(cmap, range_min, range_max, 4);
    CMap_set_CIDSysInfo(cmap, &CSI_IDENTITY);
    free(cmap_name);

    for (gid = 1; gid < cffont->num_glyphs; gid++) {
        int32_t   ucv;
        s_SID     sid;
        char     *glyph, *name, *suffix;
        unsigned char  srcCode[4];

        sid   = cff_charsets_lookup_inverse(cffont, gid);
        glyph = cff_get_string (cffont, sid);

        name  = agl_chop_suffix(glyph, &suffix);
        if (!name) {
            free(suffix);
            free(glyph);
            continue;
        }

        if (suffix) {
            free(name);
            free(suffix);
            free(glyph);
            continue;
        }

        if (agl_name_is_unicode(name)) {
            ucv = agl_name_convert_unicode(name);
            srcCode[0] = (ucv >> 24) & 0xff;
            srcCode[1] = (ucv >> 16) & 0xff;
            srcCode[2] = (ucv >>  8) & 0xff;
            srcCode[3] = ucv & 0xff;
            CMap_add_cidchar(cmap, srcCode, 4, gid);
        } else {
            agl_name *agln;

            agln = agl_lookup_list(name);
            if (!agln)
                dpx_warning("Glyph \"%s\" inaccessible (no Unicode mapping)", glyph);
            while (agln) {
                if (agln->n_components > 1) {
                    dpx_warning("Glyph \"%s\" inaccessible (composite character)", glyph);
                } else if (agln->n_components == 1) {
                    ucv = agln->unicodes[0];
                    srcCode[0] = (ucv >> 24) & 0xff;
                    srcCode[1] = (ucv >> 16) & 0xff;
                    srcCode[2] = (ucv >>  8) & 0xff;
                    srcCode[3] = ucv & 0xff;
                    CMap_add_cidchar(cmap, srcCode, 4, gid);
                }
                agln = agln->alternate;
            }
        }
        free(name);
        free(suffix);
        free(glyph);
    }
    cmap_id = CMap_cache_add(cmap);

    return cmap_id;
}

int
t1_load_UnicodeCMap (const char *font_name,
                     const char *otl_tags,  /* not supported yet */
                     int wmode)
{
    int       cmap_id = -1;
    cff_font *cffont;
    rust_input_handle_t handle = INVALID_HANDLE;

    if (!font_name)
        return -1;

    handle = dpx_open_type1_file(font_name);
    if (handle == INVALID_HANDLE)
        return -1;

    cffont = t1_load_font(NULL, 1, handle);
    ttstub_input_close(handle);
    if (!cffont)
        return -1;

    cmap_id = load_base_CMap(font_name, wmode, cffont);

    cff_close(cffont);

    if (cmap_id < 0) {
        dpx_warning("Failed to create Unicode charmap for font \"%s\".", font_name);
        return -1;
    }

    if (otl_tags) {
        dpx_warning("Glyph substitution not supported for Type1 font yet...");
    }

    return cmap_id;
}


/*
 * ToUnicode CMap
 */

static pdf_obj *
create_ToUnicode_stream (cff_font *cffont,
                         const char *font_name, const char *used_glyphs)
{
    pdf_obj *stream = NULL;
    CMap    *cmap;
    CID      cid;
    card16   gid;
    int      glyph_count, total_fail_count;
    char    *cmap_name;
#define WBUF_SIZE 1024
    unsigned char  wbuf[WBUF_SIZE];
    unsigned char *p, *endptr;
    static unsigned char range_min[2] = {0x00, 0x00};
    static unsigned char range_max[2] = {0xff, 0xff};

    if (!font_name || !used_glyphs)
        return NULL;

    cmap = CMap_new();

    cmap_name = NEW(strlen(font_name)+strlen("-UTF16")+1, char);
    strcpy(cmap_name, font_name);
    strcat(cmap_name, "-UTF16");
    CMap_set_name (cmap, cmap_name);
    free(cmap_name);

    CMap_set_wmode(cmap, 0);
    CMap_set_type (cmap, CMAP_TYPE_TO_UNICODE);
    CMap_set_CIDSysInfo(cmap, &CSI_UNICODE);

    CMap_add_codespacerange(cmap, range_min, range_max, 2);

    glyph_count = total_fail_count = 0;
    p      = wbuf;
    endptr = wbuf + WBUF_SIZE;
    for (cid = 1; cid < cffont->num_glyphs; cid++) { /* Skip .notdef */
        if (is_used_char2(used_glyphs, cid)) {
            char *glyph;
            int32_t len;
            int   fail_count;

            wbuf[0] = (cid >> 8) & 0xff;
            wbuf[1] = (cid & 0xff);

            p = wbuf + 2;
            gid = cff_charsets_lookup_inverse(cffont, cid);
            if (gid == 0)
                continue;
            glyph = cff_get_string(cffont, gid);
            if (glyph) {
                len = agl_sput_UTF16BE(glyph, &p, endptr, &fail_count);
                if (len < 1 || fail_count) {
                    total_fail_count += fail_count;
                } else {
                    CMap_add_bfchar(cmap, wbuf, 2, wbuf+2, len);
                }
                free(glyph);
            }
            glyph_count++;
        }
    }

    if (total_fail_count != 0 &&
        total_fail_count >= glyph_count/10) {
        dpx_warning("%d glyph names (out of %d) missing Unicode mapping.",
                    total_fail_count, glyph_count);
        dpx_warning("ToUnicode CMap \"%s-UTF16\" removed.", font_name);
    } else {
        stream = CMap_create_stream(cmap);
    }
    CMap_release(cmap);

    return stream;
}

pdf_obj *
CIDFont_type0_t1create_ToUnicode_stream (const char *filename, const char *fontname, const char *used_chars)
{
    pdf_obj  *ref = NULL;
    pdf_obj  *tounicode;
    cff_font *cffont;
    rust_input_handle_t handle;

    assert(filename);
    assert(fontname);
    assert(used_chars);

    handle = dpx_open_type1_file(filename);
    if (!handle) {
        return NULL;
    }

    cffont = t1_load_font(NULL, 1, handle);
    if (cffont) {
        tounicode = create_ToUnicode_stream(cffont, fontname, used_chars);
        if (tounicode) {
            ref = pdf_ref_obj(tounicode);
            pdf_release_obj(tounicode);
        }
    }
    ttstub_input_close(handle);

    return ref;
}


/* Duplicate from type1.c */
#define TYPE1_NAME_LEN_MAX   127

#define FONT_FLAG_FIXEDPITCH (1 << 0)  /* Fixed-width font */
#define FONT_FLAG_SERIF      (1 << 1)  /* Serif font */
#define FONT_FLAG_SYMBOLIC   (1 << 2)  /* Symbolic font */
#define FONT_FLAG_SCRIPT     (1 << 3)  /* Script font */
#define FONT_FLAG_STANDARD   (1 << 5)  /* Adobe Standard Character Set */
#define FONT_FLAG_ITALIC     (1 << 6)  /* Italic */
#define FONT_FLAG_ALLCAP     (1 << 16) /* All-cap font */
#define FONT_FLAG_SMALLCAP   (1 << 17) /* Small-cap font */
#define FONT_FLAG_FORCEBOLD  (1 << 18) /* Force bold at small text sizes */

static void
get_font_attr (pdf_font *font, cff_font *cffont)
{
    double capheight, ascent, descent;
    double italicangle, stemv;
    double defaultwidth, nominalwidth;
    int    flags = 0;
    int    gid;
    int    i;
    static const char *L_c[] = {
        "H", "P", "Pi", "Rho", NULL
    };
    static const char *L_d[] = {
        "p", "q", "mu", "eta", NULL
    };
    static const char *L_a[] = {
        "b", "h", "lambda", NULL
    };
    t1_ginfo gm;

    defaultwidth = 500.0;
    nominalwidth = 0.0;

    /*
     * CapHeight, Ascent, and Descent is meaningfull only for Latin/Greek/Cyrillic.
     * The BlueValues and OtherBlues also have those information.
     */
    if (cff_dict_known(cffont->topdict, "FontBBox")) {
        /* Default values */
        capheight = ascent = cff_dict_get(cffont->topdict, "FontBBox", 3);
        descent = cff_dict_get(cffont->topdict, "FontBBox", 1);
    } else {
        capheight =  680.0;
        ascent    =  690.0;
        descent   = -190.0;
    }
    if (cff_dict_known(cffont->private[0], "StdVW")) {
        stemv = cff_dict_get(cffont->private[0], "StdVW", 0);
    } else {
        /*
         * We may use the following values for StemV:
         *  Thin - ExtraLight: <= 50
         *  Light: 71
         *  Regular(Normal): 88
         *  Medium: 109
         *  SemiBold(DemiBold): 135
         *  Bold - Heavy: >= 166
         */
        stemv = 88.0;
    }
    if (cff_dict_known(cffont->topdict, "ItalicAngle")) {
        italicangle = cff_dict_get(cffont->topdict, "ItalicAngle", 0);
        if (italicangle != 0.0)
            flags |= FONT_FLAG_ITALIC;
    } else {
        italicangle = 0.0;
    }

    /*
     * Use "space", "H", "p", and "b" for various values.
     * Those characters should not "seac". (no accent)
     */
    gid = cff_glyph_lookup(cffont, "space");
    if (gid >= 0 && gid < cffont->cstrings->count) {
        t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1,
                           cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid],
                           cffont->subrs[0], &gm);
        defaultwidth = gm.wx;
    }

    for (i = 0; L_c[i] != NULL; i++) {
        gid = cff_glyph_lookup(cffont, L_c[i]);
        if (gid >= 0 && gid < cffont->cstrings->count) {
            t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1,
                               cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid],
                               cffont->subrs[0], &gm);
            capheight = gm.bbox.ury;
            break;
        }
    }

    for (i = 0; L_d[i] != NULL; i++) {
        gid = cff_glyph_lookup(cffont, L_d[i]);
        if (gid >= 0 && gid < cffont->cstrings->count) {
            t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1,
                               cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid],
                               cffont->subrs[0], &gm);
            descent = gm.bbox.lly;
            break;
        }
    }

    for (i = 0; L_a[i] != NULL; i++) {
        gid = cff_glyph_lookup(cffont, L_a[i]);
        if (gid >= 0 && gid < cffont->cstrings->count) {
            t1char_get_metrics(cffont->cstrings->data + cffont->cstrings->offset[gid] - 1,
                               cffont->cstrings->offset[gid+1] - cffont->cstrings->offset[gid],
                               cffont->subrs[0], &gm);
            ascent = gm.bbox.ury;
            break;
        }
    }

    if (defaultwidth != 0.0) {
        cff_dict_add(cffont->private[0], "defaultWidthX", 1);
        cff_dict_set(cffont->private[0], "defaultWidthX", 0, defaultwidth);
    }
    if (nominalwidth != 0.0) {
        cff_dict_add(cffont->private[0], "nominalWidthX", 1);
        cff_dict_set(cffont->private[0], "nominalWidthX", 0, nominalwidth);
    }
    if (cff_dict_known(cffont->private[0], "ForceBold") &&
        cff_dict_get(cffont->private[0], "ForceBold", 0)) {
        flags |= FONT_FLAG_FORCEBOLD;
    }
    if (cff_dict_known(cffont->private[0], "IsFixedPitch") &&
        cff_dict_get(cffont->private[0], "IsFixedPitch", 0)) {
        flags |= FONT_FLAG_FIXEDPITCH;
    }
    if (font->fontname &&
        !strstr(font->fontname, "Sans")) {
        flags |= FONT_FLAG_SERIF;
    }
    flags |= FONT_FLAG_SYMBOLIC;

    pdf_add_dict(font->descriptor,
                 pdf_new_name("CapHeight"), pdf_new_number(capheight));
    pdf_add_dict(font->descriptor,
                 pdf_new_name("Ascent"), pdf_new_number(ascent));
    pdf_add_dict(font->descriptor,
                 pdf_new_name("Descent"), pdf_new_number(descent));
    pdf_add_dict(font->descriptor,
                 pdf_new_name("ItalicAngle"), pdf_new_number(italicangle));
    pdf_add_dict(font->descriptor,
                 pdf_new_name("StemV"), pdf_new_number(stemv));
    pdf_add_dict(font->descriptor,
                 pdf_new_name("Flags"), pdf_new_number(flags));
}

static void
add_metrics (pdf_font *font, cff_font *cffont,
             unsigned char *CIDToGIDMap,
             double *widths, double default_width, CID last_cid)
{
    pdf_obj *tmp;
    double   val;
    card16   cid, gid;
    char    *used_chars;
    int      i;

    /*
     * The original FontBBox of the font is preserved, instead
     * of replacing it with tight bounding box calculated from
     * charstrings, to prevent Acrobat 4 from greeking text as
     * much as possible.
     */
    if (!cff_dict_known(cffont->topdict, "FontBBox")) {
        dpx_warning("No FontBBox found: %s", font->filename);
    } else {
        tmp = pdf_new_array();
        for (i = 0; i < 4; i++) {
            val = cff_dict_get(cffont->topdict, "FontBBox", i);
            pdf_add_array(tmp, pdf_new_number(ROUND(val, 1.0)));
        }
        pdf_add_dict(font->descriptor, pdf_new_name("FontBBox"), tmp);
    }

    used_chars = font->usedchars;

    /* FIXME:
     * This writes "CID CID width".
     * I think it's better to handle each 8 char block
     * and to use "CID_start [ w0 w1 ...]".
     */
    tmp = pdf_new_array();
    for (cid = 0; cid <= last_cid; cid++) {
        if (is_used_char2(used_chars, cid)) {
            gid = (CIDToGIDMap[2*cid] << 8)|CIDToGIDMap[2*cid+1];
            if (widths[gid] != default_width) {
                pdf_add_array(tmp, pdf_new_number(cid));
                pdf_add_array(tmp, pdf_new_number(cid));
                pdf_add_array(tmp, pdf_new_number(ROUND(widths[gid], 1.0)));
            }
        }
    }
    pdf_add_dict(font->resource,
                 pdf_new_name("DW"), pdf_new_number(default_width));
    if (pdf_array_length(tmp) > 0) {
        pdf_add_dict(font->resource, pdf_new_name("W"), pdf_ref_obj(tmp));
    }
    pdf_release_obj(tmp);
}

int
CIDFont_type0_t1dofont (pdf_font *font)
{
    cff_font *cffont;
    double    defaultwidth, nominalwidth;
    int       num_glyphs = 0;
    rust_input_handle_t handle;
    int       i, offset;
    char     *used_chars = NULL;
    card16    last_cid, gid, cid;
    unsigned char *CIDToGIDMap;

    assert(font);

    if (!font->reference) {
        return 0;
    }

    pdf_add_dict(font->resource,
                 pdf_new_name("FontDescriptor"),
                 pdf_ref_obj (font->descriptor));

    handle = dpx_open_type1_file(font->filename);
    if (!handle) {
        dpx_warning("Type1: Could not open Type1 font.");
        return -1;
    }

    cffont = t1_load_font(NULL, 0, handle);
    if (!cffont) {
        dpx_warning("Could not read Type 1 font...");
        ttstub_input_close(handle);
        return -1;
    }

    ttstub_input_close(handle);

    assert(font->fontname);
    assert(font->usedchars);

    used_chars = font->usedchars;

    cff_set_name(cffont, font->fontname);

    /* defaultWidthX, CapHeight, etc. */
    get_font_attr(font, cffont);
    if (cff_dict_known(cffont->private[0], "defaultWidthX")) {
        defaultwidth = cff_dict_get(cffont->private[0], "defaultWidthX", 0);
    } else {
        defaultwidth = 0.0;
    }
    if (cff_dict_known(cffont->private[0], "nominalWidthX")) {
        nominalwidth = cff_dict_get(cffont->private[0], "nominalWidthX", 0);
    } else {
        nominalwidth = 0.0;
    }

    num_glyphs = 0; last_cid = 0;
    add_to_used_chars2(used_chars, 0); /* .notdef */
    for (i = 0; i < (cffont->num_glyphs + 7)/8; i++) {
        int c, j;

        c = used_chars[i];
        for (j = 7; j >= 0; j--) {
            if (c & (1 << j)) {
                num_glyphs++;
                last_cid = (i + 1) * 8 - j - 1;
            }
        }
    }

    {
        cff_fdselect *fdselect;

        fdselect = NEW(1, cff_fdselect);
        fdselect->format = 3;
        fdselect->num_entries = 1;
        fdselect->data.ranges = NEW(1, cff_range3);
        fdselect->data.ranges[0].first = 0;
        fdselect->data.ranges[0].fd    = 0;
        cffont->fdselect = fdselect;
    }

    CIDToGIDMap = NEW(2*(last_cid+1), unsigned char);
    memset(CIDToGIDMap, 0, 2*(last_cid+1));
    {
        cff_charsets *charset;

        charset  = NEW(1, cff_charsets);
        charset->format = 0;
        charset->num_entries = num_glyphs-1;
        charset->data.glyphs = NEW(num_glyphs-1, s_SID);

        for (gid = 0, cid = 0; cid <= last_cid; cid++) {
            if (is_used_char2(used_chars, cid)) {
                if (gid > 0)
                    charset->data.glyphs[gid-1] = cid;
                CIDToGIDMap[2*cid  ] = (gid >> 8) & 0xff;
                CIDToGIDMap[2*cid+1] = gid & 0xff;
                gid++;
            }
        }

        cff_release_charsets(cffont->charsets);
        cffont->charsets = charset;
    }

    cff_dict_add(cffont->topdict, "CIDCount", 1);
    cff_dict_set(cffont->topdict, "CIDCount", 0, last_cid + 1);

    cffont->fdarray    = NEW(1, cff_dict *);
    cffont->fdarray[0] = cff_new_dict();
    cff_dict_add(cffont->fdarray[0], "FontName", 1);
    cff_dict_set(cffont->fdarray[0], "FontName", 0,
                 (double) cff_add_string(cffont, font->fontname + 7, 1)); /* FIXME: Skip XXXXXX+ */
    cff_dict_add(cffont->fdarray[0], "Private", 2);
    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);
    cff_dict_set(cffont->fdarray[0], "Private", 0, 0.0);

    /* FDArray  - index offset, not known yet */
    cff_dict_add(cffont->topdict, "FDArray", 1);
    cff_dict_set(cffont->topdict, "FDArray", 0, 0.0);
    /* FDSelect - offset, not known yet */
    cff_dict_add(cffont->topdict, "FDSelect", 1);
    cff_dict_set(cffont->topdict, "FDSelect", 0, 0.0);

    cff_dict_add(cffont->topdict, "charset", 1);
    cff_dict_set(cffont->topdict, "charset", 0, 0.0);

    cff_dict_add(cffont->topdict, "CharStrings", 1);
    cff_dict_set(cffont->topdict, "CharStrings", 0, 0.0);

    {
        cff_index *cstring;
        t1_ginfo   gm;
        int        max = 0;
        double    *widths;
        int        w_stat[1001], max_count, dw;

        widths = NEW(num_glyphs, double);
        memset(w_stat, 0, sizeof(int)*1001);
        offset  = 0L;
        cstring = cff_new_index((card16)num_glyphs);
        cstring->data = NULL;
        cstring->offset[0] = 1;
        gid = 0;
        for (cid = 0; cid <= last_cid; cid++) {
            if (!is_used_char2(used_chars, cid))
                continue;

            if (offset + CS_STR_LEN_MAX >= max) {
                max += CS_STR_LEN_MAX*2;
                cstring->data = RENEW(cstring->data, max, card8);
            }
            offset += t1char_convert_charstring(cstring->data + cstring->offset[gid] - 1, CS_STR_LEN_MAX,
                                                cffont->cstrings->data + cffont->cstrings->offset[cid] - 1,
                                                cffont->cstrings->offset[cid+1] - cffont->cstrings->offset[cid],
                                                cffont->subrs[0], defaultwidth, nominalwidth, &gm);
            cstring->offset[gid+1] = offset + 1;
            if (gm.use_seac) {
                dpx_warning("The \"seac\" command for an accented character found: %s", font->filename);
                free(widths);
                cff_release_index(cstring);
                free(CIDToGIDMap);
                cff_close(cffont);
                return -1;
            }
            widths[gid] = gm.wx;
            if (gm.wx >= 0.0 && gm.wx <= 1000.0) {
                w_stat[((int) gm.wx)] += 1;
            }
            gid++;
        }

        cff_release_index(cffont->cstrings);
        cffont->cstrings = cstring;

        max_count = 0; dw = -1;
        for (i = 0; i <= 1000; i++) {
            if (w_stat[i] > max_count) {
                dw        = i;
                max_count = w_stat[i];
            }
        }
        if (dw >= 0) {
            add_metrics(font, cffont, CIDToGIDMap, widths, dw, last_cid);
        } else {
            add_metrics(font, cffont, CIDToGIDMap, widths, defaultwidth, last_cid);
        }
        free(widths);
    }
    cff_release_index(cffont->subrs[0]);
    cffont->subrs[0] = NULL;

    free(CIDToGIDMap);

    cff_add_string(cffont, "Adobe", 1);
    cff_add_string(cffont, "Identity", 1);

    cff_dict_update(cffont->topdict, cffont);
    cff_dict_update(cffont->private[0], cffont);

    cff_update_string(cffont);

    /* CFF code need to be rewrote... */
    cff_dict_add(cffont->topdict, "ROS", 3);
    cff_dict_set(cffont->topdict, "ROS", 0,
                 (double) cff_get_sid(cffont, "Adobe"));
    cff_dict_set(cffont->topdict, "ROS", 1,
                 (double) cff_get_sid(cffont, "Identity"));
    cff_dict_set(cffont->topdict, "ROS", 2, 0.0);

    cffont->num_glyphs = num_glyphs;
    offset = write_fontfile(font, cffont);

    cff_close(cffont);

    if (pdf_check_version(2, 0) < 0) {
        CIDFont_type0_add_CIDSet(font, used_chars, last_cid);
    }

    return 0;
}
