-/* PSPP - computes sample statistics.
+/* PSPP - a program for statistical analysis.
Copyright (C) 2006 Free Software Foundation, Inc.
- Written by Ben Pfaff <blp@gnu.org>.
- 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 of the
- License, or (at your option) any later version.
+ 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 of the License, 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.
+ 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include "afm.h"
#include "c-strtod.h"
#include <ctype.h>
#include <errno.h>
+#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <setjmp.h>
#include "error.h"
#include "minmax.h"
+#include <libpspp/assertion.h>
#include <libpspp/pool.h>
#include <libpspp/str.h>
#define _(msgid) gettext (msgid)
/* A kern pair entry. */
-struct afm_kern_pair
+struct afm_kern_pair
{
struct afm_character *successor; /* Second character. */
int adjust; /* Adjustment. */
};
/* A ligature. */
-struct afm_ligature
+struct afm_ligature
{
struct afm_character *successor; /* Second character. */
struct afm_character *ligature; /* Resulting ligature. */
};
/* How to map between byte strings and character values. */
-enum mapping_scheme
+enum mapping_scheme
{
MAP_UNKNOWN, /* Not yet determined. */
MAP_ONE_BYTE, /* 8-bit coding. */
};
/* AFM file parser. */
-struct parser
+struct parser
{
struct pool *pool; /* Containing pool. */
struct afm *afm; /* AFM being parsed. */
/* Reads FILE_NAME as an AFM file and returns the metrics data.
Returns a null pointer if the file cannot be parsed. */
struct afm *
-afm_open (const char *file_name)
+afm_open (const char *file_name)
{
struct afm *volatile afm;
struct parser *parser;
parser->file = pool_fopen (parser->pool, file_name, "r");
parser->file_name = file_name;
parser->line_number = 0;
- if (parser->file == NULL)
+ if (parser->file == NULL)
{
error (0, errno, _("opening font metrics file \"%s\""), file_name);
- goto error;
+ goto error;
}
if (setjmp (parser->bail_out))
/* Creates and returns an empty set of metrics. */
static struct afm *
-create_afm (void)
+create_afm (void)
{
struct afm *afm;
struct afm_character *def_char;
afm->shift_out = 14;
afm->shift_in = 15;
def_char = create_character (afm);
- for (i = 0; i < 256; i++)
+ for (i = 0; i < 256; i++)
afm->undefined_codes[i] = def_char;
- for (i = 0; i < 256; i++)
- afm->codes[i] = afm->undefined_codes;
+ for (i = 0; i < 256; i++)
+ afm->codes[i] = afm->undefined_codes;
afm->chars = NULL;
afm->char_cnt = 0;
/* Creates and returns an initialized character within AFM. */
static struct afm_character *
-create_character (struct afm *afm)
+create_character (struct afm *afm)
{
struct afm_character *c = pool_alloc (afm->pool, sizeof *c);
c->code = ' ';
/* Reports the given MESSAGE at the current line in parser P
and bails out with longjmp(). */
static void
-afm_error (struct parser *p, const char *message, ...)
+afm_error (struct parser *p, const char *message, ...)
{
va_list args;
char *msg;
/* Parses an AFM file with parser P. */
static void
-parse_afm (struct parser *p)
+parse_afm (struct parser *p)
{
char *key;
do
{
key = parse_key (p);
- if (!strcmp (key, "FontName"))
+ if (!strcmp (key, "FontName"))
p->afm->findfont_name = pool_strdup (p->afm->pool,
force_get_string (p));
else if (!strcmp (key, "Ascender"))
p->afm->ascent = force_get_integer (p);
else if (!strcmp (key, "Descender"))
p->afm->descent = force_get_integer (p);
- else if (!strcmp (key, "MappingScheme"))
+ else if (!strcmp (key, "MappingScheme"))
{
int scheme = force_get_integer (p);
if (scheme == 4)
else if (!strcmp (key, "StartKernPairs")
|| !strcmp (key, "StartKernPairs0"))
parse_kern_pairs (p);
- else if (!strcmp (key, "StartTrackKern"))
+ else if (!strcmp (key, "StartTrackKern"))
skip_section (p, "EndTrackKern");
- else if (!strcmp (key, "StartComposites"))
+ else if (!strcmp (key, "StartComposites"))
skip_section (p, "EndComposites");
- else
+ else
skip_line (p);
}
while (strcmp (key, "EndFontMetrics"));
if (p->afm->findfont_name == NULL)
afm_error (p, _("required FontName is missing"));
- if (p->afm->mapping == MAP_UNKNOWN)
+ if (p->afm->mapping == MAP_UNKNOWN)
{
/* There seem to be a number of fonts out there that use a
2-byte encoding but don't announce it with
{
const char *key;
skip_line (p);
- do
+ do
{
key = parse_key (p);
skip_line (p);
metrics. Set 0 is for normal text, other sets are for
vertical text, etc. We only care about set 0.) */
static bool
-parse_set_specific (struct parser *p, const char *end_key)
+parse_set_specific (struct parser *p, const char *end_key)
{
int set;
-
- if (get_integer (p, &set) && set != 0)
+
+ if (get_integer (p, &set) && set != 0)
{
skip_section (p, end_key);
- return false;
+ return false;
}
- else
+ else
{
force_eol (p);
return true;
/* Parses a StartDirection...EndDirection section in parser P. */
static void
-parse_direction (struct parser *p)
+parse_direction (struct parser *p)
{
const char *key;
if (!parse_set_specific (p, "EndDirection"))
return;
- do
+ do
{
key = parse_key (p);
- if (!strcmp (key, "CharWidth"))
+ if (!strcmp (key, "CharWidth"))
p->afm->codes[0][0]->width = force_get_integer (p);
skip_line (p);
}
/* Parses a StartCharMetrics...EndCharMetrics section in parser
P. */
static void
-parse_char_metrics (struct parser *p)
+parse_char_metrics (struct parser *p)
{
- struct parsing_ligature
+ struct parsing_ligature
{
struct afm_character *first;
char *successor;
size_t i;
skip_line (p);
-
+
for (;;)
{
char *key;
key = parse_key (p);
if (!strcmp (key, "EndCharMetrics"))
break;
-
+
if (p->afm->char_cnt == p->char_allocated)
p->afm->chars = pool_2nrealloc (p->afm->pool, p->afm->chars,
&p->char_allocated,
sizeof *p->afm->chars);
c = create_character (p->afm);
- if (!strcmp (key, "C"))
+ if (!strcmp (key, "C"))
c->code = force_get_integer (p);
else if (!strcmp (key, "CH"))
c->code = force_get_hex_code (p);
c->code = -1;
if (c->code > p->max_code)
- p->max_code = c->code;
+ p->max_code = c->code;
p->afm->chars[p->afm->char_cnt++] = c;
if (c->code != -1)
p->afm->codes[c->code >> 8][c->code & 0xff] = c;
key = force_get_word (p);
- while (!strcmp (key, ";"))
+ while (!strcmp (key, ";"))
{
if (!get_word (p, &key))
break;
c->name = force_get_word (p);
else if (!strcmp (key, "WX") || !strcmp (key, "W0X"))
c->width = force_get_number (p);
- else if (!strcmp (key, "W") || !strcmp (key, "W0"))
+ else if (!strcmp (key, "W") || !strcmp (key, "W0"))
{
c->width = force_get_number (p);
force_get_number (p);
}
- else if (!strcmp (key, "B"))
+ else if (!strcmp (key, "B"))
{
int llx, lly, urx, ury;
llx = force_get_number (p);
c->ascent = MAX (0, ury);
c->descent = MAX (0, -lly);
}
- else if (!strcmp (key, "L"))
+ else if (!strcmp (key, "L"))
{
struct parsing_ligature *ligature;
if (ligature_cnt == ligature_allocated)
ligature->successor = force_get_word (p);
ligature->ligature = force_get_word (p);
}
- else
+ else
{
while (strcmp (key, ";"))
key = force_get_word (p);
}
skip_line (p);
- for (i = 0; i < ligature_cnt; i++)
+ for (i = 0; i < ligature_cnt; i++)
{
struct parsing_ligature *src = &ligatures[i];
struct afm_ligature *dst;
/* Parses a StartKernPairs...EndKernPairs section in parser P. */
static void
-parse_kern_pairs (struct parser *p)
+parse_kern_pairs (struct parser *p)
{
char *key;
-
+
skip_line (p);
do
int adjust;
key = parse_key (p);
- if (!strcmp (key, "KP") || !strcmp (key, "KPX"))
+ if (!strcmp (key, "KP") || !strcmp (key, "KPX"))
{
c1 = get_char_by_name (p, force_get_word (p));
c2 = get_char_by_name (p, force_get_word (p));
force_get_number (p);
add_kern_pair (p, c1, c2, adjust);
}
- else if (!strcmp (key, "KPH"))
+ else if (!strcmp (key, "KPH"))
{
c1 = get_char_by_code (p->afm, force_get_hex_code (p));
c2 = get_char_by_code (p->afm, force_get_hex_code (p));
to the metrics within parser P. */
static void
add_kern_pair (struct parser *p, struct afm_character *first,
- struct afm_character *second, int adjust)
+ struct afm_character *second, int adjust)
{
struct afm_kern_pair *kp;
-
+
first->kern_pairs = pool_nrealloc (p->afm->pool, first->kern_pairs,
first->kern_pair_cnt + 1,
sizeof *first->kern_pairs);
parser P. Reports an error if no character has the given
name. */
static struct afm_character *
-get_char_by_name (struct parser *p, const char *name)
+get_char_by_name (struct parser *p, const char *name)
{
size_t i;
- for (i = 0; i < p->afm->char_cnt; i++)
+ for (i = 0; i < p->afm->char_cnt; i++)
{
struct afm_character *c = p->afm->chars[i];
if (c->name != NULL && !strcmp (c->name, name))
Returns a default character if the font doesn't have a
character with that code. */
static struct afm_character *
-get_char_by_code (const struct afm *afm, int code_)
+get_char_by_code (const struct afm *afm, int code_)
{
uint16_t code = code_;
return afm->codes[code >> 8][code & 0xff];
\f
/* Skips white space, except for new-lines, within parser P. */
static int
-skip_spaces (struct parser *p)
+skip_spaces (struct parser *p)
{
int c;
while (isspace (c = getc (p->file)) && c != '\n')
Skips comments.
Reports an error if not at the beginning of a line. */
static char *
-parse_key (struct parser *p)
+parse_key (struct parser *p)
{
force_eol (p);
- for (;;)
+ for (;;)
{
char *key;
- do
+ do
{
p->line_number++;
- getc (p->file);
+ getc (p->file);
}
while (skip_spaces (p) == '\n');
key = force_get_word (p);
- if (strcmp (key, "Comment"))
+ if (strcmp (key, "Comment"))
return key;
skip_line (p);
/* Skips to the next line within parser P. */
static void
-skip_line (struct parser *p)
+skip_line (struct parser *p)
{
- for (;;)
+ for (;;)
{
int c = getc (p->file);
if (c == EOF)
/* Ensures that parser P is at the end of a line. */
static void
-force_eol (struct parser *p)
+force_eol (struct parser *p)
{
if (skip_spaces (p) != '\n')
afm_error (p, _("syntax error expecting end of line"));
}
-
+
/* Tries to read an integer into *INTEGER at the current position
in parser P.
Returns success. */
static bool
-get_integer (struct parser *p, int *integer)
+get_integer (struct parser *p, int *integer)
{
int c = skip_spaces (p);
- if (isdigit (c) || c == '-')
+ if (isdigit (c) || c == '-')
{
char *tail;
long tmp;
errno = 0;
tmp = strtol (force_get_word (p), &tail, 10);
- if (errno == ERANGE || tmp < INT_MIN || tmp > INT_MAX)
+ if (errno == ERANGE || tmp < INT_MIN || tmp > INT_MAX)
afm_error (p, _("number out of valid range"));
if (*tail != '\0')
afm_error (p, _("invalid numeric syntax"));
/* Returns an integer read from the current position in P.
Reports an error if unsuccessful. */
static int
-force_get_integer (struct parser *p)
+force_get_integer (struct parser *p)
{
int integer;
if (!get_integer (p, &integer))
in parser P. Stores the number's integer part into *INTEGER.
Returns success. */
static bool
-get_number (struct parser *p, int *integer)
+get_number (struct parser *p, int *integer)
{
int c = skip_spaces (p);
if (c == '-' || c == '.' || isdigit (c))
errno = 0;
number = c_strtod (force_get_word (p), &tail);
- if (errno == ERANGE || number < INT_MIN || number > INT_MAX)
+ if (errno == ERANGE || number < INT_MIN || number > INT_MAX)
afm_error (p, _("number out of valid range"));
if (*tail != '\0')
afm_error (p, _("invalid numeric syntax"));
the current position in P.
Reports an error if unsuccessful. */
static int
-force_get_number (struct parser *p)
+force_get_number (struct parser *p)
{
int integer;
if (!get_number (p, &integer))
*INTEGER from P.
Returns success. */
static bool
-get_hex_code (struct parser *p, int *integer)
+get_hex_code (struct parser *p, int *integer)
{
- if (skip_spaces (p) == '<')
+ if (skip_spaces (p) == '<')
{
if (fscanf (p->file, "<%x", integer) != 1 || getc (p->file) != '>')
afm_error (p, _("syntax error in hex constant"));
The word is allocated in P's pool.
Returns success. */
static bool
-get_word (struct parser *p, char **word)
+get_word (struct parser *p, char **word)
{
- if (skip_spaces (p) != '\n')
+ if (skip_spaces (p) != '\n')
{
struct string s;
int c;
pool_register (p->pool, free, *word);
return true;
}
- else
+ else
{
*word = NULL;
return false;
The word is allocated in P's pool.
Reports an error if unsuccessful. */
static char *
-force_get_word (struct parser *p)
+force_get_word (struct parser *p)
{
char *word;
if (!get_word (p, &word))
struct string s = DS_EMPTY_INITIALIZER;
skip_spaces (p);
- for (;;)
+ for (;;)
{
int c = getc (p->file);
if (c == EOF || c == '\n')
ungetc ('\n', p->file);
ds_rtrim (&s, ss_cstr (CC_SPACES));
- if (!ds_is_empty (&s))
+ if (!ds_is_empty (&s))
{
*string = ds_cstr (&s);
pool_register (p->pool, free, *string);
return true;
}
- else
+ else
{
*string = NULL;
ds_destroy (&s);
The word is allocated in P's pool.
Reports an error if the string is empty. */
static char *
-force_get_string (struct parser *p)
+force_get_string (struct parser *p)
{
char *string;
if (!get_string (p, &string))
\f
/* Closes AFM and frees its storage. */
void
-afm_close (struct afm *afm)
+afm_close (struct afm *afm)
{
if (afm != NULL)
pool_destroy (afm->pool);
/* Returns the string that must be passed to the PostScript
"findfont" operator to obtain AFM's font. */
const char *
-afm_get_findfont_name (const struct afm *afm)
+afm_get_findfont_name (const struct afm *afm)
{
return afm->findfont_name;
}
/* Returns the ascent for AFM, that is, the font's height above
the baseline, in units of 1/1000 of the nominal font size. */
int
-afm_get_ascent (const struct afm *afm)
+afm_get_ascent (const struct afm *afm)
{
return afm->ascent;
}
/* Returns the descent for AFM, that is, the font's depth below
the baseline, in units of 1/1000 of the nominal font size. */
int
-afm_get_descent (const struct afm *afm)
+afm_get_descent (const struct afm *afm)
{
return afm->descent;
}
/* Returns the character numbered CODE within AFM,
or a default character if the font has none. */
const struct afm_character *
-afm_get_character (const struct afm *afm, int code)
+afm_get_character (const struct afm *afm, int code)
{
return get_char_by_code (afm, code);
}
or a null pointer if there is no such ligature. */
const struct afm_character *
afm_get_ligature (const struct afm_character *first,
- const struct afm_character *second)
+ const struct afm_character *second)
{
size_t i;
uint8_t code = (*s)->code;
if (code != (*s)->code)
break;
-
+
if (code == '(' || code == ')' || code == '\\')
ds_put_format (out, "\\%c", code);
else if (!c_isprint (code))
ds_put_format (out, "\\%03o", code);
else
- ds_put_char (out, code);
+ ds_put_char (out, code);
}
ds_put_char (out, ')');
return n;
/* Initializes encoder E for output to OUT. */
static void
-binary_init (struct binary_encoder *e, struct string *out)
+binary_init (struct binary_encoder *e, struct string *out)
{
e->out = out;
e->b = e->n = 0;
/* Returns the character that represents VALUE in ASCII85
encoding. */
static int
-value_to_ascii85 (int value)
+value_to_ascii85 (int value)
{
assert (value >= 0 && value < 85);
#if C_CTYPE_ASCII
/* Appends the first N characters of the ASCII85 representation
of B to string OUT. */
static void
-append_ascii85_block (unsigned b, size_t n, struct string *out)
+append_ascii85_block (unsigned b, size_t n, struct string *out)
{
char c[5];
int i;
- for (i = 4; i >= 0; i--)
+ for (i = 4; i >= 0; i--)
{
c[i] = value_to_ascii85 (b % 85);
- b /= 85;
+ b /= 85;
}
ds_put_substring (out, ss_buffer (c, n));
}
/* Encodes BYTE with encoder E. */
static void
-binary_put (struct binary_encoder *e, uint8_t byte)
+binary_put (struct binary_encoder *e, uint8_t byte)
{
e->b = (e->b << 8) | byte;
e->n++;
if (e->b != 0)
append_ascii85_block (e->b, 5, e->out);
- else
+ else
ds_put_char (e->out, 'z');
}
}
/* Finishes up encoding with E. */
static void
-binary_finish (struct binary_encoder *e)
+binary_finish (struct binary_encoder *e)
{
- if (e->n >= 4)
+ if (e->n >= 4)
{
/* We output at least one complete ASCII85 block.
Finish up. */
size_t n = e->n % 4;
if (n > 0)
- append_ascii85_block (e->b << 8 * (4 - n), n + 1, e->out);
+ append_ascii85_block (e->b << 8 * (4 - n), n + 1, e->out);
ds_put_cstr (e->out, "~>");
}
else if (e->n > 0)
ds_put_cstr (e->out, "<");
b = e->b << 8 * (4 - e->n);
- for (i = 0; i < e->n; i++)
+ for (i = 0; i < e->n; i++)
{
ds_put_format (e->out, "%02x", b >> 24);
b <<= 8;
}
ds_put_cstr (e->out, ">");
}
- else
+ else
{
/* Empty string. */
- ds_put_cstr (e->out, "()");
+ ds_put_cstr (e->out, "()");
}
}
encode_two_byte (const struct afm_character **s, size_t n,
struct binary_encoder *e)
{
- for (; n > 0; s++, n--)
+ for (; n > 0; s++, n--)
{
uint16_t code = (*s)->code;
if (code != (*s)->code)
if (code != (*s)->code)
break;
- if (font_num != cur_font)
+ if (font_num != cur_font)
{
if (font_num == escape_char)
break;
if ((*s)->code & ~0x1ffff)
break;
- if (font_num != cur_font)
+ if (font_num != cur_font)
{
if (font_num == (escape_char & 0xff))
break;
if ((*s)->code & ~0x1ff)
break;
- if (font_num != cur_font)
+ if (font_num != cur_font)
{
binary_put (e, font_num ? shift_out : shift_in);
cur_font = font_num;
size_t
afm_encode_string (const struct afm *afm,
const struct afm_character **s, size_t n,
- struct string *out)
+ struct string *out)
{
size_t initial_length = ds_length (out);
size_t chars_left;
if (afm->mapping == MAP_ONE_BYTE)
chars_left = encode_one_byte (s, n, out);
- else
+ else
{
struct binary_encoder e;
binary_init (&e, out);
- switch (afm->mapping)
+ switch (afm->mapping)
{
case MAP_TWO_BYTE:
chars_left = encode_two_byte (s, n, &e);
case MAP_ESCAPE:
chars_left = encode_escape (s, n, afm->escape_char, &e);
break;
-
+
case MAP_DOUBLE_ESCAPE:
chars_left = encode_double_escape (s, n, afm->escape_char, &e);
break;
case MAP_SHIFT:
chars_left = encode_shift (s, n, afm->shift_in, afm->shift_out, &e);
break;
-
+
default:
- abort ();
+ NOT_REACHED ();
}
binary_finish (&e);
}