/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2009 */

/* Written by Philip Hazel, starting November 1991 */
/* Re-coded for Unicode support: August 2005 */
/* This file last modified: January 2009 */


/* This file contains code for handling text fonts */


#include "pmwhdr.h"
#include "readhdr.h"


/* Entries in the character name to Unicode code point table */

typedef struct an2uencod {
  uschar *name;                /* Adobe character name */
  int code;                    /* Unicode code point */
  int poffset;                 /* Offset for printing certain chars */
} an2uencod;

/* This table translates character names from PostScript fonts that use Adobe's
standard encoding into Unicode. In addition, for characters whose Unicode
values are greater than LOWCHARLIMIT, it includes the offset above LOWCHARLIMIT
that we use for printing these characters. */

static an2uencod an2ulist[] = {
  { US"A",               0x0041, -1 },
  { US"AE",              0x00c6, -1 },
  { US"Aacute",          0x00c1, -1 },
  { US"Abreve",          0x0102, -1 },
  { US"Acircumflex",     0x00c2, -1 },
  { US"Adieresis",       0x00c4, -1 },
  { US"Agrave",          0x00c0, -1 },
  { US"Amacron",         0x0100, -1 },
  { US"Aogonek",         0x0104, -1 },
  { US"Aring",           0x00c5, -1 },
  { US"Atilde",          0x00c3, -1 },
  { US"B",               0x0042, -1 },
  { US"C",               0x0043, -1 },
  { US"Cacute",          0x0106, -1 },
  { US"Ccaron",          0x010c, -1 },
  { US"Ccedilla",        0x00c6, -1 },
  { US"D",               0x0044, -1 },
  { US"Dcaron",          0x010e, -1 },
  { US"Dcroat",          0x0110, -1 },
  { US"Delta",           0x0394, +0 },
  { US"E",               0x0045, -1 },
  { US"Eacute",          0x00c9, -1 },
  { US"Ecaron",          0x011a, -1 },
  { US"Ecircumflex",     0x00ca, -1 },
  { US"Edieresis",       0x00cb, -1 },
  { US"Edotaccent",      0x0116, -1 },
  { US"Egrave",          0x00c8, -1 },
  { US"Emacron",         0x0112, -1 },
  { US"Eogonek",         0x0118, -1 },
  { US"Eth",             0x00d0, -1 },
  { US"Euro",            0x20ac, +1 },
  { US"F",               0x0046, -1 },
  { US"G",               0x0047, -1 },
  { US"Gbreve",          0x011e, -1 },
  { US"Gcommaaccent",    0x0122, -1 },
  { US"H",               0x0048, -1 },
  { US"I",               0x0049, -1 },
  { US"Iacute",          0x00cd, -1 },
  { US"Icircumflex",     0x00ce, -1 },
  { US"Idieresis",       0x00cf, -1 },
  { US"Idotaccent",      0x0130, -1 },
  { US"Igrave",          0x00cc, -1 },
  { US"Imacron",         0x012a, -1 },
  { US"Iogonek",         0x012e, -1 },
  { US"J",               0x004a, -1 },
  { US"K",               0x004b, -1 },
  { US"Kcommaaccent",    0x0136, -1 },
  { US"L",               0x004c, -1 },
  { US"Lacute",          0x0139, -1 },
  { US"Lcaron",          0x013d, -1 },
  { US"Lcommaaccent",    0x013b, -1 },
  { US"Lslash",          0x0141, -1 },
  { US"M",               0x004d, -1 },
  { US"N",               0x004e, -1 },
  { US"Nacute",          0x0143, -1 },
  { US"Ncaron",          0x0147, -1 },
  { US"Ncommaaccent",    0x0147, -1 },
  { US"Ntilde",          0x00d1, -1 },
  { US"O",               0x004f, -1 },
  { US"OE",              0x0152, -1 },
  { US"Oacute",          0x00d3, -1 },
  { US"Ocircumflex",     0x00d4, -1 },
  { US"Odieresis",       0x00d6, -1 },
  { US"Ograve",          0x00d2, -1 },
  { US"Ohungarumlaut",   0x0150, -1 },
  { US"Omacron",         0x014c, -1 },
  { US"Oslash",          0x00d8, -1 },
  { US"Otilde",          0x00d5, -1 },
  { US"P",               0x0050, -1 },
  { US"Q",               0x0051, -1 },
  { US"R",               0x0052, -1 },
  { US"Racute",          0x0154, -1 },
  { US"Rcaron",          0x0158, -1 },
  { US"Rcommaaccent",    0x0156, -1 },
  { US"S",               0x0053, -1 },
  { US"Sacute",          0x015a, -1 },
  { US"Scaron",          0x0160, -1 },
  { US"Scedilla",        0x015e, -1 },
  { US"Scommaaccent",    0x0218, +2 },
  { US"T",               0x0054, -1 },
  { US"Tcaron",          0x0164, -1 },
  { US"Tcommaaccent",    0x021a, +3 },
  { US"Thorn",           0x00de, -1 },
  { US"U",               0x0055, -1 },
  { US"Uacute",          0x00da, -1 },
  { US"Ucircumflex",     0x00db, -1 },
  { US"Udieresis",       0x00dc, -1 },
  { US"Ugrave",          0x00d9, -1 },
  { US"Uhungarumlaut",   0x0170, -1 },
  { US"Umacron",         0x016a, -1 },
  { US"Uogonek",         0x0172, -1 },
  { US"Uring",           0x016e, -1 },
  { US"V",               0x0056, -1 },
  { US"W",               0x0057, -1 },
  { US"X",               0x0058, -1 },
  { US"Y",               0x0059, -1 },
  { US"Yacute",          0x00dd, -1 },
  { US"Ydieresis",       0x0178, -1 },
  { US"Z",               0x005a, -1 },
  { US"Zacute",          0x0179, -1 },
  { US"Zcaron",          0x017d, -1 },
  { US"Zdotaccent",      0x017b, -1 },
  { US"a",               0x0061, -1 },
  { US"aacute",          0x00e1, -1 },
  { US"abreve",          0x0103, -1 },
  { US"acircumflex",     0x00e2, -1 },
  { US"acute",           0x00b4, -1 },
  { US"adieresis",       0x00e4, -1 },
  { US"ae",              0x00e6, -1 },
  { US"agrave",          0x00e0, -1 },
  { US"amacron",         0x0101, -1 },
  { US"ampersand",       0x0026, -1 },
  { US"aogonek",         0x0105, -1 },
  { US"aring",           0x00e5, -1 },
  { US"asciicircum",     0x005e, -1 },
  { US"asciitilde",      0x007e, -1 },
  { US"asterisk",        0x002a, -1 },
  { US"at",              0x0040, -1 },
  { US"atilde",          0x00e3, -1 },
  { US"b",               0x0062, -1 },
  { US"backslash",       0x005c, -1 },
  { US"bar",             0x007c, -1 },
  { US"braceleft",       0x007b, -1 },
  { US"braceright",      0x007d, -1 },
  { US"bracketleft",     0x005b, -1 },
  { US"bracketright",    0x005d, -1 },
  { US"breve",           0x0306, +4 },
  { US"brokenbar",       0x00a6, -1 },
  { US"bullet",          0x00b7, -1 },
  { US"c",               0x0063, -1 },
  { US"cacute",          0x0107, -1 },
  { US"caron",           0x030c, +5 },
  { US"ccaron",          0x010d, -1 },
  { US"ccedilla",        0x00e7, -1 },
  { US"cedilla",         0x00b8, -1 },
  { US"cent",            0x00a2, -1 },
  { US"circumflex",      0x0302, +6 },
  { US"colon",           0x003a, -1 },
  { US"comma",           0x002c, -1 },
  { US"commaaccent",     0x0326, +7 },
  { US"copyright",       0x00a9, -1 },
  { US"currency",        0x00a4, -1 },
  { US"d",               0x0064, -1 },
  { US"dagger",          0x2020, +8 },
  { US"daggerdbl",       0x2021, +9 },
  { US"dcaron",          0x010f, -1 },
  { US"dcroat",          0x0111, -1 },
  { US"degree",          0x00b0, -1 },
  { US"dieresis",        0x00a8, -1 },
  { US"divide",          0x00f7, -1 },
  { US"dollar",          0x0024, -1 },
  { US"dotaccent",       0x0307, 10 },
  { US"dotlessi",        0x0131, -1 },
  { US"e",               0x0065, -1 },
  { US"eacute",          0x00e9, -1 },
  { US"ecaron",          0x011b, -1 },
  { US"ecircumflex",     0x00ea, -1 },
  { US"edieresis",       0x00eb, -1 },
  { US"edotaccent",      0x0117, -1 },
  { US"egrave",          0x00e8, -1 },
  { US"eight",           0x0038, -1 },
  { US"ellipsis",        0x2026, 11 },
  { US"emacron",         0x0113, -1 },
  { US"emdash",          0x2014, 12 },
  { US"endash",          0x2013, 13 },
  { US"eogonek",         0x0119, -1 },
  { US"equal",           0x003d, -1 },
  { US"eth",             0x00f0, -1 },
  { US"exclam",          0x0021, -1 },
  { US"exclamdown",      0x00a1, -1 },
  { US"f",               0x0066, -1 },
  { US"fi",              0xfb01, 14 },
  { US"five",            0x0035, -1 },
  { US"fl",              0xfb02, 15 },
  { US"florin",          0x0192, 16 },
  { US"four",            0x0034, -1 },
  { US"fraction",        0x2044, 17 },
  { US"g",               0x0067, -1 },
  { US"gbreve",          0x011f, -1 },
  { US"gcommaaccent",    0x0123, -1 },
  { US"germandbls",      0x00df, -1 },
  { US"grave",           0x0060, -1 },
  { US"greater",         0x003e, -1 },
  { US"greaterequal",    0x2265, 18 },
  { US"guillemotleft",   0x00ab, -1 },
  { US"guillemotright",  0x00bb, -1 },
  { US"guilsinglleft",   0x2039, 19 },
  { US"guilsinglright",  0x203a, 20 },
  { US"h",               0x0068, -1 },
  { US"hungarumlaut",    0x030b, 21 },
  /* 002d is "hyphen-minus"; Unicode also has separate codes for hyphen and
  for minus. We use the latter below. */
  { US"hyphen",          0x002d, -1 },
  { US"i",               0x0069, -1 },
  { US"iacute",          0x00ed, -1 },
  { US"icircumflex",     0x00ee, -1 },
  { US"idieresis",       0x00ef, -1 },
  { US"igrave",          0x00ec, -1 },
  { US"imacron",         0x012b, -1 },
  { US"iogonek",         0x012f, -1 },
  { US"j",               0x006a, -1 },
  { US"k",               0x006b, -1 },
  { US"kcommaaccent",    0x0137, -1 },
  { US"l",               0x006c, -1 },
  { US"lacute",          0x013a, -1 },
  { US"lcaron",          0x013e, -1 },
  { US"lcommaaccent",    0x013c, -1 },
  { US"less",            0x003c, -1 },
  { US"lessequal",       0x2264, 22 },
  { US"logicalnot",      0x00ac, -1 },
  { US"lozenge",         0x25ca, 23 },
  { US"lslash",          0x0142, -1 },
  { US"m",               0x006d, -1 },
  { US"macron",          0x00af, -1 },
  { US"minus",           0x2212, 24 },
  { US"mu",              0x00b5, -1 },
  { US"multiply",        0x00d7, -1 },
  { US"n",               0x006e, -1 },
  { US"nacute",          0x0144, -1 },
  { US"ncaron",          0x0148, -1 },
  { US"ncommaaccent",    0x0146, -1 },
  { US"nine",            0x0039, -1 },
  { US"notequal",        0x2260, 25 },
  { US"ntilde",          0x00f1, -1 },
  { US"numbersign",      0x0023, -1 },
  { US"o",               0x006f, -1 },
  { US"oacute",          0x00f3, -1 },
  { US"ocircumflex",     0x00f4, -1 },
  { US"odieresis",       0x00f6, -1 },
  { US"oe",              0x0153, -1 },
  { US"ogonek",          0x0328, 26 },
  { US"ograve",          0x00f2, -1 },
  { US"ohungarumlaut",   0x0151, -1 },
  { US"omacron",         0x014d, -1 },
  { US"one",             0x0031, -1 },
  { US"onehalf",         0x00bd, -1 },
  { US"onequarter",      0x00bc, -1 },
  { US"onesuperior",     0x00b9, -1 },
  { US"ordfeminine",     0x00aa, -1 },
  { US"ordmasculine",    0x00ba, -1 },
  { US"oslash",          0x00f8, -1 },
  { US"otilde",          0x00f5, -1 },
  { US"p",               0x0070, -1 },
  { US"paragraph",       0x00b6, -1 },
  { US"parenleft",       0x0028, -1 },
  { US"parenright",      0x0029, -1 },
  { US"partialdiff",     0x2202, 27 },
  { US"percent",         0x0025, -1 },
  { US"period",          0x002e, -1 },
  { US"periodcentered",  0x2027, 28 },
  { US"perthousand",     0x2031, 29 },
  { US"plus",            0x002b, -1 },
  { US"plusminus",       0x00b1, -1 },
  { US"q",               0x0071, -1 },
  { US"question",        0x003f, -1 },
  { US"questiondown",    0x00bf, -1 },
  { US"quotedbl",        0x0022, -1 },
  { US"quotedblbase",    0x201e, 30 },
  { US"quotedblleft",    0x201c, 31 },
  { US"quotedblright",   0x201d, 32 },
  { US"quoteleft",       0x2018, 33 },
  { US"quoteright",      0x2019, 34 },
  { US"quotesinglbase",  0x201a, 35 },
  { US"quotesingle",     0x0027, -1 },
  { US"r",               0x0072, -1 },
  { US"racute",          0x0155, -1 },
  { US"radical",         0x221a, 36 },
  { US"rcaron",          0x0159, -1 },
  { US"rcommaaccent",    0x0157, -1 },
  { US"registered",      0x00ae, -1 },
  { US"ring",            0x030a, 37 },
  { US"s",               0x0073, -1 },
  { US"sacute",          0x015b, -1 },
  { US"scaron",          0x0161, -1 },
  { US"scedilla",        0x015f, -1 },
  { US"scommaaccent",    0x0219, 38 },
  { US"section",         0x00a7, -1 },
  { US"semicolon",       0x003b, -1 },
  { US"seven",           0x0037, -1 },
  { US"six",             0x0036, -1 },
  { US"slash",           0x002f, -1 },
  { US"space",           0x0020, -1 },
  { US"sterling",        0x00a3, -1 },
  { US"summation",       0x2211, 39 },
  { US"t",               0x0074, -1 },
  { US"tcaron",          0x0165, -1 },
  { US"tcommaaccent",    0x021b, 40 },
  { US"thorn",           0x00fe, -1 },
  { US"three",           0x0033, -1 },
  { US"threequarters",   0x00be, -1 },
  { US"threesuperior",   0x00b3, -1 },
  { US"tilde",           0x0303, 41 },
  { US"trademark",       0x2122, 42 },
  { US"two",             0x0032, -1 },
  { US"twosuperior",     0x00b2, -1 },
  { US"u",               0x0075, -1 },
  { US"uacute",          0x00fa, -1 },
  { US"ucircumflex",     0x00fb, -1 },
  { US"udieresis",       0x00fc, -1 },
  { US"ugrave",          0x00f9, -1 },
  { US"uhungarumlaut",   0x0171, -1 },
  { US"umacron",         0x016b, -1 },
  { US"underscore",      0x005f, -1 },
  { US"uogonek",         0x0173, -1 },
  { US"uring",           0x016f, -1 },
  { US"v",               0x0076, -1 },
  { US"w",               0x0077, -1 },
  { US"x",               0x0078, -1 },
  { US"y",               0x0079, -1 },
  { US"yacute",          0x00fd, -1 },
  { US"ydieresis",       0x00ff, -1 },
  { US"yen",             0x00a5, -1 },
  { US"z",               0x007a, -1 },
  { US"zacute",          0x017a, -1 },
  { US"zcaron",          0x017e, -1 },
  { US"zdotaccent",      0x017c, -1 },
  { US"zero",            0x0030, -1 }
};

static int an2ucount = sizeof(an2ulist)/sizeof(an2uencod);


/*************************************************
*     Convert character name to Unicode value    *
*************************************************/

/*
Arguments:
  cname    the character name
  fname    the font name (for warning)
  warn     TRUE if warning wanted for not found
  mcptr    if not NULL, where to put the special encoding value

Returns:   a Unicode code point, or -1 if not found
*/

static int
an2u(uschar *cname, uschar *fname, BOOL warn, int *mcptr)
{
int top = an2ucount;
int bot = 0;
if (mcptr != NULL) *mcptr = -1;

while (top > bot)
  {
  int mid = (top + bot)/2;
  an2uencod *an2u = an2ulist + mid;
  int c = Ustrcmp(cname, an2u->name);
  if (c == 0)
    {
    if (mcptr != NULL) *mcptr = an2u->poffset;
    return an2u->code;
    }
  if (c > 0) bot = mid + 1; else top = mid;
  }

if (warn) error_moan(21, cname, fname);   /* Warning */
return -1;
}



/*************************************************
*        Kern table sorting comparison           *
*************************************************/

/* This is the auxiliary routine used for comparing kern table entries when
sorting them.

Arguments:
  a          pointer to kerntable structure
  b          pointer to kerntable structure

Returns      difference between their "pair" values
*/

static int
table_cmp(const void *a, const void *b)
{
kerntablestr *ka = (kerntablestr *)a;
kerntablestr *kb = (kerntablestr *)b;
return ka->pair - kb->pair;
}



/*************************************************
*          Number reader for AFM files           *
*************************************************/

/*
Arguments:
  value       where to return the value
  p           character pointer

Returns:      new value of p
*/

static uschar *
read_number(int *value, uschar *p)
{
int n = 0;
int sign = 1;
while (*p != 0 && *p == ' ') p++;
if (*p == '-') { sign = -1; p++; }
while (isdigit(*p)) n = n * 10 + *p++ - '0';
*value = n * sign;
return p;
}



/*************************************************
*      Load width and kern tables for a font     *
*************************************************/

/* This is an externally-called function.

Argument:   the font id (offset in font_List)
Returns:    nothing
*/

void
font_loadtables(int fontid)
{
FILE *f = NULL;
int kerncount = 0;
int finalcount = 0;
int *widths, *r2ladjusts, *heights;
kerntablestr *kerntable;
fontstr *fs = &(font_List[fontid]);
uschar *pp;
uschar filename[256];
uschar line[256];

DEBUG(("Loading AFM for %s\n", fs->psname));

/* First look in any additional directory */

if (font_metrics_extra != NULL)
  {
  sprintf(CS filename, "%s/%s.afm", font_metrics_extra, fs->psname);
  f = Ufopen(filename, "r");
  }

/* Try the default directory if not yet found. */

if (f == NULL)
  {
  sprintf(CS filename, "%s/%s.afm", font_metrics_default, fs->psname);
  f = Ufopen(filename, "r");
  if (f == NULL)                     /* These are hard errors */
    {
    if (font_metrics_extra == NULL)  
      error_moan(59, fs->psname, font_metrics_default);
    else
      error_moan(127, fs->psname, font_metrics_extra, font_metrics_default);
    }       
  } 

widths = fs->widths = malloc(LOWCHARLIMIT * sizeof(int));
memset(widths, 0, LOWCHARLIMIT * sizeof(int));
r2ladjusts = fs->r2ladjusts = malloc(LOWCHARLIMIT * sizeof(int));
memset(r2ladjusts, 0, LOWCHARLIMIT * sizeof(int));
heights = fs->heights = NULL;
fs->kerncount = 0;

/* Process the AFM file. First find the start of the metrics; on the way, check
for the standard encoding scheme and for fixed pitch. */

for (;;)
  {
  if (Ufgets(line, sizeof(line), f) == NULL)
    error_moan(122, filename, "no metric data found", "");  /* Hard */
  if (memcmp(line, "EncodingScheme AdobeStandardEncoding", 36) == 0)
    fs->stdencoding = TRUE;
  if (memcmp(line, "IsFixedPitch true", 17) == 0)
    fs->fixedpitch = TRUE;
  if (memcmp(line, "StartCharMetrics", 16) == 0) break;
  }

/* Process the metric lines for each character */

for (;;)
  {
  uschar *ppb; 
  int width, code;
  int poffset = -1;
  int r2ladjust = 0; 

  if (Ufgets(line, sizeof(line), f) == NULL)
    error_moan(122, filename, "unexpected end of metric data", "");  /* Hard */
  if (memcmp(line, "EndCharMetrics", 14) == 0) break;

  if (memcmp(line, "C ", 2) != 0)
    error_moan(122, filename, "unrecognized metric data line: ", line); /* Hard */

  pp = line + 2;
  while (memcmp(pp, "WX", 2) != 0) pp++;
  pp = read_number(&width, pp+2);
  
  /* Look for a bounding box, but use a new pointer, because N comes first. If 
  a bounding box is found, compute a value by which to adjust the printing 
  position of this character when printing right-to-left. This is used for the 
  last character of every string, instead of the stringwidth character. 
  
        |          |--------|
        |          | char   |
        |          |  glyph |
   ^    |<-- x0 -->|--------|     x0 is the side bearing (LH bbox value)
   ^--->|<-- x1 ------------>     x1 is the right hand bbox value
   ^    ^
   ^    ^
   ^    Original print point
   ^
   New print point is x0 + x1 to the left of the old. If it were just x1,
   the edge of the character would abut the original point; instead we add
   an additional same sized bearing on the other side. */

  ppb = pp;
  while (*ppb != 0 && Ustrncmp(ppb, "B ", 2) != 0) ppb++;
  if (*ppb != 0) 
    {
    int x0, x1;  
    ppb = read_number(&x0, ppb+2);   /* x-left */
    ppb = read_number(&x1, ppb);     /* y-bottom */
    ppb = read_number(&x1, ppb);     /* x-right */
    r2ladjust = x1 + x0;
    }   

  /* If this is a StandardEncoding font, scan the list of characters so as to
  get the Unicode value for this character. */

  if (fs->stdencoding)
    {
    uschar *cname;
    while (memcmp(pp, "N ", 2) != 0) pp++;
    cname = (pp += 2);
    while (*pp != ' ') pp++;
    *pp = 0;
    code = an2u(cname, fs->psname, TRUE, &poffset);
    if (code < 0) continue;  /* Don't try to store anything! */
    }

  /* For other fonts, just use the character number directly. If there are
  unencoded characters, ignore them. These fonts include the PMW-Music font,
  which has some characters with vertical height movements. */

  else
    {
    (void)read_number(&code, line+1);
    if (code < 0) continue;
    while (*pp != 0 && memcmp(pp, "WY", 2) != 0) pp++;
    if (*pp != 0)
      {
      int height;
      pp += 2;
      (void)read_number(&height, pp);
      if (heights == NULL)
        {
        heights = fs->heights = malloc(256 * sizeof(int));
        memset(heights, 0, 256 * sizeof(int));
        }
      heights[code] = height;
      }
    }

  /* Remember that this font has certain characters */

  if (code == CHAR_FI) fs->hasfi = TRUE;

  /* Now put the widths in an appropriate place. */
  
  if (code < LOWCHARLIMIT) 
    {
    widths[code] = width; 
    r2ladjusts[code] = r2ladjust; 
    } 
  else
    {
    tree_node *tc = malloc(sizeof(tree_node));
    tc->name = malloc(8);
    tc->name[misc_ord2utf8(code, tc->name)] = 0;
    tc->val[0] = poffset;
    tc->val[1] = width;
    tc->val[2] = r2ladjust; 
    (void)Tree_InsertNode(&(fs->high_tree), tc);
    }
  }

/* Process kerning data (if any); when this is done, we are finished with the
AFM file. */

for (;;)
  {
  if (Ufgets(line, sizeof(line), f) == NULL)
    {
    (void)fclose(f);
    return;        /* No kerning data */
    }
  if (memcmp(line, "StartKernPairs", 14) == 0) break;
  }

/* Find size of kern table, and get space for it. In the past, some of Adobe's
AFM files had a habit of containing a large number of kern pairs with
zero amount of kern. We leave these out of the table and adjust the count for
searching, but don't bother to free up the unused store (it isn't a vast
amount). */

pp = line + 14;
while (*pp != 0 && *pp == ' ') pp++;
(void)read_number(&kerncount, pp);
fs->kerns = kerntable = malloc(kerncount*sizeof(kerntablestr));

finalcount = 0;
while (kerncount--)
  {
  uschar *x;
  int sign = 1;
  int value;
  int a = -1;
  int b = -1;

  if (Ufgets(line, sizeof(line), f) == NULL)
    error_moan(122, filename, "unexpected end of kerning data");  /* Hard */
  if (memcmp(line, "EndKernPairs", 12) == 0) break;

  /* Skip blank lines */

  if (Ustrlen(line) <= 1)
    {
    kerncount++;
    continue;
    }

  /* Process each kern */

  pp = line + 4;

  x = pp;
  while (*pp != 0 && *pp != ' ') pp++;
  *pp++ = 0;
  a = an2u(x, fs->psname, FALSE, NULL);

  while (*pp != 0 && *pp == ' ') pp++;
  x = pp;
  while (*pp != 0 && *pp != ' ') pp++;
  *pp++ = 0;
  b = an2u(x, fs->psname, FALSE, NULL);

  /* Read the kern value only if we have found the characters; otherwise ignore
  the kern */

  if (a >= 0 && b >= 0)
    {
    kerntable[finalcount].pair = (a << 16) + b;
    while (*pp != 0 && *pp == ' ') pp++;
    if (*pp == '-') { sign = -1; pp++; }
    (void)read_number(&value, pp);
    if (value != 0) kerntable[finalcount++].kwidth = value*sign;
    }
  }

/* Adjust the count and sort the table into ascending order */

fs->kerncount = finalcount;  /* true count */
qsort(kerntable, fs->kerncount, sizeof(kerntablestr), table_cmp);

/* Finished with the AFM file */

(void)fclose(f);

/* Early checking debugging code; retained in the source in case it is ever
needed again. */

#ifdef NEVER
debug_printf("FONT %s\n", fs->psname);
  {
  int i;

  for (i = 0; i < LOWCHARLIMIT; i++)
    {
    if (fs->widths[i] != 0) debug_printf("%04x %5d\n", i, fs->widths[i]);
    }

  for (i = LOWCHARLIMIT; i < 0xffff; i++)
    {
    tree_node *t;
    uschar key[4];
    key[0] = (i >> 8) & 255;
    key[1] = i & 255;
    key[2] = 0;
    t = tree_search(fs->high_tree, key);
    if (t != NULL) debug_printf("%04x %5d %5d %5d\n", i, t->data.val[0],
      t->data.val[1], t->data.val[2]);
    }

  debug_printf("KERNS %d\n", fs->kerncount);
  for (i = 0; i < fs->kerncount; i++)
    {
    kerntablestr *k = &(fs->kerns[i]);
    int a = (k->pair >> 16) & 0xffff;
    int b = (k->pair) & 0xffff;
    debug_printf("%04x %04x %5d\n", a, b, k->kwidth);
    }
  }
#endif
}



/*************************************************
*         Add a font to the base list            *
*************************************************/

/* This function is called from font_init() below, to add a single basic
PostScript font to the font_List vector.

Argument:  Name of PostScript font
Returns:   nothing
*/

static void
font_addbasefont(uschar *psfont)
{
fontstr *fs = &(font_List[font_basecount]);
if (font_basecount >= max_fonts) { error_moan(26, max_fonts); return; }
fs->psname = malloc(Ustrlen(psfont)+1);
Ustrcpy(fs->psname, psfont);
fs->widths = NULL;
fs->high_tree = NULL;
fs->heights = NULL;
fs->kerns = NULL;
fs->kerncount = -1;
fs->stdencoding = fs->fixedpitch = fs->hasfi = FALSE;
font_loadtables(font_basecount++);
}


/*************************************************
*        Set up the base list of fonts           *
*************************************************/

/* Called at start-up to set up the default fonts

Arguments:  none
Returns:    nothing
*/

void
font_init(void)
{
font_addbasefont(US"Times-Roman");
font_addbasefont(US"Times-Italic");
font_addbasefont(US"Times-Bold");
font_addbasefont(US"Times-BoldItalic");
font_addbasefont(US"Symbol");
font_addbasefont(US"PMW-Music");
}



/*************************************************
*              Search for given font             *
*************************************************/

/* This function searches the font list by the name of the font.

Argument:  the font name (e.g. "Times-Roman")
Returns:   the font id (offset in font_List) or -1 if not found
*/

int 
font_search(uschar *name)
{
int i;
for (i = 0; i < font_count; i++)
  if (Ustrcmp(name, font_List[i].psname) == 0) return i;
return -1;
}



/*************************************************
*         Find the width of a string             *
*************************************************/

/* The "height" (distance current point moves vertically) is placed in
font_stringheight. For ordinary text fonts, this is only non-zero when there's
a rotation, but some characters in the music font move vertically. If
font_xstretch is set, it is an absolute amount of space to add to each space
character. This is used when printing justified headings and footings.

Arguments:
  s            the string
  font         the font number
  pointsize    the font's point size

Returns:       the string width
*/

int
font_stringwidth(uschar *s, int font, int pointsize)
{
int hwidth = 0;
int vheight = 0;
int spacecount = 0;
int *widths, *heights;
int fontid = font_table[font];
int c;
int lastc = -1;
int kerncount;
BOOL stdencoding;
fontstr *fs = &(font_List[fontid]);
kerntablestr *ktable;
tree_node *high_tree;

widths = fs->widths;
high_tree = fs->high_tree;
heights = fs->heights;
ktable = fs->kerns;
kerncount = fs->kerncount;
stdencoding = fs->stdencoding;

DEBUG(("font_stringwidth %d %d \"%s\"\n", fontid, pointsize, s));

/* Open and close quotes are fudged. When kerning we have to note the kern
values. */

while (*s != 0)
  {
  GETCHARINC(c, s);
  if (c == ' ') spacecount++;

  /* Deal with characters >= 256 */

  if (c >= 256)
    {
    if (!stdencoding)
      hwidth += widths[UNKNOWN_CHAR_N];

    else if (c < LOWCHARLIMIT)
      hwidth += widths[c];

    else
      {
      tree_node *t;
      uschar utf[8];
      utf[misc_ord2utf8(c, utf)] = 0;
      t = Tree_Search(high_tree, utf);
      if (t != NULL) hwidth += t->val[1];
        else hwidth += widths[UNKNOWN_CHAR_S];
      }
    }

  /* Character is < 256. Heights apply only to low-numbered characters. */

  else
    {
    hwidth += widths[c];
    if (heights != NULL) vheight += heights[c];
    }

  /* Deal with kerning */

  if (main_kerning && ktable != NULL &&
      lastc >= 0 && lastc <= 0xffff && c <= 0xffff)
    {
    int top, bot, mid;
    unsigned int pair = (lastc << 16) | c;

    bot = 0;
    top = kerncount;

    while (top > bot)
      {
      kerntablestr *k;
      mid = (top + bot)/2;
      k = &(ktable[mid]);
      if (pair == k->pair)
        {
        hwidth += k->kwidth;
        break;
        }
      if (pair > k->pair) bot = mid + 1; else top = mid;
      }
    }

  lastc = c;
  }

/* Scale to font size, then adjust for rotation */

hwidth = mac_muldiv(hwidth, pointsize, 1000);
vheight = mac_muldiv(vheight, pointsize, 1000);

font_stringheight =
  mac_muldiv(hwidth, font_transform[1], 65536) +
  mac_muldiv(vheight, font_transform[3], 65536);

DEBUG(("font_stringwidth() %d %d\n",
       mac_muldiv(hwidth, font_transform[0], 65536) +
       mac_muldiv(vheight, font_transform[2], 65536),
       font_stringheight));

return font_xstretch * spacecount +
       mac_muldiv(hwidth, font_transform[0], 65536) +
       mac_muldiv(vheight, font_transform[2], 65536);
}




/*************************************************
*            Read a font word (+ number)         *
*************************************************/

/*
Argument:  TRUE to not give an error if the word is unrecognised
Returns:   font_rm, font_it, etc, or -1 on error
*/

int
font_fontword(BOOL soft)
{
uschar word[100];
read_word(word);
if (Ustrcmp(word, "roman") == 0) return font_rm;
if (Ustrcmp(word, "italic") == 0) return font_it;
if (Ustrcmp(word, "bold") == 0) return font_bf;
if (Ustrcmp(word, "bolditalic") == 0) return font_bi;
if (Ustrcmp(word, "symbol") == 0) return font_sy;
if (Ustrcmp(word, "music") == 0) return font_mu;
if (Ustrcmp(word, "bigmusic") == 0) return font_mf;
if (Ustrcmp(word, "extra") == 0)
  {
  int x;
  if (!read_expect_integer(&x, FALSE, FALSE)) return -1;
  if (x >= 1 && x <= MaxExtraFont) return font_xx + x - 1;
  error_moan(26, MaxExtraFont);
  }
else if (!soft)
  error_moan(10, "\"roman\", \"italic\", \"bold\", \"bolditalic\", or \"extra\"");
return -1;
}



/*************************************************
*         Set up a font rotation                 *
*************************************************/

/* The effect of this function is to modify the font_transform matrix by the
given rotation.

Argument:  rotation in millidegrees
Returns:   nothing
*/

void
font_rotate(int rotate)
{
int newmatrix[4];
double sr = sin(((double)rotate)*atan(1.0)/45000.0);
double cr = cos(((double)rotate)*atan(1.0)/45000.0);

font_sinr = (int)(sr * 1000.0);
font_cosr = (int)(cr * 1000.0);

newmatrix[0] =
  (int)(((double)font_transform[0])*cr - ((double)font_transform[1])*sr);
newmatrix[1] =
  (int)(((double)font_transform[0])*sr + ((double)font_transform[1])*cr);
newmatrix[2] =
  (int)(((double)font_transform[2])*cr - ((double)font_transform[3])*sr);
newmatrix[3] =
  (int)(((double)font_transform[2])*sr + ((double)font_transform[3])*cr);

memcpy(font_transform, newmatrix, 4*sizeof(int));
}



/*************************************************
*           Reset font transformation            *
*************************************************/

/* Historical relic: the transform matrix operates in the units that RISC OS
uses for its font transformations. These were chosen so that the RISC OS screen
display code could use the matrix directly. There seems no reason to change
this choice gratuitously. The first four matrix values have 16-bit fractions;
the other two are in millipoints.

Arguments:  none
Returns:    nothing
*/

void font_reset(void)
{
font_sinr = 0;      /* No rotation => angle is zero */
font_cosr = 1000;   /* Fixed point 1.0 */
font_transform[0] = font_transform[3] = 65536;
font_transform[1] = font_transform[2] = 0;
font_transform[4] = font_transform[5] = 0;
}

/* End of font.c */
