/*
 * Copyright © 1999, 2001 Free Software Foundation, Inc.
 *
 * $Id: afm.c,v 1.10 2001/02/13 23:38:05 danny Exp $
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, 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 software; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * This is Oleo's AFM module.
 *
 * Its purpose is to locate and interpret AFM (Adobe Font Metric) files,
 * and to determine string lengths based on that information.
 *
 * This implementation is based on Adobe's document "5004.AFM_Spec.pdf".
 *
 * The return value of AfmStringWidth is of course a number that represents
 * the string's width in points.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef	WITH_DMALLOC
#include <dstdlib.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/param.h>

#include "afm.h"
#include "cmd.h"

#define	AFM_PATH	"/share/afm"
#define	USRLOCAL_PATH	"/usr/local"
#define	USR_PATH	"/usr"
#define	GS_PATH		"/share/ghostscript/fonts"

#define	LINE_LEN	256

static char	*CurrentFontName = NULL,
		*CurrentFontSlant = NULL;
static int	CurrentFontSize = 10;
FILE		*fp;

struct charmetrics {
	unsigned int	code;
	char	*name;
	int	width;
};

struct afm {
	int			IsFixedPitch, ItalicAngle,
				FontBBox1, FontBBox2, FontBBox3, FontBBox4,
				UnderlinePosition, UnderlineThickness, CapHeight,
				XHeight, Ascender, Descender,
				StartCharMetrics;
	struct charmetrics *	charMetrics;
	int			charIndex[256];
};

static struct afm *afm = 0;

static int changed(char *a, char *b)
{
	if (a == 0 && b == 0)
		return 0;
	if (a != 0 && b != 0 && strcmp(a, b) == 0)
		return 0;
	return 1;
}

static char *Where, *line;

static int IsCommand(char *literal)
{
	int	len;

	len = strlen(literal);

	Where = line + len + 1;

	return (strncmp(literal, line, len) == 0);
}

static int Value(void)
{
	int	v;

	while (isspace(*Where))
		Where++;
	sscanf(Where, "%d", &v);
	return v;
}

static int ValueBoolean(void)
{
	while (isspace(*Where))
		Where++;
	if (strncmp(Where, "True", 4) == 0)
		return 1;
	if (strncmp(Where, "true", 4) == 0)
		return 1;
	if (strncmp(Where, "TRUE", 4) == 0)
		return 1;
	return 0;
}

static void skip(void)
{
	while (*Where != ';' && *Where != '\n' && *Where != '\0')
		Where++;
}

static int namelen(void)
{
	char	*p;
	int	i;

	for (i=0, p=Where; !isspace(*p); i++, p++) ;
	return i;
}

static void ReadCharMetrics(int n)
{
	int	i, l;

	afm->StartCharMetrics = n;

	if (afm->charMetrics)
		free(afm->charMetrics);
	afm->charMetrics = (struct charmetrics *)calloc(n, sizeof(struct charmetrics));

	for (i=0; i<n; i++) {
		if (fgets(line, LINE_LEN, fp) == 0)
			return;	/* FIX ME */
		Where = line;
		while (*Where != '\n') {
			if (isspace(*Where)) {
				Where++;
				continue;
			}
			if (*Where == 'C' && isspace(*(Where+1))) {
				afm->charMetrics[i].code = atoi(Where+2);
				skip();
			} else if (*Where == 'C' && *(Where+1) == 'X' && isspace(*(Where+2))) {
				sscanf(Where+3, "%x", &afm->charMetrics[i].code);
				skip();
			} else if (*Where == 'N' && isspace(*(Where+1))) {
				Where += 2;
				l = namelen();
				afm->charMetrics[i].name = malloc(l+1);
				afm->charMetrics[i].name[l] = '\0';
				strncpy(afm->charMetrics[i].name, Where, l);
				skip();
			} else if (*Where == 'W' && *(Where+1) == 'X' && isspace(*(Where+2))) {
				afm->charMetrics[i].width = atoi(Where+3);
				skip();
			} else {
				skip();
				/* FIX ME */
			}

			Where++;
		}
	}
}

static void
ReadAfmLine(void)
{
	if (afm == 0) {
		afm = (struct afm *)malloc(sizeof(struct afm));
		memset(afm, 0, sizeof(struct afm));
	}

	if (IsCommand("IsFixedPitch")) afm->IsFixedPitch = ValueBoolean();
	if (IsCommand("ItalicAngle")) afm->ItalicAngle = Value();
	if (IsCommand("FontBBox"))
		sscanf(Where, "%d %d %d %d", &afm->FontBBox1, &afm->FontBBox2,
			&afm->FontBBox3, &afm->FontBBox4);
	if (IsCommand("StartCharMetrics")) {
		int	n = atoi(Where);
		ReadCharMetrics(n);
	}
}

char *
GSName(char *name, char *slant)
{
	if (slant == NULL)
		slant = "";

	if (strcmp(name, "Courier") == 0 && strcmp(slant, "") == 0)
		return "n022003l";	/* NimbusMonL-Regu */
	if (strcmp(name, "CGTimes") == 0 && strcmp(slant, "") == 0)
		return "n021003l";	/* NimbusRomNo9L-Regu */
	return NULL;
}

FILE *
OpenAfmFile(char *name, char *slant)
{
	FILE	*fp;
	char	*fn, *alias;

	fn = malloc(MAXPATHLEN);

	sprintf(fn, "%s/%s%s.afm", BUILD_PREFIX AFM_PATH, name, slant ? slant : "");
	fp = fopen(fn, "r");
	if (fp)
		return fp;
#ifdef	VERBOSE
	else
		MessageAppend(0, "Failed to open %s\n", fn);
#endif

	sprintf(fn, "%s/%s%s.afm", USR_PATH GS_PATH, name, slant ? slant : "");
	fp = fopen(fn, "r");
	if (fp)
		return fp;
#ifdef	VERBOSE
	else
		MessageAppend(0, "Failed to open %s\n", fn);
#endif

	sprintf(fn, "%s/%s%s.afm", USRLOCAL_PATH GS_PATH, name, slant ? slant : "");
	fp = fopen(fn, "r");
	if (fp)
		return fp;
#ifdef	VERBOSE
	else
		MessageAppend(0, "Failed to open %s\n", fn);
#endif

	if ((alias = GSName(name, slant)) != NULL) {
		sprintf(fn, "%s/%s.afm", USR_PATH GS_PATH, alias);
		fp = fopen(fn, "r");
		if (fp)
			return fp;
#ifdef	VERBOSE
	else
		MessageAppend(0, "Failed to open %s\n", fn);
#endif
		sprintf(fn, "%s/%s.afm", USRLOCAL_PATH GS_PATH, alias);
		fp = fopen(fn, "r");
		if (fp)
			return fp;
#ifdef	VERBOSE
	else
		MessageAppend(0, "Failed to open %s\n", fn);
#endif
	}

	if (fp == 0) {
		io_info_msg(PACKAGE ": couldn't open AFM file for %s%s\n", name, slant);
		free(fn);
		return NULL;
	}
	free(fn);

	return fp;
}

void
AfmSetFont(char *name, char *slant, int size)
{
/* Have the font name/slant changed ? */
	CurrentFontSize = size;

	if ( ( ! changed(name, CurrentFontName)) &&
		( ! changed(slant, CurrentFontSlant)))
		return;

/*
 * Something's changed, need to read another file.
 *
 * First however remember current state.
 */
	if (CurrentFontName) {
		free(CurrentFontName);
		CurrentFontName = 0;
	}
	if (CurrentFontSlant) {
		free(CurrentFontSlant);
		CurrentFontSlant = 0;
	}

	if (name)
		CurrentFontName = strdup(name);
	if (slant)
		CurrentFontSlant = strdup(slant);

/*
 * Start reading file.
 */
	fp = OpenAfmFile(name, slant);
	if (fp) {
		line = malloc(LINE_LEN + 3);

		while (fgets(line, LINE_LEN, fp) != NULL) {
			ReadAfmLine();
		}

		fclose(fp);
		free(line);
	}

	AfmSetEncoding("ISOLatin1");
}

int
AfmFontHeight(void)
{
	if (! afm) {
		/* FIX ME */
		io_info_msg("AfmFontHeight: failed\n");
		return 0;
	}
	return afm->FontBBox4 * CurrentFontSize / 1000;
}

int
AfmFontWidth(void)
{
	if (! afm) {
		/* FIX ME */
		io_info_msg("AfmFontWidth: failed\n");
		return 8;
	}
	return afm->FontBBox3 * CurrentFontSize / 1000;
}

int
AfmPitch(void)
{
	float	p;

	if (! afm) {
		io_info_msg("AfmPitch: failed\n");
		return 10;	/* FIX ME this is the fallback value */
	}
	p = 72.0 * 1000 / (afm->FontBBox3 * CurrentFontSize);
	return p;
}

int
AfmStringWidth(char *s)
{
	int	r, i, cw;
	unsigned int	pc;
	char	*p;

	if (s == NULL || afm == 0)
		return 0;

	if (afm->IsFixedPitch)
		r = strlen(s) * afm->FontBBox3 * CurrentFontSize / 1000;
	else {
		if (afm->charMetrics == 0) {
			return 0;	/* FIX ME */
		}
		for (r=0, p=s; *p; p++) {
			pc = 255 & *p;
			cw = 0;
#if 1	/* Use quick access */
			if (afm->charIndex[pc] >= 0)
				cw = afm->charMetrics[afm->charIndex[pc]].width;
#else
			for (i=0; cw == 0 && i<afm->StartCharMetrics; i++)
				if (afm->charMetrics[i].code == pc) {
					cw = afm->charMetrics[i].width;
					break;
				}
#endif
			if (cw == 0) {
				fprintf(stderr, "Width of char(%c) not found\n", *p);
/*				return 0;	*/
			}
			r += cw;
		}
		r = r * CurrentFontSize / 1000;
	}

#if 0
	fprintf(stderr, "AfmStringWidth(%s) -> %d\n", s, r);
#endif

	return r;
}

static char *IsoLatin1Encoding[256] = {
	/* 0x00 */	"non-printable",
	/* 0x01 */	"non-printable",
	/* 0x02 */	"non-printable",
	/* 0x03 */	"non-printable",
	/* 0x04 */	"non-printable",
	/* 0x05 */	"non-printable",
	/* 0x06 */	"non-printable",
	/* 0x07 */	"non-printable",
	/* 0x08 */	"non-printable",
	/* 0x09 */	"non-printable",
	/* 0x0a */	"non-printable",
	/* 0x0b */	"non-printable",
	/* 0x0c */	"non-printable",
	/* 0x0d */	"non-printable",
	/* 0x0e */	"non-printable",
	/* 0x0f */	"non-printable",
	/* 0x10 */	"non-printable",
	/* 0x11 */	"non-printable",
	/* 0x12 */	"non-printable",
	/* 0x13 */	"non-printable",
	/* 0x14 */	"non-printable",
	/* 0x15 */	"non-printable",
	/* 0x16 */	"non-printable",
	/* 0x17 */	"non-printable",
	/* 0x18 */	"non-printable",
	/* 0x19 */	"non-printable",
	/* 0x1a */	"non-printable",
	/* 0x1b */	"non-printable",
	/* 0x1c */	"non-printable",
	/* 0x1d */	"non-printable",
	/* 0x1e */	"non-printable",
	/* 0x1f */	"non-printable",
	/* 0x20 */	"/space",
	/* 0x21 */	"/exclam",
	/* 0x22 */	"/quotedbl",
	/* 0x23 */	"/numbersign",
	/* 0x24 */	"/dollar",
	/* 0x25 */	"/percent",
	/* 0x26 */	"/ampersand",
	/* 0x27 */	"/quoteright",
	/* 0x28 */	"/parenleft",
	/* 0x29 */	"/parenright",
	/* 0x2a */	"/asterisk",
	/* 0x2b */	"/plus",
	/* 0x2c */	"/comma",
	/* 0x2d */	"/hyphen",
	/* 0x2e */	"/period",
	/* 0x2f */	"/slash",
	/* 0x30 */	"/zero",
	/* 0x31 */	"/one",
	/* 0x32 */	"/two",
	/* 0x33 */	"/three",
	/* 0x34 */	"/four",
	/* 0x35 */	"/five",
	/* 0x36 */	"/six",
	/* 0x37 */	"/seven",
	/* 0x38 */	"/eight",
	/* 0x39 */	"/nine",
	/* 0x3a */	"/colon",
	/* 0x3b */	"/semicolon",
	/* 0x3c */	"/less",
	/* 0x3d */	"/equal",
	/* 0x3e */	"/greater",
	/* 0x3f */	"/question",
	/* 0x40 */	"/at",
	/* 0x41 */	"/A",
	/* 0x42 */	"/B",
	/* 0x43 */	"/C",
	/* 0x44 */	"/D",
	/* 0x45 */	"/E",
	/* 0x46 */	"/F",
	/* 0x47 */	"/G",
	/* 0x48 */	"/H",
	/* 0x49 */	"/I",
	/* 0x4a */	"/J",
	/* 0x4b */	"/K",
	/* 0x4c */	"/L",
	/* 0x4d */	"/M",
	/* 0x4e */	"/N",
	/* 0x4f */	"/O",
	/* 0x50 */	"/P",
	/* 0x51 */	"/Q",
	/* 0x52 */	"/R",
	/* 0x53 */	"/S",
	/* 0x54 */	"/T",
	/* 0x55 */	"/U",
	/* 0x56 */	"/V",
	/* 0x57 */	"/W",
	/* 0x58 */	"/X",
	/* 0x59 */	"/Y",
	/* 0x5a */	"/Z",
	/* 0x5b */	"/bracketleft",
	/* 0x5c */	"/backslash",
	/* 0x5d */	"/bracketright",
	/* 0x5e */	"/asciicircum",
	/* 0x5f */	"/underscore",
	/* 0x60 */	"/quoteleft",
	/* 0x61 */	"/a",
	/* 0x62 */	"/b",
	/* 0x63 */	"/c",
	/* 0x64 */	"/d",
	/* 0x65 */	"/e",
	/* 0x66 */	"/f",
	/* 0x67 */	"/g",
	/* 0x68 */	"/h",
	/* 0x69 */	"/i",
	/* 0x6a */	"/j",
	/* 0x6b */	"/k",
	/* 0x6c */	"/l",
	/* 0x6d */	"/m",
	/* 0x6e */	"/n",
	/* 0x6f */	"/o",
	/* 0x70 */	"/p",
	/* 0x71 */	"/q",
	/* 0x72 */	"/r",
	/* 0x73 */	"/s",
	/* 0x74 */	"/t",
	/* 0x75 */	"/u",
	/* 0x76 */	"/v",
	/* 0x77 */	"/w",
	/* 0x78 */	"/x",
	/* 0x79 */	"/y",
	/* 0x7a */	"/z",
	/* 0x7b */	"/braceleft",
	/* 0x7c */	"/bar",
	/* 0x7d */	"/braceright",
	/* 0x7e */	"/tilde",
	/* 0x7f */	"non-printable",
	/* 0x80 */	"non-printable",
	/* 0x81 */	"non-printable",
	/* 0x82 */	"non-printable",
	/* 0x83 */	"non-printable",
	/* 0x84 */	"non-printable",
	/* 0x85 */	"non-printable",
	/* 0x86 */	"non-printable",
	/* 0x87 */	"non-printable",
	/* 0x88 */	"non-printable",
	/* 0x89 */	"non-printable",
	/* 0x8a */	"non-printable",
	/* 0x8b */	"non-printable",
	/* 0x8c */	"non-printable",
	/* 0x8d */	"non-printable",
	/* 0x8e */	"non-printable",
	/* 0x8f */	"non-printable",
	/* 0x90 */	"non-printable",
	/* 0x91 */	"non-printable",
	/* 0x92 */	"non-printable",
	/* 0x93 */	"non-printable",
	/* 0x94 */	"non-printable",
	/* 0x95 */	"non-printable",
	/* 0x96 */	"non-printable",
	/* 0x97 */	"non-printable",
	/* 0x98 */	"non-printable",
	/* 0x99 */	"non-printable",
	/* 0x9a */	"non-printable",
	/* 0x9b */	"non-printable",
	/* 0x9c */	"non-printable",
	/* 0x9d */	"non-printable",
	/* 0x9e */	"non-printable",
	/* 0x9f */	"non-printable",
	/* 0xa0 */	"/space",
	/* 0xa1 */	"/exclamdown",
	/* 0xa2 */	"/cent",
	/* 0xa3 */	"/sterling",
	/* 0xa4 */	"/currency",
	/* 0xa5 */	"/yen",
	/* 0xa6 */	"/brokenbar",
	/* 0xa7 */	"/section",
	/* 0xa8 */	"/dieresis",
	/* 0xa9 */	"/copyright",
	/* 0xaa */	"/ordfeminine",
	/* 0xab */	"/guillemotleft",
	/* 0xac */	"/logicalnot",
	/* 0xad */	"/hyphen",
	/* 0xae */	"/registered",
	/* 0xaf */	"/macron",
	/* 0xb0 */	"/degree",
	/* 0xb1 */	"/plusminus",
	/* 0xb2 */	"/twosuperior",
	/* 0xb3 */	"/threesuperior",
	/* 0xb4 */	"/acute",
	/* 0xb5 */	"/mu",
	/* 0xb6 */	"/paragraph",
	/* 0xb7 */	"/bullet",
	/* 0xb8 */	"/cedilla",
	/* 0xb9 */	"/dotlessi",
	/* 0xba */	"/ordmasculine",
	/* 0xbb */	"/guillemotright",
	/* 0xbc */	"/onequarter",
	/* 0xbd */	"/onehalf",
	/* 0xbe */	"/threequarters",
	/* 0xbf */	"/questiondown",
	/* 0xc0 */	"/Agrave",
	/* 0xc1 */	"/Aacute",
	/* 0xc2 */	"/Acircumflex",
	/* 0xc3 */	"/Atilde",
	/* 0xc4 */	"/Adieresis",
	/* 0xc5 */	"/Aring",
	/* 0xc6 */	"/AE",
	/* 0xc7 */	"/Ccedilla",
	/* 0xc8 */	"/Egrave",
	/* 0xc9 */	"/Eacute",
	/* 0xca */	"/Ecircumflex",
	/* 0xcb */	"/Edieresis",
	/* 0xcc */	"/Igrave",
	/* 0xcd */	"/Iacute",
	/* 0xce */	"/Icircumflex",
	/* 0xcf */	"/Idieresis",
	/* 0xd0 */	"/Eth",
	/* 0xd1 */	"/Ntilde",
	/* 0xd2 */	"/Ograve",
	/* 0xd3 */	"/Oacute",
	/* 0xd4 */	"/Ocircumflex",
	/* 0xd5 */	"/Otilde",
	/* 0xd6 */	"/Odieresis",
	/* 0xd7 */	"/multiply",
	/* 0xd8 */	"/Oslash",
	/* 0xd9 */	"/Ugrave",
	/* 0xda */	"/Uacute",
	/* 0xdb */	"/Ucircumflex",
	/* 0xdc */	"/Udieresis",
	/* 0xdd */	"/Yacute",
	/* 0xde */	"/Thorn",
	/* 0xdf */	"/germandbls",
	/* 0xe0 */	"/agrave",
	/* 0xe1 */	"/aacute",
	/* 0xe2 */	"/acircumflex",
	/* 0xe3 */	"/atilde",
	/* 0xe4 */	"/adieresis",
	/* 0xe5 */	"/aring",
	/* 0xe6 */	"/ae",
	/* 0xe7 */	"/ccedilla",
	/* 0xe8 */	"/egrave",
	/* 0xe9 */	"/eacute",
	/* 0xea */	"/ecircumflex",
	/* 0xeb */	"/edieresis",
	/* 0xec */	"/igrave",
	/* 0xed */	"/iacute",
	/* 0xee */	"/icircumflex",
	/* 0xef */	"/idieresis",
	/* 0xf0 */	"/eth",
	/* 0xf1 */	"/ntilde",
	/* 0xf2 */	"/ograve",
	/* 0xf3 */	"/oacute",
	/* 0xf4 */	"/ocircumflex",
	/* 0xf5 */	"/otilde",
	/* 0xf6 */	"/odieresis",
	/* 0xf7 */	"/divide",
	/* 0xf8 */	"/oslash",
	/* 0xf9 */	"/ugrave",
	/* 0xfa */	"/uacute",
	/* 0xfb */	"/ucircumflex",
	/* 0xfc */	"/udieresis",
	/* 0xfd */	"/yacute",
	/* 0xfe */	"/thorn",
	/* 0xff */	"/ydieresis"
};

/*
 * Process the encoding
 */
static void
DoEncoding(void)
{
	int	i, j;

	if (! afm)
		return;

	for (i=0; i<afm->StartCharMetrics; i++) {
		afm->charMetrics[i].code = -1;

		for (j=0; j<256; j++) {
			if (strcmp(IsoLatin1Encoding[j] + 1, afm->charMetrics[i].name) == 0) {
				afm->charMetrics[i].code = j;
				break;
			}
		}
	}

	/* Build the quick access */
	for (i=0; i<256; i++)
		afm->charIndex[i] = -1;
	for (i=0; i<afm->StartCharMetrics; i++)
		if (afm->charMetrics[i].code > 0 && afm->charMetrics[i].code < 256)
			afm->charIndex[afm->charMetrics[i].code] = i;
}

void
AfmSetEncoding(const char *enc)
{
#if 0
	fprintf(stderr, "AfmSetEncoding(%s)\n", enc);
#endif
	DoEncoding();

	/* FIX ME */
}


syntax highlighted by Code2HTML, v. 0.9.1