X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fpostscript.c;h=614fb5055bc3104a70d9e289536bac1980be0197;hb=8eac4df36306cd357bba29ffbfaddc537fc0be47;hp=f54ef1684f222ced82d22140d2c636c173748528;hpb=2048ef1bcb81fa766ccff3bb1cf30a886316983c;p=pspp-builds.git diff --git a/src/output/postscript.c b/src/output/postscript.c index f54ef168..614fb505 100644 --- a/src/output/postscript.c +++ b/src/output/postscript.c @@ -1,5 +1,5 @@ /* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. + Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc. Written by Ben Pfaff . This program is free software; you can redistribute it and/or @@ -19,505 +19,250 @@ #include -/*this #if encloses the remainder of the file. */ -#if !NO_POSTSCRIPT - #include -#include "chart.h" -#include "message.h" #include #include #include #include - -#if HAVE_UNISTD_H #include -#endif -#include "alloc.h" -#include "bit-vector.h" -#include "message.h" -#include "filename.h" -#include "font.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "afm.h" +#include "chart.h" +#include "error.h" #include "getline.h" -#include "hash.h" #include "intprops.h" -#include "misc.h" -#include "output.h" #include "manager.h" -#include "start-date.h" -#include "version.h" +#include "minmax.h" +#include "output.h" +#include "size_max.h" +#include "strsep.h" #include "gettext.h" #define _(msgid) gettext (msgid) -/* FIXMEs: - - optimize-text-size not implemented. - - Line buffering is the only possibility; page buffering should also - be possible. - - max-fonts-simult - - Should add a field to give a file that has a list of fonts - typically used. - - Should add an option that tells the driver it can emit %%Include:'s. - - Should have auto-encode=true stream-edit or whatever to allow - addition to list of encodings. - - Should align fonts of different sizes along their baselines (see - text()). */ - /* PostScript driver options: (defaults listed first) output-file="pspp.ps" - color=yes|no - data=clean7bit|clean8bit|binary - line-ends=lf|crlf 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 - font-dir=devps - prologue-file=ps-prologue - device-file=DESC - encoding-file=ps-encodings - auto-encode=true|false - - prop-font-family=T - fixed-font-family=C + prop-font=Times-Roman + emph-font=Times-Italic + fixed-font=Courier font-size=10000 - line-style=thick|double - line-gutter=0.5pt - line-spacing=0.5pt + line-gutter=1pt + line-spacing=1pt line-width=0.5pt - line-width-thick=1pt - - optimize-text-size=1|0|2 - optimize-line-size=1|0 - max-fonts-simult=0 Max # of fonts in printer memory at once (0=infinite) */ -/* The number of `psus' (PostScript driver UnitS) per inch. Although - this is a #define, the value is expected never to change. If it - does, review all uses. */ +/* The number of `psus' (PostScript driver UnitS) per inch. */ #define PSUS 72000 -/* Magic numbers for PostScript and EPSF drivers. */ -enum - { - MAGIC_PS, - MAGIC_EPSF - }; - -/* Orientations. */ -enum - { - OTN_PORTRAIT, /* Portrait. */ - OTN_LANDSCAPE /* Landscape. */ - }; - -/* Output options. */ -enum - { - OPO_MIRROR_HORZ = 001, /* 1=Mirror across a horizontal axis. */ - OPO_MIRROR_VERT = 002, /* 1=Mirror across a vertical axis. */ - OPO_ROTATE_180 = 004, /* 1=Rotate the page 180 degrees. */ - OPO_COLOR = 010, /* 1=Enable color. */ - OPO_HEADERS = 020, /* 1=Draw headers at top of page. */ - OPO_AUTO_ENCODE = 040, /* 1=Add encodings semi-intelligently. */ - OPO_DOUBLE_LINE = 0100 /* 1=Double lines instead of thick lines. */ - }; - -/* Data allowed in output. */ -enum - { - ODA_CLEAN7BIT, /* 0x09, 0x0a, 0x0d, 0x1b...0x7e */ - ODA_CLEAN8BIT, /* 0x09, 0x0a, 0x0d, 0x1b...0xff */ - ODA_BINARY, /* 0x00...0xff */ - ODA_COUNT - }; - -/* Types of lines for purpose of caching. */ -enum - { - horz, /* Single horizontal. */ - dbl_horz, /* Double horizontal. */ - spl_horz, /* Special horizontal. */ - vert, /* Single vertical. */ - dbl_vert, /* Double vertical. */ - spl_vert, /* Special vertical. */ - n_line_types - }; - -/* Cached line. */ -struct line_form - { - int ind; /* Independent var. Don't reorder. */ - int mdep; /* Maximum number of dependent var pairs. */ - int ndep; /* Current number of dependent var pairs. */ - int dep[1][2]; /* Dependent var pairs. */ - }; - -/* Contents of ps_driver_ext.loaded. */ -struct font_entry - { - char *dit; /* Font Groff name. */ - struct font_desc *font; /* Font descriptor. */ - }; - -/* Combines a font with a font size for benefit of generated code. */ -struct ps_font_combo - { - struct font_entry *font; /* Font. */ - int size; /* Font size. */ - int index; /* PostScript index. */ - }; - -/* A font encoding. */ -struct ps_encoding +/* A PostScript font. */ +struct font { - char *filename; /* Normalized filename of this encoding. */ - int index; /* Index value. */ + 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 { - /* User parameters. */ - int orientation; /* OTN_PORTRAIT or OTN_LANDSCAPE. */ - int output_options; /* OPO_*. */ - int data; /* ODA_*. */ + 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. */ - char eol[3]; /* End of line--CR, LF, or CRLF. */ - - char *font_dir; /* Font directory relative to font path. */ - char *prologue_fn; /* Prologue's filename relative to font dir. */ - char *desc_fn; /* DESC filename relative to font dir. */ - char *encoding_fn; /* Encoding's filename relative to font dir. */ - - char *prop_family; /* Default proportional font family. */ - char *fixed_family; /* Default fixed-pitch font family. */ - int font_size; /* Default font size (psus). */ - int line_gutter; /* Space around lines. */ int line_space; /* Space between lines. */ int line_width; /* Width of lines. */ - int line_width_thick; /* Width of thick lines. */ - int text_opt; /* Text optimization level. */ - int line_opt; /* Line optimization level. */ - int max_fonts; /* Max # of simultaneous fonts (0=infinite). */ - - /* Internal state. */ - struct file_ext file; /* Output file. */ - int page_number; /* Current page number. */ - int file_page_number; /* Page number in this file. */ - int w, l; /* Paper size. */ - struct hsh_table *lines[n_line_types]; /* Line buffers. */ - - struct font_entry *prop; /* Default Roman proportional font. */ - struct font_entry *fixed; /* Default Roman fixed-pitch font. */ - struct hsh_table *loaded; /* Fonts in memory. */ - - struct hsh_table *combos; /* Combinations of fonts with font sizes. */ - struct ps_font_combo *last_font; /* PostScript selected font. */ - int next_combo; /* Next font combo position index. */ - - struct hsh_table *encodings;/* Set of encodings. */ - int next_encoding; /* Next font encoding index. */ - - /* Currently selected font. */ - struct font_entry *current; /* Current font. */ - char *family; /* Font family. */ - int size; /* Size in psus. */ - } -ps_driver_ext; + struct font *fonts[OUTP_FONT_CNT]; + int last_font; /* Index of last font set with setfont. */ + }; /* Transform logical y-ordinate Y into a page ordinate. */ #define YT(Y) (this->length - (Y)) -/* Prototypes. */ -static int postopen (struct file_ext *); -static int preclose (struct file_ext *); +static bool handle_option (struct outp_driver *this, const char *key, + const struct string *val); static void draw_headers (struct outp_driver *this); -static int compare_font_entry (const void *, const void *, void *param); -static unsigned hash_font_entry (const void *, void *param); -static void free_font_entry (void *, void *foo); -static struct font_entry *load_font (struct outp_driver *, const char *dit); -static void init_fonts (void); -static void done_fonts (void); - -static void dump_lines (struct outp_driver *this); +static void write_ps_prologue (struct outp_driver *); -static void read_ps_encodings (struct outp_driver *this); -static int compare_ps_encoding (const void *pa, const void *pb, void *foo); -static unsigned hash_ps_encoding (const void *pa, void *foo); -static void free_ps_encoding (void *a, void *foo); -static void add_encoding (struct outp_driver *this, char *filename); -static struct ps_encoding *default_encoding (struct outp_driver *this); +static char *quote_ps_name (const char *string); -static int compare_ps_combo (const void *pa, const void *pb, void *foo); -static unsigned hash_ps_combo (const void *pa, void *foo); -static void free_ps_combo (void *a, void *foo); - -static char *quote_ps_name (char *dest, const char *string); -static char *quote_ps_string (char *dest, 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); /* Driver initialization. */ -static int -ps_open_global (struct outp_class *this UNUSED) -{ - init_fonts (); - groff_init (); - return 1; -} - -static int -ps_close_global (struct outp_class *this UNUSED) -{ - groff_done (); - done_fonts (); - return 1; -} - -static int * -ps_font_sizes (struct outp_class *this UNUSED, int *n_valid_sizes) -{ - /* Allow fonts up to 1" in height. */ - static int valid_sizes[] = - {1, PSUS, 0, 0}; - - assert (n_valid_sizes != NULL); - *n_valid_sizes = 1; - return valid_sizes; -} - -static int -ps_preopen_driver (struct outp_driver *this) +static bool +ps_open_driver (struct outp_driver *this, struct substring options) { struct ps_driver_ext *x; - - int i; + size_t i; - assert (this->driver_open == 0); - msg (VM (1), _("PostScript driver initializing as `%s'..."), this->name); - - this->ext = x = xmalloc (sizeof *x); - this->res = PSUS; - this->horiz = this->vert = 1; this->width = this->length = 0; + this->font_height = PSUS * 10 / 72; - x->orientation = OTN_PORTRAIT; - x->output_options = OPO_COLOR | OPO_HEADERS | OPO_AUTO_ENCODE; - x->data = ODA_CLEAN7BIT; - - x->left_margin = x->right_margin = - x->top_margin = x->bottom_margin = PSUS / 2; - - strcpy (x->eol, "\n"); - - x->font_dir = NULL; - x->prologue_fn = NULL; - x->desc_fn = NULL; - x->encoding_fn = NULL; - - x->prop_family = NULL; - x->fixed_family = NULL; - x->font_size = PSUS * 10 / 72; - - x->line_gutter = PSUS / 144; - x->line_space = PSUS / 144; - x->line_width = PSUS / 144; - x->line_width_thick = PSUS / 48; - - x->text_opt = -1; - x->line_opt = -1; - x->max_fonts = 0; - - x->file.filename = NULL; - x->file.mode = "wb"; - x->file.file = NULL; - x->file.sequence_no = &x->page_number; - x->file.param = this; - x->file.postopen = postopen; - x->file.preclose = preclose; + this->ext = x = xmalloc (sizeof *x); + x->file_name = xstrdup ("pspp.ps"); + x->file = NULL; + x->draw_headers = true; x->page_number = 0; - x->w = x->l = 0; - - x->file_page_number = 0; - for (i = 0; i < n_line_types; i++) - x->lines[i] = NULL; - x->last_font = NULL; - - x->prop = NULL; - x->fixed = NULL; - x->loaded = NULL; - - x->next_combo = 0; - x->combos = NULL; - - x->encodings = hsh_create (31, compare_ps_encoding, hash_ps_encoding, - free_ps_encoding, NULL); - x->next_encoding = 0; - - x->current = NULL; - x->family = NULL; - x->size = 0; - - return 1; -} + x->portrait = true; + x->paper_width = PSUS * 17 / 2; + x->paper_length = PSUS * 11; + 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; -static int -ps_postopen_driver (struct outp_driver *this) -{ - struct ps_driver_ext *x = this->ext; - - assert (this->driver_open == 0); + outp_parse_options (options, handle_option, this); - if (this->width == 0) + x->file = fn_open (x->file_name, "w"); + if (x->file == NULL) { - this->width = PSUS * 17 / 2; /* Defaults to 8.5"x11". */ - this->length = PSUS * 11; + error (0, errno, _("opening PostScript output file \"%s\""), + x->file_name); + goto error; } - if (x->text_opt == -1) - x->text_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1; - if (x->line_opt == -1) - x->line_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1; - - x->w = this->width; - x->l = this->length; - if (x->orientation == OTN_LANDSCAPE) + if (x->portrait) { - int temp = this->width; - this->width = this->length; - this->length = temp; + 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->output_options & OPO_HEADERS) + if (x->draw_headers) { - this->length -= 3 * x->font_size; - x->top_margin += 3 * x->font_size; + int header_length = 3 * this->font_height; + this->length -= header_length; + x->top_margin += header_length; } - if (NULL == x->file.filename) - x->file.filename = xstrdup ("pspp.ps"); - if (x->font_dir == NULL) - x->font_dir = xstrdup ("devps"); - if (x->prologue_fn == NULL) - x->prologue_fn = xstrdup ("ps-prologue"); - if (x->desc_fn == NULL) - x->desc_fn = xstrdup ("DESC"); - if (x->encoding_fn == NULL) - x->encoding_fn = xstrdup ("ps-encodings"); + 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 (x->prop_family == NULL) - x->prop_family = xstrdup ("H"); - if (x->fixed_family == NULL) - x->fixed_family = xstrdup ("C"); + 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; + } - read_ps_encodings (this); + 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; - x->family = NULL; - x->size = PSUS / 6; + 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); - if (this->length / x->font_size < 15) - { - msg (SE, _("PostScript driver: The defined 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 / x->font_size, - x->font_size / 1000, x->font_size % 1000); - return 0; - } + write_ps_prologue (this); - this->driver_open = 1; - msg (VM (2), _("%s: Initialization complete."), this->name); + return true; - return 1; + error: + this->class->close_driver (this); + return false; } -static int +static bool ps_close_driver (struct outp_driver *this) { struct ps_driver_ext *x = this->ext; - - int i; - - assert (this->driver_open == 1); - msg (VM (2), _("%s: Beginning closing..."), this->name); - - fn_close_ext (&x->file); - free (x->file.filename); - free (x->font_dir); - free (x->prologue_fn); - free (x->desc_fn); - free (x->encoding_fn); - free (x->prop_family); - free (x->fixed_family); - free (x->family); - for (i = 0; i < n_line_types; i++) - hsh_destroy (x->lines[i]); - hsh_destroy (x->encodings); - hsh_destroy (x->combos); - hsh_destroy (x->loaded); + bool ok; + size_t i; + + 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); - - this->driver_open = 0; - msg (VM (3), _("%s: Finished closing."), this->name); - - return 1; -} - -/* font_entry comparison function for hash tables. */ -static int -compare_font_entry (const void *a, const void *b, void *foobar UNUSED) -{ - return strcmp (((struct font_entry *) a)->dit, ((struct font_entry *) b)->dit); -} -/* font_entry hash function for hash tables. */ -static unsigned -hash_font_entry (const void *fe_, void *foobar UNUSED) -{ - const struct font_entry *fe = fe_; - return hsh_hash_string (fe->dit); -} - -/* font_entry destructor function for hash tables. */ -static void -free_font_entry (void *pa, void *foo UNUSED) -{ - struct font_entry *a = pa; - free (a->dit); - free (a); + return ok; } /* Generic option types. */ enum { - boolean_arg = -10, + output_file_arg, + paper_size_arg, + orientation_arg, + line_style_arg, + boolean_arg, pos_int_arg, dimension_arg, string_arg, @@ -525,147 +270,73 @@ enum }; /* All the options that the PostScript driver supports. */ -static struct outp_option option_tab[] = +static const struct outp_option option_tab[] = { - /* *INDENT-OFF* */ - {"output-file", 1, 0}, - {"paper-size", 2, 0}, - {"orientation", 3, 0}, - {"color", boolean_arg, 0}, - {"data", 4, 0}, - {"auto-encode", boolean_arg, 5}, + {"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-dir", string_arg, 0}, - {"prologue-file", string_arg, 1}, - {"device-file", string_arg, 2}, - {"encoding-file", string_arg, 3}, - {"prop-font-family", string_arg, 5}, - {"fixed-font-family", string_arg, 6}, {"font-size", pos_int_arg, 4}, - {"optimize-text-size", nonneg_int_arg, 0}, - {"optimize-line-size", nonneg_int_arg, 1}, - {"max-fonts-simult", nonneg_int_arg, 2}, - {"line-ends", 6, 0}, - {"line-style", 7, 0}, + + {"line-width", dimension_arg, 0}, + {"line-gutter", dimension_arg, 1}, {"line-width", dimension_arg, 2}, - {"line-gutter", dimension_arg, 3}, - {"line-width", dimension_arg, 4}, - {"line-width-thick", dimension_arg, 5}, - {"", 0, 0}, - /* *INDENT-ON* */ + {NULL, 0, 0}, }; -static struct outp_option_info option_info; -static void -ps_option (struct outp_driver *this, const char *key, const struct string *val) +static bool +handle_option (struct outp_driver *this, const char *key, + const struct string *val) { struct ps_driver_ext *x = this->ext; - int cat, subcat; - char *value = ds_c_str (val); + int subcat; + char *value = ds_cstr (val); - cat = outp_match_keyword (key, option_tab, &option_info, &subcat); - - switch (cat) + switch (outp_match_keyword (key, option_tab, &subcat)) { - case 0: - msg (SE, _("Unknown configuration parameter `%s' for PostScript device " - "driver."), key); + case -1: + error (0, 0, + _("unknown configuration parameter `%s' for PostScript device " + "driver"), key); break; - case 1: - free (x->file.filename); - x->file.filename = xstrdup (value); + case output_file_arg: + free (x->file_name); + x->file_name = xstrdup (value); break; - case 2: + case paper_size_arg: outp_get_paper_size (value, &this->width, &this->length); break; - case 3: + case orientation_arg: if (!strcmp (value, "portrait")) - x->orientation = OTN_PORTRAIT; + x->portrait = true; else if (!strcmp (value, "landscape")) - x->orientation = OTN_LANDSCAPE; - else - msg (SE, _("Unknown orientation `%s'. Valid orientations are " - "`portrait' and `landscape'."), value); - break; - case 4: - if (!strcmp (value, "clean7bit") || !strcmp (value, "Clean7Bit")) - x->data = ODA_CLEAN7BIT; - else if (!strcmp (value, "clean8bit") - || !strcmp (value, "Clean8Bit")) - x->data = ODA_CLEAN8BIT; - else if (!strcmp (value, "binary") || !strcmp (value, "Binary")) - x->data = ODA_BINARY; - else - msg (SE, _("Unknown value for `data'. Valid values are `clean7bit', " - "`clean8bit', and `binary'.")); - break; - case 6: - if (!strcmp (value, "lf")) - strcpy (x->eol, "\n"); - else if (!strcmp (value, "crlf")) - strcpy (x->eol, "\r\n"); - else - msg (SE, _("Unknown value for `line-ends'. Valid values are `lf' and " - "`crlf'.")); - break; - case 7: - if (!strcmp (value, "thick")) - x->output_options &= ~OPO_DOUBLE_LINE; - else if (!strcmp (value, "double")) - x->output_options |= OPO_DOUBLE_LINE; + x->portrait = false; else - msg (SE, _("Unknown value for `line-style'. Valid values are `thick' " - "and `double'.")); + error (0, 0, _("unknown orientation `%s' (valid orientations are " + "`portrait' and `landscape')"), value); break; case boolean_arg: - { - int setting; - int mask; - - if (!strcmp (value, "on") || !strcmp (value, "true") - || !strcmp (value, "yes") || atoi (value)) - setting = 1; - else if (!strcmp (value, "off") || !strcmp (value, "false") - || !strcmp (value, "no") || !strcmp (value, "0")) - setting = 0; - else - { - msg (SE, _("Boolean value expected for %s."), key); - return; - } - switch (subcat) - { - case 0: - mask = OPO_COLOR; - break; - case 1: - mask = OPO_HEADERS; - break; - case 2: - mask = OPO_MIRROR_HORZ; - break; - case 3: - mask = OPO_MIRROR_VERT; - break; - case 4: - mask = OPO_ROTATE_180; - break; - case 5: - mask = OPO_AUTO_ENCODE; - break; - default: - assert (0); - abort (); - } - if (setting) - x->output_options |= mask; - else - x->output_options &= ~mask; - } + 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: { @@ -676,13 +347,13 @@ ps_option (struct outp_driver *this, const char *key, const struct string *val) arg = strtol (value, &tail, 0); if (arg < 1 || errno == ERANGE || *tail) { - msg (SE, _("Positive integer required as value for `%s'."), key); + error (0, 0, _("positive integer value required for `%s'"), key); break; } if ((subcat == 4 || subcat == 5) && arg < 1000) { - msg (SE, _("Default font size must be at least 1 point (value " - "of 1000 for key `%s')."), key); + error (0, 0, _("default font size must be at least 1 point (value " + "of 1000 for key `%s')"), key); break; } switch (subcat) @@ -700,10 +371,10 @@ ps_option (struct outp_driver *this, const char *key, const struct string *val) x->bottom_margin = arg; break; case 4: - x->font_size = arg; + this->font_height = arg; break; default: - assert (0); + NOT_REACHED (); } } break; @@ -713,2169 +384,708 @@ ps_option (struct outp_driver *this, const char *key, const struct string *val) if (dimension <= 0) { - msg (SE, _("Value for `%s' must be a dimension of positive " - "length (i.e., `1in')."), key); + error (0, 0, _("value for `%s' must be a dimension of positive " + "length (i.e., `1in')"), key); break; } switch (subcat) { - case 2: + case 0: x->line_width = dimension; break; - case 3: + case 1: x->line_gutter = dimension; break; - case 4: + case 2: x->line_width = dimension; break; - case 5: - x->line_width_thick = dimension; - break; default: - assert (0); + NOT_REACHED (); } } break; case string_arg: { - char **dest; - switch (subcat) - { - case 0: - dest = &x->font_dir; - break; - case 1: - dest = &x->prologue_fn; - break; - case 2: - dest = &x->desc_fn; - break; - case 3: - dest = &x->encoding_fn; - break; - case 5: - dest = &x->prop_family; - break; - case 6: - dest = &x->fixed_family; - break; - default: - assert (0); - abort (); - } - if (*dest) - free (*dest); - *dest = xstrdup (value); - } - break; - case nonneg_int_arg: - { - char *tail; - int arg; - - errno = 0; - arg = strtol (value, &tail, 0); - if (arg < 0 || errno == ERANGE || *tail) - { - msg (SE, _("Nonnegative integer required as value for `%s'."), key); - break; - } - switch (subcat) - { - case 0: - x->text_opt = arg; - break; - case 1: - x->line_opt = arg; - break; - case 2: - x->max_fonts = arg; - break; - default: - assert (0); - } + 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: - assert (0); + NOT_REACHED (); } + + return true; } /* Looks for a PostScript font file or config file in all the - appropriate places. Returns the filename on success, NULL on + appropriate places. Returns the file name on success, NULL on failure. */ -/* PORTME: Filename operations. */ static char * -find_ps_file (struct outp_driver *this, const char *name) +find_ps_file (const char *name) { - struct ps_driver_ext *x = this->ext; - char *cp; - - /* x->font_dir + name: "devps/ps-encodings". */ - char *basename; - - /* Usually equal to groff_font_path. */ - char *pathname; - - /* Final filename. */ - char *fn; - - /* Make basename. */ - basename = local_alloc (strlen (x->font_dir) + 1 + strlen (name) + 1); - cp = stpcpy (basename, x->font_dir); - *cp++ = DIR_SEPARATOR; - strcpy (cp, name); - - /* Decide on search path. */ - { - const char *pre_pathname; - - pre_pathname = getenv ("STAT_GROFF_FONT_PATH"); - if (pre_pathname == NULL) - pre_pathname = getenv ("GROFF_FONT_PATH"); - if (pre_pathname == NULL) - pre_pathname = groff_font_path; - pathname = fn_tilde_expand (pre_pathname); - } - - /* Search all possible places for the file. */ - fn = fn_search_path (basename, pathname, NULL); - if (fn == NULL) - fn = fn_search_path (basename, config_path, NULL); - if (fn == NULL) - fn = fn_search_path (name, pathname, NULL); - if (fn == NULL) - fn = fn_search_path (name, config_path, NULL); - free (pathname); - local_free (basename); - - return fn; + 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, NULL); + free (base_name); + return file_name; + } } -/* Encodings. */ +/* Basic file operations. */ -/* Hash table comparison function for ps_encoding's. */ -static int -compare_ps_encoding (const void *pa, const void *pb, void *foo UNUSED) +/* Writes the PostScript prologue to file F. */ +static void +write_ps_prologue (struct outp_driver *this) { - const struct ps_encoding *a = pa; - const struct ps_encoding *b = pb; + struct ps_driver_ext *x = this->ext; + size_t embedded_cnt, preloaded_cnt; + size_t i; - return strcmp (a->filename, b->filename); -} + fputs ("%!PS-Adobe-3.0\n", x->file); + fputs ("%%Pages: (atend)\n", x->file); -/* Hash table hash function for ps_encoding's. */ -static unsigned -hash_ps_encoding (const void *pa, void *foo UNUSED) + 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 struct ps_encoding *a = pa; + const char *cp; - return hsh_hash_string (a->filename); + 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); } -/* Hash table free function for ps_encoding's. */ static void -free_ps_encoding (void *pa, void *foo UNUSED) +ps_open_page (struct outp_driver *this) { - struct ps_encoding *a = pa; + struct ps_driver_ext *x = this->ext; - free (a->filename); - free (a); -} + /* Assure page independence. */ + x->last_font = -1; -/* Iterates through the list of encodings used for this driver - instance, reads each of them from disk, and writes them as - PostScript code to the output file. */ -static void -output_encodings (struct outp_driver *this) -{ - struct ps_driver_ext *x = this->ext; + x->page_number++; - struct hsh_iterator iter; - struct ps_encoding *pe; + fprintf (x->file, + "%%%%Page: %d %d\n" + "%%%%BeginPageSetup\n" + "/pg save def 0.001 dup scale\n", + x->page_number, x->page_number); - struct string line, buf; + if (!x->portrait) + fprintf (x->file, + "%d 0 translate 90 rotate\n", + x->paper_width); - ds_init (&line, 128); - ds_init (&buf, 128); - for (pe = hsh_first (x->encodings, &iter); pe != NULL; - pe = hsh_next (x->encodings, &iter)) - { - FILE *f; + if (x->bottom_margin != 0 || x->left_margin != 0) + fprintf (x->file, + "%d %d translate\n", + x->left_margin, x->bottom_margin); - msg (VM (1), _("%s: %s: Opening PostScript font encoding..."), - this->name, pe->filename); - - f = fopen (pe->filename, "r"); - if (!f) - { - msg (IE, _("PostScript driver: Cannot open encoding file `%s': %s. " - "Substituting ISOLatin1Encoding for missing encoding."), - pe->filename, strerror (errno)); - fprintf (x->file.file, "/E%x ISOLatin1Encoding def%s", - pe->index, x->eol); - } - else - { - struct file_locator where; - - const char *tab[256]; - - char *pschar; - char *code; - int code_val; - char *fubar; - - const char *notdef = ".notdef"; - - int i; - - for (i = 0; i < 256; i++) - tab[i] = notdef; - - where.filename = pe->filename; - where.line_number = 0; - err_push_file_locator (&where); - - while (ds_get_config_line (f, &buf, &where)) - { - char *sp; - - if (buf.length == 0) - continue; - - pschar = strtok_r (ds_c_str (&buf), " \t\r\n", &sp); - code = strtok_r (NULL, " \t\r\n", &sp); - if (*pschar == 0 || *code == 0) - continue; - code_val = strtol (code, &fubar, 0); - if (*fubar) - { - msg (IS, _("PostScript driver: Invalid numeric format.")); - continue; - } - if (code_val < 0 || code_val > 255) - { - msg (IS, _("PostScript driver: Codes must be between 0 " - "and 255. (%d is not allowed.)"), code_val); - break; - } - tab[code_val] = local_alloc (strlen (pschar) + 1); - strcpy ((char *) (tab[code_val]), pschar); - } - err_pop_file_locator (&where); - - ds_clear (&line); - ds_printf (&line, "/E%x[", pe->index); - for (i = 0; i < 257; i++) - { - char temp[288]; - - if (i < 256) - { - quote_ps_name (temp, tab[i]); - if (tab[i] != notdef) - local_free (tab[i]); - } - else - strcpy (temp, "]def"); - - if (ds_length (&line) + strlen (temp) > 70) - { - ds_puts (&line, x->eol); - fputs (ds_c_str (&line), x->file.file); - ds_clear (&line); - } - ds_puts (&line, temp); - } - ds_puts (&line, x->eol); - fputs (ds_c_str (&line), x->file.file); - - if (fclose (f) == EOF) - msg (MW, _("PostScript driver: Error closing encoding file `%s'."), - pe->filename); - - msg (VM (2), _("%s: PostScript font encoding read successfully."), - this->name); - } - } - ds_destroy (&line); - ds_destroy (&buf); + fprintf (x->file, + "/LW %d def %d setlinewidth\n" + "%%%%EndPageSetup\n", + x->line_width, x->line_width); + + if (x->draw_headers) + draw_headers (this); } -/* Finds the ps_encoding in THIS that corresponds to the file with - name NORM_FILENAME, which must have previously been normalized with - normalize_filename(). */ -static struct ps_encoding * -get_encoding (struct outp_driver *this, const char *norm_filename) +static void +ps_close_page (struct outp_driver *this) { struct ps_driver_ext *x = this->ext; - struct ps_encoding *pe; - - pe = (struct ps_encoding *) hsh_find (x->encodings, (void *) &norm_filename); - return pe; + fputs ("%%PageTrailer\n" + "EP\n", + x->file); } -/* Searches the filesystem for an encoding file with name FILENAME; - returns its malloc'd, normalized name if found, otherwise NULL. */ -static char * -find_encoding_file (struct outp_driver *this, char *filename) +static void +ps_submit (struct outp_driver *this UNUSED, struct som_entity *s) +{ + switch (s->type) + { + case SOM_CHART: + break; + default: + NOT_REACHED (); + } +} + +/* 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) { - char *cp, *temp; - - if (filename == NULL) - return NULL; - while (isspace ((unsigned char) *filename)) - filename++; - for (cp = filename; *cp && !isspace ((unsigned char) *cp); cp++) - ; - if (cp == filename) - return NULL; - *cp = 0; - - temp = find_ps_file (this, filename); - if (temp == NULL) - return NULL; - - filename = fn_normalize (temp); - assert (filename != NULL); - free (temp); - - return filename; + struct ps_driver_ext *ext = this->ext; + fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1)); } -/* Adds the encoding represented by the not-necessarily-normalized - file FILENAME to the list of encodings, if it exists and is not - already in the list. */ +/* 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 -add_encoding (struct outp_driver *this, char *filename) +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) { - struct ps_driver_ext *x = this->ext; - struct ps_encoding **pe; - - filename = find_encoding_file (this, filename); - if (!filename) - return; - - pe = (struct ps_encoding **) hsh_probe (x->encodings, &filename); - if (*pe) + if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten) + dump_line (this, x0, y, x3, y); + else { - free (filename); - return; + 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); } - *pe = xmalloc (sizeof **pe); - (*pe)->filename = filename; - (*pe)->index = x->next_encoding++; } -/* Finds the file on disk that contains the list of encodings to - include in the output file, then adds those encodings to the list - of encodings. */ +/* 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 -read_ps_encodings (struct outp_driver *this) +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) { - struct ps_driver_ext *x = this->ext; - - /* Encodings file. */ - char *encoding_fn; /* `ps-encodings' filename. */ - FILE *f; - - struct string line; - struct file_locator where; - - /* It's okay if there's no list of encodings; not everyone cares. */ - encoding_fn = find_ps_file (this, x->encoding_fn); - if (encoding_fn == NULL) - return; - free (encoding_fn); - - msg (VM (1), _("%s: %s: Opening PostScript encoding list file."), - this->name, encoding_fn); - f = fopen (encoding_fn, "r"); - if (!f) + if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten) + dump_line (this, x, y0, x, y3); + else { - msg (IE, _("Opening %s: %s."), encoding_fn, strerror (errno)); - return; - } - - where.filename = encoding_fn; - where.line_number = 0; - err_push_file_locator (&where); - - ds_init (&line, 128); - - for (;;) - { - if (!ds_get_config_line (f, &line, &where)) - { - if (ferror (f)) - msg (ME, _("Reading %s: %s."), encoding_fn, strerror (errno)); - break; - } - - add_encoding (this, line.string); - } - - ds_destroy (&line); - err_pop_file_locator (&where); - - if (-1 == fclose (f)) - msg (MW, _("Closing %s: %s."), encoding_fn, strerror (errno)); - - msg (VM (2), _("%s: PostScript encoding list file read successfully."), this->name); -} - -/* Creates a default encoding for driver D that can be substituted for - an unavailable encoding. */ -struct ps_encoding * -default_encoding (struct outp_driver *d) -{ - struct ps_driver_ext *x = d->ext; - static struct ps_encoding *enc; - - if (!enc) - { - enc = xmalloc (sizeof *enc); - enc->filename = xstrdup (_("<>")); - enc->index = x->next_encoding++; - } - return enc; -} - -/* Basic file operations. */ - -/* Variables for the prologue. */ -struct ps_variable - { - const char *key; - const char *value; - }; - -static struct ps_variable *ps_var_tab; - -/* Searches ps_var_tab for a ps_variable with key KEY, and returns the - associated value. */ -static const char * -ps_get_var (const char *key) -{ - struct ps_variable *v; - - for (v = ps_var_tab; v->key; v++) - if (!strcmp (key, v->key)) - return v->value; - return NULL; -} - -/* Writes the PostScript prologue to file F. */ -static int -postopen (struct file_ext *f) -{ - static struct ps_variable dict[] = - { - {"bounding-box", 0}, - {"creator", 0}, - {"date", 0}, - {"data", 0}, - {"orientation", 0}, - {"user", 0}, - {"host", 0}, - {"prop-font", 0}, - {"fixed-font", 0}, - {"scale-factor", 0}, - {"paper-width", 0}, - {"paper-length", 0}, - {"left-margin", 0}, - {"top-margin", 0}, - {"line-width", 0}, - {"line-width-thick", 0}, - {"title", 0}, - {0, 0}, - }; - char boundbox[INT_STRLEN_BOUND (int) * 4 + 4]; -#if HAVE_UNISTD_H - char host[128]; -#endif - char scaling[INT_STRLEN_BOUND (int) + 5]; - time_t curtime; - struct tm *loctime; - char *p, *cp; - char paper_width[INT_STRLEN_BOUND (int) + 1]; - char paper_length[INT_STRLEN_BOUND (int) + 1]; - char left_margin[INT_STRLEN_BOUND (int) + 1]; - char top_margin[INT_STRLEN_BOUND (int) + 1]; - char line_width[INT_STRLEN_BOUND (int) + 1]; - char line_width_thick[INT_STRLEN_BOUND (int) + 1]; - - struct outp_driver *this = f->param; - struct ps_driver_ext *x = this->ext; - - char *prologue_fn = find_ps_file (this, x->prologue_fn); - FILE *prologue_file; - - char *buf = NULL; - size_t buf_size = 0; - - x->loaded = hsh_create (31, compare_font_entry, hash_font_entry, - free_font_entry, NULL); - - { - char *font_name = local_alloc (2 + max (strlen (x->prop_family), - strlen (x->fixed_family))); - - strcpy (stpcpy (font_name, x->prop_family), "R"); - x->prop = load_font (this, font_name); - - strcpy (stpcpy (font_name, x->fixed_family), "R"); - x->fixed = load_font (this, font_name); - - local_free(font_name); - } - - x->current = x->prop; - x->family = xstrdup (x->prop_family); - x->size = x->font_size; - - { - int *h = this->horiz_line_width, *v = this->vert_line_width; - - this->cp_x = this->cp_y = 0; - this->font_height = x->font_size; - { - struct char_metrics *metric; - - metric = font_get_char_metrics (x->prop->font, '0'); - this->prop_em_width = ((metric - ? metric->width : x->prop->font->space_width) - * x->font_size / 1000); - - metric = font_get_char_metrics (x->fixed->font, '0'); - this->fixed_width = ((metric - ? metric->width : x->fixed->font->space_width) - * x->font_size / 1000); - } - - h[0] = v[0] = 0; - h[1] = v[1] = 2 * x->line_gutter + x->line_width; - if (x->output_options & OPO_DOUBLE_LINE) - h[2] = v[2] = 2 * x->line_gutter + 2 * x->line_width + x->line_space; - else - h[2] = v[2] = 2 * x->line_gutter + x->line_width_thick; - h[3] = v[3] = 2 * x->line_gutter + x->line_width; - - { - int i; - - for (i = 0; i < (1 << OUTP_L_COUNT); i++) - { - int bit; - - /* Maximum width of any line type so far. */ - int max = 0; - - for (bit = 0; bit < OUTP_L_COUNT; bit++) - if ((i & (1 << bit)) && h[bit] > max) - max = h[bit]; - this->horiz_line_spacing[i] = this->vert_line_spacing[i] = max; - } - } - } - - if (x->output_options & OPO_AUTO_ENCODE) - { - /* It's okay if this is done more than once since add_encoding() - is idempotent over identical encodings. */ - add_encoding (this, x->prop->font->encoding); - add_encoding (this, x->fixed->font->encoding); - } - - x->file_page_number = 0; - - errno = 0; - if (prologue_fn == NULL) - { - msg (IE, _("Cannot find PostScript prologue. The use of `-vv' " - "on the command line is suggested as a debugging aid.")); - return 0; - } - - msg (VM (1), _("%s: %s: Opening PostScript prologue..."), - this->name, prologue_fn); - prologue_file = fopen (prologue_fn, "rb"); - if (prologue_file == NULL) - { - fclose (prologue_file); - free (prologue_fn); - msg (IE, "%s: %s", prologue_fn, strerror (errno)); - goto error; - } - - sprintf (boundbox, "0 0 %d %d", - x->w / (PSUS / 72) + (x->w % (PSUS / 72) > 0), - x->l / (PSUS / 72) + (x->l % (PSUS / 72) > 0)); - dict[0].value = boundbox; - - dict[1].value = (char *) version; - - curtime = time (NULL); - loctime = localtime (&curtime); - dict[2].value = asctime (loctime); - cp = strchr (dict[2].value, '\n'); - if (cp) - *cp = 0; - - switch (x->data) - { - case ODA_CLEAN7BIT: - dict[3].value = "Clean7Bit"; - break; - case ODA_CLEAN8BIT: - dict[3].value = "Clean8Bit"; - break; - case ODA_BINARY: - dict[3].value = "Binary"; - break; - default: - assert (0); - } - - if (x->orientation == OTN_PORTRAIT) - dict[4].value = "Portrait"; - else - dict[4].value = "Landscape"; - - /* PORTME: Determine username, net address. */ -#if HAVE_UNISTD_H - dict[5].value = getenv ("LOGNAME"); - if (!dict[5].value) - dict[5].value = getlogin (); - if (!dict[5].value) - dict[5].value = _("nobody"); - - if (gethostname (host, 128) == -1) - { - if (errno == ENAMETOOLONG) - host[127] = 0; - else - strcpy (host, _("nowhere")); - } - dict[6].value = host; -#else /* !HAVE_UNISTD_H */ - dict[5].value = _("nobody"); - dict[6].value = _("nowhere"); -#endif /* !HAVE_UNISTD_H */ - - cp = stpcpy (p = local_alloc (288), "font "); - quote_ps_string (cp, x->prop->font->internal_name); - dict[7].value = p; - - cp = stpcpy (p = local_alloc (288), "font "); - quote_ps_string (cp, x->fixed->font->internal_name); - dict[8].value = p; - - sprintf (scaling, "%.3f", PSUS / 72.0); - dict[9].value = scaling; - - sprintf (paper_width, "%g", x->w / (PSUS / 72.0)); - dict[10].value = paper_width; - - sprintf (paper_length, "%g", x->l / (PSUS / 72.0)); - dict[11].value = paper_length; - - sprintf (left_margin, "%d", x->left_margin); - dict[12].value = left_margin; - - sprintf (top_margin, "%d", x->top_margin); - dict[13].value = top_margin; - - sprintf (line_width, "%d", x->line_width); - dict[14].value = line_width; - - sprintf (line_width, "%d", x->line_width_thick); - dict[15].value = line_width_thick; - - if (!outp_title) - { - dict[16].value = cp = local_alloc (strlen (dict[17].value) + 30); - sprintf (cp, "PSPP (%s)", dict[17].value); - } - else - { - dict[16].value = local_alloc (strlen (outp_title) + 1); - strcpy ((char *) (dict[16].value), outp_title); - } - - ps_var_tab = dict; - while (-1 != getline (&buf, &buf_size, prologue_file)) - { - char *cp; - char *buf2; - int len; - - cp = strstr (buf, "!eps"); - if (cp) - { - if (this->class->magic == MAGIC_PS) - continue; - else - *cp = '\0'; - } - else - { - cp = strstr (buf, "!ps"); - if (cp) - { - if (this->class->magic == MAGIC_EPSF) - continue; - else - *cp = '\0'; - } else { - if (strstr (buf, "!!!")) - continue; - } - } - - if (!strncmp (buf, "!encodings", 10)) - output_encodings (this); - else - { - char *beg; - beg = buf2 = fn_interp_vars (buf, ps_get_var); - len = strlen (buf2); - while (isspace ((unsigned char) *beg)) - beg++, len--; - if (beg[len - 1] == '\n') - len--; - if (beg[len - 1] == '\r') - len--; - fwrite (beg, len, 1, f->file); - fputs (x->eol, f->file); - free (buf2); - } - } - if (ferror (f->file)) - msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno)); - fclose (prologue_file); - - free (prologue_fn); - free (buf); - - local_free (dict[7].value); - local_free (dict[8].value); - local_free (dict[16].value); - - if (ferror (f->file)) - goto error; - - msg (VM (2), _("%s: PostScript prologue read successfully."), this->name); - return 1; - -error: - msg (VM (1), _("%s: Error reading PostScript prologue."), this->name); - return 0; -} - -/* Writes the string STRING to buffer DEST (of at least 288 - characters) as a PostScript name object. Returns a pointer - to the null terminator of the resultant string. */ -static char * -quote_ps_name (char *dest, const char *string) -{ - const char *sp; - - for (sp = string; *sp; sp++) - switch (*sp) - { - case 'a': - case 'f': - case 'k': - case 'p': - case 'u': - case 'b': - case 'g': - case 'l': - case 'q': - case 'v': - case 'c': - case 'h': - case 'm': - case 'r': - case 'w': - case 'd': - case 'i': - case 'n': - case 's': - case 'x': - case 'e': - case 'j': - case 'o': - case 't': - case 'y': - case 'z': - case 'A': - case 'F': - case 'K': - case 'P': - case 'U': - case 'B': - case 'G': - case 'L': - case 'Q': - case 'V': - case 'C': - case 'H': - case 'M': - case 'R': - case 'W': - case 'D': - case 'I': - case 'N': - case 'S': - case 'X': - case 'E': - case 'J': - case 'O': - case 'T': - case 'Y': - case 'Z': - case '@': - case '^': - case '_': - case '|': - case '!': - case '$': - case '&': - case ':': - case ';': - case '.': - case ',': - case '-': - case '+': - break; - default: - { - char *dp = dest; - - *dp++ = '<'; - for (sp = string; *sp && dp < &dest[256]; sp++) - { - sprintf (dp, "%02x", (unsigned char) *sp); - dp += 2; - } - return stpcpy (dp, ">cvn"); - } - } - dest[0] = '/'; - return stpcpy (&dest[1], string); -} - -/* Adds the string STRING to buffer DEST as a PostScript quoted - string; returns a pointer to the null terminator added. Will not - add more than 235 characters. */ -static char * -quote_ps_string (char *dest, const char *string) -{ - const char *sp = string; - char *dp = dest; - - *dp++ = '('; - for (; *sp && dp < &dest[235]; sp++) - if (*sp == '(') - dp = stpcpy (dp, "\\("); - else if (*sp == ')') - dp = stpcpy (dp, "\\)"); - else if (*sp < 32 || (unsigned char) *sp > 127) - dp = spprintf (dp, "\\%3o", *sp); - else - *dp++ = *sp; - return stpcpy (dp, ")"); -} - -/* Writes the PostScript epilogue to file F. */ -static int -preclose (struct file_ext *f) -{ - struct outp_driver *this = f->param; - struct ps_driver_ext *x = this->ext; - struct hsh_iterator iter; - struct font_entry *fe; - - fprintf (f->file, - ("%%%%Trailer%s" - "%%%%Pages: %d%s" - "%%%%DocumentNeededResources:%s"), - x->eol, x->file_page_number, x->eol, x->eol); - - for (fe = hsh_first (x->loaded, &iter); fe != NULL; - fe = hsh_next (x->loaded, &iter)) - { - char buf[256], *cp; - - cp = stpcpy (buf, "%%+ font "); - cp = quote_ps_string (cp, fe->font->internal_name); - strcpy (cp, x->eol); - fputs (buf, f->file); - } - - hsh_destroy (x->loaded); - x->loaded = NULL; - hsh_destroy (x->combos); - x->combos = NULL; - x->last_font = NULL; - x->next_combo = 0; - - fprintf (f->file, "%%EOF%s", x->eol); - if (ferror (f->file)) - return 0; - return 1; -} - -static int -ps_open_page (struct outp_driver *this) -{ - struct ps_driver_ext *x = this->ext; - - assert (this->driver_open && !this->page_open); - - x->page_number++; - if (!fn_open_ext (&x->file)) - { - if (errno) - msg (ME, _("PostScript output driver: %s: %s"), x->file.filename, - strerror (errno)); - return 0; - } - x->file_page_number++; - - hsh_destroy (x->combos); - x->combos = hsh_create (31, compare_ps_combo, hash_ps_combo, - free_ps_combo, NULL); - x->last_font = NULL; - x->next_combo = 0; - - fprintf (x->file.file, - "%%%%Page: %d %d%s" - "%%%%BeginPageSetup%s" - "/pg save def 0.001 dup scale%s", - x->page_number, x->file_page_number, x->eol, - x->eol, - x->eol); - - if (x->orientation == OTN_LANDSCAPE) - fprintf (x->file.file, - "%d 0 translate 90 rotate%s", - x->w, x->eol); - - if (x->bottom_margin != 0 || x->left_margin != 0) - fprintf (x->file.file, - "%d %d translate%s", - x->left_margin, x->bottom_margin, x->eol); - - fprintf (x->file.file, - "/LW %d def/TW %d def %d setlinewidth%s" - "%%%%EndPageSetup%s", - x->line_width, x->line_width_thick, x->line_width, x->eol, - x->eol); - - if (!ferror (x->file.file)) - { - this->page_open = 1; - if (x->output_options & OPO_HEADERS) - draw_headers (this); - } - - this->cp_y = 0; - - return !ferror (x->file.file); -} - -static int -ps_close_page (struct outp_driver *this) -{ - struct ps_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - - if (x->line_opt) - dump_lines (this); - - fprintf (x->file.file, - "%%PageTrailer%s" - "EP%s", - x->eol, x->eol); - - this->page_open = 0; - return !ferror (x->file.file); -} - -static void -ps_submit (struct outp_driver *this UNUSED, struct som_entity *s) -{ - switch (s->type) - { - case SOM_CHART: - break; - default: - assert(0); - break; - } -} - -/* Lines. */ - -/* qsort() comparison function for int tuples. */ -static int -int_2_compare (const void *a_, const void *b_) -{ - const int *a = a_; - const int *b = b_; - - return *a < *b ? -1 : *a > *b; -} - -/* Hash table comparison function for cached lines. */ -static int -compare_line (const void *a_, const void *b_, void *foo UNUSED) -{ - const struct line_form *a = a_; - const struct line_form *b = b_; - - return a->ind < b->ind ? -1 : a->ind > b->ind; -} - -/* Hash table hash function for cached lines. */ -static unsigned -hash_line (const void *pa, void *foo UNUSED) -{ - const struct line_form *a = pa; - - return a->ind; -} - -/* Hash table free function for cached lines. */ -static void -free_line (void *pa, void *foo UNUSED) -{ - free (pa); -} - -/* Writes PostScript code to draw a line from (x1,y1) to (x2,y2) to - the output file. */ -#define dump_line(x1, y1, x2, y2) \ - fprintf (ext->file.file, "%d %d %d %d L%s", \ - x1, YT (y1), x2, YT (y2), ext->eol) - -/* Write PostScript code to draw a thick line from (x1,y1) to (x2,y2) - to the output file. */ -#define dump_thick_line(x1, y1, x2, y2) \ - fprintf (ext->file.file, "%d %d %d %d TL%s", \ - x1, YT (y1), x2, YT (y2), ext->eol) - -/* Writes a line of type TYPE to THIS driver's output file. The line - (or its center, in the case of double lines) has its independent - axis coordinate at IND; it extends from DEP1 to DEP2 on the - dependent axis. */ -static void -dump_fancy_line (struct outp_driver *this, int type, int ind, int dep1, int dep2) -{ - struct ps_driver_ext *ext = this->ext; - int ofs = ext->line_space / 2 + ext->line_width / 2; - - switch (type) - { - case horz: - dump_line (dep1, ind, dep2, ind); - break; - case dbl_horz: - if (ext->output_options & OPO_DOUBLE_LINE) - { - dump_line (dep1, ind - ofs, dep2, ind - ofs); - dump_line (dep1, ind + ofs, dep2, ind + ofs); - } - else - dump_thick_line (dep1, ind, dep2, ind); - break; - case spl_horz: - assert (0); - case vert: - dump_line (ind, dep1, ind, dep2); - break; - case dbl_vert: - if (ext->output_options & OPO_DOUBLE_LINE) - { - dump_line (ind - ofs, dep1, ind - ofs, dep2); - dump_line (ind + ofs, dep1, ind + ofs, dep2); - } - else - dump_thick_line (ind, dep1, ind, dep2); - break; - case spl_vert: - assert (0); - default: - assert (0); - } -} - -#undef dump_line - -/* Writes all the cached lines to the output file, then clears the - cache. */ -static void -dump_lines (struct outp_driver *this) -{ - struct ps_driver_ext *x = this->ext; - - struct hsh_iterator iter; - int type; - - for (type = 0; type < n_line_types; type++) - { - struct line_form *line; - - if (x->lines[type] == NULL) - continue; - - for (line = hsh_first (x->lines[type], &iter); line != NULL; - line = hsh_next (x->lines[type], &iter)) - { - int i; - int lo = INT_MIN, hi; - - qsort (line->dep, line->ndep, sizeof *line->dep, int_2_compare); - lo = line->dep[0][0]; - hi = line->dep[0][1]; - for (i = 1; i < line->ndep; i++) - if (line->dep[i][0] <= hi + 1) - { - int min_hi = line->dep[i][1]; - if (min_hi > hi) - hi = min_hi; - } - else - { - dump_fancy_line (this, type, line->ind, lo, hi); - lo = line->dep[i][0]; - hi = line->dep[i][1]; - } - dump_fancy_line (this, type, line->ind, lo, hi); - } - - hsh_destroy (x->lines[type]); - x->lines[type] = NULL; - } -} - -/* (Same args as dump_fancy_line()). Either dumps the line directly - to the output file, or adds it to the cache, depending on the - user-selected line optimization mode. */ -static void -line (struct outp_driver *this, int type, int ind, int dep1, int dep2) -{ - struct ps_driver_ext *ext = this->ext; - struct line_form **f; - - assert (dep2 >= dep1); - if (ext->line_opt == 0) - { - dump_fancy_line (this, type, ind, dep1, dep2); - return; - } - - if (ext->lines[type] == NULL) - ext->lines[type] = hsh_create (31, compare_line, hash_line, - free_line, NULL); - f = (struct line_form **) hsh_probe (ext->lines[type], &ind); - if (*f == NULL) - { - *f = xmalloc (sizeof **f + sizeof (int[15][2])); - (*f)->ind = ind; - (*f)->mdep = 16; - (*f)->ndep = 1; - (*f)->dep[0][0] = dep1; - (*f)->dep[0][1] = dep2; - return; - } - if ((*f)->ndep >= (*f)->mdep) - { - (*f)->mdep += 16; - *f = xrealloc (*f, sizeof **f + sizeof (int[2]) * ((*f)->mdep - 1)); - } - (*f)->dep[(*f)->ndep][0] = dep1; - (*f)->dep[(*f)->ndep][1] = dep2; - (*f)->ndep++; -} - -static void -ps_line_horz (struct outp_driver *this, const struct rect *r, - const struct color *c UNUSED, int style) -{ - /* Must match output.h:OUTP_L_*. */ - static const int types[OUTP_L_COUNT] = - {-1, horz, dbl_horz, spl_horz}; - - int y = (r->y1 + r->y2) / 2; - - assert (this->driver_open && this->page_open); - assert (style >= 0 && style < OUTP_L_COUNT); - style = types[style]; - if (style != -1) - line (this, style, y, r->x1, r->x2); -} - -static void -ps_line_vert (struct outp_driver *this, const struct rect *r, - const struct color *c UNUSED, int style) -{ - /* Must match output.h:OUTP_L_*. */ - static const int types[OUTP_L_COUNT] = - {-1, vert, dbl_vert, spl_vert}; - - int x = (r->x1 + r->x2) / 2; - - assert (this->driver_open && this->page_open); - assert (style >= 0 && style < OUTP_L_COUNT); - style = types[style]; - if (style != -1) - line (this, style, x, r->y1, r->y2); -} - -#define L (style->l != OUTP_L_NONE) -#define R (style->r != OUTP_L_NONE) -#define T (style->t != OUTP_L_NONE) -#define B (style->b != OUTP_L_NONE) - -static void -ps_line_intersection (struct outp_driver *this, const struct rect *r, - const struct color *c UNUSED, - const struct outp_styles *style) -{ - struct ps_driver_ext *ext = this->ext; - - int x = (r->x1 + r->x2) / 2; - int y = (r->y1 + r->y2) / 2; - int ofs = (ext->line_space + ext->line_width) / 2; - int x1 = x - ofs, x2 = x + ofs; - int y1 = y - ofs, y2 = y + ofs; - - assert (this->driver_open && this->page_open); - assert (!((style->l != style->r && style->l != OUTP_L_NONE - && style->r != OUTP_L_NONE) - || (style->t != style->b && style->t != OUTP_L_NONE - && style->b != OUTP_L_NONE))); - - switch ((style->l | style->r) | ((style->t | style->b) << 8)) - { - case (OUTP_L_SINGLE) | (OUTP_L_SINGLE << 8): - case (OUTP_L_SINGLE) | (OUTP_L_NONE << 8): - case (OUTP_L_NONE) | (OUTP_L_SINGLE << 8): - if (L) - line (this, horz, y, r->x1, x); - if (R) - line (this, horz, y, x, r->x2); - if (T) - line (this, vert, x, r->y1, y); - if (B) - line (this, vert, x, y, r->y2); - break; - case (OUTP_L_SINGLE) | (OUTP_L_DOUBLE << 8): - case (OUTP_L_NONE) | (OUTP_L_DOUBLE << 8): - if (L) - line (this, horz, y, r->x1, x1); - if (R) - line (this, horz, y, x2, r->x2); - if (T) - line (this, dbl_vert, x, r->y1, y); - if (B) - line (this, dbl_vert, x, y, r->y2); - if ((L && R) && !(T && B)) - line (this, horz, y, x1, x2); - break; - case (OUTP_L_DOUBLE) | (OUTP_L_SINGLE << 8): - case (OUTP_L_DOUBLE) | (OUTP_L_NONE << 8): - if (L) - line (this, dbl_horz, y, r->x1, x); - if (R) - line (this, dbl_horz, y, x, r->x2); - if (T) - line (this, vert, x, r->y1, y); - if (B) - line (this, vert, x, y, r->y2); - if ((T && B) && !(L && R)) - line (this, vert, x, y1, y2); - break; - case (OUTP_L_DOUBLE) | (OUTP_L_DOUBLE << 8): - if (L) - line (this, dbl_horz, y, r->x1, x); - if (R) - line (this, dbl_horz, y, x, r->x2); - if (T) - line (this, dbl_vert, x, r->y1, y); - if (B) - line (this, dbl_vert, x, y, r->y2); - if (T && B && !L) - line (this, vert, x1, y1, y2); - if (T && B && !R) - line (this, vert, x2, y1, y2); - if (L && R && !T) - line (this, horz, y1, x1, x2); - if (L && R && !B) - line (this, horz, y2, x1, x2); - break; - default: - assert (0); - } -} - -static void -ps_box (struct outp_driver *this UNUSED, const struct rect *r UNUSED, - const struct color *bord UNUSED, const struct color *fill UNUSED) -{ - assert (this->driver_open && this->page_open); -} - -static void -ps_polyline_begin (struct outp_driver *this UNUSED, - const struct color *c UNUSED) -{ - assert (this->driver_open && this->page_open); -} -static void -ps_polyline_point (struct outp_driver *this UNUSED, int x UNUSED, int y UNUSED) -{ - assert (this->driver_open && this->page_open); -} -static void -ps_polyline_end (struct outp_driver *this UNUSED) -{ - assert (this->driver_open && this->page_open); -} - -/* Returns the width of string S for THIS driver. */ -static int -text_width (struct outp_driver *this, char *s) -{ - struct outp_text text; - - text.options = OUTP_T_JUST_LEFT; - ls_init (&text.s, s, strlen (s)); - this->class->text_metrics (this, &text); - return text.h; -} - -/* Write string S at location (X,Y) with width W for THIS driver. */ -static void -out_text_plain (struct outp_driver *this, char *s, int x, int y, int w) -{ - struct outp_text text; - - text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT; - ls_init (&text.s, s, strlen (s)); - text.h = w; - text.v = this->font_height; - text.x = x; - text.y = y; - this->class->text_draw (this, &text); -} - -/* Draw top of page headers for THIS driver. */ -static void -draw_headers (struct outp_driver *this) -{ - struct ps_driver_ext *ext = this->ext; - - struct font_entry *old_current = ext->current; - char *old_family = xstrdup (ext->family); /* FIXME */ - int old_size = ext->size; - - int fh = this->font_height; - int y = -3 * fh; - - fprintf (ext->file.file, "%d %d %d %d GB%s", - 0, YT (y), this->width, YT (y + 2 * fh + ext->line_gutter), - ext->eol); - this->class->text_set_font_family (this, "T"); - - y += ext->line_width + ext->line_gutter; - - { - int rh_width; - char buf[128]; - - sprintf (buf, _("%s - Page %d"), get_start_date (), ext->page_number); - rh_width = text_width (this, buf); - - out_text_plain (this, buf, this->width - this->prop_em_width - rh_width, - y, rh_width); - - if (outp_title && outp_subtitle) - out_text_plain (this, outp_title, this->prop_em_width, y, - this->width - 3 * this->prop_em_width - rh_width); - - y += fh; - } - - { - int rh_width; - char buf[128]; - char *string = outp_subtitle ? outp_subtitle : outp_title; - - sprintf (buf, "%s - %s", version, host_system); - rh_width = text_width (this, buf); - - out_text_plain (this, buf, this->width - this->prop_em_width - rh_width, - y, rh_width); - - if (string) - out_text_plain (this, string, this->prop_em_width, y, - this->width - 3 * this->prop_em_width - rh_width); - - y += fh; - } - - ext->current = old_current; - free (ext->family); - ext->family = old_family; - ext->size = old_size; -} - - -/* Text. */ - -static void -ps_text_set_font_by_name (struct outp_driver *this, const char *dit) -{ - struct ps_driver_ext *x = this->ext; - struct font_entry *fe; - - assert (this->driver_open && this->page_open); - - /* Short-circuit common fonts. */ - if (!strcmp (dit, "PROP")) - { - x->current = x->prop; - x->size = x->font_size; - return; - } - else if (!strcmp (dit, "FIXED")) - { - x->current = x->fixed; - x->size = x->font_size; - return; - } - - /* Find font_desc corresponding to Groff name dit. */ - fe = hsh_find (x->loaded, &dit); - if (fe == NULL) - fe = load_font (this, dit); - x->current = fe; -} - -static void -ps_text_set_font_by_position (struct outp_driver *this, int pos) -{ - struct ps_driver_ext *x = this->ext; - char *dit; - - assert (this->driver_open && this->page_open); - - /* Determine font name by suffixing position string to font family - name. */ - { - char *cp; - - dit = local_alloc (strlen (x->family) + 3); - cp = stpcpy (dit, x->family); - switch (pos) - { - case OUTP_F_R: - *cp++ = 'R'; - break; - case OUTP_F_I: - *cp++ = 'I'; - break; - case OUTP_F_B: - *cp++ = 'B'; - break; - case OUTP_F_BI: - *cp++ = 'B'; - *cp++ = 'I'; - break; - default: - assert(0); - } - *cp++ = 0; - } - - /* Find font_desc corresponding to Groff name dit. */ - { - struct font_entry *fe = hsh_find (x->loaded, &dit); - if (fe == NULL) - fe = load_font (this, dit); - x->current = fe; - } - - local_free (dit); -} - -static void -ps_text_set_font_family (struct outp_driver *this, const char *s) -{ - struct ps_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - - free(x->family); - x->family = xstrdup (s); -} - -static const char * -ps_text_get_font_name (struct outp_driver *this) -{ - struct ps_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - return x->current->font->name; -} - -static const char * -ps_text_get_font_family (struct outp_driver *this) -{ - struct ps_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - return x->family; + 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); + } } -static int -ps_text_set_size (struct outp_driver *this, int size) -{ - struct ps_driver_ext *x = this->ext; - - assert (this->driver_open && this->page_open); - x->size = PSUS / 72000 * size; - return 1; -} +/* 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; -static int -ps_text_get_size (struct outp_driver *this, int *em_width) -{ - struct ps_driver_ext *x = 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); + } - assert (this->driver_open && this->page_open); - if (em_width) - *em_width = (x->current->font->space_width * x->size) / 1000; - return x->size / (PSUS / 72000); + 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); + } } -/* An output character. */ -struct output_char - { - struct font_entry *font; /* Font of character. */ - int size; /* Size of character. */ - int x, y; /* Location of character. */ - unsigned char ch; /* Character. */ - char separate; /* Must be separate from previous char. */ - }; - -/* Hash table comparison function for ps_combo structs. */ +/* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH + and with the given JUSTIFICATION for THIS driver. */ static int -compare_ps_combo (const void *pa, const void *pb, void *foo UNUSED) +draw_text (struct outp_driver *this, + const char *string, int x, int y, int max_width, + enum outp_justification justification) { - const struct ps_font_combo *a = pa; - const struct ps_font_combo *b = pb; - - return !((a->font == b->font) && (a->size == b->size)); -} + struct outp_text text; + int width; -/* Hash table hash function for ps_combo structs. */ -static unsigned -hash_ps_combo (const void *pa, void *foo UNUSED) -{ - const struct ps_font_combo *a = pa; - unsigned name_hash = hsh_hash_string (a->font->font->internal_name); - return name_hash ^ hsh_hash_int (a->size); + 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; } -/* Hash table free function for ps_combo structs. */ +/* Writes LEFT left-justified and RIGHT right-justified within + (X0...X1) at Y. LEFT or RIGHT or both may be null. */ static void -free_ps_combo (void *a, void *foo UNUSED) +draw_header_line (struct outp_driver *this, + const char *left, const char *right, + int x0, int x1, int y) { - free (a); + 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); } -/* Causes PostScript code to be output that switches to the font - CP->FONT and font size CP->SIZE. The first time a particular - font/size combination is used on a particular page, this involves - outputting PostScript code to load the font. */ +/* Draw top of page headers for THIS driver. */ static void -switch_font (struct outp_driver *this, const struct output_char *cp) +draw_headers (struct outp_driver *this) { struct ps_driver_ext *ext = this->ext; - struct ps_font_combo srch, **fc; - - srch.font = cp->font; - srch.size = cp->size; - - fc = (struct ps_font_combo **) hsh_probe (ext->combos, &srch); - if (*fc) - { - fprintf (ext->file.file, "F%x%s", (*fc)->index, ext->eol); - } - else - { - char *filename; - struct ps_encoding *encoding; - char buf[512], *bp; - - *fc = xmalloc (sizeof **fc); - (*fc)->font = cp->font; - (*fc)->size = cp->size; - (*fc)->index = ext->next_combo++; - - filename = find_encoding_file (this, cp->font->font->encoding); - if (filename) - { - encoding = get_encoding (this, filename); - free (filename); - } - else - { - msg (IE, _("PostScript driver: Cannot find encoding `%s' for " - "PostScript font `%s'."), cp->font->font->encoding, - cp->font->font->internal_name); - encoding = default_encoding (this); - } - - if (cp->font != ext->fixed && cp->font != ext->prop) - { - bp = stpcpy (buf, "%%IncludeResource: font "); - bp = quote_ps_string (bp, cp->font->font->internal_name); - bp = stpcpy (bp, ext->eol); - } - else - bp = buf; + 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; - bp = spprintf (bp, "/F%x E%x %d", (*fc)->index, encoding->index, - cp->size); - bp = quote_ps_name (bp, cp->font->font->internal_name); - sprintf (bp, " SF%s", ext->eol); - fputs (buf, ext->file.file); - } - ext->last_font = *fc; + 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); } - -/* (write_text) Writes the accumulated line buffer to the output - file. */ -#define output_line() \ - do \ - { \ - lp = stpcpy (lp, ext->eol); \ - *lp = 0; \ - fputs (line, ext->file.file); \ - lp = line; \ - } \ - while (0) - -/* (write_text) Adds the string representing number X to the line - buffer, flushing the buffer to disk beforehand if necessary. */ -#define put_number(X) \ - do \ - { \ - int n = nsprintf (number, "%d", X); \ - if (n + lp > &line[75]) \ - output_line (); \ - lp = stpcpy (lp, number); \ - } \ - while (0) - -/* Outputs PostScript code to THIS driver's output file to display the - characters represented by the output_char's between CP and END, - using the associated outp_text T to determine formatting. WIDTH is - the width of the output region; WIDTH_LEFT is the amount of the - WIDTH that is not taken up by text (so it can be used to determine - justification). */ + +/* 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, - const struct output_char *cp, const struct output_char *end, - struct outp_text *t, int width UNUSED, int width_left) + 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; - int ofs; + struct afm *afm = ext->fonts[font]->metrics; + struct string out; + size_t i, j; - int last_y; + 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; - char number[INT_STRLEN_BOUND (int) + 1]; - char line[80]; - char *lp; + fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0)); - switch (t->options & OUTP_T_JUST_MASK) + if (ext->last_font != font) { - case OUTP_T_JUST_LEFT: - ofs = 0; - break; - case OUTP_T_JUST_RIGHT: - ofs = width_left; - break; - case OUTP_T_JUST_CENTER: - ofs = width_left / 2; - break; - default: - assert (0); - abort (); + ext->last_font = font; + fprintf (ext->file, "F%d setfont\n", font); } - lp = line; - last_y = INT_MIN; - while (cp < end) + ds_init_empty (&out); + for (i = 0; i < char_cnt; i = j) { - int x = cp->x + ofs; - int y = cp->y + (cp->font->font->ascent * cp->size / 1000); - - if (ext->last_font == NULL - || cp->font != ext->last_font->font - || cp->size != ext->last_font->size) - switch_font (this, cp); - - *lp++ = '('; - do - { - /* PORTME! */ - static unsigned char literal_chars[ODA_COUNT][32] = - { - {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, - {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - }, - {0x7e, 0xd6, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - } - }; - - if (TEST_BIT (literal_chars[ext->data], cp->ch)) - *lp++ = cp->ch; - else - switch ((char) cp->ch) - { - case '(': - lp = stpcpy (lp, "\\("); - break; - case ')': - lp = stpcpy (lp, "\\)"); - break; - default: - lp = spprintf (lp, "\\%03o", cp->ch); - break; - } - cp++; - } - while (cp < end && lp < &line[70] && cp->separate == 0); - *lp++ = ')'; - - put_number (x); + for (j = i + 1; j < char_cnt; j++) + if (kerns[j] != 0) + break; - if (y != last_y) - { - *lp++ = ' '; - put_number (YT (y)); - *lp++ = ' '; - *lp++ = 'S'; - last_y = y; - } - else - { - *lp++ = ' '; - *lp++ = 'T'; - } - - if (lp >= &line[70]) - output_line (); + 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++; + } + } } - if (lp != line) - output_line (); + ds_destroy (&out); } -#undef output_line -#undef put_number +/* 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. */ + }; -/* Displays the text in outp_text T, if DRAW is nonzero; or, merely - determine the text metrics, if DRAW is zero. */ +/* Adjusts S to complete a line of text, + and draws the current line if appropriate. */ static void -text (struct outp_driver *this, struct outp_text *t, int draw) +finish_line (struct outp_driver *this, struct text_state *s) { - struct ps_driver_ext *ext = this->ext; - - /* Output. */ - struct output_char *buf; /* Output buffer. */ - struct output_char *buf_end; /* End of output buffer. */ - struct output_char *buf_loc; /* Current location in output buffer. */ - - /* Saved state. */ - struct font_entry *old_current = ext->current; - char *old_family = xstrdup (ext->family); /* FIXME */ - int old_size = ext->size; + int width; - /* Input string. */ - char *cp, *end; + 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; + } - /* Current location. */ - int x, y; + /* Update maximum width. */ + width = s->text->h - s->width_left; + if (width > s->max_width) + s->max_width = width; - /* Keeping track of what's left over. */ - int width; /* Width available for characters. */ - int width_left, height_left; /* Width, height left over. */ - int max_height; /* Tallest character on this line so far. */ + /* Move to next line. */ + s->width_left = s->text->h; + s->height_left -= this->font_height; - /* Previous character. */ - int prev_char; + /* No spaces on this line yet. */ + s->space_char = NULL; +} - /* Information about location of previous space. */ - char *space_char; /* Character after space. */ - struct output_char *space_buf_loc; /* Buffer location after space. */ - int space_width_left; /* Width of characters before space. */ +/* 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; - /* Name of the current character. */ - const char *char_name; - char local_char_name[2] = {0, 0}; + s.text = text; + s.draw = draw; - local_char_name[0] = local_char_name[1] = 0; + s.glyphs = NULL; + s.glyph_kerns = NULL; + glyph_cap = 0; - buf = local_alloc (sizeof *buf * 128); - buf_end = &buf[128]; - buf_loc = buf; + s.glyph_cnt = 0; + s.width_left = s.text->h; + s.height_left = s.text->v; - assert (!ls_null_p (&t->s)); - cp = ls_c_str (&t->s); - end = ls_end (&t->s); - if (draw) - { - x = t->x; - y = t->y; - } - else - x = y = 0; - width = width_left = (t->options & OUTP_T_HORZ) ? t->h : INT_MAX; - height_left = (t->options & OUTP_T_VERT) ? t->v : INT_MAX; - max_height = 0; - prev_char = -1; - space_char = NULL; - space_buf_loc = NULL; - space_width_left = 0; - + s.space_char = 0; - if (!width || !height_left) - goto exit; + s.max_width = 0; - while (cp < end) + cp = ss_data (s.text->string); + while (s.height_left >= this->font_height && cp < ss_end (s.text->string)) { - struct char_metrics *metric; - int cur_char; - int kern_amt; + const struct afm_character *cur; int char_width; - int separate = 0; - - /* Set char_name to the name of the character or ligature at - *cp. */ - local_char_name[0] = *cp; - char_name = local_char_name; - if (ext->current->font->ligatures && *cp == 'f') - { - int lig = 0; - char_name = NULL; - - if (cp < end - 1) - switch (cp[1]) - { - case 'i': - lig = LIG_fi, char_name = "fi"; - break; - case 'l': - lig = LIG_fl, char_name = "fl"; - break; - case 'f': - if (cp < end - 2) - switch (cp[2]) - { - case 'i': - lig = LIG_ffi, char_name = "ffi"; - goto got_ligature; - case 'l': - lig = LIG_ffl, char_name = "ffl"; - goto got_ligature; - } - lig = LIG_ff, char_name = "ff"; - got_ligature: - break; - } - if ((lig & ext->current->font->ligatures) == 0) - { - local_char_name[0] = *cp; /* 'f' */ - char_name = local_char_name; - } - } - else if (*cp == '\n') - { - if (draw) - { - write_text (this, buf, buf_loc, t, width, width_left); - buf_loc = buf; - x = t->x; - y += max_height; - } - - width_left = width; - height_left -= max_height; - max_height = 0; - kern_amt = 0; - separate = 1; - cp++; - - /* FIXME: when we're page buffering it will be necessary to - set separate to 1. */ - continue; - } - cp += strlen (char_name); + int kern_adjust; - /* Figure out what size this character is, and what kern - adjustment we need. */ - cur_char = font_char_name_to_index (char_name); - metric = font_get_char_metrics (ext->current->font, cur_char); - if (!metric) - { - static struct char_metrics m; - metric = &m; - m.width = ext->current->font->space_width; - m.code = *char_name; - } - kern_amt = font_get_kern_adjust (ext->current->font, prev_char, - cur_char); - if (kern_amt) - { - kern_amt = (kern_amt * ext->size / 1000); - separate = 1; - } - char_width = metric->width * ext->size / 1000; + 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_char == space_index && buf_loc > buf) - { - space_char = cp; - space_buf_loc = buf_loc; - space_width_left = width_left; - } - - /* Drop down to a new line if there's no room left on this - line. */ - if (char_width + kern_amt > width_left) + if (cur->code == ' ' && cp > ss_data (s.text->string)) { - /* Regress to previous space, if any. */ - if (space_char) - { - cp = space_char; - width_left = space_width_left; - buf_loc = space_buf_loc; - } - - if (draw) - { - write_text (this, buf, buf_loc, t, width, width_left); - buf_loc = buf; - x = t->x; - y += max_height; - } - - width_left = width; - height_left -= max_height; - max_height = 0; - kern_amt = 0; - - if (space_char) - { - space_char = NULL; - prev_char = -1; - /* FIXME: when we're page buffering it will be - necessary to set separate to 1. */ - continue; - } - separate = 1; + s.space_char = cp; + s.space_glyph_cnt = s.glyph_cnt; + s.space_width_left = s.width_left; } - if (ext->size > max_height) - max_height = ext->size; - if (max_height > height_left) - goto exit; - /* Actually draw the character. */ - if (draw) + /* Enough room on this line? */ + if (char_width + kern_adjust > s.width_left) { - if (buf_loc >= buf_end) - { - int buf_len = buf_end - buf; - - if (buf_len == 128) - { - struct output_char *new_buf; - - new_buf = xmalloc (sizeof *new_buf * 256); - memcpy (new_buf, buf, sizeof *new_buf * 128); - buf_loc = new_buf + 128; - buf_end = new_buf + 256; - local_free (buf); - buf = new_buf; - } - else - { - buf = xnrealloc (buf, buf_len * 2, sizeof *buf); - buf_loc = buf + buf_len; - buf_end = buf + buf_len * 2; - } - } - - x += kern_amt; - buf_loc->font = ext->current; - buf_loc->size = ext->size; - buf_loc->x = x; - buf_loc->y = y; - buf_loc->ch = metric->code; - buf_loc->separate = separate; - buf_loc++; - x += char_width; + 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; + } } - /* Prepare for next iteration. */ - width_left -= char_width + kern_amt; - prev_char = cur_char; + 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; } - height_left -= max_height; - if (buf_loc > buf && draw) - write_text (this, buf, buf_loc, t, width, width_left); - -exit: - if (!(t->options & OUTP_T_HORZ)) - t->h = INT_MAX - width_left; - if (!(t->options & OUTP_T_VERT)) - t->v = INT_MAX - height_left; - else - t->v -= height_left; - if (buf_end - buf == 128) - local_free (buf); - else - free (buf); - ext->current = old_current; - free (ext->family); - ext->family = old_family; - ext->size = old_size; -} - -static void -ps_text_metrics (struct outp_driver *this, struct outp_text *t) -{ - assert (this->driver_open && this->page_open); - text (this, t, 0); -} - -static void -ps_text_draw (struct outp_driver *this, struct outp_text *t) -{ - assert (this->driver_open && this->page_open); - text (this, t, 1); -} - -/* Font loader. */ - -/* Translate a filename to a font. */ -struct filename2font - { - char *filename; /* Normalized filename. */ - struct font_desc *font; - }; - -/* Table of `filename2font's. */ -static struct hsh_table *ps_fonts; - -/* Hash table comparison function for filename2font structs. */ -static int -compare_filename2font (const void *a, const void *b, void *param UNUSED) -{ - return strcmp (((struct filename2font *) a)->filename, - ((struct filename2font *) b)->filename); -} + if (s.height_left >= this->font_height && s.glyph_cnt > 0) + finish_line (this, &s); -/* Hash table hash function for filename2font structs. */ -static unsigned -hash_filename2font (const void *f2f_, void *param UNUSED) -{ - const struct filename2font *f2f = f2f_; - return hsh_hash_string (f2f->filename); + if (width != NULL) + *width = s.max_width; + if (height != NULL) + *height = text->v - s.height_left; + free (s.glyphs); + free (s.glyph_kerns); } -/* Initializes the global font list by creating the hash table for - translation of filenames to font_desc structs. */ static void -init_fonts (void) +ps_text_metrics (struct outp_driver *this, const struct outp_text *t, + int *width, int *height) { - ps_fonts = hsh_create (31, compare_filename2font, hash_filename2font, - NULL, NULL); + text (this, t, false, width, height); } static void -done_fonts (void) -{ - hsh_destroy (ps_fonts); -} - -/* Loads the font having Groff name DIT into THIS driver instance. - Specifically, adds it into the THIS driver's `loaded' hash - table. */ -static struct font_entry * -load_font (struct outp_driver *this, const char *dit) +ps_text_draw (struct outp_driver *this, const struct outp_text *t) { - struct ps_driver_ext *x = this->ext; - char *filename1, *filename2; - void **entry; - struct font_entry *fe; - - filename1 = find_ps_file (this, dit); - if (!filename1) - filename1 = xstrdup (dit); - filename2 = fn_normalize (filename1); - free (filename1); - - entry = hsh_probe (ps_fonts, &filename2); - if (*entry == NULL) - { - struct filename2font *f2f; - struct font_desc *f = groff_read_font (filename2); - - if (f == NULL) - { - if (x->fixed) - f = x->fixed->font; - else - f = default_font (); - } - - f2f = xmalloc (sizeof *f2f); - f2f->filename = filename2; - f2f->font = f; - *entry = f2f; - } - else - free (filename2); - - fe = xmalloc (sizeof *fe); - fe->dit = xstrdup (dit); - fe->font = ((struct filename2font *) * entry)->font; - *hsh_probe (x->loaded, &dit) = fe; - - return fe; + assert (this->page_open); + text (this, t, true, NULL, NULL); } - + static void ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch) { @@ -2888,12 +1098,12 @@ ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch) int x_origin, y_origin; ch->file = tmpfile (); - if (ch->file == NULL) + if (ch->file == NULL) { ch->lp = NULL; return; } - + size = this->width < this->length ? this->width : this->length; x_origin = x->left_margin + (size - this->width) / 2; y_origin = x->bottom_margin + (size - this->length) / 2; @@ -2909,7 +1119,7 @@ ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch) #endif } -static void +static void ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED) { #ifndef NO_CHARTS @@ -2917,103 +1127,311 @@ ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED) char buf[BUFSIZ]; static int doc_num = 0; - if (this->page_open) - { - this->class->close_page (this); - this->page_open = 0; - } - this->class->open_page (this); - fprintf (x->file.file, - "/sp save def%s" - "%d %d translate 1000 dup scale%s" - "userdict begin%s" - "/showpage { } def%s" - "0 setgray 0 setlinecap 1 setlinewidth%s" - "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear%s" - "%%%%BeginDocument: %d%s", - x->eol, - -x->left_margin, -x->bottom_margin, x->eol, - x->eol, - x->eol, - x->eol, - x->eol, - doc_num++, x->eol); + outp_eject_page (this); + fprintf (x->file, + "/sp save def\n" + "%d %d translate 1000 dup scale\n" + "userdict begin\n" + "/showpage { } def\n" + "0 setgray 0 setlinecap 1 setlinewidth\n" + "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n" + "%%%%BeginDocument: %d\n", + -x->left_margin, -x->bottom_margin, + doc_num++); rewind (ch->file); - while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file.file)) + while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file)) continue; fclose (ch->file); - fprintf (x->file.file, - "%%%%EndDocument%s" - "end%s" - "sp restore%s", - x->eol, - x->eol, - x->eol); - this->class->close_page (this); - this->page_open = 0; + fputs ("%%EndDocument\n" + "end\n" + "sp restore\n", + x->file); + outp_close_page (this); #endif } + +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; + } -/* PostScript driver class. */ -struct outp_class postscript_class = + /* 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) { - "postscript", - MAGIC_PS, - 0, + if (font != NULL) + { + afm_close (font->metrics); + free (font->embed_fn); + free (font->encoding_fn); + free (font); + } +} - ps_open_global, - ps_close_global, - ps_font_sizes, +/* 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; - ps_preopen_driver, - ps_option, - ps_postopen_driver, - ps_close_driver, + if (font->embed_fn != NULL) + embed_font (this, font); + else + fprintf (x->file, "%%%%IncludeResource: font %s\n", + afm_get_findfont_name (font->metrics)); - ps_open_page, - ps_close_page, + 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); - ps_submit, + if (font->encoding_fn != NULL) + reencode_font (this, font); - ps_line_horz, - ps_line_vert, - ps_line_intersection, - - ps_box, - ps_polyline_begin, - ps_polyline_point, - ps_polyline_end, - - ps_text_set_font_by_name, - ps_text_set_font_by_position, - ps_text_set_font_family, - ps_text_get_font_name, - ps_text_get_font_family, - ps_text_set_size, - ps_text_get_size, - ps_text_metrics, - ps_text_draw, + fprintf (x->file, "/F%d ED\n", index); +} - ps_chart_initialise, - ps_chart_finalise -}; +/* 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; + } +} -/* EPSF driver class. FIXME: Probably doesn't work right. */ -struct outp_class epsf_class = +/* 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) { - "epsf", - MAGIC_EPSF, - 0, + 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; - ps_open_global, - ps_close_global, - ps_font_sizes, + int line_number; + FILE *file; - ps_preopen_driver, - ps_option, - ps_postopen_driver, + 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, @@ -3021,28 +1439,10 @@ struct outp_class epsf_class = ps_submit, - ps_line_horz, - ps_line_vert, - ps_line_intersection, - - ps_box, - ps_polyline_begin, - ps_polyline_point, - ps_polyline_end, - - ps_text_set_font_by_name, - ps_text_set_font_by_position, - ps_text_set_font_family, - ps_text_get_font_name, - ps_text_get_font_family, - ps_text_set_size, - ps_text_get_size, + ps_line, ps_text_metrics, ps_text_draw, ps_chart_initialise, ps_chart_finalise - }; - -#endif /* NO_POSTSCRIPT */