+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 2006 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 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.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-#include "afm.h"
-#include "c-ctype.h"
-#include "c-strtod.h"
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <setjmp.h>
-#include "error.h"
-#include "minmax.h"
-#include <libpspp/assertion.h>
-#include <libpspp/pool.h>
-#include <libpspp/str.h>
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* A kern pair entry. */
-struct afm_kern_pair
- {
- struct afm_character *successor; /* Second character. */
- int adjust; /* Adjustment. */
- };
-
-/* A 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
- {
- MAP_UNKNOWN, /* Not yet determined. */
- MAP_ONE_BYTE, /* 8-bit coding. */
- MAP_TWO_BYTE, /* 16-bit coding. */
- MAP_ESCAPE, /* 8-bit coding with escape to change fonts. */
- MAP_DOUBLE_ESCAPE, /* 8-bit coding with multiple escapes. */
- MAP_SHIFT /* 8-bit coding with 2 fonts that toggle. */
- };
-
-/* AFM file data. */
-struct afm
- {
- struct pool *pool; /* Containing pool. */
- char *findfont_name; /* Name for PostScript /findfont operator. */
- int ascent; /* Height above the baseline (non-negative). */
- int descent; /* Depth below the baseline (non-negative). */
-
- /* Encoding characters into strings. */
- enum mapping_scheme mapping; /* Basic mapping scheme. */
- char escape_char; /* MAP_ESCAPE only: escape character to use. */
- char shift_out; /* MAP_SHIFT only: selects font 0. */
- char shift_in; /* MAP_SHIFT only: selects font 1. */
-
- /* Characters. */
- struct afm_character *undefined_codes[256];
- struct afm_character **codes[256];
- struct afm_character **chars;
- size_t char_cnt;
- };
-
-/* AFM file parser. */
-struct parser
- {
- struct pool *pool; /* Containing pool. */
- struct afm *afm; /* AFM being parsed. */
- FILE *file; /* File being parsed. */
- const char *file_name; /* Name of file being parsed. */
- int line_number; /* Current line number in file. */
- jmp_buf bail_out; /* longjmp() target for error handling. */
-
- size_t char_allocated;
- int max_code;
- };
-
-static struct afm *create_afm (void);
-static struct afm_character *create_character (struct afm *);
-
-static void afm_error (struct parser *, const char *, ...)
- PRINTF_FORMAT (2, 3)
- NO_RETURN;
-
-static void parse_afm (struct parser *);
-static void skip_section (struct parser *, const char *end_key);
-static bool parse_set_specific (struct parser *, const char *end_key);
-static void parse_direction (struct parser *);
-static void parse_char_metrics (struct parser *);
-static void parse_kern_pairs (struct parser *);
-static void add_kern_pair (struct parser *p,
- struct afm_character *, struct afm_character *,
- int adjust);
-
-static int skip_spaces (struct parser *);
-static char *parse_key (struct parser *);
-static void skip_line (struct parser *);
-static void force_eol (struct parser *);
-static bool get_integer (struct parser *, int *);
-static int force_get_integer (struct parser *);
-static bool get_number (struct parser *, int *);
-static int force_get_number (struct parser *);
-static bool get_hex_code (struct parser *, int *);
-static int force_get_hex_code (struct parser *);
-static bool get_word (struct parser *, char **);
-static char *force_get_word (struct parser *);
-static bool get_string (struct parser *, char **);
-static char *force_get_string (struct parser *);
-
-static struct afm_character *get_char_by_name (struct parser *, const char *);
-static struct afm_character *get_char_by_code (const struct afm *, int code);
-
-/* 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)
-{
- struct afm *volatile afm;
- struct parser *parser;
-
- parser = pool_create_container (struct parser, pool);
- afm = parser->afm = create_afm ();
- parser->file = pool_fopen (parser->pool, file_name, "r");
- parser->file_name = file_name;
- parser->line_number = 0;
- if (parser->file == NULL)
- {
- error (0, errno, _("opening font metrics file \"%s\""), file_name);
- goto error;
- }
-
- if (setjmp (parser->bail_out))
- goto error;
-
- parse_afm (parser);
- pool_destroy (parser->pool);
- return afm;
-
- error:
- pool_destroy (parser->pool);
- pool_destroy (afm->pool);
- return create_afm ();
-}
-
-/* Creates and returns an empty set of metrics. */
-static struct afm *
-create_afm (void)
-{
- struct afm *afm;
- struct afm_character *def_char;
- size_t i;
-
- afm = pool_create_container (struct afm, pool);
- afm->findfont_name = NULL;
- afm->ascent = 0;
- afm->descent = 0;
- afm->mapping = MAP_UNKNOWN;
- afm->escape_char = 255;
- afm->shift_out = 14;
- afm->shift_in = 15;
- def_char = create_character (afm);
- for (i = 0; i < 256; i++)
- afm->undefined_codes[i] = def_char;
- for (i = 0; i < 256; i++)
- afm->codes[i] = afm->undefined_codes;
- afm->chars = NULL;
- afm->char_cnt = 0;
-
- return afm;
-}
-
-/* Creates and returns an initialized character within AFM. */
-static struct afm_character *
-create_character (struct afm *afm)
-{
- struct afm_character *c = pool_alloc (afm->pool, sizeof *c);
- c->code = ' ';
- c->name = NULL;
- c->width = 12000;
- c->ascent = 0;
- c->descent = 0;
- c->kern_pairs = NULL;
- c->kern_pair_cnt = 0;
- c->ligatures = NULL;
- c->ligature_cnt = 0;
- return c;
-}
-
-/* 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, ...)
-{
- va_list args;
- char *msg;
-
- va_start (args, message);
- msg = xasprintf (message, args);
- va_end (args);
-
- error_at_line (0, 0, p->file_name, p->line_number, "%s", msg);
- free (msg);
-
- longjmp (p->bail_out, 1);
-}
-
-/* Parses an AFM file with parser P. */
-static void
-parse_afm (struct parser *p)
-{
- char *key;
-
- p->char_allocated = 0;
- p->max_code = 0;
-
- key = force_get_word (p);
- if (strcmp (key, "StartFontMetrics"))
- afm_error (p, _("first line must be StartFontMetrics"));
- skip_line (p);
-
- do
- {
- key = parse_key (p);
- 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"))
- {
- int scheme = force_get_integer (p);
- if (scheme == 4)
- p->afm->mapping = MAP_ONE_BYTE;
- else if (scheme == 2 || scheme == 5 || scheme == 6)
- p->afm->mapping = MAP_TWO_BYTE;
- else if (scheme == 3)
- p->afm->mapping = MAP_ESCAPE;
- else if (scheme == 7)
- p->afm->mapping = MAP_DOUBLE_ESCAPE;
- else if (scheme == 8)
- p->afm->mapping = MAP_SHIFT;
- else
- afm_error (p, _("unsupported MappingScheme %d"), scheme);
- }
- else if (!strcmp (key, "EscChar"))
- p->afm->escape_char = force_get_integer (p);
- else if (!strcmp (key, "StartDirection"))
- parse_direction (p);
- else if (!strcmp (key, "StartCharMetrics"))
- parse_char_metrics (p);
- else if (!strcmp (key, "StartKernPairs")
- || !strcmp (key, "StartKernPairs0"))
- parse_kern_pairs (p);
- else if (!strcmp (key, "StartTrackKern"))
- skip_section (p, "EndTrackKern");
- else if (!strcmp (key, "StartComposites"))
- skip_section (p, "EndComposites");
- 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)
- {
- /* There seem to be a number of fonts out there that use a
- 2-byte encoding but don't announce it with
- MappingScheme. */
- p->afm->mapping = p->max_code > 255 ? MAP_TWO_BYTE : MAP_ONE_BYTE;
- }
-}
-
-/* Reads lines from parser P until one starts with END_KEY. */
-static void
-skip_section (struct parser *p, const char *end_key)
-{
- const char *key;
- skip_line (p);
- do
- {
- key = parse_key (p);
- skip_line (p);
- }
- while (strcmp (key, end_key));
-}
-
-/* Attempts to read an integer from parser P.
- If one is found, and it is nonzero, skips lines until END_KEY
- is encountered and returns false.
- Otherwise, skips the rest of the line and returns true.
- (This is useful because AFM files can have multiple sets of
- 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)
-{
- int set;
-
- if (get_integer (p, &set) && set != 0)
- {
- skip_section (p, end_key);
- return false;
- }
- else
- {
- force_eol (p);
- return true;
- }
-}
-
-/* Parses a StartDirection...EndDirection section in parser P. */
-static void
-parse_direction (struct parser *p)
-{
- const char *key;
-
- if (!parse_set_specific (p, "EndDirection"))
- return;
-
- do
- {
- key = parse_key (p);
- if (!strcmp (key, "CharWidth"))
- p->afm->codes[0][0]->width = force_get_integer (p);
- skip_line (p);
- }
- while (strcmp (key, "EndDirection"));
-}
-
-/* Parses a StartCharMetrics...EndCharMetrics section in parser
- P. */
-static void
-parse_char_metrics (struct parser *p)
-{
- struct parsing_ligature
- {
- struct afm_character *first;
- char *successor;
- char *ligature;
- };
-
- struct parsing_ligature *ligatures = NULL;
- size_t ligature_cnt = 0;
- size_t ligature_allocated = 0;
-
- size_t i;
-
- skip_line (p);
-
- for (;;)
- {
- char *key;
- struct afm_character *c;
-
- 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"))
- c->code = force_get_integer (p);
- else if (!strcmp (key, "CH"))
- c->code = force_get_hex_code (p);
- else
- afm_error (p, _("CharMetrics line must start with C or CH"));
- if (c->code < 0 || c->code > 65535)
- c->code = -1;
-
- if (c->code > p->max_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, ";"))
- {
- if (!get_word (p, &key))
- break;
-
- if (!strcmp (key, "N"))
- 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"))
- {
- c->width = force_get_number (p);
- force_get_number (p);
- }
- else if (!strcmp (key, "B"))
- {
- int llx, lly, urx, ury;
- llx = force_get_number (p);
- lly = force_get_number (p);
- urx = force_get_number (p);
- ury = force_get_number (p);
- c->ascent = MAX (0, ury);
- c->descent = MAX (0, -lly);
- }
- else if (!strcmp (key, "L"))
- {
- struct parsing_ligature *ligature;
- if (ligature_cnt == ligature_allocated)
- ligatures = pool_2nrealloc (p->pool, ligatures,
- &ligature_allocated,
- sizeof *ligatures);
- ligature = &ligatures[ligature_cnt++];
- ligature->first = c;
- ligature->successor = force_get_word (p);
- ligature->ligature = force_get_word (p);
- }
- else
- {
- while (strcmp (key, ";"))
- key = force_get_word (p);
- continue;
- }
- if (!get_word (p, &key))
- break;
- }
- }
- skip_line (p);
-
- for (i = 0; i < ligature_cnt; i++)
- {
- struct parsing_ligature *src = &ligatures[i];
- struct afm_ligature *dst;
- src->first->ligatures = pool_nrealloc (p->afm->pool,
- src->first->ligatures,
- src->first->ligature_cnt + 1,
- sizeof *src->first->ligatures);
- dst = &src->first->ligatures[src->first->ligature_cnt++];
- dst->successor = get_char_by_name (p, src->successor);
- dst->ligature = get_char_by_name (p, src->ligature);
- }
-}
-
-/* Parses a StartKernPairs...EndKernPairs section in parser P. */
-static void
-parse_kern_pairs (struct parser *p)
-{
- char *key;
-
- skip_line (p);
-
- do
- {
- struct afm_character *c1, *c2;
- int adjust;
-
- key = parse_key (p);
- 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));
- adjust = force_get_number (p);
- if (!strcmp (key, "KP"))
- force_get_number (p);
- add_kern_pair (p, c1, c2, adjust);
- }
- 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));
- adjust = force_get_number (p);
- force_get_number (p);
- add_kern_pair (p, c1, c2, adjust);
- }
- else
- skip_line (p);
- }
- while (strcmp (key, "EndKernPairs"));
-}
-
-/* Adds a kern pair that adjusts (FIRST, SECOND) by ADJUST units
- 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_kern_pair *kp;
-
- first->kern_pairs = pool_nrealloc (p->afm->pool, first->kern_pairs,
- first->kern_pair_cnt + 1,
- sizeof *first->kern_pairs);
- kp = &first->kern_pairs[first->kern_pair_cnt++];
- kp->successor = second;
- kp->adjust = adjust;
-}
-
-/* Returns the character with the given NAME with the metrics for
- 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)
-{
- size_t 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))
- return c;
- }
- afm_error (p, _("reference to unknown character \"%s\""), name);
-}
-
-/* Returns the character with the given CODE within AFM.
- 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_)
-{
- 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)
-{
- int c;
- while (isspace (c = getc (p->file)) && c != '\n')
- continue;
- ungetc (c, p->file);
- return c;
-}
-
-/* Parses a word at the beginning of a line.
- Skips comments.
- Reports an error if not at the beginning of a line. */
-static char *
-parse_key (struct parser *p)
-{
- force_eol (p);
- for (;;)
- {
- char *key;
-
- do
- {
- p->line_number++;
- getc (p->file);
- }
- while (skip_spaces (p) == '\n');
-
- key = force_get_word (p);
- if (strcmp (key, "Comment"))
- return key;
-
- skip_line (p);
- }
-}
-
-/* Skips to the next line within parser P. */
-static void
-skip_line (struct parser *p)
-{
- for (;;)
- {
- int c = getc (p->file);
- if (c == EOF)
- afm_error (p, _("expected end of file"));
- if (c == '\n')
- break;
- }
- ungetc ('\n', p->file);
-}
-
-/* Ensures that parser P is at the end of a line. */
-static void
-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)
-{
- int c = skip_spaces (p);
- 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)
- afm_error (p, _("number out of valid range"));
- if (*tail != '\0')
- afm_error (p, _("invalid numeric syntax"));
- *integer = tmp;
-
- return true;
- }
- else
- return false;
-}
-
-/* Returns an integer read from the current position in P.
- Reports an error if unsuccessful. */
-static int
-force_get_integer (struct parser *p)
-{
- int integer;
- if (!get_integer (p, &integer))
- afm_error (p, _("syntax error expecting integer"));
- return integer;
-}
-
-/* Tries to read a floating-point number at the current position
- in parser P. Stores the number's integer part into *INTEGER.
- Returns success. */
-static bool
-get_number (struct parser *p, int *integer)
-{
- int c = skip_spaces (p);
- if (c == '-' || c == '.' || isdigit (c))
- {
- char *tail;
- double number;
-
- errno = 0;
- number = c_strtod (force_get_word (p), &tail);
- 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"));
- *integer = number;
-
- return true;
- }
- else
- return false;
-}
-
-/* Returns the integer part of a floating-point number read from
- the current position in P.
- Reports an error if unsuccessful. */
-static int
-force_get_number (struct parser *p)
-{
- int integer;
- if (!get_number (p, &integer))
- afm_error (p, _("syntax error expecting number"));
- return integer;
-}
-
-/* Tries to read an integer expressed in hexadecimal into
- *INTEGER from P.
- Returns success. */
-static bool
-get_hex_code (struct parser *p, int *integer)
-{
- if (skip_spaces (p) == '<')
- {
- if (fscanf (p->file, "<%x", integer) != 1 || getc (p->file) != '>')
- afm_error (p, _("syntax error in hex constant"));
- return true;
- }
- else
- return false;
-}
-
-/* Reads an integer expressed in hexadecimal and returns its
- value.
- Reports an error if unsuccessful. */
-static int
-force_get_hex_code (struct parser *p)
-{
- int integer;
- if (!get_hex_code (p, &integer))
- afm_error (p, _("syntax error expecting hex constant"));
- return integer;
-}
-
-/* Tries to read a word from P into *WORD.
- The word is allocated in P's pool.
- Returns success. */
-static bool
-get_word (struct parser *p, char **word)
-{
- if (skip_spaces (p) != '\n')
- {
- struct string s;
- int c;
-
- ds_init_empty (&s);
- while (!isspace (c = getc (p->file)) && c != EOF)
- ds_put_char (&s, c);
- ungetc (c, p->file);
- *word = ds_cstr (&s);
- pool_register (p->pool, free, *word);
- return true;
- }
- else
- {
- *word = NULL;
- return false;
- }
-}
-
-/* Reads a word from P and returns it.
- The word is allocated in P's pool.
- Reports an error if unsuccessful. */
-static char *
-force_get_word (struct parser *p)
-{
- char *word;
- if (!get_word (p, &word))
- afm_error (p, _("unexpected end of line"));
- return word;
-}
-
-/* Reads a string, consisting of the remainder of the current
- line, from P, and stores it in *STRING.
- Leading and trailing spaces are removed.
- The word is allocated in P's pool.
- Returns true if a non-empty string was successfully read,
- false otherwise. */
-static bool
-get_string (struct parser *p, char **string)
-{
- struct string s = DS_EMPTY_INITIALIZER;
-
- skip_spaces (p);
- for (;;)
- {
- int c = getc (p->file);
- if (c == EOF || c == '\n')
- break;
- ds_put_char (&s, c);
- }
- ungetc ('\n', p->file);
- ds_rtrim (&s, ss_cstr (CC_SPACES));
-
- if (!ds_is_empty (&s))
- {
- *string = ds_cstr (&s);
- pool_register (p->pool, free, *string);
- return true;
- }
- else
- {
- *string = NULL;
- ds_destroy (&s);
- return false;
- }
-}
-
-/* Reads a string, consisting of the remainder of the current
- line, from P, and returns it.
- Leading and trailing spaces are removed.
- The word is allocated in P's pool.
- Reports an error if the string is empty. */
-static char *
-force_get_string (struct parser *p)
-{
- char *string;
- if (!get_string (p, &string))
- afm_error (p, _("unexpected end of line expecting string"));
- return string;
-}
-\f
-/* Closes AFM and frees its storage. */
-void
-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)
-{
- 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)
-{
- 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)
-{
- 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)
-{
- return get_char_by_code (afm, code);
-}
-
-/* Returns the ligature formed when FIRST is followed by SECOND,
- 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)
-{
- size_t i;
-
- for (i = 0; i < first->ligature_cnt; i++)
- if (first->ligatures[i].successor == second)
- return first->ligatures[i].ligature;
- return NULL;
-}
-
-/* Returns the pair kerning x-adjustment when FIRST is followed
- by SECOND, or 0 if no pair kerning should be done for the
- given pair of characters. */
-int
-afm_get_kern_adjustment (const struct afm_character *first,
- const struct afm_character *second)
-{
- size_t i;
-
- for (i = 0; i < first->kern_pair_cnt; i++)
- if (first->kern_pairs[i].successor == second)
- return first->kern_pairs[i].adjust;
- return 0;
-}
-\f
-/* Encodes the N characters in S as a PostScript string in OUT,
- using a single-byte encoding.
- Returns the number of characters remaining after all those
- that could be successfully encoded were. */
-static size_t
-encode_one_byte (const struct afm_character **s, size_t n,
- struct string *out)
-{
- ds_put_char (out, '(');
- for (; n > 0; s++, n--)
- {
- 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, ')');
- return n;
-}
-
-/* State of binary encoder for PostScript. */
-struct binary_encoder
- {
- struct string *out; /* Output string. */
- uint32_t b; /* Accumulated bytes for base-85 encoding. */
- size_t n; /* Number of bytes in b (0...3). */
- };
-
-/* Initializes encoder E for output to OUT. */
-static void
-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)
-{
- assert (value >= 0 && value < 85);
-#if C_CTYPE_ASCII
- return value + 33;
-#else
- return ("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK"
- "LMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstu")[value];
-#endif
-}
-
-/* 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)
-{
- char c[5];
- int i;
-
- for (i = 4; i >= 0; i--)
- {
- c[i] = value_to_ascii85 (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)
-{
- e->b = (e->b << 8) | byte;
- e->n++;
- if (e->n % 4 == 0)
- {
- if (e->n == 4)
- ds_put_cstr (e->out, "<~");
-
- if (e->b != 0)
- append_ascii85_block (e->b, 5, e->out);
- else
- ds_put_char (e->out, 'z');
- }
-}
-
-/* Finishes up encoding with E. */
-static void
-binary_finish (struct binary_encoder *e)
-{
- 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);
- ds_put_cstr (e->out, "~>");
- }
- else if (e->n > 0)
- {
- /* It's cheaper (or at least the same cost) to encode this
- string in hexadecimal. */
- uint32_t b;
- size_t i;
-
- ds_put_cstr (e->out, "<");
- b = e->b << 8 * (4 - e->n);
- for (i = 0; i < e->n; i++)
- {
- ds_put_format (e->out, "%02x", b >> 24);
- b <<= 8;
- }
- ds_put_cstr (e->out, ">");
- }
- else
- {
- /* Empty string. */
- ds_put_cstr (e->out, "()");
- }
-}
-
-/* Encodes the N characters in S into encoder E,
- using a two-byte encoding.
- Returns the number of characters remaining after all those
- that could be successfully encoded were. */
-static size_t
-encode_two_byte (const struct afm_character **s, size_t n,
- struct binary_encoder *e)
-{
- for (; n > 0; s++, n--)
- {
- uint16_t code = (*s)->code;
- if (code != (*s)->code)
- break;
-
- binary_put (e, code >> 8);
- binary_put (e, code);
- }
- return n;
-}
-
-/* Encodes the N characters in S into encoder E,
- using an escape-based encoding with ESCAPE_CHAR as escape.
- Returns the number of characters remaining after all those
- that could be successfully encoded were. */
-static size_t
-encode_escape (const struct afm_character **s, size_t n,
- unsigned char escape_char,
- struct binary_encoder *e)
-{
- uint8_t cur_font = 0;
-
- for (; n > 0; s++, n--)
- {
- uint16_t code = (*s)->code;
- uint8_t font_num = code >> 8;
- uint8_t char_num = code & 0xff;
- if (code != (*s)->code)
- break;
-
- if (font_num != cur_font)
- {
- if (font_num == escape_char)
- break;
- binary_put (e, escape_char);
- binary_put (e, font_num);
- cur_font = font_num;
- }
- binary_put (e, char_num);
- }
- return n;
-}
-
-/* Encodes the N characters in S into encoder E,
- using an double escape-based encoding with ESCAPE_CHAR as
- escape.
- Returns the number of characters remaining after all those
- that could be successfully encoded were. */
-static size_t
-encode_double_escape (const struct afm_character **s, size_t n,
- unsigned char escape_char,
- struct binary_encoder *e)
-{
- unsigned cur_font = 0;
-
- for (; n > 0; s++, n--)
- {
- unsigned font_num = (*s)->code >> 8;
- uint8_t char_num = (*s)->code & 0xff;
- if ((*s)->code & ~0x1ffff)
- break;
-
- if (font_num != cur_font)
- {
- if (font_num == (escape_char & 0xff))
- break;
- if (font_num >= 256)
- binary_put (e, escape_char);
- binary_put (e, escape_char);
- binary_put (e, font_num & 0xff);
- cur_font = font_num;
- }
- binary_put (e, char_num);
- }
- return n;
-}
-
-/* Encodes the N characters in S into encoder E,
- using a shift-based encoding with SHIFT_IN and SHIFT_OUT as
- shift characters.
- Returns the number of characters remaining after all those
- that could be successfully encoded were. */
-static size_t
-encode_shift (const struct afm_character **s, size_t n,
- unsigned char shift_in, unsigned char shift_out,
- struct binary_encoder *e)
-{
- unsigned cur_font = 0;
-
- for (; n > 0; s++, n--)
- {
- int font_num = ((*s)->code & 0x100) != 0;
- uint8_t char_num = (*s)->code & 0xff;
- if ((*s)->code & ~0x1ff)
- break;
-
- if (font_num != cur_font)
- {
- binary_put (e, font_num ? shift_out : shift_in);
- cur_font = font_num;
- }
- binary_put (e, char_num);
- }
- return n;
-}
-
-/* Encodes the N characters in S into a PostScript string in OUT,
- according to AFM's character encoding.
- Returns the number of characters successfully encoded,
- which may be less than N if an unencodable character was
- encountered. */
-size_t
-afm_encode_string (const struct afm *afm,
- const struct afm_character **s, size_t n,
- 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
- {
- struct binary_encoder e;
-
- binary_init (&e, out);
- switch (afm->mapping)
- {
- case MAP_TWO_BYTE:
- chars_left = encode_two_byte (s, n, &e);
- break;
-
- 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:
- NOT_REACHED ();
- }
- binary_finish (&e);
- }
-
- if (chars_left == n)
- ds_truncate (out, initial_length);
- return n - chars_left;
-}
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2006, 2007, 2009 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 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.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#include <config.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <data/file-name.h>
-#include <libpspp/assertion.h>
-#include <libpspp/bit-vector.h>
-#include <libpspp/compiler.h>
-#include <libpspp/freaderror.h>
-#include <libpspp/hash.h>
-#include <libpspp/misc.h>
-#include <libpspp/start-date.h>
-#include <libpspp/version.h>
-#include <output/afm.h>
-#include <output/chart-provider.h>
-#include <output/chart.h>
-#include <output/manager.h>
-#include <output/output.h>
-
-#include "error.h"
-#include "intprops.h"
-#include "minmax.h"
-#include "xalloc.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-/* PostScript driver options: (defaults listed first)
-
- output-file="pspp.ps"
-
- paper-size=letter (see "papersize" file)
- orientation=portrait|landscape
- headers=on|off
-
- left-margin=0.5in
- right-margin=0.5in
- top-margin=0.5in
- bottom-margin=0.5in
-
- prop-font=Times-Roman
- emph-font=Times-Italic
- fixed-font=Courier
- font-size=10000
-
- line-gutter=1pt
- line-spacing=1pt
- line-width=0.5pt
- */
-
-/* The number of `psus' (PostScript driver UnitS) per inch. */
-#define PSUS 72000
-
-/* A PostScript font. */
-struct font
- {
- struct afm *metrics; /* Metrics. */
- char *embed_fn; /* Name of file to embed. */
- char *encoding_fn; /* Name of file with encoding. */
- };
-
-/* PostScript output driver extension record. */
-struct ps_driver_ext
- {
- char *file_name; /* Output file name. */
- FILE *file; /* Output file. */
-
- bool draw_headers; /* Draw headers at top of page? */
- int page_number; /* Current page number. */
-
- bool portrait; /* Portrait mode? */
- int paper_width; /* Width of paper before dropping margins. */
- int paper_length; /* Length of paper before dropping margins. */
- int left_margin; /* Left margin in psus. */
- int right_margin; /* Right margin in psus. */
- int top_margin; /* Top margin in psus. */
- int bottom_margin; /* Bottom margin in psus. */
-
- int line_gutter; /* Space around lines. */
- int line_space; /* Space between lines. */
- int line_width; /* Width of lines. */
-
- struct font *fonts[OUTP_FONT_CNT];
- int last_font; /* Index of last font set with setfont. */
-
- int doc_num; /* %%DocumentNumber counter. */
- };
-
-/* Transform logical y-ordinate Y into a page ordinate. */
-#define YT(Y) (this->length - (Y))
-
-static bool handle_option (void *this, const char *key,
- const struct string *val);
-static void draw_headers (struct outp_driver *this);
-
-static void write_ps_prologue (struct outp_driver *);
-
-static char *quote_ps_name (const char *string);
-
-static struct font *load_font (const char *string);
-static void free_font (struct font *);
-static void setup_font (struct outp_driver *this, struct font *, int index);
-\f
-/* Driver initialization. */
-
-static bool
-ps_open_driver (const char *name, int types, struct substring options)
-{
- struct outp_driver *this;
- struct ps_driver_ext *x;
- size_t i;
-
- this = outp_allocate_driver (&postscript_class, name, types);
- this->width = this->length = 0;
- this->font_height = PSUS * 10 / 72;
-
- this->ext = x = xmalloc (sizeof *x);
- x->file_name = xstrdup ("pspp.ps");
- x->file = NULL;
- x->draw_headers = true;
- x->page_number = 0;
- x->portrait = true;
- outp_get_paper_size ("", &x->paper_width, &x->paper_length);
- x->left_margin = PSUS / 2;
- x->right_margin = PSUS / 2;
- x->top_margin = PSUS / 2;
- x->bottom_margin = PSUS / 2;
- x->line_gutter = PSUS / 72;
- x->line_space = PSUS / 72;
- x->line_width = PSUS / 144;
- for (i = 0; i < OUTP_FONT_CNT; i++)
- x->fonts[i] = NULL;
- x->doc_num = 0;
-
- outp_parse_options (this->name, options, handle_option, this);
-
- x->file = fn_open (x->file_name, "w");
- if (x->file == NULL)
- {
- error (0, errno, _("opening PostScript output file \"%s\""),
- x->file_name);
- goto error;
- }
-
- if (x->portrait)
- {
- this->width = x->paper_width;
- this->length = x->paper_length;
- }
- else
- {
- this->width = x->paper_length;
- this->length = x->paper_width;
- }
- this->width -= x->left_margin + x->right_margin;
- this->length -= x->top_margin + x->bottom_margin;
- if (x->draw_headers)
- {
- int header_length = 3 * this->font_height;
- this->length -= header_length;
- x->top_margin += header_length;
- }
-
- for (i = 0; i < OUTP_FONT_CNT; i++)
- if (x->fonts[i] == NULL)
- {
- const char *default_fonts[OUTP_FONT_CNT];
- default_fonts[OUTP_FIXED] = "Courier.afm";
- default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
- default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
- x->fonts[i] = load_font (default_fonts[i]);
- if (x->fonts[i] == NULL)
- goto error;
- }
-
- if (this->length / this->font_height < 15)
- {
- error (0, 0, _("The defined PostScript page is not long "
- "enough to hold margins and headers, plus least 15 "
- "lines of the default fonts. In fact, there's only "
- "room for %d lines of each font at the default size "
- "of %d.%03d points."),
- this->length / this->font_height,
- this->font_height / 1000, this->font_height % 1000);
- goto error;
- }
-
- this->fixed_width =
- afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
- * this->font_height / 1000;
- this->prop_em_width =
- afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
- * this->font_height / 1000;
-
- this->horiz_line_width[OUTP_L_NONE] = 0;
- this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
- this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
- + 2 * x->line_width);
- memcpy (this->vert_line_width, this->horiz_line_width,
- sizeof this->vert_line_width);
-
- write_ps_prologue (this);
-
- outp_register_driver (this);
- return true;
-
- error:
- this->class->close_driver (this);
- outp_free_driver (this);
- return false;
-}
-
-static bool
-ps_close_driver (struct outp_driver *this)
-{
- struct ps_driver_ext *x = this->ext;
- bool ok = true;
- size_t i;
-
- if (x->file != NULL)
- {
- fprintf (x->file,
- "%%%%Trailer\n"
- "%%%%Pages: %d\n"
- "%%%%EOF\n",
- x->page_number);
-
- ok = fn_close (x->file_name, x->file) == 0;
- if (!ok)
- error (0, errno, _("closing PostScript output file \"%s\""),
- x->file_name);
- }
-
- free (x->file_name);
- for (i = 0; i < OUTP_FONT_CNT; i++)
- free_font (x->fonts[i]);
- free (x);
-
- return ok;
-}
-
-/* Generic option types. */
-enum
-{
- output_file_arg,
- paper_size_arg,
- orientation_arg,
- line_style_arg,
- boolean_arg,
- pos_int_arg,
- dimension_arg,
- string_arg,
- nonneg_int_arg
-};
-
-/* All the options that the PostScript driver supports. */
-static const struct outp_option option_tab[] =
-{
- {"output-file", output_file_arg,0},
- {"paper-size", paper_size_arg, 0},
- {"orientation", orientation_arg,0},
-
- {"headers", boolean_arg, 1},
-
- {"prop-font", string_arg, OUTP_PROPORTIONAL},
- {"emph-font", string_arg, OUTP_EMPHASIS},
- {"fixed-font", string_arg, OUTP_FIXED},
-
- {"left-margin", pos_int_arg, 0},
- {"right-margin", pos_int_arg, 1},
- {"top-margin", pos_int_arg, 2},
- {"bottom-margin", pos_int_arg, 3},
- {"font-size", pos_int_arg, 4},
-
- {"line-width", dimension_arg, 0},
- {"line-gutter", dimension_arg, 1},
- {"line-width", dimension_arg, 2},
- {NULL, 0, 0},
-};
-
-static bool
-handle_option (void *this_, const char *key,
- const struct string *val)
-{
- struct outp_driver *this = this_;
- struct ps_driver_ext *x = this->ext;
- int subcat;
- char *value = ds_cstr (val);
-
- switch (outp_match_keyword (key, option_tab, &subcat))
- {
- case -1:
- error (0, 0,
- _("unknown configuration parameter `%s' for PostScript device "
- "driver"), key);
- break;
- case output_file_arg:
- free (x->file_name);
- x->file_name = xstrdup (value);
- break;
- case paper_size_arg:
- outp_get_paper_size (value, &this->width, &this->length);
- break;
- case orientation_arg:
- if (!strcmp (value, "portrait"))
- x->portrait = true;
- else if (!strcmp (value, "landscape"))
- x->portrait = false;
- else
- error (0, 0, _("unknown orientation `%s' (valid orientations are "
- "`portrait' and `landscape')"), value);
- break;
- case boolean_arg:
- if (!strcmp (value, "on") || !strcmp (value, "true")
- || !strcmp (value, "yes") || atoi (value))
- x->draw_headers = true;
- else if (!strcmp (value, "off") || !strcmp (value, "false")
- || !strcmp (value, "no") || !strcmp (value, "0"))
- x->draw_headers = false;
- else
- {
- error (0, 0, _("boolean value expected for %s"), key);
- return false;
- }
- break;
- case pos_int_arg:
- {
- char *tail;
- int arg;
-
- errno = 0;
- arg = strtol (value, &tail, 0);
- if (arg < 1 || errno == ERANGE || *tail)
- {
- error (0, 0, _("positive integer value required for `%s'"), key);
- break;
- }
- if ((subcat == 4 || subcat == 5) && arg < 1000)
- {
- error (0, 0, _("default font size must be at least 1 point (value "
- "of 1000 for key `%s')"), key);
- break;
- }
- switch (subcat)
- {
- case 0:
- x->left_margin = arg;
- break;
- case 1:
- x->right_margin = arg;
- break;
- case 2:
- x->top_margin = arg;
- break;
- case 3:
- x->bottom_margin = arg;
- break;
- case 4:
- this->font_height = arg;
- break;
- default:
- NOT_REACHED ();
- }
- }
- break;
- case dimension_arg:
- {
- int dimension = outp_evaluate_dimension (value);
-
- if (dimension <= 0)
- break;
- switch (subcat)
- {
- case 0:
- x->line_width = dimension;
- break;
- case 1:
- x->line_gutter = dimension;
- break;
- case 2:
- x->line_width = dimension;
- break;
- default:
- NOT_REACHED ();
- }
- }
- break;
- case string_arg:
- {
- struct font *font = load_font (value);
- if (font != NULL)
- {
- struct font **dst = &x->fonts[subcat];
- if (*dst != NULL)
- free_font (*dst);
- *dst = font;
- }
- }
- break;
- default:
- NOT_REACHED ();
- }
-
- return true;
-}
-
-/* Looks for a PostScript font file or config file in all the
- appropriate places. Returns the file name on success, NULL on
- failure. */
-static char *
-find_ps_file (const char *name)
-{
- if (fn_is_absolute (name))
- return xstrdup (name);
- else
- {
- char *base_name = xasprintf ("psfonts/%s", name);
- char *file_name = fn_search_path (base_name, config_path);
- free (base_name);
- return file_name;
- }
-}
-\f
-/* Basic file operations. */
-
-/* Writes the PostScript prologue to file F. */
-static void
-write_ps_prologue (struct outp_driver *this)
-{
- struct ps_driver_ext *x = this->ext;
- size_t embedded_cnt, preloaded_cnt;
- size_t i;
-
- fputs ("%!PS-Adobe-3.0\n", x->file);
- fputs ("%%Pages: (atend)\n", x->file);
-
- embedded_cnt = preloaded_cnt = 0;
- for (i = 0; i < OUTP_FONT_CNT; i++)
- {
- bool embed = x->fonts[i]->embed_fn != NULL;
- embedded_cnt += embed;
- preloaded_cnt += !embed;
- }
- if (preloaded_cnt > 0)
- {
- fputs ("%%DocumentNeededResources: font", x->file);
- for (i = 0; i < OUTP_FONT_CNT; i++)
- {
- struct font *f = x->fonts[i];
- if (f->embed_fn == NULL)
- fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
- }
- fputs ("\n", x->file);
- }
- if (embedded_cnt > 0)
- {
- fputs ("%%DocumentSuppliedResources: font", x->file);
- for (i = 0; i < OUTP_FONT_CNT; i++)
- {
- struct font *f = x->fonts[i];
- if (f->embed_fn != NULL)
- fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
- }
- fputs ("\n", x->file);
- }
- fputs ("%%Copyright: This prologue is public domain.\n", x->file);
- fprintf (x->file, "%%%%Creator: %s\n", version);
- fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
- x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
- fprintf (x->file, "%%%%Orientation: %s\n",
- x->portrait ? "Portrait" : "Landscape");
- fputs ("%%EndComments\n", x->file);
- fputs ("%%BeginDefaults\n", x->file);
- fputs ("%%PageResources: font", x->file);
- for (i = 0; i < OUTP_FONT_CNT; i++)
- fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
- fputs ("\n", x->file);
- fputs ("%%EndDefaults\n", x->file);
- fputs ("%%BeginProlog\n", x->file);
- fputs ("/ED{exch def}bind def\n", x->file);
- fputs ("/L{moveto lineto stroke}bind def\n", x->file);
- fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
- fputs ("/S{show}bind def\n", x->file);
- fputs ("/GS{glyphshow}def\n", x->file);
- fputs ("/RF{\n", x->file);
- fputs (" exch dup maxlength 1 add dict begin\n", x->file);
- fputs (" {\n", x->file);
- fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
- fputs (" }forall\n", x->file);
- fputs (" /Encoding ED\n", x->file);
- fputs (" currentdict end\n", x->file);
- fputs ("}bind def\n", x->file);
- fputs ("/F{setfont}bind def\n", x->file);
- fputs ("/EP{\n", x->file);
- fputs (" pg restore\n", x->file);
- fputs (" showpage\n", x->file);
- fputs ("}bind def\n", x->file);
- fputs ("/GB{\n", x->file);
- fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
- fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
- x->file);
- fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
- fputs ("}bind def\n", x->file);
- fputs ("/K{0 rmoveto}bind def\n", x->file);
- fputs ("%%EndProlog\n", x->file);
- fputs ("%%BeginSetup\n", x->file);
- for (i = 0; i < OUTP_FONT_CNT; i++)
- setup_font (this, x->fonts[i], i);
- fputs ("%%EndSetup\n", x->file);
-}
-
-/* Returns STRING as a Postscript name, which is just '/'
- followed by STRING unless characters need to be quoted.
- The caller must free the string. */
-static char *
-quote_ps_name (const char *string)
-{
- const char *cp;
-
- for (cp = string; *cp != '\0'; cp++)
- {
- unsigned char c = *cp;
- if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
- && (cp == string || !isdigit (c)))
- {
- struct string out = DS_EMPTY_INITIALIZER;
- ds_put_char (&out, '<');
- for (cp = string; *cp != '\0'; cp++)
- {
- c = *cp;
- ds_put_format (&out, "%02x", c);
- }
- ds_put_cstr (&out, ">cvn");
- return ds_cstr (&out);
- }
- }
- return xasprintf ("/%s", string);
-}
-
-static void
-ps_open_page (struct outp_driver *this)
-{
- struct ps_driver_ext *x = this->ext;
-
- /* Assure page independence. */
- x->last_font = -1;
-
- x->page_number++;
-
- fprintf (x->file,
- "%%%%Page: %d %d\n"
- "%%%%BeginPageSetup\n"
- "/pg save def 0.001 dup scale\n",
- x->page_number, x->page_number);
-
- if (!x->portrait)
- fprintf (x->file,
- "%d 0 translate 90 rotate\n",
- x->paper_width);
-
- if (x->bottom_margin != 0 || x->left_margin != 0)
- fprintf (x->file,
- "%d %d translate\n",
- x->left_margin, x->bottom_margin);
-
- fprintf (x->file,
- "/LW %d def %d setlinewidth\n"
- "%%%%EndPageSetup\n",
- x->line_width, x->line_width);
-
- if (x->draw_headers)
- draw_headers (this);
-}
-
-static void
-ps_close_page (struct outp_driver *this)
-{
- struct ps_driver_ext *x = this->ext;
- fputs ("%%PageTrailer\n"
- "EP\n",
- x->file);
-}
-
-static void
-ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
-{
- switch (s->type)
- {
- default:
- NOT_REACHED ();
- }
-}
-\f
-/* Draws a line from (x0,y0) to (x1,y1). */
-static void
-dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
-{
- struct ps_driver_ext *ext = this->ext;
- fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
-}
-
-/* Draws a horizontal line X0...X2 at Y if LEFT says so,
- shortening it to X0...X1 if SHORTEN is true.
- Draws a horizontal line X1...X3 at Y if RIGHT says so,
- shortening it to X2...X3 if SHORTEN is true. */
-static void
-horz_line (struct outp_driver *this,
- int x0, int x1, int x2, int x3, int y,
- enum outp_line_style left, enum outp_line_style right,
- bool shorten)
-{
- if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
- dump_line (this, x0, y, x3, y);
- else
- {
- if (left != OUTP_L_NONE)
- dump_line (this, x0, y, shorten ? x1 : x2, y);
- if (right != OUTP_L_NONE)
- dump_line (this, shorten ? x2 : x1, y, x3, y);
- }
-}
-
-/* Draws a vertical line Y0...Y2 at X if TOP says so,
- shortening it to Y0...Y1 if SHORTEN is true.
- Draws a vertical line Y1...Y3 at X if BOTTOM says so,
- shortening it to Y2...Y3 if SHORTEN is true. */
-static void
-vert_line (struct outp_driver *this,
- int y0, int y1, int y2, int y3, int x,
- enum outp_line_style top, enum outp_line_style bottom,
- bool shorten)
-{
- if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
- dump_line (this, x, y0, x, y3);
- else
- {
- if (top != OUTP_L_NONE)
- dump_line (this, x, y0, x, shorten ? y1 : y2);
- if (bottom != OUTP_L_NONE)
- dump_line (this, x, shorten ? y2 : y1, x, y3);
- }
-}
-
-/* Draws a generalized intersection of lines in the rectangle
- (X0,Y0)-(X3,Y3). The line coming from the top to the center
- is of style TOP, from left to center of style LEFT, from
- bottom to center of style BOTTOM, and from right to center of
- style RIGHT. */
-static void
-ps_line (struct outp_driver *this,
- int x0, int y0, int x3, int y3,
- enum outp_line_style top, enum outp_line_style left,
- enum outp_line_style bottom, enum outp_line_style right)
-{
- /* The algorithm here is somewhat subtle, to allow it to handle
- all the kinds of intersections that we need.
-
- Three additional ordinates are assigned along the x axis. The
- first is xc, midway between x0 and x3. The others are x1 and
- x2; for a single vertical line these are equal to xc, and for
- a double vertical line they are the ordinates of the left and
- right half of the double line.
-
- yc, y1, and y2 are assigned similarly along the y axis.
-
- The following diagram shows the coordinate system and output
- for double top and bottom lines, single left line, and no
- right line:
-
- x0 x1 xc x2 x3
- y0 ________________________
- | # # |
- | # # |
- | # # |
- | # # |
- | # # |
- y1 = y2 = yc |######### # |
- | # # |
- | # # |
- | # # |
- | # # |
- y3 |________#_____#_______|
- */
- struct ps_driver_ext *ext = this->ext;
-
- /* Offset from center of each line in a pair of double lines. */
- int double_line_ofs = (ext->line_space + ext->line_width) / 2;
-
- /* Are the lines along each axis single or double?
- (It doesn't make sense to have different kinds of line on the
- same axis, so we don't try to gracefully handle that case.) */
- bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
- bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
-
- /* When horizontal lines are doubled,
- the left-side line along y1 normally runs from x0 to x2,
- and the right-side line along y1 from x3 to x1.
- If the top-side line is also doubled, we shorten the y1 lines,
- so that the left-side line runs only to x1,
- and the right-side line only to x2.
- Otherwise, the horizontal line at y = y1 below would cut off
- the intersection, which looks ugly:
- x0 x1 x2 x3
- y0 ________________________
- | # # |
- | # # |
- | # # |
- | # # |
- y1 |######### ########|
- | |
- | |
- y2 |######################|
- | |
- | |
- y3 |______________________|
- It is more of a judgment call when the horizontal line is
- single. We actually choose to cut off the line anyhow, as
- shown in the first diagram above.
- */
- bool shorten_y1_lines = top == OUTP_L_DOUBLE;
- bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
- bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
- int horz_line_ofs = double_vert ? double_line_ofs : 0;
- int xc = (x0 + x3) / 2;
- int x1 = xc - horz_line_ofs;
- int x2 = xc + horz_line_ofs;
-
- bool shorten_x1_lines = left == OUTP_L_DOUBLE;
- bool shorten_x2_lines = right == OUTP_L_DOUBLE;
- bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
- int vert_line_ofs = double_horz ? double_line_ofs : 0;
- int yc = (y0 + y3) / 2;
- int y1 = yc - vert_line_ofs;
- int y2 = yc + vert_line_ofs;
-
- if (!double_horz)
- horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
- else
- {
- horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
- horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
- }
-
- if (!double_vert)
- vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
- else
- {
- vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
- vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
- }
-}
-
-/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
- and with the given JUSTIFICATION for THIS driver. */
-static int
-draw_text (struct outp_driver *this,
- const char *string, int x, int y, int max_width,
- enum outp_justification justification)
-{
- struct outp_text text;
- int width;
-
- text.font = OUTP_PROPORTIONAL;
- text.justification = justification;
- text.string = ss_cstr (string);
- text.h = max_width;
- text.v = this->font_height;
- text.x = x;
- text.y = y;
- this->class->text_metrics (this, &text, &width, NULL);
- this->class->text_draw (this, &text);
- return width;
-}
-
-/* Writes LEFT left-justified and RIGHT right-justified within
- (X0...X1) at Y. LEFT or RIGHT or both may be null. */
-static void
-draw_header_line (struct outp_driver *this,
- const char *left, const char *right,
- int x0, int x1, int y)
-{
- int right_width = 0;
- if (right != NULL)
- right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
- + this->prop_em_width);
- if (left != NULL)
- draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
-}
-
-/* Draw top of page headers for THIS driver. */
-static void
-draw_headers (struct outp_driver *this)
-{
- struct ps_driver_ext *ext = this->ext;
- char *r1, *r2;
- int x0, x1;
- int y;
-
- y = -3 * this->font_height;
- x0 = this->prop_em_width;
- x1 = this->width - this->prop_em_width;
-
- /* Draw box. */
- fprintf (ext->file, "%d %d %d %d GB\n",
- 0, YT (y),
- this->width, YT (y + 2 * this->font_height + ext->line_gutter));
- y += ext->line_width + ext->line_gutter;
-
- r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
- r2 = xasprintf ("%s - %s", version, host_system);
-
- draw_header_line (this, outp_title, r1, x0, x1, y);
- y += this->font_height;
-
- draw_header_line (this, outp_subtitle, r2, x0, x1, y);
-
- free (r1);
- free (r2);
-}
-\f
-/* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
- given FONT.
- The characters are justified according to JUSTIFICATION in a
- field that has WIDTH_LEFT space remaining after the characters
- themselves are accounted for.
- Before character I is written, its x-position is adjusted by
- KERNS[I]. */
-static void
-write_text (struct outp_driver *this,
- int x0, int y0,
- enum outp_font font,
- enum outp_justification justification,
- const struct afm_character **chars, int *kerns, size_t char_cnt,
- int width_left)
-{
- struct ps_driver_ext *ext = this->ext;
- struct afm *afm = ext->fonts[font]->metrics;
- struct string out;
- size_t i, j;
-
- if (justification == OUTP_RIGHT)
- x0 += width_left;
- else if (justification == OUTP_CENTER)
- x0 += width_left / 2;
- y0 += afm_get_ascent (afm) * this->font_height / 1000;
-
- fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
-
- if (ext->last_font != font)
- {
- ext->last_font = font;
- fprintf (ext->file, "F%d setfont\n", font);
- }
-
- ds_init_empty (&out);
- for (i = 0; i < char_cnt; i = j)
- {
- for (j = i + 1; j < char_cnt; j++)
- if (kerns[j] != 0)
- break;
-
- if (kerns[i] != 0)
- fprintf (ext->file, "%d K", kerns[i]);
- while (i < j)
- {
- size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
- if (encoded > 0)
- {
- fprintf (ext->file, "%sS\n", ds_cstr (&out));
- ds_clear (&out);
- i += encoded;
- }
-
- if (i < j)
- {
- fprintf (ext->file, "/%s GS\n", chars[i]->name);
- i++;
- }
- }
- }
- ds_destroy (&out);
-}
-
-/* State of a text formatting operation. */
-struct text_state
- {
- /* Input. */
- const struct outp_text *text;
- bool draw;
-
- /* Output. */
- const struct afm_character **glyphs;
- int *glyph_kerns;
-
- /* State. */
- size_t glyph_cnt; /* Number of glyphs output. */
- int width_left; /* Width left over. */
- int height_left; /* Height left over. */
-
- /* State as of last space. */
- const char *space_char; /* Just past last space. */
- size_t space_glyph_cnt; /* Number of glyphs as of last space. */
- int space_width_left; /* Width left over as of last space. */
-
- /* Statistics. */
- int max_width; /* Widest line so far. */
- };
-
-/* Adjusts S to complete a line of text,
- and draws the current line if appropriate. */
-static void
-finish_line (struct outp_driver *this, struct text_state *s)
-{
- int width;
-
- if (s->draw)
- {
- write_text (this,
- s->text->x, s->text->y + (s->text->v - s->height_left),
- s->text->font,
- s->text->justification,
- s->glyphs, s->glyph_kerns, s->glyph_cnt,
- s->width_left);
- s->glyph_cnt = 0;
- }
-
- /* Update maximum width. */
- width = s->text->h - s->width_left;
- if (width > s->max_width)
- s->max_width = width;
-
- /* Move to next line. */
- s->width_left = s->text->h;
- s->height_left -= this->font_height;
-
- /* No spaces on this line yet. */
- s->space_char = NULL;
-}
-
-/* Format TEXT on THIS driver.
- If DRAW is nonzero, draw the text.
- The width of the widest line is stored into *WIDTH, if WIDTH
- is nonnull.
- The total height of the text written is stored into *HEIGHT,
- if HEIGHT is nonnull. */
-static void
-text (struct outp_driver *this, const struct outp_text *text, bool draw,
- int *width, int *height)
-{
- struct ps_driver_ext *ext = this->ext;
- struct afm *afm = ext->fonts[text->font]->metrics;
- const char *cp;
- size_t glyph_cap;
- struct text_state s;
-
- s.text = text;
- s.draw = draw;
-
- s.glyphs = NULL;
- s.glyph_kerns = NULL;
- glyph_cap = 0;
-
- s.glyph_cnt = 0;
- s.width_left = s.text->h;
- s.height_left = s.text->v;
-
- s.space_char = 0;
-
- s.max_width = 0;
-
- cp = ss_data (s.text->string);
- while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
- {
- const struct afm_character *cur;
- int char_width;
- int kern_adjust;
-
- if (*cp == '\n')
- {
- finish_line (this, &s);
- cp++;
- continue;
- }
-
- /* Get character and resolve ligatures. */
- cur = afm_get_character (afm, *cp);
- while (++cp < ss_end (s.text->string))
- {
- const struct afm_character *next = afm_get_character (afm, *cp);
- const struct afm_character *ligature = afm_get_ligature (cur, next);
- if (ligature == NULL)
- break;
- cur = ligature;
- }
- char_width = cur->width * this->font_height / 1000;
-
- /* Get kern adjustment. */
- if (s.glyph_cnt > 0)
- kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
- * this->font_height / 1000);
- else
- kern_adjust = 0;
-
- /* Record the current status if this is a space character. */
- if (cur->code == ' ' && cp > ss_data (s.text->string))
- {
- s.space_char = cp;
- s.space_glyph_cnt = s.glyph_cnt;
- s.space_width_left = s.width_left;
- }
-
- /* Enough room on this line? */
- if (char_width + kern_adjust > s.width_left)
- {
- if (s.space_char == NULL)
- {
- finish_line (this, &s);
- kern_adjust = 0;
- }
- else
- {
- cp = s.space_char;
- s.glyph_cnt = s.space_glyph_cnt;
- s.width_left = s.space_width_left;
- finish_line (this, &s);
- continue;
- }
- }
-
- if (s.glyph_cnt >= glyph_cap)
- {
- glyph_cap = 2 * (glyph_cap + 8);
- s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
- s.glyph_kerns = xnrealloc (s.glyph_kerns,
- glyph_cap, sizeof *s.glyph_kerns);
- }
- s.glyphs[s.glyph_cnt] = cur;
- s.glyph_kerns[s.glyph_cnt] = kern_adjust;
- s.glyph_cnt++;
-
- s.width_left -= char_width + kern_adjust;
- }
- if (s.height_left >= this->font_height && s.glyph_cnt > 0)
- finish_line (this, &s);
-
- if (width != NULL)
- *width = s.max_width;
- if (height != NULL)
- *height = text->v - s.height_left;
- free (s.glyphs);
- free (s.glyph_kerns);
-}
-
-static void
-ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
- int *width, int *height)
-{
- text (this, t, false, width, height);
-}
-
-static void
-ps_text_draw (struct outp_driver *this, const struct outp_text *t)
-{
- assert (this->page_open);
- text (this, t, true, NULL, NULL);
-}
-\f
-static void embed_font (struct outp_driver *this, struct font *font);
-static void reencode_font (struct outp_driver *this, struct font *font);
-
-/* Loads and returns the font for STRING, which has the format
- "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
- PFA or PFB file's name, and ENC is the encoding file's name.
- PFA and ENC are optional.
- Returns a null pointer if unsuccessful. */
-static struct font *
-load_font (const char *string_)
-{
- char *string = xstrdup (string_);
- struct font *font;
- char *position = string;
- char *token;
- char *afm_file_name;
-
- font = xmalloc (sizeof *font);
- font->metrics = NULL;
- font->embed_fn = NULL;
- font->encoding_fn = NULL;
-
- token = strsep (&position, ",");
- if (token == NULL)
- {
- error (0, 0, _("\"%s\": bad font specification"), string);
- goto error;
- }
-
- /* Read AFM file. */
- afm_file_name = find_ps_file (token);
- if (afm_file_name == NULL)
- {
- error (0, 0, _("could not find AFM file \"%s\""), token);
- goto error;
- }
- font->metrics = afm_open (afm_file_name);
- free (afm_file_name);
- if (font->metrics == NULL)
- goto error;
-
- /* Find font file to embed. */
- token = strsep (&position, ",");
- if (token != NULL && *token != '\0')
- {
- font->embed_fn = find_ps_file (token);
- if (font->embed_fn == NULL)
- error (0, 0, _("could not find font \"%s\""), token);
- }
-
- /* Find encoding. */
- token = strsep (&position, ",");
- if (token != NULL && *token == '\0')
- {
- font->encoding_fn = find_ps_file (token);
- if (font->encoding_fn == NULL)
- error (0, 0, _("could not find encoding \"%s\""), token);
- }
-
- free (string);
- return font;
-
- error:
- free (string);
- free_font (font);
- return NULL;
-}
-
-/* Frees FONT. */
-static void
-free_font (struct font *font)
-{
- if (font != NULL)
- {
- afm_close (font->metrics);
- free (font->embed_fn);
- free (font->encoding_fn);
- free (font);
- }
-}
-
-/* Emits PostScript code to embed FONT (if necessary), scale it
- to the proper size, re-encode it (if necessary), and store the
- resulting font as an object named F#, where INDEX is
- substituted for #. */
-static void
-setup_font (struct outp_driver *this, struct font *font, int index)
-{
- struct ps_driver_ext *x = this->ext;
- char *ps_name;
-
- if (font->embed_fn != NULL)
- embed_font (this, font);
- else
- fprintf (x->file, "%%%%IncludeResource: font %s\n",
- afm_get_findfont_name (font->metrics));
-
- ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
- fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
- free (ps_name);
-
- if (font->encoding_fn != NULL)
- reencode_font (this, font);
-
- fprintf (x->file, "/F%d ED\n", index);
-}
-
-/* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
- end-of-file or on error. */
-static void
-copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
-{
- while (copy_bytes > 0)
- {
- char buffer[BUFSIZ];
- unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
- size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
- size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
- if (write_bytes != chunk_bytes)
- break;
- copy_bytes -= chunk_bytes;
- }
-}
-
-/* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
- end-of-file or on error. The bytes are translated into
- hexadecimal during copying and broken into lines with
- new-line characters. */
-static void
-copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
-{
- unsigned long i;
-
- for (i = 0; i < copy_bytes; i++)
- {
- int c = getc (src);
- if (c == EOF)
- break;
- if (i > 0 && i % 36 == 0)
- putc ('\n', dst);
- fprintf (dst, "%02X", c);
- }
- putc ('\n', dst);
-}
-
-/* Embeds the given FONT into THIS driver's output stream. */
-static void
-embed_font (struct outp_driver *this, struct font *font)
-{
- struct ps_driver_ext *x = this->ext;
- FILE *file;
- int c;
-
- file = fopen (font->embed_fn, "rb");
- if (file == NULL)
- {
- error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
- return;
- }
-
- fprintf (x->file, "%%%%BeginResource: font %s\n",
- afm_get_findfont_name (font->metrics));
-
- c = getc (file);
- ungetc (c, file);
- if (c != 128)
- {
- /* PFA file. Copy literally. */
- copy_bytes_literally (file, x->file, ULONG_MAX);
- }
- else
- {
- /* PFB file. Translate as specified in Adobe Technical
- Note #5040. */
- while ((c = getc (file)) == 128)
- {
- int type;
- unsigned long length;
-
- type = getc (file);
- if (type == 3)
- break;
-
- length = getc (file);
- length |= (unsigned long) getc (file) << 8;
- length |= (unsigned long) getc (file) << 16;
- length |= (unsigned long) getc (file) << 24;
-
- if (type == 1)
- copy_bytes_literally (file, x->file, length);
- else if (type == 2)
- copy_bytes_as_hex (file, x->file, length);
- else
- break;
- }
- }
- if (freaderror (file))
- error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
- fputs ("%%EndResource\n", x->file);
-}
-
-/* Re-encodes FONT according to the specified encoding. */
-static void
-reencode_font (struct outp_driver *this, struct font *font)
-{
- struct ps_driver_ext *x = this->ext;
-
- struct string line;
-
- int line_number;
- FILE *file;
-
- char *tab[256];
-
- int i;
-
- file = fopen (font->encoding_fn, "r");
- if (file == NULL)
- {
- error (errno, 0, _("cannot open font encoding file \"%s\""),
- font->encoding_fn);
- return;
- }
-
- for (i = 0; i < 256; i++)
- tab[i] = NULL;
-
- line_number = 0;
-
- ds_init_empty (&line);
- while (ds_read_config_line (&line, &line_number, file))
- {
- char *pschar, *code;
- char *save_ptr, *tail;
- int code_val;
-
- if (ds_is_empty (&line) == 0)
- continue;
-
- pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
- code = strtok_r (NULL, " \t\r\n", &save_ptr);
- if (pschar == NULL || code == NULL)
- continue;
-
- code_val = strtol (code, &tail, 0);
- if (*tail)
- {
- error_at_line (0, 0, font->encoding_fn, line_number,
- _("invalid numeric format"));
- continue;
- }
- if (code_val < 0 || code_val > 255)
- continue;
- if (tab[code_val] != 0)
- free (tab[code_val]);
- tab[code_val] = xstrdup (pschar);
- }
- ds_destroy (&line);
-
- fputs ("[", x->file);
- for (i = 0; i < 256; i++)
- {
- char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
- fprintf (x->file, "%s\n", name);
- free (name);
- free (tab[i]);
- }
- fputs ("] RF\n", x->file);
-
- if (freaderror (file) != 0)
- error (errno, 0, _("closing Postscript encoding \"%s\""),
- font->encoding_fn);
-}
-
-/* PostScript driver class. */
-const struct outp_class postscript_class =
-{
- "postscript",
- 0,
-
- ps_open_driver,
- ps_close_driver,
-
- ps_open_page,
- ps_close_page,
- NULL,
-
- NULL, /* output_chart */
-
- ps_submit,
-
- ps_line,
- ps_text_metrics,
- ps_text_draw,
-};