/* encoding.c - definition of the char encodings used
   Copyright 1988-2017 Free Software Foundation, Inc.

   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 3, 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, see
   <https://www.gnu.org/licenses/gpl-3.0.html>.  */

#include <config.h>

#include <string.h>

#include "encoding.h"
#include "routines.h"
#include "jobs.h"
#include "message.h"
#include "pathwalk.h"
#include "pair_ht.h"
#include "fonts.h"
#include "xobstack.h"
#include "dsc.h"
#include "getshline.h"
#include "document.h"
#include "quotearg.h"

/* Suffix of the Encoding Description Files */
#define EDF_SUFFIX ".edf"


/* FIXME: THe comment is no longer exact
  Dealing with the encodings is a long and painful work.

  There are many things to be done, in as short time as possible.
  The big problem is to associate an array of the width of the
  characters for a given font in a given encoding (two keys).

  a2ps first loads the prologue definitions.  In that file, there are
  lines such as:

  %Face: Plain Courier
  /p {
  false UL
  false BX
  fc setfont
  Show
  } bind def

  By this line, it is requested to encode the font Courier, which
  will be used through the name fc.  One of the problems is that for
  instance Courier does not suppport Latin2 encoding and other.
  Then we define some substitution, related to each encoding.  As
  an example, in latin2.edf, there is the subsitution Courier->
  Courier-Ogonkify.

  So we must be carreful to separate various instanciation of the fonts
  (fc) from one encoding to another.  This is done through ps dictionaries.
  fc is define in latin1dict, latin2dict, with different encodings.  It
  works very well with the ps interpreters: there are no problems
  to encounter later, when using these dictionaries.

  Still, building them is painful if one wants to stay open.

  One may ask: << Why not
   %Face: Plain Courier
  /p {
  false UL
  false BX
  FontPlain setfont		%% Here is the difference
  Show
  >>
  The answer is that in that case if two faces use the same base
  font, them we use twice the memory we need.

  One may reply: << Why not
  %Face: Plain Courier
  /p {
  false UL
  false BX
  FontCourier setfont		%% Here is the difference
  Show
  >>
  The problem is then when somebody wants to use a separate Courier
  instance, with another size, in a header.  Then there should be two
  different FontCourier. Too bad.

  It gets even worse when one knows that all this cannot be resolved
  with the subsitution, since these are known _after_ the prologue
  is loaded.  And I don't want any more cross-references between
  prologues and encodings.

  So the decision, not great, but the easier, will be the second one,
  the one which uses too much memory:

  %Face: Plain Courier
  /p {
  false UL
  false BX
  FontPlain setfont		%% Here is the difference
  Show
  >>

  because it is the most open solution.  Especially if the prologue
  makes use of some other faces (in the headers for instance).
*/

/************************************************************************/
/*			encoding.map handling				*/
/************************************************************************/
/*
  These entries allow to define several aliases for a single encoding.
  This is extremely useful for instance when a2ps-the-prog parse a mail
  and finds tags that define the charset to use.  It could read 8859-1,
  or ISO-8859-1 or iso-8859-1 though the user would like latin1.

  The scheme, of course, should be much more general than just skipping
  ISO|iso-8859, since it is even _wrong_ for, say, latin5 (8859-9)

  From an implementation point of view, it is nothing but a double
  strings hash table (cf. pair_ht.[ch]).
 */
struct pair_htable *
encodings_map_new (void)
{
  return pair_table_new ();
}

/*
 * What is the KEY corresponding to ALIAS?
 */
static char *
encodings_map_resolve_alias (struct pair_htable * encodings_map,
                             const char * alias)
{
  return pair_get (encodings_map, alias);
}

/*
 * Read a sheets map file
 */
static int
load_encodings_map (struct a2ps_job * job, const char *file)
{
  return pair_table_load (job->encodings_map, file);
}

/*
 * Read the encodings.map file
 */
int
load_main_encodings_map (struct a2ps_job * job)
{
  char * file;

  /* System's */
  file = xpw_find_file (job->common.path, "encoding.map", NULL);
  load_encodings_map (job, file);
  if (msg_test (msg_enc))
    {
      fprintf (stderr, "Read encoding.map:\n");
      pair_table_list_long (job->encodings_map, stderr);
    }
  return 1;
}

/************************************************************************/
/*		        wx handling					*/
/************************************************************************/
static inline unsigned int *
wx_new (void)
{
  int i;
  unsigned int *res = XNMALLOC (256, unsigned int);

  for (i = 0 ; i < 256 ; i++)
    res [i] = 0;

  return res;
}

static void
wx_self_print (unsigned int * wx, FILE * stream)
{
  int j;

  if (wx)
    for (j = 0 ; j < 256 ; j += 8)
      fprintf (stream,
               "%3d: %04u %04u %04u %04u %04u %04u %04u %04u\n", j,
               wx [j + 0], wx [j + 1], wx [j + 2], wx [j + 3],
               wx [j + 4], wx [j + 5], wx [j + 6], wx [j + 7]);
  else
    fputs ("<No WX defined>\n", stream);
}


/************************************************************************/
/*		     fonts entries handling				*/
/************************************************************************/
/*
  Font entries are hashed, but they will be used only at
  initialization: the first thing to do will be to associate
  the font's to the face which uses this font.

  But it is not yet known.  First we read the information on the
  encoding required (which allows us to discover that for instance
  Courier is _not_ to use with Latin2 (use Courier-Ogonki), and
  later, reading the PS prologue, we will learn what face uses
  what font.  At this moment we will associate the font's wx to the
  face.
*/

/*
 * For a font (actually encoding for a given char set)
 * it wx.
 */
struct font_entry {
  char * key;		/* eg. Courier-Ogonki                           */
  int used;		/* If used, need to be dump in the PS		*/
  int reencode;		/* E.g. Symbol is not to reencode		*/
  unsigned int * wx;	/* Not an array, because some fonts may
                         * share their wx (Courier familly for instance	*/
};

static unsigned long
font_entry_hash_1 (struct font_entry * entry)
{
  return_STRING_HASH_1 (entry->key);
}

static unsigned long
font_entry_hash_2 (struct font_entry * entry)
{
  return_STRING_HASH_2 (entry->key);
}

static int
font_entry_hash_cmp (struct font_entry * x, struct font_entry * y)
{
  return_STRING_COMPARE (x->key, y->key);
}

static int
font_entry_hash_qcmp (struct font_entry ** x, struct font_entry ** y)
{
  return_STRING_COMPARE ((*x)->key, (*y)->key);
}

/*
 * We make a copy of the name, but not of the array
 */
static inline struct font_entry *
font_entry_new (const char * name, unsigned int * wx)
{
  struct font_entry * res = XMALLOC (struct font_entry);
  res->key = xstrdup (name);
  res->used = false;
  res->wx = wx;
  return res;
}

static void
font_entry_self_print (struct font_entry * entry, FILE * stream)
{
  fprintf (stream, "***** %s ***** (%s, %s) ",
           entry->key,
           entry->used ? "used" : "not used",
           entry->reencode ? "to share between encodings" : "reencode");
  wx_self_print (entry->wx, stream);
}

/************************************************************************/
/*		     fonts entries hash table handling			*/
/************************************************************************/
static struct hash_table_s *
font_table_new (void)
{
  struct hash_table_s * res;

  res = XMALLOC (hash_table);
  hash_init (res, 32,
             (hash_func_t) font_entry_hash_1,
             (hash_func_t) font_entry_hash_2,
             (hash_cmp_func_t) font_entry_hash_cmp);
  return res;
}

static void
font_table_self_print (struct hash_table_s * table, FILE * stream)
{
  int i;
  struct font_entry ** entries;
  entries = (struct font_entry **)
    hash_dump (table, NULL,
               (hash_cmp_func_t) font_entry_hash_qcmp);

  for (i = 0 ; entries[i] ; i++)
    font_entry_self_print (entries [i], stream);
  putc ('\n', stream);
}

/*
 * We make a copy of the name, but not of the array
 */
static inline void
font_entry_add (struct hash_table_s * table,
                const char * name, unsigned int * wx)
{
  hash_insert (table, font_entry_new (name, wx));
}

static inline struct font_entry *
font_entry_get (struct hash_table_s * table, const char * name)
{
  struct font_entry token, * item;
  token.key = (char *) name;
  item = (struct font_entry *) hash_find_item (table, &token);
  return item;
}

/************************************************************************/
/*		     encodings entries handling				*/
/************************************************************************/
/*
 * Association of suffixes rules, and corresponding style sheet
 * (The hashing is upon `alias')
 */
struct slantfont_info {
  char * name;
  char * src;
  float ratio;
};

struct encoding {
  char * key;			/* e.g. latin1			*/
  char * name;			/* e.g. ISO Latin 1		*/
  int    composite_flag;	/* flag for composite font	*/
  char * documentation;		/* Useful pieces of text	*/

  char * default_font;		/* When a font can't be used
                                   define the font to use	*/
  struct pair_htable * substitutes;	/* e.g. in latin2, don't use
                                         * Courier, but Courier-Ogonki	*/

  struct pair_htable * composite;

  struct slantfont_info     slantfont[NB_FACES];

  char * vector[256];	/* Define the char set			*/
  struct darray * font_names_used;
                                /* E.g. Courier has been used, but
                                 * Actually Courier_Ogonki is the real
                                 * font used (cf infra)			*/
  struct hash_table_s * fonts;	/* Contains cells that are
                                 * 1. name of font, 2. int wx[256]      */
  unsigned int * faces_wx[NB_FACES];

  unsigned int   composite_wx[NB_FACES];     /* fixed length font width */
  float          composite_ratio[NB_FACES];  /* size ratio of additonal and original font */
};

/*
 * Create an encoding, reading its associated file.wx
 */
static struct encoding *
encoding_new (const char * key)
{
  int i;

  struct encoding * res = XMALLOC (struct encoding);

  res->key = xstrdup (key);
  res->name = NULL;
  res->default_font = NULL;
  res->documentation = NULL;
  res->composite_flag = false;

  /* Vector will be set by setup */
  res->substitutes = pair_table_new ();
  res->composite = pair_table_new ();
  res->slantfont[0].name = NULL;
  res->font_names_used = da_new ("List of font names", 10,
                                 da_linear, 10,
                                 (da_print_func_t) da_str_print,
                                 (da_cmp_func_t) da_str_cmp);
  res->fonts = font_table_new ();
  for (i = 0 ; i < NB_FACES ; i++)
    res->faces_wx [i] = NULL;
  for (i = 0 ; base_faces [i] != -1 ; i++)
    res->faces_wx [base_faces [i]] = wx_new ();
  return res;
}

/*
 * Add a subsitution in the current encoding
 */
static void
encoding_add_font_substitute (struct encoding * encoding,
                              const char * orig, const char * subs)
{
  pair_add (encoding->substitutes, orig, subs);
}

/*
 * Add a composite font in the current encoding
 */
static void
encoding_add_composite_font (struct encoding * encoding,
                              const char * orig, const char * subs,
                              int wx, float ratio)
{
  encoding->composite_flag = true;
  pair_add2 (encoding->composite, orig, subs, wx, ratio);
}

/*
 * Add a composite font in the current encoding
 */
static void
encoding_add_slant_font (struct encoding * encoding,
                         const char * new, const char * src,
                         float ratio)
{
  static int num =  0;
  encoding->slantfont[num].name = strdup(new);
  encoding->slantfont[num].src  = strdup(src);
  encoding->slantfont[num].ratio = ratio;
  encoding->slantfont[num+1].name = NULL;
  num++;
}

/*
 * When FONT_NAME is used with ENCODING, return the
 * real font name to use (e.g., in latin2, Courier-Ogonki
 * should be returned when asked for Courier).
 *
 * Actually, now FONT_NAME can be a `,' separated list
 * of fonts desired, in decreasing order of preference
 */
const char *
encoding_resolve_font_substitute (struct a2ps_job * job,
                                  struct encoding * encoding,
                                  const char * font_list)
{
  const char * res = NULL;
  char * font_list_copy;
  char * font_name;
  char * last_font_name;

  astrcpy (font_list_copy, font_list);
  font_name = strtok (font_list_copy, ",<>;");

  do
    {
      last_font_name = font_name;
      /* Find if there is a substitute for that font */
      res = pair_get (encoding->substitutes, font_name);
      if (res)
        res = xstrdup (res);
      else
        /* No. Check if this font is supported */
        if (font_exists (job, font_name))
          /* Avoid returning sth alloca'd */
          res = xstrdup (font_name);
    }
  while (!res && (font_name = strtok (NULL, ",<>;")));

  /* We've found nothing interesting.  Last chance is the default
   * font */
  if (!res)
    {
      if (encoding->default_font)
        res = encoding->default_font;
      else
        error (1, 0, "cannot find font %s, nor any substitute",
               last_font_name);
    }

  message (msg_enc,
           (stderr, "In encoding %s, composite font for %s is resolved as %s\n",
            encoding->key, font_name, res));
  return res;
}

/*
 * Get composite font size and ratio
 */
static unsigned
composite_font_info_get_wx(struct encoding * encoding,
                           const char * font_list)
{
  int wx = -1;
  char * font_list_copy;
  char * font_name;
  astrcpy (font_list_copy, font_list);
  font_name = strtok (font_list_copy, ",<>;");

  wx = pair_get_wx (encoding->composite, font_name);
  if (wx < 0)
      wx = pair_get_wx (encoding->composite, "default_composite__");

  return (unsigned) wx;
}

static float
composite_font_info_get_ratio(struct encoding * encoding,
                              const char * font_list)
{
  float ratio= -1;
  char * font_list_copy;
  char * font_name;

  astrcpy (font_list_copy, font_list);
  font_name = strtok (font_list_copy, ",<>;");

  ratio = pair_get_ratio (encoding->composite, font_name);
  if (ratio<0)
      ratio = pair_get_ratio (encoding->composite, "default_composite__");
  return ratio;
}

const char *
encoding_resolve_composite_font (struct encoding * encoding,
                                  const char * font_list)
{
  const char * res = NULL;
  char * font_list_copy;
  char * font_name;

  astrcpy (font_list_copy, font_list);
  font_name = strtok (font_list_copy, ",<>;");

  /* Find if there is a substitute for that font */
  res = pair_get (encoding->composite, font_name);

  /* We've found nothing interesting.  Last chance is the default
   * font */
  if (!res)
    {
      res = pair_get (encoding->composite, "default_composite__");

      if (!res)
        error (1, 0, "cannot find font %s, nor any composite font",
               font_name);
    }

  message (msg_enc,
           (stderr, "In encoding %s, font %s is resolved as %s\n",
            encoding->key, font_name, res));
  return res;
}

/*
 * Read of *.edf file, given its path
 */
#define GET_TOKEN(from) (strtok ((from), " \t\n"))
#define CHECK_TOKEN()                                                   \
  if (token2 == NULL)                                                   \
    error_at_line (1, 0, fname, firstline,				\
                   _("missing argument for `%s'"), quotearg (token));

static void
load_encoding_description_file (a2ps_job * job,
                                struct encoding * encoding)
{
  FILE * stream;
  char * buf = NULL;
  char * fname;
  size_t bufsiz = 0;
  char * token, * token2;
  unsigned firstline = 0, lastline = 0;
  static int first_time = 1;
  static struct obstack documentation_stack;

  if (first_time)
    {
      /* Initialize the obstack we use to record the docuementation
       * lines */
      first_time = 0;
      obstack_init (&documentation_stack);
    }

  message (msg_enc,
           (stderr, "Loading encoding file `%s.edf'\n", encoding->key));
  fname = xpw_find_file (job->common.path, encoding->key, ".edf");
  stream = xrfopen (fname);

  while (getshline_numbered (&firstline, &lastline,
                             &buf, &bufsiz, stream) != -1)
    {
      token = GET_TOKEN (buf);

      if (!token)
        /* Blank line */
        continue;

      if (STREQ (token, "Vector:"))
        {
          int c = 0;
          char buf2 [256];

          while (c < 256)
            {
              if (!fgets (buf2, sizeof (buf2), stream))
                error_at_line (1, 0, fname, firstline,
                               _("missing argument for `%s'"), "`Vector:'");
              lastline++;
              token = GET_TOKEN (buf2);
              encoding->vector [c++] = xstrdup (token);
              while ((token2 = GET_TOKEN (NULL)))
                encoding->vector [c++] = xstrdup (token2);
          }
        }
      else if (STREQ (token, "Name:"))
        {
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          encoding->name = xstrdup (token2);
        }
      else if (STREQ (token, "Default:"))
        {
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          encoding->default_font = xstrdup (token2);
        }
      else if (STREQ (token, "Documentation"))
        {
          char * documentation;
          char buf2 [BUFSIZ];
          size_t read_length;

          /* We don't use getshline, because we do want the
           * ``empty'' lines to be kept: they separate the
           * paragraphs */
          while (fgets (buf2, sizeof(buf2), stream)
                 && !strprefix ("EndDocumentation", buf2))
            {
              read_length = strlen (buf2);
              if (read_length < sizeof (buf2))
                lastline++;
              /* Grow the obstack with the doc content */
              obstack_grow (&documentation_stack, buf2, read_length);
            }
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
          if (!strprefix ("EndDocumentation", buf2))
            error (1, 0, fname, firstline,
                   _("missing argument for `%s'"), "`Documentation'");
#pragma GCC diagnostic pop
          /* Finish the obstack, and store in the encoding entry */
          obstack_1grow (&documentation_stack, '\0');
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
          documentation =
            (char *) obstack_finish (&documentation_stack);
#pragma GCC diagnostic pop
          obstack_free (&documentation_stack, documentation);
          encoding->documentation = xstrdup (documentation);
        }
      else if (STREQ (token, "Substitute:"))
        {
          char * orig, * subs;

          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          orig = token2;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          subs = token2;
          encoding_add_font_substitute (encoding, orig, subs);
        }
      else if (STREQ (token, "DefaultComposite:"))
        {
          char * subs;
          int wx;
          float ratio;

          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          subs = token2;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          wx = (int)(float)(atof(token2))*1000;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          ratio = (float) atof(token2);
          encoding_add_composite_font(encoding, "default_composite__",
                                      subs, wx, ratio);
        }
      else if (STREQ (token, "Composite:"))
        {
          char * orig, * subs;
          int wx;
          float ratio;

          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          orig = token2;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          subs = token2;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          wx = (int)(float)(atof(token2))*1000;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          ratio = (float) atof(token2);
          encoding_add_composite_font(encoding, orig, subs, wx, ratio);
        }
      else if (STREQ (token, "SlantFont:"))
        {
          char * new, * src;
          float ratio;
          unsigned int num;

          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          new = token2;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          src = token2;
          token2 = GET_TOKEN (NULL);
          CHECK_TOKEN ();
          ratio = (float) atof(token2);
          for (num = 0 ; encoding->slantfont[num].name ; num ++ );
          if (num > sizeof encoding->slantfont - 1){
              error_at_line (1, 0, fname, firstline,
                           _("too many slant fonts: `%s'"), new);
          }
          encoding_add_slant_font(encoding, new, src, ratio);
        }
      else
        error_at_line (1, 0, fname, firstline,
                       _("invalid option `%s'"), quotearg (token));

    }
  fclose (stream);
}

/*
 * Used by --list-encodings
 */
static void
encoding_print_signature (struct encoding * item, FILE * stream)
{
  size_t i, title_len;

  title_len = (strlen (" ()")
               + strlen (item->name)
               + strlen (item->key));
  fprintf (stream, "%s (%s)\n", item->name, item->key);
  for (i = 0 ; i < title_len ; i++)
    putc ('-', stream);
  putc ('\n', stream);

  documentation_print_plain (item->documentation, "%s", stream);
}

/*
 * Output the correct setup of ENCODING
 */
static void
encoding_output_ps_vector (struct a2ps_job * job,
                           struct encoding * encoding)
{
  int c;

  output (job->ps_encodings, "%%%%BeginResource: encoding %sEncoding\n",
          encoding->name);
  output (job->ps_encodings, "/%sEncoding [",
          encoding->name);
  for (c = 0 ; c < 256 ; c++)
    {
      if (!(c % 8))
        output_char (job->ps_encodings, '\n');
      output (job->ps_encodings, "/%s ", encoding->vector [c]);
    }
  output (job->ps_encodings, "\n] def\n");
  output (job->ps_encodings, "%%%%EndResource\n");
}

/*
 * Used for very verbose reports
 */
void
encoding_self_print (struct encoding * item, FILE * stream)
{
  int c;

  encoding_print_signature (item, stream);

  if (! IS_EMPTY (item->default_font))
    fprintf (stream, "Default font is `%s'\n", item->default_font);

  fprintf (stream, "Substitution of fonts:\n");
  pair_table_list_long (item->substitutes, stream);
  fprintf (stream, "Name of fonts used (before substitution):\n");
  da_self_print (item->font_names_used, stream);

  fprintf (stream, "Encoding array:");
  for (c = 0 ; c < 256 ; c++)
    {
      if (!(c % 8))
        putc ('\n', stream);
      fprintf (stream, "%-10s ", item->vector [c]);
    }
  putc ('\n', stream);

  fprintf (stream, "Fonts:\n");
  font_table_self_print (item->fonts, stream);
  if (msg_test (msg_enc))
    {
      enum face_e face;
      int i;
      fprintf (stream, "Faces:\n");
      for (i = 0 ; base_faces [i] != -1 ; i++)
        {
          face = base_faces [i];
          fprintf (stream, "Face %s (%d)\n", face_to_string (face), face);
          wx_self_print (item->faces_wx [face], stream);
        }
    }
}

/*
 * Return the name of ENC
 */
const char *
encoding_get_name (struct encoding * enc)
{
  return enc->name;
}

/*
 * Return the key of ENC
 */
const char *
encoding_get_key (struct encoding * enc)
{
  return enc->key;
}

int
encoding_char_exists (struct encoding * enc,
                            enum face_e face, char c)
{
  return enc->faces_wx[face][(unsigned char)c] != 0;
}

/*
 * Return the flag of composite flag
 */
int
encoding_get_composite_flag (struct encoding * enc)
{
  return enc->composite_flag;
}

/*
 * Prepare the environment (a dictionary) for the support
 * of ENCODING, dump it into STREAM.
 *
 * This routine is called at the end of the whole job, while
 * undiverting, so that everything we need to know is known.
 */
static void
dump_encoding_setup (FILE * stream,
                     struct a2ps_job * job,
                     struct encoding * encoding)
{
  size_t i, nb;
  size_t ns;
  const char * real_font_name;		/* After subsitution	*/
  char ** font_names = (char **) encoding->font_names_used->content;


  /* How many fonts are there? */
  da_qsort (encoding->font_names_used);
  da_unique (encoding->font_names_used);

  /* We do not want to reencode the fonts that should not be
   * reencoded */
  for (i = 0 ; i < encoding->font_names_used->len ; i++)
    {
      real_font_name = encoding_resolve_font_substitute (job, encoding,
                                                         font_names [i]);
      if (!font_is_to_reencode (job, real_font_name))
        da_remove_at (encoding->font_names_used, i);
    }

  /* The number of fonts that, finally, have to be encoded
   * in the current ENCODING	*/
  nb = encoding->font_names_used->len;

  /* The number of slant fonts */
  for (i= 0, ns=0 ; encoding->slantfont[i].name ; i++ )
      ns++;

  /* Create the dictionary and fill it */
  fprintf (stream, "%% Dictionary for %s support\n",
          encoding->name);
  fprintf (stream, "/%sdict %zu dict begin\n", encoding->key,
           (encoding->composite_flag == true)? nb+nb+ns:nb+ns);
  for (i = 0 ; i < nb ; i++)
    fprintf (stream, "  /f%s %sEncoding /%s reencode_font\n",
             font_names [i],
             encoding->name,
             encoding_resolve_font_substitute (job, encoding, font_names [i]));

  /* Slant font setting */
  for (i = 0 ; encoding->slantfont[i].name ; i++ )
    fprintf (stream, "  /%s /%s %f slantfont  definefont pop\n",
             encoding->slantfont[i].name,
             encoding->slantfont[i].src,
             (double) encoding->slantfont[i].ratio);

  /*
   * Composite font setting.
   * If kanji font size is larger than alphabet character,
   * set base font size to kanji charactor size.
   */
  if (encoding->composite_flag == true) {
    for (i = 0 ; i < nb ; i++)
      fprintf (stream, "  /f%s /f%s /%s %f %f false  compositefont "
                       "%f scalefont def\n",
               font_names [i],
               font_names [i],
               encoding_resolve_composite_font (encoding, font_names [i]),
               (double) encoding->composite_ratio[i],
               (encoding->composite_ratio[i] > 1.0f) ?
               0 : (double) ((1.0f - encoding->composite_ratio[i]) / 2.0f),
               (encoding->composite_ratio[i] > 1.0f) ?
               (double) (1.0f / encoding->composite_ratio[i]) : 1.0);
  }
  fputs ("currentdict end def\n", stream);
}

/************************************************************************/
/*		     encodings hash table handling			*/
/************************************************************************/
/*
 * 1. hash table service routines on cells
 */
static unsigned long
encoding_hash_1 (struct encoding * entry)
{
  return_STRING_HASH_1 (entry->key);
}

static unsigned long
encoding_hash_2 (struct encoding * entry)
{
  return_STRING_HASH_2 (entry->key);
}

static int
encoding_hash_cmp (struct encoding * x, struct encoding * y)
{
  return_STRING_COMPARE (x->key, y->key);
}

static int
encoding_hash_qcmp (struct encoding ** x,
                          struct encoding ** y)
{
  return_STRING_COMPARE ((*x)->key, (*y)->key);
}

/*
 * 2. Hash table routines
 */
struct hash_table_s *
encodings_table_new (void)
{
  struct hash_table_s * res;

  res = XMALLOC (hash_table);
  hash_init (res, 32,
             (hash_func_t) encoding_hash_1,
             (hash_func_t) encoding_hash_2,
             (hash_cmp_func_t) encoding_hash_cmp);
  return res;
}

/*
 * Create an encoding, reading its associated file.edf
 */
static void
encoding_setup (struct a2ps_job * job,
                      struct encoding * encoding)
{
  char * cp;

  load_encoding_description_file (job, encoding);
  switch (job->output_format)
    {
    case ps:
    case eps:     /* Producing PostScript */
      /* FIXME: Remove this
       * Include its encoding definition */
      /* This, too, should be handled by the pseudo PDD files */
      cp = ALLOCA (char, strlen (encoding->name) + strlen ("Encoding") + 1);
      sprintf (cp, "%sEncoding", encoding->name);

      add_supplied_resource (job, "encoding", cp);
      encoding_output_ps_vector (job, encoding);
      break;
    }
}

/*
 * Add an encoding in the hash table,
 * Reading its associated file.edf
 */
static void
encoding_add (struct a2ps_job * job, const char * key)
{
  struct encoding * encoding;

  encoding = encoding_new (key);
  encoding_setup (job, encoding);

  /* If yet the association between faces and fonts is known,
   * get the WX per face */
  if (face_eo_font_is_set (job))
    encoding_build_faces_wx (job, encoding);
  if (msg_test (msg_enc))
    encoding_self_print (encoding, stderr);
  hash_insert (job->encodings, encoding);
}

void
encoding_add_font_name_used (struct encoding * encoding,
                                   const char * name)
{
  da_append (encoding->font_names_used, xstrdup (name));
}

/*
 * Make an encoding do the association between the fonts
 * and the faces, so that the faces can be immediately used when
 * looking for the wx.
 *
 * The resolution of substitution for the fonts must be done here too,
 * because it depends on the encoding.
 */
void
encoding_build_faces_wx (a2ps_job * job, struct encoding * encoding)
{
  int i;
  enum face_e face;
  const char * true_font_name;

  for (i = 0 ; base_faces [i] != -1 ; i++)
    {
      face = base_faces [i];
      encoding_add_font_name_used (encoding,
                                   job->face_eo_font [face]);
      /* E.g. in Latin 2, don't use Courier but Courier-Ogonki */
      true_font_name =
        encoding_resolve_font_substitute (job, encoding,
                                          job->face_eo_font [face]);
      /* Get the wx related to the FACE */
      font_info_get_wx_for_vector (job,
                                   true_font_name,
                                   encoding->vector,
                                   encoding->faces_wx [face]);

      if (encoding->composite_flag)
        {
          encoding->composite_ratio[i] =
            composite_font_info_get_ratio(encoding,
                                          job->face_eo_font [face]);

          encoding->composite_wx[i] =
            composite_font_info_get_wx(encoding,
                                       job->face_eo_font [face]);

          /* If kanji font size is larger than alphabet character,
             fit kanji charactor size to base font size */
          if (encoding->composite_ratio[i] < 1.0f)
            encoding->composite_wx[i] *= (unsigned) encoding->composite_ratio[i];
        }
    }
}

/*
 * Retrieve an encoding
 */
static struct encoding *
encoding_get (struct a2ps_job * job, const char * key)
{
  struct encoding token, *res;

  token.key = (char *) key;
  res = (struct encoding *) hash_find_item (job->encodings, &token);

  if (!res)
    {	/* The encoding has never been read yet */
      encoding_add (job, key);
      res = (struct encoding *) hash_find_item (job->encodings, &token);
    }
  return res;
}

/*
 * Return the encoding and NULL if none
 */
struct encoding *
get_encoding_by_alias (struct a2ps_job * job, char * alias)
{
  const char * key;

  key = encodings_map_resolve_alias (job->encodings_map, strlower (alias));
  if (key)
    return encoding_get (job, key);
  else
    /* This is not a valid alias */
    return NULL;
}

/*
 * Prepare all the encodings used.  This is easy: there
 * are all the members of JOB->encodings
 */
void
dump_encodings_setup (FILE * stream,
                      struct a2ps_job * job)
{
  int i;
  struct encoding ** encodings;

  /* Get the list of the encodings */
  encodings = (struct encoding **)
    hash_dump (job->encodings, NULL,
               (hash_cmp_func_t) encoding_hash_qcmp);

  for (i = 0 ; encodings [i] ; i++)
    dump_encoding_setup (stream, job, encodings [i]);
}

/************************************************************************/
/*		     Computing the width of a char/string		*/
/************************************************************************/
/*
 *	Returns the WX of a char (including M- and ^)
 */
unsigned int
char_WX (a2ps_job * job, unsigned char c)
{
#define _WX_(char)  (job->encoding->faces_wx[job->status->face][(int) char])
  unsigned int wx = _WX_(c);

  /* Only printable characters have a positive wx */
  if (wx)
    return wx;

  switch (job->unprintable_format)
    {
    case hexa:
      {
        char buf [3];
        sprintf (buf, "%02x", c);
        return _WX_ ('\\') + _WX_('x') + _WX_ (buf[0]) + _WX_ (buf[1]);
      }

    case octal:
      {
        char buf [4];
        sprintf (buf, "%03o", c);
        return (_WX_ ('\\')
                + _WX_ (buf [0]) + _WX_ (buf [1]) + _WX_ (buf [2]));
      }

    case space:
      return _WX_(' ');

    case question_mark:
      return _WX_('?');

    case caret:
      if (0177 < c)
        {
          wx += _WX_('M') + _WX_('-');
          c &= 0177;
        }
      if (c == 0177)
        wx += _WX_('^') + _WX_('?');
      else if (c < ' ')
        wx += _WX_ ('^') + _WX_ (c + '@');
      else
        wx += _WX_ (c);
      return wx;

    case Emacs:
      if (0177 < c)
        {
          wx += _WX_('M') + _WX_('-');
          c &= 0177;
        }
      if (c == 0177)
        wx += _WX_('C') + _WX_('-') + _WX_('?');
      else if (c < ' ')
        wx += _WX_('C') + _WX_('-') + _WX_ (c + '@');
      else
        wx += _WX_ (c);
      return wx;

    default:
      abort ();
    }

  return 0;	/* For -Wall */
}

unsigned
char_composite_WX (a2ps_job * job)
{
  return (unsigned) ((float) job->encoding->composite_wx[job->status->face] /
                     job->encoding->composite_ratio[job->status->face]);
}

/*
 *	Returns the WX of a string (including M- and ^)
 */
unsigned int
string_WX (a2ps_job * job, char * string)
{
  unsigned int result=0;

  for (/* skip */; *string ; string ++)
    result += char_WX(job, (unsigned char) *string);

  return result;
}

/*
 * Use this encoding now
 */
void
set_encoding (struct a2ps_job * job, struct encoding * enc)
{
  job->encoding = enc;
}

/* Compare names with strverscmp, so that latin 15 is after latin 2.  */

static int
da_encoding_name_cmp (char * key1, char * key2, struct a2ps_job * job)
{
  return strverscmp ((char *) encoding_get (job, key1)->name,
                     (char *) encoding_get (job, key2)->name);
}

/*
 * Report the known encodings for --list-features
 */
void
list_encodings_short (a2ps_job * job, FILE * stream)
{
  fputs (_("Known Encodings"), stream);
  putc ('\n', stream);
  pw_lister_on_suffix (stream, job->common.path, EDF_SUFFIX);
}

/*
 * Report the known encodings for --list-encodings
 */
void
list_encodings_long (a2ps_job * job, FILE * stream)
{
  struct darray * entries;
  struct encoding * encoding;
  size_t i;

  entries = pw_glob_on_suffix (job->common.path, EDF_SUFFIX);

  /* We want them to be in order of the names, not keys */
  da_qsort_with_arg (entries,
                     (da_cmp_arg_func_t) da_encoding_name_cmp, job);

  fputs (_("Known Encodings"), stream);
  putc ('\n', stream);

  for (i = 0 ; i < entries->len ; i++)
    {
      /* Don't forget to cut the suffix */
      encoding = encoding_get (job, entries->content[i]);
      encoding_print_signature (encoding, stream);
      putc ('\n', stream);
    }
}
/************************************************************************/
/*		Report in Texinfo format				*/
/************************************************************************/
/*
 * Print a short signature (i.e., name, comments)
 * In texinfo format
 */
static void
encoding_texinfo_print_signature (struct encoding * encoding,
                                        FILE * stream)
{
  fprintf (stream, "@deftp {Encoding} {%s} (@file{%s.edf})\n",
           encoding->name, encoding->key);

  documentation_print_texinfo (encoding->documentation, "%s", stream);
  fputs ("@end deftp\n\n", stream);
}

/*
 * Report the known encodings for --list-encodings
 */
void
list_texinfo_encodings_long (a2ps_job * job, FILE * stream)
{
  struct darray * entries;
  struct encoding * encoding;
  unsigned int i;

  entries = pw_glob_on_suffix (job->common.path, EDF_SUFFIX);

  /* We want them to be in order of the names, not keys */
  da_qsort_with_arg (entries,
                     (da_cmp_arg_func_t) da_encoding_name_cmp, job);

  fputs ("The known encodings are:\n", stream);

  for (i = 0 ; i < entries->len ; i++)
    {
      /* Don't forget to cut the suffix */
      encoding = encoding_get (job, entries->content[i]);
      encoding_texinfo_print_signature (encoding, stream);
    }
}
