/* 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: pcsymbol.c,v 1.15 2003/09/06 17:11:03 stefan Exp $ */

/* pcsymbol.c */
/* PCL5 user-defined symbol set commands */
#include "stdio_.h"			/* std.h + NULL */
#include "plvalue.h"
#include "pcommand.h"
#include "pcstate.h"
#include "pcfont.h"
#include "pcsymbol.h"


private int /* ESC * c <id> R */
pcl_symbol_set_id_code(pcl_args_t *pargs, pcl_state_t *pcs)
{	uint id = uint_arg(pargs);
	id_set_value(pcs->symbol_set_id, id);
	return 0;
}

private int /* ESC ( f <count> W */
pcl_define_symbol_set(pcl_args_t *pargs, pcl_state_t *pcs)
{	uint count = uint_arg(pargs);
	const pl_symbol_map_t *psm = (pl_symbol_map_t *)arg_data(pargs);
	uint header_size;
	uint first_code, last_code;
	gs_memory_t *mem = pcs->memory;
	pl_symbol_map_t *header;
	pcl_symbol_set_t *symsetp;
	pl_glyph_vocabulary_t gv;

#define psm_header_size 18
	if ( count < psm_header_size )
	  return e_Range;
	header_size = pl_get_uint16(psm->header_size);
	if ( header_size < psm_header_size ||
	     psm->id[0] != id_key(pcs->symbol_set_id)[0] ||
	     psm->id[1] != id_key(pcs->symbol_set_id)[1] ||
	     psm->type > 2
	   )
	  return e_Range;
	switch ( psm->format )
	  {
	  case 1:
	  case 3:
	    break;
	  default:
	    return e_Range;
	  }
	first_code = pl_get_uint16(psm->first_code);
	last_code = pl_get_uint16(psm->last_code);
	gv = (psm->character_requirements[7] & 07)==1? plgv_Unicode: plgv_MSL;
	{ int num_codes = last_code - first_code + 1;
	  int i;

	  if ( num_codes <= 0 || count < psm_header_size + num_codes * 2 )
	    return e_Range;
	  header =
	    (pl_symbol_map_t *)gs_alloc_bytes(mem,
					      sizeof(pl_symbol_map_t),
					      "pcl_font_header(header)");
	  if ( header == 0 )
	    return_error(pcs->memory, e_Memory);
	  memcpy((void *)header, (void *)psm, psm_header_size);
	  /* specify that we do not allow these sets to map to and fro
             msl and unicode */
	  header->mapping_type = PLGV_NO_MAPPING;
	  /*
	   * Byte swap the codes now, so that we don't have to byte swap
	   * them every time we access them.
	   */
	  for ( i = num_codes; --i >= 0; )
	    header->codes[i] =
	      pl_get_uint16((byte *)psm + psm_header_size + i * 2);
	}
#undef psm_header_size

	/* Symbol set may already exist; if so, we may be replacing one of
	 * its existing maps or adding one for a new glyph vocabulary. */
	if ( pl_dict_find(&pcs->soft_symbol_sets, id_key(pcs->symbol_set_id),
	    2, (void **)&symsetp) )
	  {
	    if ( symsetp->maps[gv] != NULL )
	      gs_free_object(mem, symsetp->maps[gv], "symset map");
	  }
	else
	  { pl_glyph_vocabulary_t gx;
	    symsetp = (pcl_symbol_set_t *)gs_alloc_bytes(mem,
		sizeof(pcl_symbol_set_t), "symset dict value");
	    if ( !symsetp )
	      return_error(pcs->memory, e_Memory);
	    for ( gx = plgv_MSL; gx < plgv_next; gx++ )
	      symsetp->maps[gx] = NULL;
	    symsetp->storage = pcds_temporary;
	    pl_dict_put(&pcs->soft_symbol_sets, id_key(pcs->symbol_set_id),
		2, symsetp);
	  }
	symsetp->maps[gv] = header;

	return 0;
}

private int /* ESC * c <ssc_enum> S */
pcl_symbol_set_control(pcl_args_t *pargs, pcl_state_t *pcs)
{	gs_const_string key;
	void *value;
	pl_dict_enum_t denum;

	switch ( int_arg(pargs) )
	  {
	  case 0:
	    { /* Delete all user-defined symbol sets. */
	      /* Note: When deleting symbol set(s), it is easier (safer?)
	       * to decache and reselect fonts unconditionally.  (Consider,
	       * for example, deleting a downloaded overload of a built-in
	       * which might be the default ID.) */
	      pl_dict_release(&pcs->soft_symbol_sets);
	      pcl_decache_font(pcs, -1);
	    }
	    return 0;
	  case 1:
	    { /* Delete all temporary symbol sets. */
	      pl_dict_enum_stack_begin(&pcs->soft_symbol_sets, &denum, false);
	      while ( pl_dict_enum_next(&denum, &key, &value) )
		if ( ((pcl_symbol_set_t *)value)->storage == pcds_temporary )
		  pl_dict_undef(&pcs->soft_symbol_sets, key.data, key.size);
	      pcl_decache_font(pcs, -1);
	    }
	    return 0;
	  case 2:
	    { /* Delete symbol set <symbol_set_id>. */
	      pl_dict_undef(&pcs->soft_symbol_sets,
		  id_key(pcs->symbol_set_id), 2);
	      pcl_decache_font(pcs, -1);
	    }
	    return 0;
	  case 4:
	    { /* Make <symbol_set_id> temporary. */
	      if ( pl_dict_find(&pcs->soft_symbol_sets,
		  id_key(pcs->symbol_set_id), 2, &value) )
		((pcl_symbol_set_t *)value)->storage = pcds_temporary;
	    }
	    return 0;
	  case 5:
	    { /* Make <symbol_set_id> permanent. */
	      if ( pl_dict_find(&pcs->soft_symbol_sets,
		  id_key(pcs->symbol_set_id), 2, &value) )
		((pcl_symbol_set_t *)value)->storage = pcds_permanent;
	    }
	    return 0;
	  default:
	    return 0;
	  }
}

private void	/* free any symbol maps as well as dict value entry */
pcsymbol_dict_value_free(gs_memory_t *mem, void *value, client_name_t cname)
{	pcl_symbol_set_t *ssp = (pcl_symbol_set_t *)value;
	pl_glyph_vocabulary_t gx;

	if ( ssp->storage != pcds_internal )
	  {
	    for ( gx = plgv_MSL; gx < plgv_next; gx++ )
	      {
		if ( ssp->maps[gx] != NULL )
		  gs_free_object(mem, (void*)ssp->maps[gx], cname);
	      }
	  }
	gs_free_object(mem, value, cname);
}

private int
pcl_load_built_in_symbol_sets(pcl_state_t *pcs)
{
	const pl_symbol_map_t **maplp;
	pcl_symbol_set_t *symsetp;
	pl_glyph_vocabulary_t gv;

	for ( maplp = &pl_built_in_symbol_maps[0]; *maplp; maplp++ )
	  {
	    const pl_symbol_map_t *mapp = *maplp;
	    /* Create entry for symbol set if this is the first map for
	     * that set. */
	    if ( !pl_dict_find(&pcs->built_in_symbol_sets, mapp->id, 2,
		(void **)&symsetp) )
	      { pl_glyph_vocabulary_t gx;
		symsetp = (pcl_symbol_set_t *)gs_alloc_bytes(pcs->memory,
		    sizeof(pcl_symbol_set_t), "symset init dict value");
		if ( !symsetp )
		  return_error(pcs->memory, e_Memory);
		for ( gx = plgv_MSL; gx < plgv_next; gx++ )
		  symsetp->maps[gx] = NULL;
		symsetp->storage = pcds_internal;
	      }
	    gv = (mapp->character_requirements[7] & 07)==1?
		plgv_Unicode: plgv_MSL;
	    pl_dict_put(&pcs->built_in_symbol_sets, mapp->id, 2, symsetp);
	    symsetp->maps[gv] = (pl_symbol_map_t *)mapp;
	  }
	return 0;
}

bool
pcl_check_symbol_support(const byte *symset_req, const byte *font_sup)
{	int i;

	/* if glyph vocabularies match, following will work on the
	 * last 3 bits of last byte.  Note that the font-support bits
	 * are inverted (0 means available). 
	 */
	for ( i = 0; i < 7; i++ )
	  if ( symset_req[i] & font_sup[i] )
	    return false;		/* something needed, not present */
	/* check the last byte but not the glyph vocabularies. */
	if ((symset_req[7] >> 3) & (font_sup[7] >> 3))
	    return false;

	return true;
}


/* Find the symbol map for a particular symbol set and glyph vocabulary,
 * if it exists.
 * There are two dictionaries--one for soft (downloaded) symbol sets and
 * one for built-ins.  These are searched separately.  The actual maps
 * present for a symbol set may overlap between soft and built-in. */
pl_symbol_map_t *
pcl_find_symbol_map(const pcl_state_t *pcs, const byte *id,
  pl_glyph_vocabulary_t gv)
{	
    pcl_symbol_set_t *setp;
	
    if ( pl_dict_find((pl_dict_t *)&pcs->soft_symbol_sets,
		      id, 2, (void **)&setp) &&
	 setp->maps[gv] != NULL )
	return setp->maps[gv];
    if ( pl_dict_find((pl_dict_t *)&pcs->built_in_symbol_sets,
		      id, 2, (void**)&setp) ) {
	/* simple case we found a matching symbol set */
	if ( setp->maps[gv] != NULL )
	    return setp->maps[gv];
	/* we requested a unicode symbol set and found an msl
	   symbol set that can be mapped to unicode */
	if ( (gv == plgv_Unicode)  &&
	     (setp->maps[plgv_MSL]) &&
	     ((setp->maps[plgv_MSL])->mapping_type == PLGV_M2U_MAPPING) )
	    return setp->maps[plgv_MSL];
	/* we requested an msl symbol set and found a unicode
	   symbol set that can be mapped to msl */
	if ( (gv == plgv_MSL) &&
	     (setp->maps[plgv_Unicode]) &&
	     ((setp->maps[plgv_Unicode])->mapping_type == PLGV_U2M_MAPPING) )
	    return setp->maps[plgv_Unicode];
    }
    return NULL;
}

/* Initialization */
private int
pcsymbol_do_registration(
    pcl_parser_state_t *pcl_parser_state,
    gs_memory_t *mem
)
{		/* Register commands */
	DEFINE_CLASS_COMMAND_ARGS(mem, '*', 'c', 'R', "Symbol Set ID Code",
				  pcl_symbol_set_id_code,
				  pca_neg_error|pca_big_error)
	DEFINE_CLASS_COMMAND_ARGS(mem, '(', 'f', 'W', "Define Symbol Set",
				  pcl_define_symbol_set, pca_bytes)
	DEFINE_CLASS_COMMAND_ARGS(mem, '*', 'c', 'S', "Symbol Set Control",
				  pcl_symbol_set_control,
				  pca_neg_ignore|pca_big_ignore)
	return 0;
}

private void
pcsymbol_do_reset(pcl_state_t *pcs, pcl_reset_type_t type)
{	
    if ( type & (pcl_reset_initial | pcl_reset_printer | pcl_reset_overlay) ) {
	id_set_value(pcs->symbol_set_id, 0);
	if ( type & pcl_reset_initial ) {
	    /* Don't set a parent relationship from soft to built-in
	     * symbol sets.  Although it is arguably useful, it's
	     * better to avoid it and keep anyone who's looking at the
	     * soft symbol sets from mucking up the permanent ones. */
	    pl_dict_init(&pcs->soft_symbol_sets, pcs->memory,
			 pcsymbol_dict_value_free);
	    pl_dict_init(&pcs->built_in_symbol_sets, pcs->memory,
			 pcsymbol_dict_value_free);
	    /* NB.  Symbol sets are require for RTL/HPGL/2 mode for
	     * stickfonts but we shouldn't load all of them. */
	    if ( pcl_load_built_in_symbol_sets(pcs) < 0 )
		dprintf(pcs->memory, "Internal error, no symbol sets found");
	}
	else if ( type & pcl_reset_printer ) { 
	    pcl_args_t args;
	    arg_set_uint(&args, 1);	/* delete temporary symbol sets */
	    pcl_symbol_set_control(&args, pcs);
	}
    }
    if ( type & pcl_reset_permanent ) {
	pl_dict_release(&pcs->soft_symbol_sets);
	pl_dict_release(&pcs->built_in_symbol_sets);
    }
}

private int
pcsymbol_do_copy(pcl_state_t *psaved, const pcl_state_t *pcs,
  pcl_copy_operation_t operation)
{	if ( operation & pcl_copy_after )
	  { /* Don't restore the downloaded symbol set dictionary. */
	    psaved->built_in_symbol_sets = pcs->built_in_symbol_sets;
	  }
	return 0;
}

const pcl_init_t pcsymbol_init = {
  pcsymbol_do_registration, pcsymbol_do_reset, pcsymbol_do_copy
};
