1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 #include <libpspp/message.h>
34 #include <libpspp/alloc.h>
35 #include <libpspp/bit-vector.h>
36 #include <libpspp/compiler.h>
37 #include <libpspp/message.h>
38 #include <data/filename.h>
41 #include <libpspp/hash.h>
43 #include <libpspp/misc.h>
46 #include <libpspp/start-date.h>
47 #include <libpspp/version.h>
50 #define _(msgid) gettext (msgid)
54 optimize-text-size not implemented.
56 Line buffering is the only possibility; page buffering should also
61 Should add a field to give a file that has a list of fonts
64 Should add an option that tells the driver it can emit %%Include:'s.
66 Should have auto-encode=true stream-edit or whatever to allow
67 addition to list of encodings.
69 Should align fonts of different sizes along their baselines (see
72 /* PostScript driver options: (defaults listed first)
76 data=clean7bit|clean8bit|binary
79 paper-size=letter (see "papersize" file)
80 orientation=portrait|landscape
89 prologue-file=ps-prologue
91 encoding-file=ps-encodings
92 auto-encode=true|false
98 line-style=thick|double
104 optimize-text-size=1|0|2
105 optimize-line-size=1|0
106 max-fonts-simult=0 Max # of fonts in printer memory at once (0=infinite)
109 /* The number of `psus' (PostScript driver UnitS) per inch. Although
110 this is a #define, the value is expected never to change. If it
111 does, review all uses. */
114 /* Magic numbers for PostScript and EPSF drivers. */
124 OTN_PORTRAIT, /* Portrait. */
125 OTN_LANDSCAPE /* Landscape. */
128 /* Output options. */
131 OPO_MIRROR_HORZ = 001, /* 1=Mirror across a horizontal axis. */
132 OPO_MIRROR_VERT = 002, /* 1=Mirror across a vertical axis. */
133 OPO_ROTATE_180 = 004, /* 1=Rotate the page 180 degrees. */
134 OPO_COLOR = 010, /* 1=Enable color. */
135 OPO_HEADERS = 020, /* 1=Draw headers at top of page. */
136 OPO_AUTO_ENCODE = 040, /* 1=Add encodings semi-intelligently. */
137 OPO_DOUBLE_LINE = 0100 /* 1=Double lines instead of thick lines. */
140 /* Data allowed in output. */
143 ODA_CLEAN7BIT, /* 0x09, 0x0a, 0x0d, 0x1b...0x7e */
144 ODA_CLEAN8BIT, /* 0x09, 0x0a, 0x0d, 0x1b...0xff */
145 ODA_BINARY, /* 0x00...0xff */
149 /* Types of lines for purpose of caching. */
152 horz, /* Single horizontal. */
153 dbl_horz, /* Double horizontal. */
154 spl_horz, /* Special horizontal. */
155 vert, /* Single vertical. */
156 dbl_vert, /* Double vertical. */
157 spl_vert, /* Special vertical. */
164 int ind; /* Independent var. Don't reorder. */
165 int mdep; /* Maximum number of dependent var pairs. */
166 int ndep; /* Current number of dependent var pairs. */
167 int dep[1][2]; /* Dependent var pairs. */
170 /* Contents of ps_driver_ext.loaded. */
173 char *dit; /* Font Groff name. */
174 struct font_desc *font; /* Font descriptor. */
177 /* Combines a font with a font size for benefit of generated code. */
180 struct font_entry *font; /* Font. */
181 int size; /* Font size. */
182 int index; /* PostScript index. */
185 /* A font encoding. */
188 char *filename; /* Normalized filename of this encoding. */
189 int index; /* Index value. */
192 /* PostScript output driver extension record. */
195 /* User parameters. */
196 int orientation; /* OTN_PORTRAIT or OTN_LANDSCAPE. */
197 int output_options; /* OPO_*. */
198 int data; /* ODA_*. */
200 int left_margin; /* Left margin in psus. */
201 int right_margin; /* Right margin in psus. */
202 int top_margin; /* Top margin in psus. */
203 int bottom_margin; /* Bottom margin in psus. */
205 char eol[3]; /* End of line--CR, LF, or CRLF. */
207 char *font_dir; /* Font directory relative to font path. */
208 char *prologue_fn; /* Prologue's filename relative to font dir. */
209 char *desc_fn; /* DESC filename relative to font dir. */
210 char *encoding_fn; /* Encoding's filename relative to font dir. */
212 char *prop_family; /* Default proportional font family. */
213 char *fixed_family; /* Default fixed-pitch font family. */
214 int font_size; /* Default font size (psus). */
216 int line_gutter; /* Space around lines. */
217 int line_space; /* Space between lines. */
218 int line_width; /* Width of lines. */
219 int line_width_thick; /* Width of thick lines. */
221 int text_opt; /* Text optimization level. */
222 int line_opt; /* Line optimization level. */
223 int max_fonts; /* Max # of simultaneous fonts (0=infinite). */
225 /* Internal state. */
226 struct file_ext file; /* Output file. */
227 int page_number; /* Current page number. */
228 int file_page_number; /* Page number in this file. */
229 int w, l; /* Paper size. */
230 struct hsh_table *lines[n_line_types]; /* Line buffers. */
232 struct font_entry *prop; /* Default Roman proportional font. */
233 struct font_entry *fixed; /* Default Roman fixed-pitch font. */
234 struct hsh_table *loaded; /* Fonts in memory. */
236 struct hsh_table *combos; /* Combinations of fonts with font sizes. */
237 struct ps_font_combo *last_font; /* PostScript selected font. */
238 int next_combo; /* Next font combo position index. */
240 struct hsh_table *encodings;/* Set of encodings. */
241 int next_encoding; /* Next font encoding index. */
243 /* Currently selected font. */
244 struct font_entry *current; /* Current font. */
245 char *family; /* Font family. */
246 int size; /* Size in psus. */
250 /* Transform logical y-ordinate Y into a page ordinate. */
251 #define YT(Y) (this->length - (Y))
254 static int postopen (struct file_ext *);
255 static int preclose (struct file_ext *);
256 static void draw_headers (struct outp_driver *this);
258 static int compare_font_entry (const void *, const void *, void *param);
259 static unsigned hash_font_entry (const void *, void *param);
260 static void free_font_entry (void *, void *foo);
261 static struct font_entry *load_font (struct outp_driver *, const char *dit);
262 static void init_fonts (void);
263 static void done_fonts (void);
265 static void dump_lines (struct outp_driver *this);
267 static void read_ps_encodings (struct outp_driver *this);
268 static int compare_ps_encoding (const void *pa, const void *pb, void *foo);
269 static unsigned hash_ps_encoding (const void *pa, void *foo);
270 static void free_ps_encoding (void *a, void *foo);
271 static void add_encoding (struct outp_driver *this, char *filename);
272 static struct ps_encoding *default_encoding (struct outp_driver *this);
274 static int compare_ps_combo (const void *pa, const void *pb, void *foo);
275 static unsigned hash_ps_combo (const void *pa, void *foo);
276 static void free_ps_combo (void *a, void *foo);
278 static char *quote_ps_name (char *dest, const char *string);
279 static char *quote_ps_string (char *dest, const char *string);
281 /* Driver initialization. */
284 ps_open_global (struct outp_class *this UNUSED)
292 ps_close_global (struct outp_class *this UNUSED)
300 ps_font_sizes (struct outp_class *this UNUSED, int *n_valid_sizes)
302 /* Allow fonts up to 1" in height. */
303 static int valid_sizes[] =
306 assert (n_valid_sizes != NULL);
312 ps_preopen_driver (struct outp_driver *this)
314 struct ps_driver_ext *x;
318 assert (this->driver_open == 0);
319 msg (VM (1), _("PostScript driver initializing as `%s'..."), this->name);
321 this->ext = x = xmalloc (sizeof *x);
323 this->horiz = this->vert = 1;
324 this->width = this->length = 0;
326 x->orientation = OTN_PORTRAIT;
327 x->output_options = OPO_COLOR | OPO_HEADERS | OPO_AUTO_ENCODE;
328 x->data = ODA_CLEAN7BIT;
330 x->left_margin = x->right_margin =
331 x->top_margin = x->bottom_margin = PSUS / 2;
333 strcpy (x->eol, "\n");
336 x->prologue_fn = NULL;
338 x->encoding_fn = NULL;
340 x->prop_family = NULL;
341 x->fixed_family = NULL;
342 x->font_size = PSUS * 10 / 72;
344 x->line_gutter = PSUS / 144;
345 x->line_space = PSUS / 144;
346 x->line_width = PSUS / 144;
347 x->line_width_thick = PSUS / 48;
353 x->file.filename = NULL;
356 x->file.sequence_no = &x->page_number;
357 x->file.param = this;
358 x->file.postopen = postopen;
359 x->file.preclose = preclose;
363 x->file_page_number = 0;
364 for (i = 0; i < n_line_types; i++)
375 x->encodings = hsh_create (31, compare_ps_encoding, hash_ps_encoding,
376 free_ps_encoding, NULL);
377 x->next_encoding = 0;
387 ps_postopen_driver (struct outp_driver *this)
389 struct ps_driver_ext *x = this->ext;
391 assert (this->driver_open == 0);
393 if (this->width == 0)
395 this->width = PSUS * 17 / 2; /* Defaults to 8.5"x11". */
396 this->length = PSUS * 11;
399 if (x->text_opt == -1)
400 x->text_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
401 if (x->line_opt == -1)
402 x->line_opt = (this->device & OUTP_DEV_SCREEN) ? 0 : 1;
406 if (x->orientation == OTN_LANDSCAPE)
408 int temp = this->width;
409 this->width = this->length;
412 this->width -= x->left_margin + x->right_margin;
413 this->length -= x->top_margin + x->bottom_margin;
414 if (x->output_options & OPO_HEADERS)
416 this->length -= 3 * x->font_size;
417 x->top_margin += 3 * x->font_size;
419 if (NULL == x->file.filename)
420 x->file.filename = xstrdup ("pspp.ps");
422 if (x->font_dir == NULL)
423 x->font_dir = xstrdup ("devps");
424 if (x->prologue_fn == NULL)
425 x->prologue_fn = xstrdup ("ps-prologue");
426 if (x->desc_fn == NULL)
427 x->desc_fn = xstrdup ("DESC");
428 if (x->encoding_fn == NULL)
429 x->encoding_fn = xstrdup ("ps-encodings");
431 if (x->prop_family == NULL)
432 x->prop_family = xstrdup ("H");
433 if (x->fixed_family == NULL)
434 x->fixed_family = xstrdup ("C");
436 read_ps_encodings (this);
441 if (this->length / x->font_size < 15)
443 msg (SE, _("PostScript driver: The defined page is not long "
444 "enough to hold margins and headers, plus least 15 "
445 "lines of the default fonts. In fact, there's only "
446 "room for %d lines of each font at the default size "
447 "of %d.%03d points."),
448 this->length / x->font_size,
449 x->font_size / 1000, x->font_size % 1000);
453 this->driver_open = 1;
454 msg (VM (2), _("%s: Initialization complete."), this->name);
460 ps_close_driver (struct outp_driver *this)
462 struct ps_driver_ext *x = this->ext;
466 assert (this->driver_open == 1);
467 msg (VM (2), _("%s: Beginning closing..."), this->name);
469 fn_close_ext (&x->file);
470 free (x->file.filename);
472 free (x->prologue_fn);
474 free (x->encoding_fn);
475 free (x->prop_family);
476 free (x->fixed_family);
478 for (i = 0; i < n_line_types; i++)
479 hsh_destroy (x->lines[i]);
480 hsh_destroy (x->encodings);
481 hsh_destroy (x->combos);
482 hsh_destroy (x->loaded);
485 this->driver_open = 0;
486 msg (VM (3), _("%s: Finished closing."), this->name);
491 /* font_entry comparison function for hash tables. */
493 compare_font_entry (const void *a, const void *b, void *foobar UNUSED)
495 return strcmp (((struct font_entry *) a)->dit, ((struct font_entry *) b)->dit);
498 /* font_entry hash function for hash tables. */
500 hash_font_entry (const void *fe_, void *foobar UNUSED)
502 const struct font_entry *fe = fe_;
503 return hsh_hash_string (fe->dit);
506 /* font_entry destructor function for hash tables. */
508 free_font_entry (void *pa, void *foo UNUSED)
510 struct font_entry *a = pa;
515 /* Generic option types. */
525 /* All the options that the PostScript driver supports. */
526 static struct outp_option option_tab[] =
529 {"output-file", 1, 0},
530 {"paper-size", 2, 0},
531 {"orientation", 3, 0},
532 {"color", boolean_arg, 0},
534 {"auto-encode", boolean_arg, 5},
535 {"headers", boolean_arg, 1},
536 {"left-margin", pos_int_arg, 0},
537 {"right-margin", pos_int_arg, 1},
538 {"top-margin", pos_int_arg, 2},
539 {"bottom-margin", pos_int_arg, 3},
540 {"font-dir", string_arg, 0},
541 {"prologue-file", string_arg, 1},
542 {"device-file", string_arg, 2},
543 {"encoding-file", string_arg, 3},
544 {"prop-font-family", string_arg, 5},
545 {"fixed-font-family", string_arg, 6},
546 {"font-size", pos_int_arg, 4},
547 {"optimize-text-size", nonneg_int_arg, 0},
548 {"optimize-line-size", nonneg_int_arg, 1},
549 {"max-fonts-simult", nonneg_int_arg, 2},
551 {"line-style", 7, 0},
552 {"line-width", dimension_arg, 2},
553 {"line-gutter", dimension_arg, 3},
554 {"line-width", dimension_arg, 4},
555 {"line-width-thick", dimension_arg, 5},
559 static struct outp_option_info option_info;
562 ps_option (struct outp_driver *this, const char *key, const struct string *val)
564 struct ps_driver_ext *x = this->ext;
566 char *value = ds_c_str (val);
568 cat = outp_match_keyword (key, option_tab, &option_info, &subcat);
573 msg (SE, _("Unknown configuration parameter `%s' for PostScript device "
577 free (x->file.filename);
578 x->file.filename = xstrdup (value);
581 outp_get_paper_size (value, &this->width, &this->length);
584 if (!strcmp (value, "portrait"))
585 x->orientation = OTN_PORTRAIT;
586 else if (!strcmp (value, "landscape"))
587 x->orientation = OTN_LANDSCAPE;
589 msg (SE, _("Unknown orientation `%s'. Valid orientations are "
590 "`portrait' and `landscape'."), value);
593 if (!strcmp (value, "clean7bit") || !strcmp (value, "Clean7Bit"))
594 x->data = ODA_CLEAN7BIT;
595 else if (!strcmp (value, "clean8bit")
596 || !strcmp (value, "Clean8Bit"))
597 x->data = ODA_CLEAN8BIT;
598 else if (!strcmp (value, "binary") || !strcmp (value, "Binary"))
599 x->data = ODA_BINARY;
601 msg (SE, _("Unknown value for `data'. Valid values are `clean7bit', "
602 "`clean8bit', and `binary'."));
605 if (!strcmp (value, "lf"))
606 strcpy (x->eol, "\n");
607 else if (!strcmp (value, "crlf"))
608 strcpy (x->eol, "\r\n");
610 msg (SE, _("Unknown value for `line-ends'. Valid values are `lf' and "
614 if (!strcmp (value, "thick"))
615 x->output_options &= ~OPO_DOUBLE_LINE;
616 else if (!strcmp (value, "double"))
617 x->output_options |= OPO_DOUBLE_LINE;
619 msg (SE, _("Unknown value for `line-style'. Valid values are `thick' "
627 if (!strcmp (value, "on") || !strcmp (value, "true")
628 || !strcmp (value, "yes") || atoi (value))
630 else if (!strcmp (value, "off") || !strcmp (value, "false")
631 || !strcmp (value, "no") || !strcmp (value, "0"))
635 msg (SE, _("Boolean value expected for %s."), key);
647 mask = OPO_MIRROR_HORZ;
650 mask = OPO_MIRROR_VERT;
653 mask = OPO_ROTATE_180;
656 mask = OPO_AUTO_ENCODE;
663 x->output_options |= mask;
665 x->output_options &= ~mask;
674 arg = strtol (value, &tail, 0);
675 if (arg < 1 || errno == ERANGE || *tail)
677 msg (SE, _("Positive integer required as value for `%s'."), key);
680 if ((subcat == 4 || subcat == 5) && arg < 1000)
682 msg (SE, _("Default font size must be at least 1 point (value "
683 "of 1000 for key `%s')."), key);
689 x->left_margin = arg;
692 x->right_margin = arg;
698 x->bottom_margin = arg;
710 int dimension = outp_evaluate_dimension (value, NULL);
714 msg (SE, _("Value for `%s' must be a dimension of positive "
715 "length (i.e., `1in')."), key);
721 x->line_width = dimension;
724 x->line_gutter = dimension;
727 x->line_width = dimension;
730 x->line_width_thick = dimension;
746 dest = &x->prologue_fn;
752 dest = &x->encoding_fn;
755 dest = &x->prop_family;
758 dest = &x->fixed_family;
766 *dest = xstrdup (value);
775 arg = strtol (value, &tail, 0);
776 if (arg < 0 || errno == ERANGE || *tail)
778 msg (SE, _("Nonnegative integer required as value for `%s'."), key);
802 /* Looks for a PostScript font file or config file in all the
803 appropriate places. Returns the filename on success, NULL on
805 /* PORTME: Filename operations. */
807 find_ps_file (struct outp_driver *this, const char *name)
809 struct ps_driver_ext *x = this->ext;
812 /* x->font_dir + name: "devps/ps-encodings". */
815 /* Usually equal to groff_font_path. */
818 /* Final filename. */
822 basename = local_alloc (strlen (x->font_dir) + 1 + strlen (name) + 1);
823 cp = stpcpy (basename, x->font_dir);
824 *cp++ = DIR_SEPARATOR;
827 /* Decide on search path. */
829 const char *pre_pathname;
831 pre_pathname = getenv ("STAT_GROFF_FONT_PATH");
832 if (pre_pathname == NULL)
833 pre_pathname = getenv ("GROFF_FONT_PATH");
834 if (pre_pathname == NULL)
835 pre_pathname = groff_font_path;
836 pathname = fn_tilde_expand (pre_pathname);
839 /* Search all possible places for the file. */
840 fn = fn_search_path (basename, pathname, NULL);
842 fn = fn_search_path (basename, config_path, NULL);
844 fn = fn_search_path (name, pathname, NULL);
846 fn = fn_search_path (name, config_path, NULL);
848 local_free (basename);
855 /* Hash table comparison function for ps_encoding's. */
857 compare_ps_encoding (const void *pa, const void *pb, void *foo UNUSED)
859 const struct ps_encoding *a = pa;
860 const struct ps_encoding *b = pb;
862 return strcmp (a->filename, b->filename);
865 /* Hash table hash function for ps_encoding's. */
867 hash_ps_encoding (const void *pa, void *foo UNUSED)
869 const struct ps_encoding *a = pa;
871 return hsh_hash_string (a->filename);
874 /* Hash table free function for ps_encoding's. */
876 free_ps_encoding (void *pa, void *foo UNUSED)
878 struct ps_encoding *a = pa;
884 /* Iterates through the list of encodings used for this driver
885 instance, reads each of them from disk, and writes them as
886 PostScript code to the output file. */
888 output_encodings (struct outp_driver *this)
890 struct ps_driver_ext *x = this->ext;
892 struct hsh_iterator iter;
893 struct ps_encoding *pe;
895 struct string line, buf;
897 ds_init (&line, 128);
899 for (pe = hsh_first (x->encodings, &iter); pe != NULL;
900 pe = hsh_next (x->encodings, &iter))
904 msg (VM (1), _("%s: %s: Opening PostScript font encoding..."),
905 this->name, pe->filename);
907 f = fopen (pe->filename, "r");
910 msg (IE, _("PostScript driver: Cannot open encoding file `%s': %s. "
911 "Substituting ISOLatin1Encoding for missing encoding."),
912 pe->filename, strerror (errno));
913 fprintf (x->file.file, "/E%x ISOLatin1Encoding def%s",
918 struct file_locator where;
920 const char *tab[256];
927 const char *notdef = ".notdef";
931 for (i = 0; i < 256; i++)
934 where.filename = pe->filename;
935 where.line_number = 0;
936 err_push_file_locator (&where);
938 while (ds_get_config_line (f, &buf, &where))
945 pschar = strtok_r (ds_c_str (&buf), " \t\r\n", &sp);
946 code = strtok_r (NULL, " \t\r\n", &sp);
947 if (*pschar == 0 || *code == 0)
949 code_val = strtol (code, &fubar, 0);
952 msg (IS, _("PostScript driver: Invalid numeric format."));
955 if (code_val < 0 || code_val > 255)
957 msg (IS, _("PostScript driver: Codes must be between 0 "
958 "and 255. (%d is not allowed.)"), code_val);
961 tab[code_val] = local_alloc (strlen (pschar) + 1);
962 strcpy ((char *) (tab[code_val]), pschar);
964 err_pop_file_locator (&where);
967 ds_printf (&line, "/E%x[", pe->index);
968 for (i = 0; i < 257; i++)
974 quote_ps_name (temp, tab[i]);
975 if (tab[i] != notdef)
979 strcpy (temp, "]def");
981 if (ds_length (&line) + strlen (temp) > 70)
983 ds_puts (&line, x->eol);
984 fputs (ds_c_str (&line), x->file.file);
987 ds_puts (&line, temp);
989 ds_puts (&line, x->eol);
990 fputs (ds_c_str (&line), x->file.file);
992 if (fclose (f) == EOF)
993 msg (MW, _("PostScript driver: Error closing encoding file `%s'."),
996 msg (VM (2), _("%s: PostScript font encoding read successfully."),
1004 /* Finds the ps_encoding in THIS that corresponds to the file with
1005 name NORM_FILENAME, which must have previously been normalized with
1006 normalize_filename(). */
1007 static struct ps_encoding *
1008 get_encoding (struct outp_driver *this, const char *norm_filename)
1010 struct ps_driver_ext *x = this->ext;
1011 struct ps_encoding *pe;
1013 pe = (struct ps_encoding *) hsh_find (x->encodings, (void *) &norm_filename);
1017 /* Searches the filesystem for an encoding file with name FILENAME;
1018 returns its malloc'd, normalized name if found, otherwise NULL. */
1020 find_encoding_file (struct outp_driver *this, char *filename)
1024 if (filename == NULL)
1026 while (isspace ((unsigned char) *filename))
1028 for (cp = filename; *cp && !isspace ((unsigned char) *cp); cp++)
1034 temp = find_ps_file (this, filename);
1038 filename = fn_normalize (temp);
1039 assert (filename != NULL);
1045 /* Adds the encoding represented by the not-necessarily-normalized
1046 file FILENAME to the list of encodings, if it exists and is not
1047 already in the list. */
1049 add_encoding (struct outp_driver *this, char *filename)
1051 struct ps_driver_ext *x = this->ext;
1052 struct ps_encoding **pe;
1054 filename = find_encoding_file (this, filename);
1058 pe = (struct ps_encoding **) hsh_probe (x->encodings, &filename);
1064 *pe = xmalloc (sizeof **pe);
1065 (*pe)->filename = filename;
1066 (*pe)->index = x->next_encoding++;
1069 /* Finds the file on disk that contains the list of encodings to
1070 include in the output file, then adds those encodings to the list
1073 read_ps_encodings (struct outp_driver *this)
1075 struct ps_driver_ext *x = this->ext;
1077 /* Encodings file. */
1078 char *encoding_fn; /* `ps-encodings' filename. */
1082 struct file_locator where;
1084 /* It's okay if there's no list of encodings; not everyone cares. */
1085 encoding_fn = find_ps_file (this, x->encoding_fn);
1086 if (encoding_fn == NULL)
1090 msg (VM (1), _("%s: %s: Opening PostScript encoding list file."),
1091 this->name, encoding_fn);
1092 f = fopen (encoding_fn, "r");
1095 msg (IE, _("Opening %s: %s."), encoding_fn, strerror (errno));
1099 where.filename = encoding_fn;
1100 where.line_number = 0;
1101 err_push_file_locator (&where);
1103 ds_init (&line, 128);
1107 if (!ds_get_config_line (f, &line, &where))
1110 msg (ME, _("Reading %s: %s."), encoding_fn, strerror (errno));
1114 add_encoding (this, line.string);
1118 err_pop_file_locator (&where);
1120 if (-1 == fclose (f))
1121 msg (MW, _("Closing %s: %s."), encoding_fn, strerror (errno));
1123 msg (VM (2), _("%s: PostScript encoding list file read successfully."), this->name);
1126 /* Creates a default encoding for driver D that can be substituted for
1127 an unavailable encoding. */
1128 struct ps_encoding *
1129 default_encoding (struct outp_driver *d)
1131 struct ps_driver_ext *x = d->ext;
1132 static struct ps_encoding *enc;
1136 enc = xmalloc (sizeof *enc);
1137 enc->filename = xstrdup (_("<<default encoding>>"));
1138 enc->index = x->next_encoding++;
1143 /* Basic file operations. */
1145 /* Variables for the prologue. */
1152 static struct ps_variable *ps_var_tab;
1154 /* Searches ps_var_tab for a ps_variable with key KEY, and returns the
1155 associated value. */
1157 ps_get_var (const char *key)
1159 struct ps_variable *v;
1161 for (v = ps_var_tab; v->key; v++)
1162 if (!strcmp (key, v->key))
1167 /* Writes the PostScript prologue to file F. */
1169 postopen (struct file_ext *f)
1171 static struct ps_variable dict[] =
1173 {"bounding-box", 0},
1182 {"scale-factor", 0},
1184 {"paper-length", 0},
1188 {"line-width-thick", 0},
1192 char boundbox[INT_STRLEN_BOUND (int) * 4 + 4];
1196 char scaling[INT_STRLEN_BOUND (int) + 5];
1200 char paper_width[INT_STRLEN_BOUND (int) + 1];
1201 char paper_length[INT_STRLEN_BOUND (int) + 1];
1202 char left_margin[INT_STRLEN_BOUND (int) + 1];
1203 char top_margin[INT_STRLEN_BOUND (int) + 1];
1204 char line_width[INT_STRLEN_BOUND (int) + 1];
1205 char line_width_thick[INT_STRLEN_BOUND (int) + 1];
1207 struct outp_driver *this = f->param;
1208 struct ps_driver_ext *x = this->ext;
1210 char *prologue_fn = find_ps_file (this, x->prologue_fn);
1211 FILE *prologue_file;
1214 size_t buf_size = 0;
1216 x->loaded = hsh_create (31, compare_font_entry, hash_font_entry,
1217 free_font_entry, NULL);
1220 char *font_name = local_alloc (2 + max (strlen (x->prop_family),
1221 strlen (x->fixed_family)));
1223 strcpy (stpcpy (font_name, x->prop_family), "R");
1224 x->prop = load_font (this, font_name);
1226 strcpy (stpcpy (font_name, x->fixed_family), "R");
1227 x->fixed = load_font (this, font_name);
1229 local_free(font_name);
1232 x->current = x->prop;
1233 x->family = xstrdup (x->prop_family);
1234 x->size = x->font_size;
1237 int *h = this->horiz_line_width, *v = this->vert_line_width;
1239 this->cp_x = this->cp_y = 0;
1240 this->font_height = x->font_size;
1242 struct char_metrics *metric;
1244 metric = font_get_char_metrics (x->prop->font, '0');
1245 this->prop_em_width = ((metric
1246 ? metric->width : x->prop->font->space_width)
1247 * x->font_size / 1000);
1249 metric = font_get_char_metrics (x->fixed->font, '0');
1250 this->fixed_width = ((metric
1251 ? metric->width : x->fixed->font->space_width)
1252 * x->font_size / 1000);
1256 h[1] = v[1] = 2 * x->line_gutter + x->line_width;
1257 if (x->output_options & OPO_DOUBLE_LINE)
1258 h[2] = v[2] = 2 * x->line_gutter + 2 * x->line_width + x->line_space;
1260 h[2] = v[2] = 2 * x->line_gutter + x->line_width_thick;
1261 h[3] = v[3] = 2 * x->line_gutter + x->line_width;
1266 for (i = 0; i < (1 << OUTP_L_COUNT); i++)
1270 /* Maximum width of any line type so far. */
1273 for (bit = 0; bit < OUTP_L_COUNT; bit++)
1274 if ((i & (1 << bit)) && h[bit] > max)
1276 this->horiz_line_spacing[i] = this->vert_line_spacing[i] = max;
1281 if (x->output_options & OPO_AUTO_ENCODE)
1283 /* It's okay if this is done more than once since add_encoding()
1284 is idempotent over identical encodings. */
1285 add_encoding (this, x->prop->font->encoding);
1286 add_encoding (this, x->fixed->font->encoding);
1289 x->file_page_number = 0;
1292 if (prologue_fn == NULL)
1294 msg (IE, _("Cannot find PostScript prologue. The use of `-vv' "
1295 "on the command line is suggested as a debugging aid."));
1299 msg (VM (1), _("%s: %s: Opening PostScript prologue..."),
1300 this->name, prologue_fn);
1301 prologue_file = fopen (prologue_fn, "rb");
1302 if (prologue_file == NULL)
1304 fclose (prologue_file);
1306 msg (IE, "%s: %s", prologue_fn, strerror (errno));
1310 sprintf (boundbox, "0 0 %d %d",
1311 x->w / (PSUS / 72) + (x->w % (PSUS / 72) > 0),
1312 x->l / (PSUS / 72) + (x->l % (PSUS / 72) > 0));
1313 dict[0].value = boundbox;
1315 dict[1].value = (char *) version;
1317 curtime = time (NULL);
1318 loctime = localtime (&curtime);
1319 dict[2].value = asctime (loctime);
1320 cp = strchr (dict[2].value, '\n');
1327 dict[3].value = "Clean7Bit";
1330 dict[3].value = "Clean8Bit";
1333 dict[3].value = "Binary";
1339 if (x->orientation == OTN_PORTRAIT)
1340 dict[4].value = "Portrait";
1342 dict[4].value = "Landscape";
1344 /* PORTME: Determine username, net address. */
1346 dict[5].value = getenv ("LOGNAME");
1348 dict[5].value = getlogin ();
1350 dict[5].value = _("nobody");
1352 if (gethostname (host, 128) == -1)
1354 if (errno == ENAMETOOLONG)
1357 strcpy (host, _("nowhere"));
1359 dict[6].value = host;
1360 #else /* !HAVE_UNISTD_H */
1361 dict[5].value = _("nobody");
1362 dict[6].value = _("nowhere");
1363 #endif /* !HAVE_UNISTD_H */
1365 cp = stpcpy (p = local_alloc (288), "font ");
1366 quote_ps_string (cp, x->prop->font->internal_name);
1369 cp = stpcpy (p = local_alloc (288), "font ");
1370 quote_ps_string (cp, x->fixed->font->internal_name);
1373 sprintf (scaling, "%.3f", PSUS / 72.0);
1374 dict[9].value = scaling;
1376 sprintf (paper_width, "%g", x->w / (PSUS / 72.0));
1377 dict[10].value = paper_width;
1379 sprintf (paper_length, "%g", x->l / (PSUS / 72.0));
1380 dict[11].value = paper_length;
1382 sprintf (left_margin, "%d", x->left_margin);
1383 dict[12].value = left_margin;
1385 sprintf (top_margin, "%d", x->top_margin);
1386 dict[13].value = top_margin;
1388 sprintf (line_width, "%d", x->line_width);
1389 dict[14].value = line_width;
1391 sprintf (line_width, "%d", x->line_width_thick);
1392 dict[15].value = line_width_thick;
1396 dict[16].value = cp = local_alloc (16);
1397 strcpy (cp, "PSPP");
1401 dict[16].value = local_alloc (strlen (outp_title) + 1);
1402 strcpy ((char *) (dict[16].value), outp_title);
1406 while (-1 != getline (&buf, &buf_size, prologue_file))
1412 cp = strstr (buf, "!eps");
1415 if (this->class->magic == MAGIC_PS)
1422 cp = strstr (buf, "!ps");
1425 if (this->class->magic == MAGIC_EPSF)
1430 if (strstr (buf, "!!!"))
1435 if (!strncmp (buf, "!encodings", 10))
1436 output_encodings (this);
1440 ds_create(&line, buf);
1441 fn_interp_vars(&line, ps_get_var);
1442 ds_ltrim_spaces(&line);
1443 len = ds_length(&line);
1444 fwrite (ds_c_str(&line), len, 1, f->file);
1448 fputs (x->eol, f->file);
1451 if (ferror (f->file))
1452 msg (IE, _("Reading `%s': %s."), prologue_fn, strerror (errno));
1453 fclose (prologue_file);
1458 local_free (dict[7].value);
1459 local_free (dict[8].value);
1460 local_free (dict[16].value);
1462 if (ferror (f->file))
1465 msg (VM (2), _("%s: PostScript prologue read successfully."), this->name);
1469 msg (VM (1), _("%s: Error reading PostScript prologue."), this->name);
1473 /* Writes the string STRING to buffer DEST (of at least 288
1474 characters) as a PostScript name object. Returns a pointer
1475 to the null terminator of the resultant string. */
1477 quote_ps_name (char *dest, const char *string)
1481 for (sp = string; *sp; sp++)
1555 for (sp = string; *sp && dp < &dest[256]; sp++)
1557 sprintf (dp, "%02x", (unsigned char) *sp);
1560 return stpcpy (dp, ">cvn");
1564 return stpcpy (&dest[1], string);
1567 /* Adds the string STRING to buffer DEST as a PostScript quoted
1568 string; returns a pointer to the null terminator added. Will not
1569 add more than 235 characters. */
1571 quote_ps_string (char *dest, const char *string)
1573 const char *sp = string;
1577 for (; *sp && dp < &dest[235]; sp++)
1579 dp = stpcpy (dp, "\\(");
1580 else if (*sp == ')')
1581 dp = stpcpy (dp, "\\)");
1582 else if (*sp < 32 || (unsigned char) *sp > 127)
1583 dp = spprintf (dp, "\\%3o", *sp);
1586 return stpcpy (dp, ")");
1589 /* Writes the PostScript epilogue to file F. */
1591 preclose (struct file_ext *f)
1593 struct outp_driver *this = f->param;
1594 struct ps_driver_ext *x = this->ext;
1595 struct hsh_iterator iter;
1596 struct font_entry *fe;
1601 "%%%%DocumentNeededResources:%s"),
1602 x->eol, x->file_page_number, x->eol, x->eol);
1604 for (fe = hsh_first (x->loaded, &iter); fe != NULL;
1605 fe = hsh_next (x->loaded, &iter))
1609 cp = stpcpy (buf, "%%+ font ");
1610 cp = quote_ps_string (cp, fe->font->internal_name);
1611 strcpy (cp, x->eol);
1612 fputs (buf, f->file);
1615 hsh_destroy (x->loaded);
1617 hsh_destroy (x->combos);
1619 x->last_font = NULL;
1622 fprintf (f->file, "%%EOF%s", x->eol);
1623 if (ferror (f->file))
1629 ps_open_page (struct outp_driver *this)
1631 struct ps_driver_ext *x = this->ext;
1633 assert (this->driver_open && !this->page_open);
1636 if (!fn_open_ext (&x->file))
1639 msg (ME, _("PostScript output driver: %s: %s"), x->file.filename,
1643 x->file_page_number++;
1645 hsh_destroy (x->combos);
1646 x->combos = hsh_create (31, compare_ps_combo, hash_ps_combo,
1647 free_ps_combo, NULL);
1648 x->last_font = NULL;
1651 fprintf (x->file.file,
1653 "%%%%BeginPageSetup%s"
1654 "/pg save def 0.001 dup scale%s",
1655 x->page_number, x->file_page_number, x->eol,
1659 if (x->orientation == OTN_LANDSCAPE)
1660 fprintf (x->file.file,
1661 "%d 0 translate 90 rotate%s",
1664 if (x->bottom_margin != 0 || x->left_margin != 0)
1665 fprintf (x->file.file,
1666 "%d %d translate%s",
1667 x->left_margin, x->bottom_margin, x->eol);
1669 fprintf (x->file.file,
1670 "/LW %d def/TW %d def %d setlinewidth%s"
1671 "%%%%EndPageSetup%s",
1672 x->line_width, x->line_width_thick, x->line_width, x->eol,
1675 if (!ferror (x->file.file))
1677 this->page_open = 1;
1678 if (x->output_options & OPO_HEADERS)
1679 draw_headers (this);
1684 return !ferror (x->file.file);
1688 ps_close_page (struct outp_driver *this)
1690 struct ps_driver_ext *x = this->ext;
1692 assert (this->driver_open && this->page_open);
1697 fprintf (x->file.file,
1702 this->page_open = 0;
1703 return !ferror (x->file.file);
1707 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
1721 /* qsort() comparison function for int tuples. */
1723 int_2_compare (const void *a_, const void *b_)
1728 return *a < *b ? -1 : *a > *b;
1731 /* Hash table comparison function for cached lines. */
1733 compare_line (const void *a_, const void *b_, void *foo UNUSED)
1735 const struct line_form *a = a_;
1736 const struct line_form *b = b_;
1738 return a->ind < b->ind ? -1 : a->ind > b->ind;
1741 /* Hash table hash function for cached lines. */
1743 hash_line (const void *pa, void *foo UNUSED)
1745 const struct line_form *a = pa;
1750 /* Hash table free function for cached lines. */
1752 free_line (void *pa, void *foo UNUSED)
1757 /* Writes PostScript code to draw a line from (x1,y1) to (x2,y2) to
1759 #define dump_line(x1, y1, x2, y2) \
1760 fprintf (ext->file.file, "%d %d %d %d L%s", \
1761 x1, YT (y1), x2, YT (y2), ext->eol)
1763 /* Write PostScript code to draw a thick line from (x1,y1) to (x2,y2)
1764 to the output file. */
1765 #define dump_thick_line(x1, y1, x2, y2) \
1766 fprintf (ext->file.file, "%d %d %d %d TL%s", \
1767 x1, YT (y1), x2, YT (y2), ext->eol)
1769 /* Writes a line of type TYPE to THIS driver's output file. The line
1770 (or its center, in the case of double lines) has its independent
1771 axis coordinate at IND; it extends from DEP1 to DEP2 on the
1774 dump_fancy_line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1776 struct ps_driver_ext *ext = this->ext;
1777 int ofs = ext->line_space / 2 + ext->line_width / 2;
1782 dump_line (dep1, ind, dep2, ind);
1785 if (ext->output_options & OPO_DOUBLE_LINE)
1787 dump_line (dep1, ind - ofs, dep2, ind - ofs);
1788 dump_line (dep1, ind + ofs, dep2, ind + ofs);
1791 dump_thick_line (dep1, ind, dep2, ind);
1796 dump_line (ind, dep1, ind, dep2);
1799 if (ext->output_options & OPO_DOUBLE_LINE)
1801 dump_line (ind - ofs, dep1, ind - ofs, dep2);
1802 dump_line (ind + ofs, dep1, ind + ofs, dep2);
1805 dump_thick_line (ind, dep1, ind, dep2);
1816 /* Writes all the cached lines to the output file, then clears the
1819 dump_lines (struct outp_driver *this)
1821 struct ps_driver_ext *x = this->ext;
1823 struct hsh_iterator iter;
1826 for (type = 0; type < n_line_types; type++)
1828 struct line_form *line;
1830 if (x->lines[type] == NULL)
1833 for (line = hsh_first (x->lines[type], &iter); line != NULL;
1834 line = hsh_next (x->lines[type], &iter))
1837 int lo = INT_MIN, hi;
1839 qsort (line->dep, line->ndep, sizeof *line->dep, int_2_compare);
1840 lo = line->dep[0][0];
1841 hi = line->dep[0][1];
1842 for (i = 1; i < line->ndep; i++)
1843 if (line->dep[i][0] <= hi + 1)
1845 int min_hi = line->dep[i][1];
1851 dump_fancy_line (this, type, line->ind, lo, hi);
1852 lo = line->dep[i][0];
1853 hi = line->dep[i][1];
1855 dump_fancy_line (this, type, line->ind, lo, hi);
1858 hsh_destroy (x->lines[type]);
1859 x->lines[type] = NULL;
1863 /* (Same args as dump_fancy_line()). Either dumps the line directly
1864 to the output file, or adds it to the cache, depending on the
1865 user-selected line optimization mode. */
1867 line (struct outp_driver *this, int type, int ind, int dep1, int dep2)
1869 struct ps_driver_ext *ext = this->ext;
1870 struct line_form **f;
1872 assert (dep2 >= dep1);
1873 if (ext->line_opt == 0)
1875 dump_fancy_line (this, type, ind, dep1, dep2);
1879 if (ext->lines[type] == NULL)
1880 ext->lines[type] = hsh_create (31, compare_line, hash_line,
1882 f = (struct line_form **) hsh_probe (ext->lines[type], &ind);
1885 *f = xmalloc (sizeof **f + sizeof (int[15][2]));
1889 (*f)->dep[0][0] = dep1;
1890 (*f)->dep[0][1] = dep2;
1893 if ((*f)->ndep >= (*f)->mdep)
1896 *f = xrealloc (*f, sizeof **f + sizeof (int[2]) * ((*f)->mdep - 1));
1898 (*f)->dep[(*f)->ndep][0] = dep1;
1899 (*f)->dep[(*f)->ndep][1] = dep2;
1904 ps_line_horz (struct outp_driver *this, const struct rect *r,
1905 const struct color *c UNUSED, int style)
1907 /* Must match output.h:OUTP_L_*. */
1908 static const int types[OUTP_L_COUNT] =
1909 {-1, horz, dbl_horz, spl_horz};
1911 int y = (r->y1 + r->y2) / 2;
1913 assert (this->driver_open && this->page_open);
1914 assert (style >= 0 && style < OUTP_L_COUNT);
1915 style = types[style];
1917 line (this, style, y, r->x1, r->x2);
1921 ps_line_vert (struct outp_driver *this, const struct rect *r,
1922 const struct color *c UNUSED, int style)
1924 /* Must match output.h:OUTP_L_*. */
1925 static const int types[OUTP_L_COUNT] =
1926 {-1, vert, dbl_vert, spl_vert};
1928 int x = (r->x1 + r->x2) / 2;
1930 assert (this->driver_open && this->page_open);
1931 assert (style >= 0 && style < OUTP_L_COUNT);
1932 style = types[style];
1934 line (this, style, x, r->y1, r->y2);
1937 #define L (style->l != OUTP_L_NONE)
1938 #define R (style->r != OUTP_L_NONE)
1939 #define T (style->t != OUTP_L_NONE)
1940 #define B (style->b != OUTP_L_NONE)
1943 ps_line_intersection (struct outp_driver *this, const struct rect *r,
1944 const struct color *c UNUSED,
1945 const struct outp_styles *style)
1947 struct ps_driver_ext *ext = this->ext;
1949 int x = (r->x1 + r->x2) / 2;
1950 int y = (r->y1 + r->y2) / 2;
1951 int ofs = (ext->line_space + ext->line_width) / 2;
1952 int x1 = x - ofs, x2 = x + ofs;
1953 int y1 = y - ofs, y2 = y + ofs;
1955 assert (this->driver_open && this->page_open);
1956 assert (!((style->l != style->r && style->l != OUTP_L_NONE
1957 && style->r != OUTP_L_NONE)
1958 || (style->t != style->b && style->t != OUTP_L_NONE
1959 && style->b != OUTP_L_NONE)));
1961 switch ((style->l | style->r) | ((style->t | style->b) << 8))
1963 case (OUTP_L_SINGLE) | (OUTP_L_SINGLE << 8):
1964 case (OUTP_L_SINGLE) | (OUTP_L_NONE << 8):
1965 case (OUTP_L_NONE) | (OUTP_L_SINGLE << 8):
1967 line (this, horz, y, r->x1, x);
1969 line (this, horz, y, x, r->x2);
1971 line (this, vert, x, r->y1, y);
1973 line (this, vert, x, y, r->y2);
1975 case (OUTP_L_SINGLE) | (OUTP_L_DOUBLE << 8):
1976 case (OUTP_L_NONE) | (OUTP_L_DOUBLE << 8):
1978 line (this, horz, y, r->x1, x1);
1980 line (this, horz, y, x2, r->x2);
1982 line (this, dbl_vert, x, r->y1, y);
1984 line (this, dbl_vert, x, y, r->y2);
1985 if ((L && R) && !(T && B))
1986 line (this, horz, y, x1, x2);
1988 case (OUTP_L_DOUBLE) | (OUTP_L_SINGLE << 8):
1989 case (OUTP_L_DOUBLE) | (OUTP_L_NONE << 8):
1991 line (this, dbl_horz, y, r->x1, x);
1993 line (this, dbl_horz, y, x, r->x2);
1995 line (this, vert, x, r->y1, y);
1997 line (this, vert, x, y, r->y2);
1998 if ((T && B) && !(L && R))
1999 line (this, vert, x, y1, y2);
2001 case (OUTP_L_DOUBLE) | (OUTP_L_DOUBLE << 8):
2003 line (this, dbl_horz, y, r->x1, x);
2005 line (this, dbl_horz, y, x, r->x2);
2007 line (this, dbl_vert, x, r->y1, y);
2009 line (this, dbl_vert, x, y, r->y2);
2011 line (this, vert, x1, y1, y2);
2013 line (this, vert, x2, y1, y2);
2015 line (this, horz, y1, x1, x2);
2017 line (this, horz, y2, x1, x2);
2025 ps_box (struct outp_driver *this UNUSED, const struct rect *r UNUSED,
2026 const struct color *bord UNUSED, const struct color *fill UNUSED)
2028 assert (this->driver_open && this->page_open);
2032 ps_polyline_begin (struct outp_driver *this UNUSED,
2033 const struct color *c UNUSED)
2035 assert (this->driver_open && this->page_open);
2038 ps_polyline_point (struct outp_driver *this UNUSED, int x UNUSED, int y UNUSED)
2040 assert (this->driver_open && this->page_open);
2043 ps_polyline_end (struct outp_driver *this UNUSED)
2045 assert (this->driver_open && this->page_open);
2048 /* Returns the width of string S for THIS driver. */
2050 text_width (struct outp_driver *this, char *s)
2052 struct outp_text text;
2054 text.options = OUTP_T_JUST_LEFT;
2055 ls_init (&text.s, s, strlen (s));
2056 this->class->text_metrics (this, &text);
2060 /* Write string S at location (X,Y) with width W for THIS driver. */
2062 out_text_plain (struct outp_driver *this, char *s, int x, int y, int w)
2064 struct outp_text text;
2066 text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
2067 ls_init (&text.s, s, strlen (s));
2069 text.v = this->font_height;
2072 this->class->text_draw (this, &text);
2075 /* Draw top of page headers for THIS driver. */
2077 draw_headers (struct outp_driver *this)
2079 struct ps_driver_ext *ext = this->ext;
2081 struct font_entry *old_current = ext->current;
2082 char *old_family = xstrdup (ext->family); /* FIXME */
2083 int old_size = ext->size;
2085 int fh = this->font_height;
2088 fprintf (ext->file.file, "%d %d %d %d GB%s",
2089 0, YT (y), this->width, YT (y + 2 * fh + ext->line_gutter),
2091 this->class->text_set_font_family (this, "T");
2093 y += ext->line_width + ext->line_gutter;
2099 sprintf (buf, _("%s - Page %d"), get_start_date (), ext->page_number);
2100 rh_width = text_width (this, buf);
2102 out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2105 if (outp_title && outp_subtitle)
2106 out_text_plain (this, outp_title, this->prop_em_width, y,
2107 this->width - 3 * this->prop_em_width - rh_width);
2115 char *string = outp_subtitle ? outp_subtitle : outp_title;
2117 sprintf (buf, "%s - %s", version, host_system);
2118 rh_width = text_width (this, buf);
2120 out_text_plain (this, buf, this->width - this->prop_em_width - rh_width,
2124 out_text_plain (this, string, this->prop_em_width, y,
2125 this->width - 3 * this->prop_em_width - rh_width);
2130 ext->current = old_current;
2132 ext->family = old_family;
2133 ext->size = old_size;
2140 ps_text_set_font_by_name (struct outp_driver *this, const char *dit)
2142 struct ps_driver_ext *x = this->ext;
2143 struct font_entry *fe;
2145 assert (this->driver_open && this->page_open);
2147 /* Short-circuit common fonts. */
2148 if (!strcmp (dit, "PROP"))
2150 x->current = x->prop;
2151 x->size = x->font_size;
2154 else if (!strcmp (dit, "FIXED"))
2156 x->current = x->fixed;
2157 x->size = x->font_size;
2161 /* Find font_desc corresponding to Groff name dit. */
2162 fe = hsh_find (x->loaded, &dit);
2164 fe = load_font (this, dit);
2169 ps_text_set_font_by_position (struct outp_driver *this, int pos)
2171 struct ps_driver_ext *x = this->ext;
2174 assert (this->driver_open && this->page_open);
2176 /* Determine font name by suffixing position string to font family
2181 dit = local_alloc (strlen (x->family) + 3);
2182 cp = stpcpy (dit, x->family);
2204 /* Find font_desc corresponding to Groff name dit. */
2206 struct font_entry *fe = hsh_find (x->loaded, &dit);
2208 fe = load_font (this, dit);
2216 ps_text_set_font_family (struct outp_driver *this, const char *s)
2218 struct ps_driver_ext *x = this->ext;
2220 assert (this->driver_open && this->page_open);
2223 x->family = xstrdup (s);
2227 ps_text_get_font_name (struct outp_driver *this)
2229 struct ps_driver_ext *x = this->ext;
2231 assert (this->driver_open && this->page_open);
2232 return x->current->font->name;
2236 ps_text_get_font_family (struct outp_driver *this)
2238 struct ps_driver_ext *x = this->ext;
2240 assert (this->driver_open && this->page_open);
2245 ps_text_set_size (struct outp_driver *this, int size)
2247 struct ps_driver_ext *x = this->ext;
2249 assert (this->driver_open && this->page_open);
2250 x->size = PSUS / 72000 * size;
2255 ps_text_get_size (struct outp_driver *this, int *em_width)
2257 struct ps_driver_ext *x = this->ext;
2259 assert (this->driver_open && this->page_open);
2261 *em_width = (x->current->font->space_width * x->size) / 1000;
2262 return x->size / (PSUS / 72000);
2265 /* An output character. */
2268 struct font_entry *font; /* Font of character. */
2269 int size; /* Size of character. */
2270 int x, y; /* Location of character. */
2271 unsigned char ch; /* Character. */
2272 char separate; /* Must be separate from previous char. */
2275 /* Hash table comparison function for ps_combo structs. */
2277 compare_ps_combo (const void *pa, const void *pb, void *foo UNUSED)
2279 const struct ps_font_combo *a = pa;
2280 const struct ps_font_combo *b = pb;
2282 return !((a->font == b->font) && (a->size == b->size));
2285 /* Hash table hash function for ps_combo structs. */
2287 hash_ps_combo (const void *pa, void *foo UNUSED)
2289 const struct ps_font_combo *a = pa;
2290 unsigned name_hash = hsh_hash_string (a->font->font->internal_name);
2291 return name_hash ^ hsh_hash_int (a->size);
2294 /* Hash table free function for ps_combo structs. */
2296 free_ps_combo (void *a, void *foo UNUSED)
2301 /* Causes PostScript code to be output that switches to the font
2302 CP->FONT and font size CP->SIZE. The first time a particular
2303 font/size combination is used on a particular page, this involves
2304 outputting PostScript code to load the font. */
2306 switch_font (struct outp_driver *this, const struct output_char *cp)
2308 struct ps_driver_ext *ext = this->ext;
2309 struct ps_font_combo srch, **fc;
2311 srch.font = cp->font;
2312 srch.size = cp->size;
2314 fc = (struct ps_font_combo **) hsh_probe (ext->combos, &srch);
2317 fprintf (ext->file.file, "F%x%s", (*fc)->index, ext->eol);
2322 struct ps_encoding *encoding;
2325 *fc = xmalloc (sizeof **fc);
2326 (*fc)->font = cp->font;
2327 (*fc)->size = cp->size;
2328 (*fc)->index = ext->next_combo++;
2330 filename = find_encoding_file (this, cp->font->font->encoding);
2333 encoding = get_encoding (this, filename);
2338 msg (IE, _("PostScript driver: Cannot find encoding `%s' for "
2339 "PostScript font `%s'."), cp->font->font->encoding,
2340 cp->font->font->internal_name);
2341 encoding = default_encoding (this);
2344 if (cp->font != ext->fixed && cp->font != ext->prop)
2346 bp = stpcpy (buf, "%%IncludeResource: font ");
2347 bp = quote_ps_string (bp, cp->font->font->internal_name);
2348 bp = stpcpy (bp, ext->eol);
2353 bp = spprintf (bp, "/F%x E%x %d", (*fc)->index, encoding->index,
2355 bp = quote_ps_name (bp, cp->font->font->internal_name);
2356 sprintf (bp, " SF%s", ext->eol);
2357 fputs (buf, ext->file.file);
2359 ext->last_font = *fc;
2362 /* (write_text) Writes the accumulated line buffer to the output
2364 #define output_line() \
2367 lp = stpcpy (lp, ext->eol); \
2369 fputs (line, ext->file.file); \
2374 /* (write_text) Adds the string representing number X to the line
2375 buffer, flushing the buffer to disk beforehand if necessary. */
2376 #define put_number(X) \
2379 int n = nsprintf (number, "%d", X); \
2380 if (n + lp > &line[75]) \
2382 lp = stpcpy (lp, number); \
2386 /* Outputs PostScript code to THIS driver's output file to display the
2387 characters represented by the output_char's between CP and END,
2388 using the associated outp_text T to determine formatting. WIDTH is
2389 the width of the output region; WIDTH_LEFT is the amount of the
2390 WIDTH that is not taken up by text (so it can be used to determine
2393 write_text (struct outp_driver *this,
2394 const struct output_char *cp, const struct output_char *end,
2395 struct outp_text *t, int width UNUSED, int width_left)
2397 struct ps_driver_ext *ext = this->ext;
2402 char number[INT_STRLEN_BOUND (int) + 1];
2406 switch (t->options & OUTP_T_JUST_MASK)
2408 case OUTP_T_JUST_LEFT:
2411 case OUTP_T_JUST_RIGHT:
2414 case OUTP_T_JUST_CENTER:
2415 ofs = width_left / 2;
2426 int x = cp->x + ofs;
2427 int y = cp->y + (cp->font->font->ascent * cp->size / 1000);
2429 if (ext->last_font == NULL
2430 || cp->font != ext->last_font->font
2431 || cp->size != ext->last_font->size)
2432 switch_font (this, cp);
2438 static unsigned char literal_chars[ODA_COUNT][32] =
2440 {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2441 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
2442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2443 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2445 {0x00, 0x00, 0x00, 0xf8, 0xff, 0xfc, 0xff, 0xff,
2446 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2447 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2448 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2450 {0x7e, 0xd6, 0xff, 0xfb, 0xff, 0xfc, 0xff, 0xff,
2451 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2452 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2453 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
2457 if (TEST_BIT (literal_chars[ext->data], cp->ch))
2460 switch ((char) cp->ch)
2463 lp = stpcpy (lp, "\\(");
2466 lp = stpcpy (lp, "\\)");
2469 lp = spprintf (lp, "\\%03o", cp->ch);
2474 while (cp < end && lp < &line[70] && cp->separate == 0);
2482 put_number (YT (y));
2493 if (lp >= &line[70])
2503 /* Displays the text in outp_text T, if DRAW is nonzero; or, merely
2504 determine the text metrics, if DRAW is zero. */
2506 text (struct outp_driver *this, struct outp_text *t, int draw)
2508 struct ps_driver_ext *ext = this->ext;
2511 struct output_char *buf; /* Output buffer. */
2512 struct output_char *buf_end; /* End of output buffer. */
2513 struct output_char *buf_loc; /* Current location in output buffer. */
2516 struct font_entry *old_current = ext->current;
2517 char *old_family = xstrdup (ext->family); /* FIXME */
2518 int old_size = ext->size;
2523 /* Current location. */
2526 /* Keeping track of what's left over. */
2527 int width; /* Width available for characters. */
2528 int width_left, height_left; /* Width, height left over. */
2529 int max_height; /* Tallest character on this line so far. */
2531 /* Previous character. */
2534 /* Information about location of previous space. */
2535 char *space_char; /* Character after space. */
2536 struct output_char *space_buf_loc; /* Buffer location after space. */
2537 int space_width_left; /* Width of characters before space. */
2539 /* Name of the current character. */
2540 const char *char_name;
2541 char local_char_name[2] = {0, 0};
2543 local_char_name[0] = local_char_name[1] = 0;
2545 buf = local_alloc (sizeof *buf * 128);
2546 buf_end = &buf[128];
2549 assert (!ls_null_p (&t->s));
2550 cp = ls_c_str (&t->s);
2551 end = ls_end (&t->s);
2559 width = width_left = (t->options & OUTP_T_HORZ) ? t->h : INT_MAX;
2560 height_left = (t->options & OUTP_T_VERT) ? t->v : INT_MAX;
2564 space_buf_loc = NULL;
2565 space_width_left = 0;
2568 if (!width || !height_left)
2573 struct char_metrics *metric;
2579 /* Set char_name to the name of the character or ligature at
2581 local_char_name[0] = *cp;
2582 char_name = local_char_name;
2583 if (ext->current->font->ligatures && *cp == 'f')
2592 lig = LIG_fi, char_name = "fi";
2595 lig = LIG_fl, char_name = "fl";
2602 lig = LIG_ffi, char_name = "ffi";
2605 lig = LIG_ffl, char_name = "ffl";
2608 lig = LIG_ff, char_name = "ff";
2612 if ((lig & ext->current->font->ligatures) == 0)
2614 local_char_name[0] = *cp; /* 'f' */
2615 char_name = local_char_name;
2618 else if (*cp == '\n')
2622 write_text (this, buf, buf_loc, t, width, width_left);
2629 height_left -= max_height;
2635 /* FIXME: when we're page buffering it will be necessary to
2636 set separate to 1. */
2639 cp += strlen (char_name);
2641 /* Figure out what size this character is, and what kern
2642 adjustment we need. */
2643 cur_char = font_char_name_to_index (char_name);
2644 metric = font_get_char_metrics (ext->current->font, cur_char);
2647 static struct char_metrics m;
2649 m.width = ext->current->font->space_width;
2650 m.code = *char_name;
2652 kern_amt = font_get_kern_adjust (ext->current->font, prev_char,
2656 kern_amt = (kern_amt * ext->size / 1000);
2659 char_width = metric->width * ext->size / 1000;
2661 /* Record the current status if this is a space character. */
2662 if (cur_char == space_index && buf_loc > buf)
2665 space_buf_loc = buf_loc;
2666 space_width_left = width_left;
2669 /* Drop down to a new line if there's no room left on this
2671 if (char_width + kern_amt > width_left)
2673 /* Regress to previous space, if any. */
2677 width_left = space_width_left;
2678 buf_loc = space_buf_loc;
2683 write_text (this, buf, buf_loc, t, width, width_left);
2690 height_left -= max_height;
2698 /* FIXME: when we're page buffering it will be
2699 necessary to set separate to 1. */
2704 if (ext->size > max_height)
2705 max_height = ext->size;
2706 if (max_height > height_left)
2709 /* Actually draw the character. */
2712 if (buf_loc >= buf_end)
2714 int buf_len = buf_end - buf;
2718 struct output_char *new_buf;
2720 new_buf = xmalloc (sizeof *new_buf * 256);
2721 memcpy (new_buf, buf, sizeof *new_buf * 128);
2722 buf_loc = new_buf + 128;
2723 buf_end = new_buf + 256;
2729 buf = xnrealloc (buf, buf_len * 2, sizeof *buf);
2730 buf_loc = buf + buf_len;
2731 buf_end = buf + buf_len * 2;
2736 buf_loc->font = ext->current;
2737 buf_loc->size = ext->size;
2740 buf_loc->ch = metric->code;
2741 buf_loc->separate = separate;
2746 /* Prepare for next iteration. */
2747 width_left -= char_width + kern_amt;
2748 prev_char = cur_char;
2750 height_left -= max_height;
2751 if (buf_loc > buf && draw)
2752 write_text (this, buf, buf_loc, t, width, width_left);
2755 if (!(t->options & OUTP_T_HORZ))
2756 t->h = INT_MAX - width_left;
2757 if (!(t->options & OUTP_T_VERT))
2758 t->v = INT_MAX - height_left;
2760 t->v -= height_left;
2761 if (buf_end - buf == 128)
2765 ext->current = old_current;
2767 ext->family = old_family;
2768 ext->size = old_size;
2772 ps_text_metrics (struct outp_driver *this, struct outp_text *t)
2774 assert (this->driver_open && this->page_open);
2779 ps_text_draw (struct outp_driver *this, struct outp_text *t)
2781 assert (this->driver_open && this->page_open);
2787 /* Translate a filename to a font. */
2788 struct filename2font
2790 char *filename; /* Normalized filename. */
2791 struct font_desc *font;
2794 /* Table of `filename2font's. */
2795 static struct hsh_table *ps_fonts;
2797 /* Hash table comparison function for filename2font structs. */
2799 compare_filename2font (const void *a, const void *b, void *param UNUSED)
2801 return strcmp (((struct filename2font *) a)->filename,
2802 ((struct filename2font *) b)->filename);
2805 /* Hash table hash function for filename2font structs. */
2807 hash_filename2font (const void *f2f_, void *param UNUSED)
2809 const struct filename2font *f2f = f2f_;
2810 return hsh_hash_string (f2f->filename);
2813 /* Initializes the global font list by creating the hash table for
2814 translation of filenames to font_desc structs. */
2818 ps_fonts = hsh_create (31, compare_filename2font, hash_filename2font,
2825 hsh_destroy (ps_fonts);
2828 /* Loads the font having Groff name DIT into THIS driver instance.
2829 Specifically, adds it into the THIS driver's `loaded' hash
2831 static struct font_entry *
2832 load_font (struct outp_driver *this, const char *dit)
2834 struct ps_driver_ext *x = this->ext;
2835 char *filename1, *filename2;
2837 struct font_entry *fe;
2839 filename1 = find_ps_file (this, dit);
2841 filename1 = xstrdup (dit);
2842 filename2 = fn_normalize (filename1);
2845 entry = hsh_probe (ps_fonts, &filename2);
2848 struct filename2font *f2f;
2849 struct font_desc *f = groff_read_font (filename2);
2856 f = default_font ();
2859 f2f = xmalloc (sizeof *f2f);
2860 f2f->filename = filename2;
2867 fe = xmalloc (sizeof *fe);
2868 fe->dit = xstrdup (dit);
2869 fe->font = ((struct filename2font *) * entry)->font;
2870 *hsh_probe (x->loaded, &dit) = fe;
2876 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
2881 struct ps_driver_ext *x = this->ext;
2882 char page_size[128];
2884 int x_origin, y_origin;
2886 ch->file = tmpfile ();
2887 if (ch->file == NULL)
2893 size = this->width < this->length ? this->width : this->length;
2894 x_origin = x->left_margin + (size - this->width) / 2;
2895 y_origin = x->bottom_margin + (size - this->length) / 2;
2897 snprintf (page_size, sizeof page_size,
2898 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
2899 (double) size / PSUS, (double) size / PSUS,
2900 (double) x_origin / PSUS, (double) y_origin / PSUS);
2902 ch->pl_params = pl_newplparams ();
2903 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
2904 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
2909 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
2912 struct ps_driver_ext *x = this->ext;
2914 static int doc_num = 0;
2916 if (this->page_open)
2918 this->class->close_page (this);
2919 this->page_open = 0;
2921 this->class->open_page (this);
2922 fprintf (x->file.file,
2924 "%d %d translate 1000 dup scale%s"
2926 "/showpage { } def%s"
2927 "0 setgray 0 setlinecap 1 setlinewidth%s"
2928 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear%s"
2929 "%%%%BeginDocument: %d%s",
2931 -x->left_margin, -x->bottom_margin, x->eol,
2939 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file.file))
2943 fprintf (x->file.file,
2950 this->class->close_page (this);
2951 this->page_open = 0;
2955 /* PostScript driver class. */
2956 struct outp_class postscript_class =
2978 ps_line_intersection,
2985 ps_text_set_font_by_name,
2986 ps_text_set_font_by_position,
2987 ps_text_set_font_family,
2988 ps_text_get_font_name,
2989 ps_text_get_font_family,
2995 ps_chart_initialise,
2999 /* EPSF driver class. FIXME: Probably doesn't work right. */
3000 struct outp_class epsf_class =
3022 ps_line_intersection,
3029 ps_text_set_font_by_name,
3030 ps_text_set_font_by_position,
3031 ps_text_set_font_family,
3032 ps_text_get_font_name,
3033 ps_text_get_font_family,
3039 ps_chart_initialise,