#include "afmint.h"
#include "afm.h"
static char *default_path = "/usr/local/lib/ps:/usr/lib/ps";
static char *error_names[] =
{
"AFM Success",
"AFM Error",
"out of memory",
"illegal argument",
"unknown font",
"syntax error",
"unsupported format",
"file IO failed",
"file is not an AFM file",
};
static void read_font_map ___P ((AFMHandle handle, char *name));
static void apply_encoding ___P ((AFMFont font, AFMEncodingTable *enc,
unsigned int flags));
void
afm_error_to_string (AFMError error, char *buf)
{
char *syserr;
int code, syserrno;
code = error & 0xffff;
syserrno = (error >> 16) & 0xffff;
if (syserrno)
syserr = strerror (syserrno);
else
syserr = NULL;
if (code >= NUM_ERRORS)
{
sprintf (buf, "afm_error_to_string(): illegal error code: %d\n",
error);
return;
}
if (code == 0)
sprintf (buf, "AFM Success");
else if (code == 1)
sprintf (buf, "%s%s%s", "AFM Error",
syserr ? ":" : "",
syserr ? syserr : "");
else
sprintf (buf, "AFM Error: %s%s%s", error_names[code],
syserr ? ": " : "",
syserr ? syserr : "");
}
AFMError
afm_create (const char *path, unsigned int verbose_level,
AFMHandle *handle_return)
{
AFMHandle handle;
AFMError error = AFM_SUCCESS;
const char *cp, *cp2;
int len;
char buf[512];
struct stat stat_st;
handle = (AFMHandle) calloc (1, sizeof (*handle));
if (handle == NULL)
{
error = AFM_ERROR_MEMORY;
goto error_out;
}
handle->font_map = strhash_init ();
if (handle->font_map == NULL)
{
error = AFM_ERROR_MEMORY;
goto error_out;
}
handle->verbose = verbose_level;
if (path == NULL)
path = default_path;
afm_message (handle, 1, "AFM: scanning path...\n");
for (cp = path; cp; cp = strchr (cp, PATH_SEPARATOR))
{
if (cp != path)
cp++;
cp2 = strchr (cp, PATH_SEPARATOR);
if (cp2)
len = cp2 - cp;
else
len = strlen (cp);
memcpy (buf, cp, len);
buf[len] = '\0';
if (len > 0 && buf[len - 1] == '/')
buf[len - 1] = '\0';
strcat (buf, "/font.map");
if (stat (buf, &stat_st) == 0)
read_font_map (handle, buf);
}
*handle_return = handle;
return AFM_SUCCESS;
error_out:
(void) afm_destroy (handle);
return error;
}
AFMError
afm_destroy (AFMHandle handle)
{
char *key;
int keylen;
char *cp;
if (handle == NULL)
return AFM_ERROR_ARGUMENT;
while (strhash_get_first (handle->font_map, &key, &keylen, (void *) &cp))
free (cp);
strhash_free (handle->font_map);
free (handle);
return AFM_SUCCESS;
}
AFMError
afm_set_verbose (AFMHandle handle, unsigned int level)
{
if (handle == NULL)
return AFM_ERROR_ARGUMENT;
handle->verbose = level;
return AFM_SUCCESS;
}
AFMError
afm_font_prefix (AFMHandle handle, const char *fontname,
const char **prefix_return)
{
char *filename;
if (handle == NULL || fontname == NULL || prefix_return == NULL)
return AFM_ERROR_ARGUMENT;
if (!strhash_get (handle->font_map, fontname, strlen (fontname),
(void *) &filename))
return AFM_ERROR_UNKNOWN_FONT;
*prefix_return = filename;
return AFM_SUCCESS;
}
AFMError
afm_open_font (AFMHandle handle, unsigned int info_level,
const char *fontname, AFMFont *font_return)
{
char *filename;
char fname[512];
if (handle == NULL || fontname == NULL)
return AFM_ERROR_ARGUMENT;
if (!strhash_get (handle->font_map, fontname, strlen (fontname),
(void *) &filename))
return AFM_ERROR_UNKNOWN_FONT;
sprintf (fname, "%s.afm", filename);
return afm_open_file (handle, info_level, fname, font_return);
}
AFMError
afm_open_file (AFMHandle handle, unsigned int info_level,
const char *filename, AFMFont *font_return)
{
AFMFont font;
AFMError error = AFM_SUCCESS;
if (handle == NULL || filename == NULL)
return AFM_ERROR_ARGUMENT;
font = (AFMFont) calloc (1, sizeof (*font));
if (font == NULL)
return AFM_ERROR_MEMORY;
font->private
= (struct afm_font_private_data_st *) calloc (1, sizeof (*font->private));
if (font->private == NULL)
{
error = AFM_ERROR_MEMORY;
goto error_out;
}
font->private->fontnames = strhash_init ();
if (font->private->fontnames == NULL)
{
error = AFM_ERROR_MEMORY;
goto error_out;
}
font->private->compositenames = strhash_init ();
if (font->private->compositenames == NULL)
{
error = AFM_ERROR_MEMORY;
goto error_out;
}
font->info_level = info_level;
if (setjmp (handle->jmpbuf))
{
error = handle->parse_error;
goto error_out;
}
else
{
afm_parse_file (handle, filename, font);
}
*font_return = font;
return AFM_SUCCESS;
error_out:
(void) afm_close_font (font);
return error;
}
#define FREE(ptr) if (ptr) free (ptr)
AFMError
afm_close_font (AFMFont font)
{
int i;
if (font == NULL)
return AFM_ERROR_ARGUMENT;
FREE (font->global_info.FontName);
FREE (font->global_info.FullName);
FREE (font->global_info.FamilyName);
FREE (font->global_info.Weight);
FREE (font->global_info.Version);
FREE (font->global_info.Notice);
FREE (font->global_info.EncodingScheme);
FREE (font->global_info.CharacterSet);
for (i = 0; i < font->num_character_metrics; i++)
FREE (font->character_metrics[i].name);
FREE (font->character_metrics);
for (i = 0; i < font->num_composites; i++)
FREE (font->composites[i].name);
FREE (font->composites);
for (i = 0; i < font->num_kern_pairs; i++)
{
FREE (font->kern_pairs[i].name1);
FREE (font->kern_pairs[i].name2);
}
FREE (font->kern_pairs);
FREE (font->track_kerns);
strhash_free (font->private->fontnames);
strhash_free (font->private->compositenames);
free (font);
return AFM_SUCCESS;
}
#define STR(str) (str ? str : "")
#define BOOL(val) (val ? "true" : "false")
void
afm_font_dump (FILE *fp, AFMFont font)
{
int i;
fprintf (fp, "AFM Format Specification version: %g\n", font->version);
fprintf (fp, "Global Font Information\n");
fprintf (fp, " FontName:\t%s\n", STR (font->global_info.FontName));
fprintf (fp, " FullName:\t%s\n", STR (font->global_info.FullName));
fprintf (fp, " FamilyName:\t%s\n", STR (font->global_info.FamilyName));
fprintf (fp, " Weight:\t%s\n", STR (font->global_info.Weight));
fprintf (fp, " FontBBox:\t%g %g %g %g\n",
font->global_info.FontBBox_llx, font->global_info.FontBBox_lly,
font->global_info.FontBBox_urx, font->global_info.FontBBox_ury);
fprintf (fp, " Version:\t%s\n", STR (font->global_info.Version));
fprintf (fp, " Notice:\t%s\n", STR (font->global_info.Notice));
fprintf (fp, " EncodingScheme:\t%s\n",
STR (font->global_info.EncodingScheme));
fprintf (fp, " MappingScheme:\t%ld\n", font->global_info.MappingScheme);
fprintf (fp, " EscChar:\t%ld\n", font->global_info.EscChar);
fprintf (fp, " CharacterSet:\t%s\n", STR (font->global_info.CharacterSet));
fprintf (fp, " Characters:\t%ld\n", font->global_info.Characters);
fprintf (fp, " IsBaseFont:\t%s\n", BOOL(font->global_info.IsBaseFont));
fprintf (fp, " VVector:\t%g %g\n",
font->global_info.VVector_0, font->global_info.VVector_1);
fprintf (fp, " IsFixedV:\t%s\n", BOOL(font->global_info.IsFixedV));
fprintf (fp, " CapHeight:\t%g\n", font->global_info.CapHeight);
fprintf (fp, " XHeight:\t%g\n", font->global_info.XHeight);
fprintf (fp, " Ascender:\t%g\n", font->global_info.Ascender);
fprintf (fp, " Descender:\t%g\n", font->global_info.Descender);
for (i = 0; i < 2; i++)
if (font->writing_direction_metrics[i].is_valid)
{
fprintf (fp, "Writing Direction %d\n", i);
fprintf (fp, " UnderlinePosition: %g\n",
font->writing_direction_metrics[i].UnderlinePosition);
fprintf (fp, " UnderlineThickness: %g\n",
font->writing_direction_metrics[i].UnderlineThickness);
fprintf (fp, " ItalicAngle: %g\n",
font->writing_direction_metrics[i].ItalicAngle);
fprintf (fp, " CharWidth: %g %g\n",
font->writing_direction_metrics[i].CharWidth_x,
font->writing_direction_metrics[i].CharWidth_y);
fprintf (fp, " IsFixedPitch: %s\n",
BOOL (font->writing_direction_metrics[i].IsFixedPitch));
}
fprintf (fp, "Individual Character Metrics %ld\n",
font->num_character_metrics);
for (i = 0; i < font->num_character_metrics; i++)
{
AFMIndividualCharacterMetrics *cm;
cm = &font->character_metrics[i];
fprintf (fp, " C %ld ; N %s ; B %g %g %g %g\n",
cm->character_code, STR (cm->name),
cm->llx, cm->lly, cm->urx, cm->ury);
fprintf (fp,
" W0X %g ; W0Y %g ; W1X %g ; W1Y %g ; VV %g %g\n",
cm->w0x, cm->w0y, cm->w1x, cm->w1y, cm->vv_x, cm->vv_y);
}
fprintf (fp, "Composite Character Data %ld\n", font->num_composites);
for (i = 0; i < font->num_composites; i++)
{
AFMComposite *cm;
int j;
cm = &font->composites[i];
fprintf (fp, " CC %s %ld", cm->name, cm->num_components);
for (j = 0; j < cm->num_components; j++)
fprintf (fp, " ; PCC %s %g %g",
cm->components[j].name,
cm->components[j].deltax,
cm->components[j].deltay);
fprintf (fp, "\n");
}
fprintf (fp, "Pair-Wise Kerning %ld\n", font->num_kern_pairs);
for (i = 0; i < font->num_kern_pairs; i++)
{
AFMPairWiseKerning *kp;
kp = &font->kern_pairs[i];
fprintf (fp, " KP %s %s %g %g\n", STR (kp->name1), STR (kp->name2),
kp->kx, kp->ky);
}
fprintf (fp, "Track Kerning %ld\n", font->num_track_kerns);
for (i = 0; i < font->num_track_kerns; i++)
{
AFMTrackKern *tk;
tk = &font->track_kerns[i];
fprintf (fp, " TrackKern %ld %g %g %g %g\n", tk->degree,
tk->min_ptsize, tk->min_kern,
tk->max_ptsize, tk->max_kern);
}
}
AFMError
afm_font_stringwidth (AFMFont font, AFMNumber ptsize, char *string,
unsigned int stringlen, AFMNumber *w0x_return,
AFMNumber *w0y_return)
{
unsigned int i;
AFMNumber x = 0.0;
AFMNumber y = 0.0;
AFMIndividualCharacterMetrics *cm;
if (!font || !string || !font->writing_direction_metrics[0].is_valid)
return AFM_ERROR_ARGUMENT;
if (font->writing_direction_metrics[0].IsFixedPitch)
{
x = stringlen * font->writing_direction_metrics[0].CharWidth_x;
y = stringlen * font->writing_direction_metrics[0].CharWidth_y;
}
else
{
for (i = 0; i < stringlen; i++)
{
cm = font->encoding[(unsigned char) string[i]];
if (cm == AFM_ENC_NONE || cm == AFM_ENC_NON_EXISTENT)
{
x += font->private->undef->w0x;
y += font->private->undef->w0y;
}
else
{
x += cm->w0x;
y += cm->w0y;
}
}
}
*w0x_return = x / UNITS_PER_POINT * ptsize;
*w0y_return = y / UNITS_PER_POINT * ptsize;
return AFM_SUCCESS;
}
AFMError
afm_font_charwidth (AFMFont font, AFMNumber ptsize, char ch,
AFMNumber *w0x_return, AFMNumber *w0y_return)
{
AFMNumber x = 0.0;
AFMNumber y = 0.0;
AFMIndividualCharacterMetrics *cm;
if (!font || !font->writing_direction_metrics[0].is_valid)
return AFM_ERROR_ARGUMENT;
if (font->writing_direction_metrics[0].IsFixedPitch)
{
x = font->writing_direction_metrics[0].CharWidth_x;
y = font->writing_direction_metrics[0].CharWidth_y;
}
else
{
cm = font->encoding[(unsigned char) ch];
if (cm == AFM_ENC_NONE || cm == AFM_ENC_NON_EXISTENT)
{
x = font->private->undef->w0x;
y = font->private->undef->w0y;
}
else
{
x = cm->w0x;
y = cm->w0y;
}
}
*w0x_return = x / UNITS_PER_POINT * ptsize;
*w0y_return = y / UNITS_PER_POINT * ptsize;
return AFM_SUCCESS;
}
AFMError
afm_font_encode (AFMFont font, unsigned char code, char *name,
unsigned int flags)
{
AFMIndividualCharacterMetrics *cm;
AFMComposite *comp;
if (font == NULL)
return AFM_ERROR_ARGUMENT;
if (name)
{
if (!strhash_get (font->private->fontnames, name, strlen (name),
(void *) &cm))
{
if ((flags & AFM_ENCODE_ACCEPT_COMPOSITES) == 0
|| strhash_get (font->private->compositenames, name,
strlen (name), (void *) &comp) == 0)
cm = AFM_ENC_NON_EXISTENT;
else
{
if (!strhash_get (font->private->fontnames,
comp->components[0].name,
strlen (comp->components[0].name),
(void *) &cm))
cm = AFM_ENC_NON_EXISTENT;
}
}
}
else
cm = AFM_ENC_NONE;
font->encoding[(unsigned int) code] = cm;
return AFM_SUCCESS;
}
AFMError
afm_font_encoding (AFMFont font, AFMEncoding enc, unsigned int flags)
{
int i;
AFMIndividualCharacterMetrics *cm;
if (font == NULL)
return AFM_ERROR_ARGUMENT;
switch (enc)
{
case AFM_ENCODING_DEFAULT:
for (i = 0; i < 256; i++)
font->encoding[i] = AFM_ENC_NONE;
for (i = 0; i < font->num_character_metrics; i++)
{
cm = &font->character_metrics[i];
font->encoding[cm->character_code] = cm;
}
break;
case AFM_ENCODING_ISO_8859_1:
apply_encoding (font, afm_88591_encoding, flags);
break;
case AFM_ENCODING_ISO_8859_2:
apply_encoding (font, afm_88592_encoding, flags);
break;
case AFM_ENCODING_ISO_8859_3:
apply_encoding (font, afm_88593_encoding, flags);
break;
case AFM_ENCODING_ISO_8859_4:
apply_encoding (font, afm_88594_encoding, flags);
break;
case AFM_ENCODING_ISO_8859_5:
apply_encoding (font, afm_88595_encoding, flags);
break;
case AFM_ENCODING_ISO_8859_7:
apply_encoding (font, afm_88597_encoding, flags);
break;
case AFM_ENCODING_IBMPC:
apply_encoding (font, afm_ibmpc_encoding, flags);
break;
case AFM_ENCODING_ASCII:
apply_encoding (font, afm_88591_encoding, flags);
for (i = 128; i < 256; i++)
font->encoding[i] = AFM_ENC_NONE;
break;
case AFM_ENCODING_MAC:
apply_encoding (font, afm_mac_encoding, flags);
break;
case AFM_ENCODING_VMS:
apply_encoding (font, afm_vms_encoding, flags);
break;
case AFM_ENCODING_HP8:
apply_encoding (font, afm_hp8_encoding, flags);
break;
case AFM_ENCODING_KOI8:
apply_encoding (font, afm_koi8_encoding, flags);
break;
}
return AFM_SUCCESS;
}
void
afm_message (AFMHandle handle, int level, char *message)
{
if (handle->verbose < level)
return;
fprintf (stderr, "%s", message);
}
void
afm_error (AFMHandle handle, char *message)
{
fprintf (stderr, "AFM Error: %s\n", message);
}
static void
read_font_map (AFMHandle handle, char *name)
{
FILE *fp;
char buf[512];
char fullname[512];
unsigned int dirlen;
char *cp, *cp2;
char msg[256];
sprintf (msg, "AFM: reading font map \"%s\"\n", name);
afm_message (handle, 1, msg);
fp = fopen (name, "r");
if (fp == NULL)
{
sprintf (msg, "AFM: couldn't open font map \"%s\": %s\n", name,
strerror (errno));
afm_message (handle, 1, msg);
return;
}
cp = strrchr (name, '/');
if (cp)
{
dirlen = cp - name + 1;
memcpy (fullname, name, dirlen);
}
else
{
dirlen = 2;
memcpy (fullname, "./", dirlen);
}
while (fgets (buf, sizeof (buf), fp))
{
char font[256];
char file[256];
if (sscanf (buf, "%s %s", font, file) != 2)
{
sprintf (msg, "malformed line in font map \"%s\":\n%s",
name, buf);
afm_error (handle, msg);
continue;
}
if (strhash_get (handle->font_map, font, strlen (font), (void *) &cp))
continue;
strcpy (fullname + dirlen, file);
cp = (char *) malloc (strlen (fullname) + 1);
if (cp == NULL)
{
afm_error (handle, "couldn't add font: out of memory");
goto out;
}
strcpy (cp, fullname);
sprintf (msg, "AFM: font mapping: %s -> %s\n", font, cp);
afm_message (handle, 2, msg);
(void) strhash_put (handle->font_map, font, strlen (font), cp,
(void *) &cp2);
}
out:
fclose (fp);
}
static void
apply_encoding (AFMFont font, AFMEncodingTable *enc, unsigned int flags)
{
int i;
AFMIndividualCharacterMetrics *cm;
AFMComposite *comp;
for (i = 0; enc[i].code >= 0; i++)
{
if (enc[i].character == AFM_ENC_NONE)
font->encoding[enc[i].code] = AFM_ENC_NONE;
else if (enc[i].character == AFM_ENC_NON_EXISTENT)
font->encoding[enc[i].code] = AFM_ENC_NON_EXISTENT;
else
{
if (strhash_get (font->private->fontnames, enc[i].character,
strlen (enc[i].character), (void *) &cm))
font->encoding[enc[i].code] = cm;
else
{
if ((flags & AFM_ENCODE_ACCEPT_COMPOSITES) == 0
|| strhash_get (font->private->compositenames,
enc[i].character, strlen (enc[i].character),
(void *) &comp) == 0)
font->encoding[enc[i].code] = AFM_ENC_NON_EXISTENT;
else
{
if (strhash_get (font->private->fontnames,
comp->components[0].name,
strlen (comp->components[0].name),
(void *) &cm))
font->encoding[enc[i].code] = cm;
else
font->encoding[enc[i].code] = AFM_ENC_NON_EXISTENT;
}
}
}
}
}