1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 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
29 #include <libpspp/alloc.h>
30 #include <libpspp/bit-vector.h>
31 #include <libpspp/compiler.h>
32 #include <libpspp/freaderror.h>
33 #include <libpspp/hash.h>
34 #include <libpspp/misc.h>
35 #include <libpspp/start-date.h>
36 #include <libpspp/version.h>
38 #include <data/file-name.h>
52 #define _(msgid) gettext (msgid)
54 /* PostScript driver options: (defaults listed first)
58 paper-size=letter (see "papersize" file)
59 orientation=portrait|landscape
68 emph-font=Times-Italic
77 /* The number of `psus' (PostScript driver UnitS) per inch. */
80 /* A PostScript font. */
83 struct afm *metrics; /* Metrics. */
84 char *embed_fn; /* Name of file to embed. */
85 char *encoding_fn; /* Name of file with encoding. */
88 /* PostScript output driver extension record. */
91 char *file_name; /* Output file name. */
92 FILE *file; /* Output file. */
94 bool draw_headers; /* Draw headers at top of page? */
95 int page_number; /* Current page number. */
97 bool portrait; /* Portrait mode? */
98 int paper_width; /* Width of paper before dropping margins. */
99 int paper_length; /* Length of paper before dropping margins. */
100 int left_margin; /* Left margin in psus. */
101 int right_margin; /* Right margin in psus. */
102 int top_margin; /* Top margin in psus. */
103 int bottom_margin; /* Bottom margin in psus. */
105 int line_gutter; /* Space around lines. */
106 int line_space; /* Space between lines. */
107 int line_width; /* Width of lines. */
109 struct font *fonts[OUTP_FONT_CNT];
110 int last_font; /* Index of last font set with setfont. */
114 /* Transform logical y-ordinate Y into a page ordinate. */
115 #define YT(Y) (this->length - (Y))
117 static bool handle_option (struct outp_driver *this, const char *key,
118 const struct string *val);
119 static void draw_headers (struct outp_driver *this);
121 static void write_ps_prologue (struct outp_driver *);
123 static char *quote_ps_name (const char *string);
125 static struct font *load_font (const char *string);
126 static void free_font (struct font *);
127 static void setup_font (struct outp_driver *this, struct font *, int index);
129 /* Driver initialization. */
132 ps_open_driver (struct outp_driver *this, const struct string *options)
134 struct ps_driver_ext *x;
137 this->width = this->length = 0;
138 this->font_height = PSUS * 10 / 72;
140 this->ext = x = xmalloc (sizeof *x);
141 x->file_name = xstrdup ("pspp.ps");
143 x->draw_headers = true;
146 x->paper_width = PSUS * 17 / 2;
147 x->paper_length = PSUS * 11;
148 x->left_margin = PSUS / 2;
149 x->right_margin = PSUS / 2;
150 x->top_margin = PSUS / 2;
151 x->bottom_margin = PSUS / 2;
152 x->line_gutter = PSUS / 72;
153 x->line_space = PSUS / 72;
154 x->line_width = PSUS / 144;
155 for (i = 0; i < OUTP_FONT_CNT; i++)
158 outp_parse_options (options, handle_option, this);
160 x->file = fn_open (x->file_name, "w");
163 error (0, errno, _("opening PostScript output file \"%s\""),
170 this->width = x->paper_width;
171 this->length = x->paper_length;
175 this->width = x->paper_length;
176 this->length = x->paper_width;
178 this->width -= x->left_margin + x->right_margin;
179 this->length -= x->top_margin + x->bottom_margin;
182 int header_length = 3 * this->font_height;
183 this->length -= header_length;
184 x->top_margin += header_length;
187 for (i = 0; i < OUTP_FONT_CNT; i++)
188 if (x->fonts[i] == NULL)
190 const char *default_fonts[OUTP_FONT_CNT];
191 default_fonts[OUTP_FIXED] = "Courier.afm";
192 default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
193 default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
194 x->fonts[i] = load_font (default_fonts[i]);
195 if (x->fonts[i] == NULL)
199 if (this->length / this->font_height < 15)
201 error (0, 0, _("The defined PostScript page is not long "
202 "enough to hold margins and headers, plus least 15 "
203 "lines of the default fonts. In fact, there's only "
204 "room for %d lines of each font at the default size "
205 "of %d.%03d points."),
206 this->length / this->font_height,
207 this->font_height / 1000, this->font_height % 1000);
212 afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
213 * this->font_height / 1000;
214 this->prop_em_width =
215 afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
216 * this->font_height / 1000;
218 this->horiz_line_width[OUTP_L_NONE] = 0;
219 this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
220 this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
221 + 2 * x->line_width);
222 memcpy (this->vert_line_width, this->horiz_line_width,
223 sizeof this->vert_line_width);
225 write_ps_prologue (this);
230 this->class->close_driver (this);
235 ps_close_driver (struct outp_driver *this)
237 struct ps_driver_ext *x = this->ext;
247 ok = fn_close (x->file_name, x->file) == 0;
249 error (0, errno, _("closing PostScript output file \"%s\""), x->file_name);
251 for (i = 0; i < OUTP_FONT_CNT; i++)
252 free_font (x->fonts[i]);
258 /* Generic option types. */
272 /* All the options that the PostScript driver supports. */
273 static struct outp_option option_tab[] =
275 {"output-file", output_file_arg,0},
276 {"paper-size", paper_size_arg, 0},
277 {"orientation", orientation_arg,0},
279 {"headers", boolean_arg, 1},
281 {"prop-font", string_arg, OUTP_PROPORTIONAL},
282 {"emph-font", string_arg, OUTP_EMPHASIS},
283 {"fixed-font", string_arg, OUTP_FIXED},
285 {"left-margin", pos_int_arg, 0},
286 {"right-margin", pos_int_arg, 1},
287 {"top-margin", pos_int_arg, 2},
288 {"bottom-margin", pos_int_arg, 3},
289 {"font-size", pos_int_arg, 4},
291 {"line-width", dimension_arg, 0},
292 {"line-gutter", dimension_arg, 1},
293 {"line-width", dimension_arg, 2},
298 handle_option (struct outp_driver *this, const char *key,
299 const struct string *val)
301 struct ps_driver_ext *x = this->ext;
303 char *value = ds_c_str (val);
305 switch (outp_match_keyword (key, option_tab, &subcat))
309 _("unknown configuration parameter `%s' for PostScript device "
312 case output_file_arg:
314 x->file_name = xstrdup (value);
317 outp_get_paper_size (value, &this->width, &this->length);
319 case orientation_arg:
320 if (!strcmp (value, "portrait"))
322 else if (!strcmp (value, "landscape"))
325 error (0, 0, _("unknown orientation `%s' (valid orientations are "
326 "`portrait' and `landscape')"), value);
329 if (!strcmp (value, "on") || !strcmp (value, "true")
330 || !strcmp (value, "yes") || atoi (value))
331 x->draw_headers = true;
332 else if (!strcmp (value, "off") || !strcmp (value, "false")
333 || !strcmp (value, "no") || !strcmp (value, "0"))
334 x->draw_headers = false;
337 error (0, 0, _("boolean value expected for %s"), key);
347 arg = strtol (value, &tail, 0);
348 if (arg < 1 || errno == ERANGE || *tail)
350 error (0, 0, _("positive integer value required for `%s'"), key);
353 if ((subcat == 4 || subcat == 5) && arg < 1000)
355 error (0, 0, _("default font size must be at least 1 point (value "
356 "of 1000 for key `%s')"), key);
362 x->left_margin = arg;
365 x->right_margin = arg;
371 x->bottom_margin = arg;
374 this->font_height = arg;
383 int dimension = outp_evaluate_dimension (value, NULL);
387 error (0, 0, _("value for `%s' must be a dimension of positive "
388 "length (i.e., `1in')"), key);
394 x->line_width = dimension;
397 x->line_gutter = dimension;
400 x->line_width = dimension;
409 struct font *font = load_font (value);
412 struct font **dst = &x->fonts[subcat];
426 /* Looks for a PostScript font file or config file in all the
427 appropriate places. Returns the file name on success, NULL on
430 find_ps_file (const char *name)
432 if (fn_is_absolute (name))
433 return xstrdup (name);
436 char *base_name = xasprintf ("psfonts/%s", name);
437 char *file_name = fn_search_path (base_name, config_path, NULL);
443 /* Basic file operations. */
445 /* Writes the PostScript prologue to file F. */
447 write_ps_prologue (struct outp_driver *this)
449 struct ps_driver_ext *x = this->ext;
450 size_t embedded_cnt, preloaded_cnt;
453 fputs ("%!PS-Adobe-3.0\n", x->file);
454 fputs ("%%Pages: (atend)\n", x->file);
456 embedded_cnt = preloaded_cnt = 0;
457 for (i = 0; i < OUTP_FONT_CNT; i++)
459 bool embed = x->fonts[i]->embed_fn != NULL;
460 embedded_cnt += embed;
461 preloaded_cnt += !embed;
463 if (preloaded_cnt > 0)
465 fputs ("%%DocumentNeededResources: font", x->file);
466 for (i = 0; i < OUTP_FONT_CNT; i++)
468 struct font *f = x->fonts[i];
469 if (f->embed_fn == NULL)
470 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
472 fputs ("\n", x->file);
474 if (embedded_cnt > 0)
476 fputs ("%%DocumentSuppliedResources: font", x->file);
477 for (i = 0; i < OUTP_FONT_CNT; i++)
479 struct font *f = x->fonts[i];
480 if (f->embed_fn != NULL)
481 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
483 fputs ("\n", x->file);
485 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
486 fprintf (x->file, "%%%%Creator: %s\n", version);
487 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
488 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
489 fprintf (x->file, "%%%%Orientation: %s\n",
490 x->portrait ? "Portrait" : "Landscape");
491 fputs ("%%EndComments\n", x->file);
492 fputs ("%%BeginDefaults\n", x->file);
493 fputs ("%%PageResources: font", x->file);
494 for (i = 0; i < OUTP_FONT_CNT; i++)
495 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
496 fputs ("\n", x->file);
497 fputs ("%%EndDefaults\n", x->file);
498 fputs ("%%BeginProlog\n", x->file);
499 fputs ("/ED{exch def}bind def\n", x->file);
500 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
501 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
502 fputs ("/S{show}bind def\n", x->file);
503 fputs ("/GS{glyphshow}def\n", x->file);
504 fputs ("/RF{\n", x->file);
505 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
506 fputs (" {\n", x->file);
507 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
508 fputs (" }forall\n", x->file);
509 fputs (" /Encoding ED\n", x->file);
510 fputs (" currentdict end\n", x->file);
511 fputs ("}bind def\n", x->file);
512 fputs ("/F{setfont}bind def\n", x->file);
513 fputs ("/EP{\n", x->file);
514 fputs (" pg restore\n", x->file);
515 fputs (" showpage\n", x->file);
516 fputs ("}bind def\n", x->file);
517 fputs ("/GB{\n", x->file);
518 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
519 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
521 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
522 fputs ("}bind def\n", x->file);
523 fputs ("/K{0 rmoveto}bind def\n", x->file);
524 fputs ("%%EndProlog\n", x->file);
525 fputs ("%%BeginSetup\n", x->file);
526 for (i = 0; i < OUTP_FONT_CNT; i++)
527 setup_font (this, x->fonts[i], i);
528 fputs ("%%EndSetup\n", x->file);
531 /* Returns STRING as a Postscript name, which is just '/'
532 followed by STRING unless characters need to be quoted.
533 The caller must free the string. */
535 quote_ps_name (const char *string)
539 for (cp = string; *cp != '\0'; cp++)
541 unsigned char c = *cp;
542 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
543 && (cp == string || !isdigit (c)))
545 struct string out = DS_INITIALIZER;
547 for (cp = string; *cp != '\0'; cp++)
550 ds_printf (&out, "%02x", c);
552 ds_puts (&out, ">cvn");
553 return ds_c_str (&out);
556 return xasprintf ("/%s", string);
560 ps_open_page (struct outp_driver *this)
562 struct ps_driver_ext *x = this->ext;
564 /* Assure page independence. */
571 "%%%%BeginPageSetup\n"
572 "/pg save def 0.001 dup scale\n",
573 x->page_number, x->page_number);
577 "%d 0 translate 90 rotate\n",
580 if (x->bottom_margin != 0 || x->left_margin != 0)
583 x->left_margin, x->bottom_margin);
586 "/LW %d def %d setlinewidth\n"
587 "%%%%EndPageSetup\n",
588 x->line_width, x->line_width);
595 ps_close_page (struct outp_driver *this)
597 struct ps_driver_ext *x = this->ext;
598 fputs ("%%PageTrailer\n"
604 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
616 /* Draws a line from (x0,y0) to (x1,y1). */
618 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
620 struct ps_driver_ext *ext = this->ext;
621 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
624 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
625 shortening it to X0...X1 if SHORTEN is true.
626 Draws a horizontal line X1...X3 at Y if RIGHT says so,
627 shortening it to X2...X3 if SHORTEN is true. */
629 horz_line (struct outp_driver *this,
630 int x0, int x1, int x2, int x3, int y,
631 enum outp_line_style left, enum outp_line_style right,
634 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
635 dump_line (this, x0, y, x3, y);
638 if (left != OUTP_L_NONE)
639 dump_line (this, x0, y, shorten ? x1 : x2, y);
640 if (right != OUTP_L_NONE)
641 dump_line (this, shorten ? x2 : x1, y, x3, y);
645 /* Draws a vertical line Y0...Y2 at X if TOP says so,
646 shortening it to Y0...Y1 if SHORTEN is true.
647 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
648 shortening it to Y2...Y3 if SHORTEN is true. */
650 vert_line (struct outp_driver *this,
651 int y0, int y1, int y2, int y3, int x,
652 enum outp_line_style top, enum outp_line_style bottom,
655 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
656 dump_line (this, x, y0, x, y3);
659 if (top != OUTP_L_NONE)
660 dump_line (this, x, y0, x, shorten ? y1 : y2);
661 if (bottom != OUTP_L_NONE)
662 dump_line (this, x, shorten ? y2 : y1, x, y3);
666 /* Draws a generalized intersection of lines in the rectangle
667 (X0,Y0)-(X3,Y3). The line coming from the top to the center
668 is of style TOP, from left to center of style LEFT, from
669 bottom to center of style BOTTOM, and from right to center of
672 ps_line (struct outp_driver *this,
673 int x0, int y0, int x3, int y3,
674 enum outp_line_style top, enum outp_line_style left,
675 enum outp_line_style bottom, enum outp_line_style right)
677 /* The algorithm here is somewhat subtle, to allow it to handle
678 all the kinds of intersections that we need.
680 Three additional ordinates are assigned along the x axis. The
681 first is xc, midway between x0 and x3. The others are x1 and
682 x2; for a single vertical line these are equal to xc, and for
683 a double vertical line they are the ordinates of the left and
684 right half of the double line.
686 yc, y1, and y2 are assigned similarly along the y axis.
688 The following diagram shows the coordinate system and output
689 for double top and bottom lines, single left line, and no
693 y0 ________________________
699 y1 = y2 = yc |######### # |
704 y3 |________#_____#_______|
706 struct ps_driver_ext *ext = this->ext;
708 /* Offset from center of each line in a pair of double lines. */
709 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
711 /* Are the lines along each axis single or double?
712 (It doesn't make sense to have different kinds of line on the
713 same axis, so we don't try to gracefully handle that case.) */
714 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
715 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
717 /* When horizontal lines are doubled,
718 the left-side line along y1 normally runs from x0 to x2,
719 and the right-side line along y1 from x3 to x1.
720 If the top-side line is also doubled, we shorten the y1 lines,
721 so that the left-side line runs only to x1,
722 and the right-side line only to x2.
723 Otherwise, the horizontal line at y = y1 below would cut off
724 the intersection, which looks ugly:
726 y0 ________________________
731 y1 |######### ########|
734 y2 |######################|
737 y3 |______________________|
738 It is more of a judgment call when the horizontal line is
739 single. We actually choose to cut off the line anyhow, as
740 shown in the first diagram above.
742 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
743 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
744 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
745 int horz_line_ofs = double_vert ? double_line_ofs : 0;
746 int xc = (x0 + x3) / 2;
747 int x1 = xc - horz_line_ofs;
748 int x2 = xc + horz_line_ofs;
750 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
751 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
752 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
753 int vert_line_ofs = double_horz ? double_line_ofs : 0;
754 int yc = (y0 + y3) / 2;
755 int y1 = yc - vert_line_ofs;
756 int y2 = yc + vert_line_ofs;
759 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
762 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
763 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
767 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
770 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
771 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
775 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
776 and with the given JUSTIFICATION for THIS driver. */
778 draw_text (struct outp_driver *this,
779 const char *string, int x, int y, int max_width,
780 enum outp_justification justification)
782 struct outp_text text;
785 text.font = OUTP_PROPORTIONAL;
786 text.justification = justification;
787 ls_init (&text.string, (char *) string, strlen (string));
789 text.v = this->font_height;
792 this->class->text_metrics (this, &text, &width, NULL);
793 this->class->text_draw (this, &text);
797 /* Writes LEFT left-justified and RIGHT right-justified within
798 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
800 draw_header_line (struct outp_driver *this,
801 const char *left, const char *right,
802 int x0, int x1, int y)
806 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
807 + this->prop_em_width);
809 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
812 /* Draw top of page headers for THIS driver. */
814 draw_headers (struct outp_driver *this)
816 struct ps_driver_ext *ext = this->ext;
821 y = -3 * this->font_height;
822 x0 = this->prop_em_width;
823 x1 = this->width - this->prop_em_width;
826 fprintf (ext->file, "%d %d %d %d GB\n",
828 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
829 y += ext->line_width + ext->line_gutter;
831 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
832 r2 = xasprintf ("%s - %s", version, host_system);
834 draw_header_line (this, outp_title, r1, x0, x1, y);
835 y += this->font_height;
837 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
843 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
845 The characters are justified according to JUSTIFICATION in a
846 field that has WIDTH_LEFT space remaining after the characters
847 themselves are accounted for.
848 Before character I is written, its x-position is adjusted by
851 write_text (struct outp_driver *this,
854 enum outp_justification justification,
855 const struct afm_character **chars, int *kerns, size_t char_cnt,
858 struct ps_driver_ext *ext = this->ext;
859 struct afm *afm = ext->fonts[font]->metrics;
863 if (justification == OUTP_RIGHT)
865 else if (justification == OUTP_CENTER)
866 x0 += width_left / 2;
867 y0 += afm_get_ascent (afm) * this->font_height / 1000;
869 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
871 if (ext->last_font != font)
873 ext->last_font = font;
874 fprintf (ext->file, "F%d setfont\n", font);
878 for (i = 0; i < char_cnt; i = j)
880 for (j = i + 1; j < char_cnt; j++)
885 fprintf (ext->file, "%d K", kerns[i]);
888 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
891 fprintf (ext->file, "%sS\n", ds_c_str (&out));
898 fprintf (ext->file, "/%s GS\n", chars[i]->name);
906 /* State of a text formatting operation. */
910 const struct outp_text *text;
914 const struct afm_character **glyphs;
918 size_t glyph_cnt; /* Number of glyphs output. */
919 int width_left; /* Width left over. */
920 int height_left; /* Height left over. */
922 /* State as of last space. */
923 const char *space_char; /* Just past last space. */
924 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
925 int space_width_left; /* Width left over as of last space. */
928 int max_width; /* Widest line so far. */
931 /* Adjusts S to complete a line of text,
932 and draws the current line if appropriate. */
934 finish_line (struct outp_driver *this, struct text_state *s)
941 s->text->x, s->text->y + (s->text->v - s->height_left),
943 s->text->justification,
944 s->glyphs, s->glyph_kerns, s->glyph_cnt,
949 /* Update maximum width. */
950 width = s->text->h - s->width_left;
951 if (width > s->max_width)
952 s->max_width = width;
954 /* Move to next line. */
955 s->width_left = s->text->h;
956 s->height_left -= this->font_height;
958 /* No spaces on this line yet. */
959 s->space_char = NULL;
962 /* Format TEXT on THIS driver.
963 If DRAW is nonzero, draw the text.
964 The width of the widest line is stored into *WIDTH, if WIDTH
966 The total height of the text written is stored into *HEIGHT,
967 if HEIGHT is nonnull. */
969 text (struct outp_driver *this, const struct outp_text *text, bool draw,
970 int *width, int *height)
972 struct ps_driver_ext *ext = this->ext;
973 struct afm *afm = ext->fonts[text->font]->metrics;
982 s.glyph_kerns = NULL;
986 s.width_left = s.text->h;
987 s.height_left = s.text->v;
993 cp = ls_c_str (&s.text->string);
994 while (s.height_left >= this->font_height && cp < ls_end (&s.text->string))
996 const struct afm_character *cur;
1002 finish_line (this, &s);
1007 /* Get character and resolve ligatures. */
1008 cur = afm_get_character (afm, *cp);
1009 while (++cp < ls_end (&s.text->string))
1011 const struct afm_character *next = afm_get_character (afm, *cp);
1012 const struct afm_character *ligature = afm_get_ligature (cur, next);
1013 if (ligature == NULL)
1017 char_width = cur->width * this->font_height / 1000;
1019 /* Get kern adjustment. */
1020 if (s.glyph_cnt > 0)
1021 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1022 * this->font_height / 1000);
1026 /* Record the current status if this is a space character. */
1027 if (cur->code == ' ' && cp > ls_c_str (&s.text->string))
1030 s.space_glyph_cnt = s.glyph_cnt;
1031 s.space_width_left = s.width_left;
1034 /* Enough room on this line? */
1035 if (char_width + kern_adjust > s.width_left)
1037 if (s.space_char == NULL)
1039 finish_line (this, &s);
1045 s.glyph_cnt = s.space_glyph_cnt;
1046 s.width_left = s.space_width_left;
1047 finish_line (this, &s);
1052 if (s.glyph_cnt >= glyph_cap)
1054 glyph_cap = 2 * (glyph_cap + 8);
1055 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1056 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1057 glyph_cap, sizeof *s.glyph_kerns);
1059 s.glyphs[s.glyph_cnt] = cur;
1060 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1063 s.width_left -= char_width + kern_adjust;
1065 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1066 finish_line (this, &s);
1069 *width = s.max_width;
1071 *height = text->v - s.height_left;
1073 free (s.glyph_kerns);
1077 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1078 int *width, int *height)
1080 text (this, t, false, width, height);
1084 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1086 assert (this->page_open);
1087 text (this, t, true, NULL, NULL);
1091 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
1096 struct ps_driver_ext *x = this->ext;
1097 char page_size[128];
1099 int x_origin, y_origin;
1101 ch->file = tmpfile ();
1102 if (ch->file == NULL)
1108 size = this->width < this->length ? this->width : this->length;
1109 x_origin = x->left_margin + (size - this->width) / 2;
1110 y_origin = x->bottom_margin + (size - this->length) / 2;
1112 snprintf (page_size, sizeof page_size,
1113 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
1114 (double) size / PSUS, (double) size / PSUS,
1115 (double) x_origin / PSUS, (double) y_origin / PSUS);
1117 ch->pl_params = pl_newplparams ();
1118 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
1119 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
1124 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
1127 struct ps_driver_ext *x = this->ext;
1129 static int doc_num = 0;
1131 outp_eject_page (this);
1134 "%d %d translate 1000 dup scale\n"
1136 "/showpage { } def\n"
1137 "0 setgray 0 setlinecap 1 setlinewidth\n"
1138 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
1139 "%%%%BeginDocument: %d\n",
1140 -x->left_margin, -x->bottom_margin,
1144 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
1148 fputs ("%%%%EndDocument\n"
1152 outp_close_page (this);
1156 static void embed_font (struct outp_driver *this, struct font *font);
1157 static void reencode_font (struct outp_driver *this, struct font *font);
1159 /* Loads and returns the font for STRING, which has the format
1160 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1161 PFA or PFB file's name, and ENC is the encoding file's name.
1162 PFA and ENC are optional.
1163 Returns a null pointer if unsuccessful. */
1164 static struct font *
1165 load_font (const char *string_)
1167 char *string = xstrdup (string_);
1169 char *position = string;
1171 char *afm_file_name;
1173 font = xmalloc (sizeof *font);
1174 font->metrics = NULL;
1175 font->embed_fn = NULL;
1176 font->encoding_fn = NULL;
1178 token = strsep (&position, ",");
1181 error (0, 0, _("\"%s\": bad font specification"), string);
1185 /* Read AFM file. */
1186 afm_file_name = find_ps_file (token);
1187 if (afm_file_name == NULL)
1189 error (0, 0, _("could not find AFM file \"%s\""), token);
1192 font->metrics = afm_open (afm_file_name);
1193 free (afm_file_name);
1194 if (font->metrics == NULL)
1197 /* Find font file to embed. */
1198 token = strsep (&position, ",");
1199 if (token != NULL && *token != '\0')
1201 font->embed_fn = find_ps_file (token);
1202 if (font->embed_fn == NULL)
1203 error (0, 0, _("could not find font \"%s\""), token);
1206 /* Find encoding. */
1207 token = strsep (&position, ",");
1208 if (token != NULL && *token == '\0')
1210 font->encoding_fn = find_ps_file (token);
1211 if (font->encoding_fn == NULL)
1212 error (0, 0, _("could not find encoding \"%s\""), token);
1226 free_font (struct font *font)
1230 afm_close (font->metrics);
1231 free (font->embed_fn);
1232 free (font->encoding_fn);
1237 /* Emits PostScript code to embed FONT (if necessary), scale it
1238 to the proper size, re-encode it (if necessary), and store the
1239 resulting font as an object named F#, where INDEX is
1240 substituted for #. */
1242 setup_font (struct outp_driver *this, struct font *font, int index)
1244 struct ps_driver_ext *x = this->ext;
1247 if (font->embed_fn != NULL)
1248 embed_font (this, font);
1250 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1251 afm_get_findfont_name (font->metrics));
1253 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1254 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1257 if (font->encoding_fn != NULL)
1258 reencode_font (this, font);
1260 fprintf (x->file, "/F%d ED\n", index);
1263 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1264 end-of-file or on error. */
1266 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1268 while (copy_bytes > 0)
1270 char buffer[BUFSIZ];
1271 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1272 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1273 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1274 if (write_bytes != chunk_bytes)
1276 copy_bytes -= chunk_bytes;
1280 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1281 end-of-file or on error. The bytes are translated into
1282 hexadecimal during copying and broken into lines with
1283 new-line characters. */
1285 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1289 for (i = 0; i < copy_bytes; i++)
1294 if (i > 0 && i % 36 == 0)
1296 fprintf (dst, "%02X", c);
1301 /* Embeds the given FONT into THIS driver's output stream. */
1303 embed_font (struct outp_driver *this, struct font *font)
1305 struct ps_driver_ext *x = this->ext;
1309 file = fopen (font->embed_fn, "rb");
1312 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1316 fprintf (x->file, "%%%%BeginResource: font %s\n",
1317 afm_get_findfont_name (font->metrics));
1323 /* PFA file. Copy literally. */
1324 copy_bytes_literally (file, x->file, ULONG_MAX);
1328 /* PFB file. Translate as specified in Adobe Technical
1330 while ((c = getc (file)) == 128)
1333 unsigned long length;
1339 length = getc (file);
1340 length |= (unsigned long) getc (file) << 8;
1341 length |= (unsigned long) getc (file) << 16;
1342 length |= (unsigned long) getc (file) << 24;
1345 copy_bytes_literally (file, x->file, length);
1347 copy_bytes_as_hex (file, x->file, length);
1352 if (freaderror (file))
1353 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1354 fputs ("%%EndResource\n", x->file);
1357 /* Re-encodes FONT according to the specified encoding. */
1359 reencode_font (struct outp_driver *this, struct font *font)
1361 struct ps_driver_ext *x = this->ext;
1372 file = fopen (font->encoding_fn, "r");
1375 error (errno, 0, _("cannot open font encoding file \"%s\""),
1380 for (i = 0; i < 256; i++)
1386 while (ds_get_config_line (file, &line, &line_number))
1388 char *pschar, *code;
1389 char *save_ptr, *tail;
1392 if (ds_is_empty (&line) == 0)
1395 pschar = strtok_r (ds_c_str (&line), " \t\r\n", &save_ptr);
1396 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1397 if (pschar == NULL || code == NULL)
1400 code_val = strtol (code, &tail, 0);
1403 error_at_line (0, 0, font->encoding_fn, line_number,
1404 _("invalid numeric format"));
1407 if (code_val < 0 || code_val > 255)
1409 if (tab[code_val] != 0)
1410 free (tab[code_val]);
1411 tab[code_val] = xstrdup (pschar);
1415 fputs ("[", x->file);
1416 for (i = 0; i < 256; i++)
1418 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1419 fprintf (x->file, "%s\n", name);
1423 fputs ("] RF\n", x->file);
1425 if (freaderror (file) != 0)
1426 error (errno, 0, "closing Postscript encoding \"%s\"", font->encoding_fn);
1429 /* PostScript driver class. */
1430 struct outp_class postscript_class =
1447 ps_chart_initialise,