/* Portions Copyright (C) 2001 artofcode LLC.
   Portions Copyright (C) 1996, 2001 Artifex Software Inc.
   Portions Copyright (C) 1988, 2000 Aladdin Enterprises.
   This software is based in part on the work of the Independent JPEG Group.
   All Rights Reserved.

   This software is distributed under license and may not be copied, modified
   or distributed except as expressly authorized under the terms of that
   license.  Refer to licensing information at http://www.artifex.com/ or
   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
   San Rafael, CA  94903, (415)492-9861, for further information. */
/*$Id: pluchar.c 2680 2006-11-16 17:42:43Z stefan $ */

/* plchar.c */
/* PCL font handling library -- operations on individual characters */
#include "math_.h"
#include "memory_.h"
#include "stdio_.h"             /* for gdebug.h */
#include "gdebug.h"
#include "gserror.h"
#include "gserrors.h"
#include "gstypes.h"
#include "gsmemory.h"
#include "gsstruct.h"
#include "gsmatrix.h"
#include "gsstate.h"
#include "gschar.h"
#include "gsimage.h"
#include "gspaint.h"
#include "gspath.h"
#include "gsbittab.h"
#include "gxarith.h"            /* for igcd */
#include "gxfont.h"
#include "gxfont42.h"
#include "plfont.h"
#include "plvalue.h"
#include "gscoord.h"
#include "gsstate.h"
#include "gxdevice.h"
#include "gxdevmem.h"
#include "gxpath.h"
/* We really shouldn't need the following, but currently they are needed */
/* for pgs->path and penum->log2_current_scale in pl_tt_build_char. */
#include "gxfixed.h"
#include "gxchar.h"
#include "gxfcache.h"
#include "gzstate.h"

/* agfa includes */
#undef true
#undef false
#undef frac_bits
#include "cgconfig.h"
#include "ufstport.h"
#include "shareinc.h"
#include "gxfapiu.h"


/* ---------------- UFST utilities ---------------- */

#define UFST_SCALE  16
#define FXD_ONE     (1L << UFST_SCALE)      /* fixed point 1.0000 */

private const pl_font_t *   plfont_last;    /* last selected font */
static  const gs_matrix     pl_identmtx = { identity_matrix_body };

extern IF_STATE if_state;
extern PIF_STATE pIFS;
/*
 * Set up a generic FONTCONTEXT structure.
 *
 * NB: UFST automatically inverts the y-axis when generating a bitmap, so
 *     it is necessary to account for that change in this routine.
 */
void
pl_init_fc(
    const pl_font_t *   plfont,
    gs_state *          pgs,
    int                 need_outline,
    FONTCONTEXT *       pfc,
    bool                width_request)
{
    gs_font *               pfont = plfont->pfont;

    /* set the current tranformation matrix - EM's... if this is a
       width request we don't necessarily have a current graphics
       state... use identity for resolution and ctm */
    gs_matrix mat;
    floatp xres, yres;

    if ( width_request ) {
        gs_make_identity(&mat);
        xres = yres = 1;
    } else {
        gs_currentmatrix(pgs, &mat);
        xres = gs_currentdevice(pgs)->HWResolution[0];
        yres = gs_currentdevice(pgs)->HWResolution[1];
    }
    pfc->font_id = 0;
    pfc->xspot = F_ONE;
    pfc->yspot = F_ONE;
    /* symbol set id used for no symbol set mapping */
    pfc->ssnum = 0x8000;
    /* filled in if downloaded font and/or symbol set. */
    pfc->font_hdr = NULL;
    pfc->dl_ssnum = 0;
    /* union selector for transformation type m0..m3 - pcl uses pt.
       size */
    pfc->fc_type = FC_MAT2_TYPE;
    /* calculate point size, set size etc based on current CTM in EM's */
    {
        floatp hx = hypot(mat.xx, mat.xy);
        floatp hy = hypot(mat.yx, mat.yy);
        /* fixed point scaling */
        floatp mscale = 1L << 16;
        pfc->s.m2.matrix_scale = 16;
        pfc->s.m2.point_size = (int)((hy * plfont->pts_per_inch / yres) + 0.5) * 8; /* 1/8ths */
        pfc->s.m2.set_size = (int)((hx * plfont->pts_per_inch / xres) + 0.5) * 8;
        pfc->s.m2.m[0] = mscale * mat.xx / hx;
        pfc->s.m2.m[1] = mscale * -mat.xy / hx;
        pfc->s.m2.m[2] = mscale * mat.yx / hy;
        pfc->s.m2.m[3] = mscale * -mat.yy / hy;
        pfc->s.m2.world_scale = 16;
        pfc->s.m2.xworld_res = mscale * xres;
        pfc->s.m2.yworld_res = mscale * yres;
    }

    if ( need_outline ) {
        pfc->s.m2.m[1] = -pfc->s.m2.m[1];
        pfc->s.m2.m[3] = -pfc->s.m2.m[3];
    }

    /* always use our own symbol set mapping */
    pfc->ExtndFlags = EF_NOSYMSETMAP;
    if (plfont->is_xl_format) {
        pfc->ExtndFlags = EF_XLFONT_TYPE;
        if ((pfont->WMode & 0x1) != 0)  /* vertical substitution */
            pfc->ExtndFlags |= EF_VERTSUBS_TYPE;
    }
    else if (plfont->scaling_technology == plfst_TrueType && plfont->large_sizes) {
	 pfc->ExtndFlags = EF_FORMAT16_TYPE | EF_GALLEYSEG_TYPE;
	 if ((pfont->WMode & 0x1) != 0)  /* vertical substitution */
	     pfc->ExtndFlags |= EF_VERTSUBS_TYPE;
    }
    pfc->ExtndFlags |= EF_NOUSBOUNDBOX; /* UFST 5.0+ addition */

    /* handle artificial emboldening */
    if (plfont->bold_fraction && !need_outline) {
        pfc->pcl6bold = 32768 * plfont->bold_fraction;
    }
    else
        pfc->pcl6bold = 0;
    /* set the format */
    pfc->format = FC_PCL6_EMU | FC_INCHES_TYPE;
    pfc->format |= (need_outline ? FC_LINEAR_TYPE : FC_BITMAP_TYPE);
}

/*
 * Set the current UFST font (any type).
 */
private int
pl_set_ufst_font(const pl_font_t * plfont, FONTCONTEXT * pfc)
{
    uint    status = CGIFfont(FSA pfc);

    if (status != 0)
        dprintf1("CGIFfont error %d\n", status);
    else
        plfont_last = plfont;   /* record this font for use in call-backs */
    return status;
}

/*
 * Set the current path from a character outline. This is more general than
 * may be necessary, depending on how the UFST module is compiled.
 */
private int
image_outline_char(
    PIFOUTLINE              pols,
    const gs_matrix_fixed * pmat,
    gx_path *               ppath,
    gs_font *               pfont )
{
    UW16                    il, numLoops = pols->ol.num_loops;
    byte *                  pbase = (byte *)&pols->ol.loop;
    int                     ishift = fixed_fraction_bits + pols->VLCpower;
    fixed                   tx = pmat->tx_fixed, ty = pmat->ty_fixed;

    for (il = 0; il < numLoops; il++) {
        OUTLINE_LOOP *  ploop = &pols->ol.loop[il];
        uint            numSegmts = ploop->num_segmts;
        byte *          pseg = pbase + ploop->segmt_offset;
        PINTRVECTOR     pcoord = (PINTRVECTOR)(pbase + ploop->coord_offset);
        int             code;

        while (numSegmts-- > 0) {
            int             segtype = *pseg++;
            int             ip, npts;
            gs_fixed_point  pt[3];

            if (segtype == 2 || segtype > 3)
                return_error(gs_error_rangecheck);

            npts = (segtype == 3 ? 3 : 1);
            for (ip = 0; ip < npts; ip++, ++pcoord) {
                pt[ip].x = (pcoord->x << ishift) + tx;
                pt[ip].y = (pcoord->y << ishift) + ty;
            }

            switch (segtype) {

              case 0:       /* moveto */
                code = gx_path_add_point(ppath, pt[0].x, pt[0].y);
                break;

              case 1:       /* lineto */
                code = gx_path_add_line(ppath, pt[0].x, pt[0].y);
                break;

              case 3:       /* curveto */
                code = gx_path_add_curve( ppath,
                                          pt[0].x, pt[0].y,
                                          pt[1].x, pt[1].y,
                                          pt[2].x, pt[2].y );
            }       
            if (code < 0)
                return code;
        }
        if ((code = gx_path_close_subpath(ppath)) < 0)
            return code;
    }               
    return 0;
}

/*
 * Get the widt from a UFST character (any type). The caller should have
 * set the font type in advance.
 */
private int
pl_ufst_char_width(
    uint                char_code,
    const void *        pgs,
    gs_point *          pwidth,
    FONTCONTEXT *       pfc )
{
    
    UW16                chIdloc = char_code;
    UW16                fontWidth[2];
    int                 status;
    WIDTH_LIST_INPUT_ENTRY fcode;
    if (pwidth != NULL)
        pwidth->x = pwidth->y = 0;

    CGIFchIdptr(FSA (VOID *)&chIdloc, NULL);
    fcode.CharType.TT_unicode = char_code;
    if ((status = CGIFwidth2(FSA &fcode, 1, 4, fontWidth)) != 0) {
        dprintf1("CGIFwidth error %d\n", status);
        return status;
    }
    if (fontWidth[0] == ERR_char_unavailable || fontWidth[1] == 0)
        return 1;
    else if (pwidth != NULL) {
        floatp  fontw = (floatp)fontWidth[0] / (floatp)fontWidth[1];
        int     code =  gs_distance_transform(fontw, 0.0, &pl_identmtx, pwidth);

        return code < 0 ? code : 0;
    } else
        return 0;
}

/*
 * Generate a UFST character.
 */
private int
pl_ufst_make_char(
    gs_show_enum *      penum,
    gs_state *          pgs,
    gs_font *           pfont,
    gs_char             chr,
    FONTCONTEXT *       pfc )
{       
    gs_imager_state *   pis = (gs_imager_state *)pgs;
    MEM_HANDLE          memhdl;
    UW16                status, chIdloc = chr;
    gs_matrix           sv_ctm, tmp_ctm;
    int wasValid;

    /* ignore illegitimate characters */
    if (chr == 0xffff)
        return 0;

    CGIFchIdptr(FSA (VOID *)&chIdloc, NULL);
    if ( (status = CGIFchar_handle(FSA chr, &memhdl, 0)) != 0 &&
         status != ERR_fixed_space                          ) {

        /* if too large for a bitmap, try an outline */
        if (status >= ERR_bm_gt_oron && status <= ERRdu_pix_range) {
            pfc->format = (pfc->format & ~FC_BITMAP_TYPE) | FC_CUBIC_TYPE;
            if ((status = CGIFfont(FSA pfc)) == 0) {
                CGIFchIdptr(FSA (VOID *)&chIdloc, NULL);
                status = CGIFchar_handle(FSA chr, &memhdl, 0);
            }
        }
        if (status != 0) {
            dprintf2("CGIFchar_handle error %d for char=0x%x\n", status, chr);
            gs_setcharwidth(penum, pgs, 0.0, 0.0);
            return 0;   /* returning status causes the job to be aborted */
        }
    }

    wasValid = pgs->char_tm_valid;
    /* move to device space */
    gs_currentmatrix(pgs, &sv_ctm);
    gs_make_identity(&tmp_ctm);
    tmp_ctm.tx = sv_ctm.tx;
    tmp_ctm.ty = sv_ctm.ty;
    gs_setmatrix(pgs, &tmp_ctm);
    pgs->char_tm_valid = wasValid;

    if (FC_ISBITMAP(pfc)) {
        PIFBITMAP       psbm = (PIFBITMAP)MEMptr(memhdl);
        float           wbox[6];
        gs_image_t      image;
        gs_image_enum * ienum;
        int             code;
        gs_point        aw;

        /* set up the cache device */
        gs_distance_transform( (floatp)psbm->escapement / psbm->du_emx,
                                0.0,
                                &sv_ctm,
                                &aw );

        wbox[0] = aw.x;
        wbox[1] = aw.y;
        wbox[2] = psbm->xorigin / 16.0 + psbm->left_indent;
        wbox[3] = -psbm->yorigin / 16.0 + psbm->top_indent;
        wbox[4] = psbm->black_width + wbox[2];
        wbox[5] = psbm->black_depth + wbox[3];

        /* if (status == ERR_fixed_space) 
         *   we are relying on ufst to 
         *   send a zero sized image; we then cache the escapements of the space character 
         *   psbm->bm = psbm->width = psbm->height = 0;  
         *   note that the outline code can't be reached on ERR_fixed_space 
         */

        if ((code = gs_setcachedevice(penum, pgs, wbox)) < 0) {
            MEMfree(FSA CACHE_POOL, memhdl);
            gs_setmatrix(pgs, &sv_ctm);
            return code;
        }

        /* set up the image */
        ienum = gs_image_enum_alloc(pgs->memory, "pl_ufst_make_char");
        if (ienum == 0) {
            MEMfree(FSA CACHE_POOL, memhdl);
            gs_setmatrix(pgs, &sv_ctm);
            return_error(gs_error_VMerror);
        }
        gs_image_t_init_mask(&image, true);
        image.Width = psbm->width << 3;
        image.Height = psbm->depth;
        gs_make_identity(&image.ImageMatrix);
        image.ImageMatrix.tx = -psbm->xorigin / 16.0;
        image.ImageMatrix.ty = psbm->yorigin / 16.0;
        image.adjust = true;
        code = image_bitmap_char( ienum,
                                  &image,
                                  (byte *)psbm->bm,
                                  psbm->width,
                                  0,
                                  NULL,
                                  pgs );
        gs_free_object(pgs->memory, ienum, "pl_ufst_make_char");
        MEMfree(FSA CACHE_POOL, memhdl);
        gs_setmatrix(pgs, &sv_ctm);
        return (code < 0 ? code : 0);

    } else {    /* outline */
        PIFOUTLINE  pols = (PIFOUTLINE)MEMptr(memhdl);
        float       scale = pow(2, pols->VLCpower);
        float       wbox[6];
        int         code;
        gs_point    aw;

        /* set up the cache device */
        gs_distance_transform( (floatp)pols->escapement / pols->du_emx,
                               0.0,
                               &sv_ctm,
                               &aw );
        wbox[0] = aw.x;
        wbox[1] = aw.y;
        wbox[2] = scale * pols->left;
        wbox[3] = scale * pols->bottom;
        wbox[4] = scale * pols->right;
        wbox[5] = scale * pols->top;

        if (status == ERR_fixed_space) {
            MEMfree(FSA CACHE_POOL, memhdl);
             code = gs_setcharwidth(penum, pgs, wbox[0], wbox[1]);
             gs_setmatrix(pgs, &sv_ctm);
             return code;
         } else if ((code = gs_setcachedevice(penum, pgs, wbox)) < 0) {
             MEMfree(FSA CACHE_POOL, memhdl);
             gs_setmatrix(pgs, &sv_ctm);
             return code;
         }

        code = image_outline_char(pols, &pis->ctm, pgs->path, pfont);
        if (code >= 0) {
            code = gs_fill(pgs);
        }
        MEMfree(FSA CACHE_POOL, memhdl);
        gs_setmatrix(pgs, &sv_ctm);
        return (code < 0 ? code : 0);
    }    
}


/* ---------------- TrueType font support ---------------- */

/* Look up a character in the TrueType character-to-TT-glyph map. */
/* Return a pointer to the glyph's slot (chr != gs_no_char) or where */
/* it should be added (chr == gs_no_char). */
private pl_tt_char_glyph_t *
pl_tt_lookup_char(const pl_font_t *plfont, gs_glyph glyph)
{       uint size = plfont->char_glyphs.size;
        uint skip = plfont->char_glyphs.skip;
        uint index = glyph % size;
        pl_tt_char_glyph_t *ptcg;
        pl_tt_char_glyph_t *result = 0;

        while ( (ptcg = plfont->char_glyphs.table + index)->chr != gs_no_char ?
                ptcg->chr != glyph : ptcg->glyph
              )
          { if ( ptcg->chr == gs_no_char )
              result = ptcg;
            index = (index >= skip ? index : index + size) - skip;
          }
        return (result ? result : ptcg);
}

/* Get a string from a TrueType font. */
private int
pl_tt_string_proc(gs_font_type42 *pfont, ulong offset, uint length,
  const byte **pdata)
{       pl_font_t *plfont = pfont->client_data;

        *pdata = plfont->header + plfont->offsets.GT +
          (plfont->large_sizes ? 6 : 4) + offset;
        return 0;
}

/* Return the vertical substitute for a glyph, if it has one; */
/* otherwise return gs_no_glyph. */
private gs_glyph
pl_font_vertical_glyph(gs_glyph glyph, const pl_font_t *plfont)
{       long VT = plfont->offsets.VT;
        const byte *vtseg;
        uint i, len;

        if ( VT < 0 )
          return gs_no_glyph;
        vtseg = plfont->header + VT;
        if ( plfont->large_sizes )
          len = pl_get_uint32(vtseg + 2),
          i = 6;
        else
          len = pl_get_uint16(vtseg + 2),
          i = 4;
        len += i;
        for ( ; i < len; i += 4 )
          if ( glyph == pl_get_uint16(vtseg + i) )
            return pl_get_uint16(vtseg + i + 2);
        return gs_no_glyph;
}
#define access(base, length, vptr)\
  (*pfont->data.string_proc)(pfont, (ulong)(base), length, &vptr)

#ifndef gs_imager_state_DEFINED
#  define gs_imager_state_DEFINED
typedef struct gs_imager_state_s gs_imager_state;
#endif

/* Opaque type for a path */
#ifndef gx_path_DEFINED
#  define gx_path_DEFINED
typedef struct gx_path_s gx_path;
#endif

/* ---------------- MicroType font support ---------------- */
/*
 * MicroType accepts unicode values a glyph identifiers, so no explicit
 * encoding is necessary.
 */
private gs_glyph
pl_mt_encode_char(gs_font * pfont, gs_char pchr, gs_glyph_space_t not_used)
{
    return (gs_glyph)pchr;
}


/*
 * Set the current UFST font to be a MicroType font.
 */
private int
pl_set_mt_font(
    gs_state            *pgs,
    const pl_font_t *   plfont,
    int                 need_outline,
    FONTCONTEXT *       pfc )
{
    pl_init_fc(plfont, pgs, need_outline, pfc, /* width request iff */ pgs == NULL);
    pfc->font_id = ((gs_font_base *)(plfont->pfont))->UID.id;
#ifdef UFST_FROM_ROM
    pfc->format |= FC_ROM_TYPE | FC_NOUSBOUNDBOX;
#endif
    pfc->format |=  FC_FCO_TYPE;
    return pl_set_ufst_font(plfont, pfc);
}

/* Render a MicroType character. */
private int
pl_mt_build_char(
    gs_show_enum *      penum,
    gs_state *          pgs,
    gs_font *           pfont,
    gs_char             chr,
    gs_glyph            glyph )
{
    const pl_font_t *   plfont = (const pl_font_t *)pfont->client_data;
    FONTCONTEXT         fc;

    if ( pl_set_mt_font( pgs,
                         plfont,
                         gs_show_in_charpath(penum),
                         &fc ) != 0 )
        return 0;
    return pl_ufst_make_char(penum, pgs, pfont, chr, &fc);
}

#define MAX_LIST_SIZE 100
int list_size = 0;

typedef struct pl_glyph_width_node_s pl_glyph_width_node_t;

struct pl_glyph_width_node_s {
    uint char_code;
    uint font_id;
    gs_point width;
    pl_glyph_width_node_t *next;
};

pl_glyph_width_node_t *head = NULL;
/* add at the front of the list */

int 
pl_glyph_width_cache_node_add(gs_memory_t *mem, gs_id font_id, uint char_code, gs_point *pwidth)
{
    pl_glyph_width_node_t *node = 
        (pl_glyph_width_node_t *)gs_alloc_bytes(mem,
                                               sizeof(pl_glyph_width_node_t),
                                               "pl_glyph_width_cache_node_add");
    if ( node == NULL )
        return -1;
    if ( head == NULL ) {
        head = node;
        head->next = NULL;
    } else {
        node->next = head;
        head = node;
    }
    
    head->char_code = char_code;
    head->font_id = font_id;
    head->width = *pwidth;
    list_size++;
    return 0;
}

int
pl_glyph_width_cache_node_search(gs_id font_id, uint char_code, gs_point *pwidth)
{
    pl_glyph_width_node_t *current = head;
    while ( current ) {
        if ( char_code == current->char_code && font_id == current->font_id ) {
            *pwidth = current->width;
            return 0;
        }
        current = current->next;
    }
    return -1;
}

void
pl_glyph_width_list_remove(gs_memory_t *mem)
{
    pl_glyph_width_node_t *current = head;
    while (current) {
        pl_glyph_width_node_t *next = current->next;
        gs_free_object(mem, current, "pl_glyph_width_list_remove");
        current = next;
    }
    head = NULL;
    list_size = 0;
    return;
}


/* Get character existence and escapement for an MicroType font. */
private int
pl_mt_char_width(
    const pl_font_t *       plfont,
    const void *            pgs,
    uint                    char_code,
    gs_point *              pwidth )
{
    FONTCONTEXT             fc;
    int code;
    if ( list_size > MAX_LIST_SIZE )
        pl_glyph_width_list_remove(plfont->pfont->memory);
    code = pl_glyph_width_cache_node_search(plfont->pfont->id, char_code, pwidth);
    if ( code < 0 ) /* not found */ {
        /* FIXME inconsitant error code return values follow */
        if (pl_set_mt_font(NULL /* graphics state */, plfont, false, &fc) != 0)
            return 0;
        code = pl_ufst_char_width(char_code, pgs, pwidth, &fc);
        if ( code == 0 )
            code = pl_glyph_width_cache_node_add(plfont->pfont->memory, 
                                                 plfont->pfont->id,
                                                 char_code, pwidth);
    }
    return code;
}

private int
pl_mt_char_metrics(const pl_font_t *plfont, const void *pgs, uint char_code, float metrics[4])
{
    gs_point width;
    metrics[0] = metrics[1] = metrics[2] = metrics[3] = 0;
    if ( 0 == pl_mt_char_width(plfont, pgs, char_code, &width) ) {
      /* width is correct, 
         stefan foo: lsb is missing. */
        metrics[2] = width.x;
        /* metrics[0] = left_side_bearing;
         */
    }
    return 0;
}

/*
 * Callback from UFST to pass PCLEO IF character data starting with header.
 *
 * For TrueType fonts, the glyph table within a font is indexed by the glyph
 * id., rather than the unicode. The char_glyphs table in the font maps the
 * latter to the former.
 */
LPUB8 pl_PCLchId2ptr(FSP UW16 chId)
{
    const pl_font_t *   plfont = plfont_last;

    if (plfont_last == NULL)
        return NULL;    /* something wrong */

    /* check for a TrueType font */
    if (plfont->char_glyphs.table != NULL) {
        pl_tt_char_glyph_t *    ptcg = pl_tt_lookup_char(plfont, chId);

        if (ptcg->chr == gs_no_char)
            return NULL;    /* something wrong */
        chId = ptcg->glyph;
    }
    return (LPUB8)pl_font_lookup_glyph(plfont, chId)->data;
}

/*
 * callback from UFST to pass PCLEO TT character data starting with header.
 */
LPUB8 pl_PCLglyphID2Ptr(FSP UW16 chId)
{
    if (plfont_last == NULL)
        return NULL;    /* something wrong */
    else
        return (LPUB8)(pl_font_lookup_glyph(plfont_last, chId)->data);
}

/*
 * callback from UFST to pass PCLEO compound character data starting
 * with header.
 */
LPUB8 pl_PCLEO_charptr(LPUB8 pfont_hdr, UW16 char_code)
{
    if (plfont_last == NULL || plfont_last->header != pfont_hdr) {
        dprintf2("fontheader active=0x%x requested=0x%x\n",
                  (plfont_last == NULL ? 0 : plfont_last->header),
                  pfont_hdr );
        return NULL; /* something wrong */
    } else
        return pl_PCLchId2ptr(FSA char_code);
}

void plu_set_callbacks() 
{
   gx_set_UFST_Callbacks(pl_PCLEO_charptr, pl_PCLchId2ptr, pl_PCLglyphID2Ptr);
   /* nothing */
}

void
pl_mt_init_procs(gs_font_base *pfont)
{       pfont->procs.encode_char = pl_mt_encode_char;
        pfont->procs.build_char = pl_mt_build_char;
#define plfont ((pl_font_t *)pfont->client_data)
        plfont->char_width = pl_mt_char_width;
        plfont->char_metrics = pl_mt_char_metrics;
#undef plfont
}

