1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
26 #include <libpspp/alloc.h>
27 #include <libpspp/assertion.h>
28 #include <libpspp/bit-vector.h>
29 #include <libpspp/compiler.h>
30 #include <libpspp/freaderror.h>
31 #include <libpspp/hash.h>
32 #include <libpspp/misc.h>
33 #include <libpspp/start-date.h>
34 #include <libpspp/version.h>
36 #include <data/file-name.h>
48 #define _(msgid) gettext (msgid)
50 /* PostScript driver options: (defaults listed first)
54 paper-size=letter (see "papersize" file)
55 orientation=portrait|landscape
64 emph-font=Times-Italic
73 /* The number of `psus' (PostScript driver UnitS) per inch. */
76 /* A PostScript font. */
79 struct afm *metrics; /* Metrics. */
80 char *embed_fn; /* Name of file to embed. */
81 char *encoding_fn; /* Name of file with encoding. */
84 /* PostScript output driver extension record. */
87 char *file_name; /* Output file name. */
88 FILE *file; /* Output file. */
90 bool draw_headers; /* Draw headers at top of page? */
91 int page_number; /* Current page number. */
93 bool portrait; /* Portrait mode? */
94 int paper_width; /* Width of paper before dropping margins. */
95 int paper_length; /* Length of paper before dropping margins. */
96 int left_margin; /* Left margin in psus. */
97 int right_margin; /* Right margin in psus. */
98 int top_margin; /* Top margin in psus. */
99 int bottom_margin; /* Bottom margin in psus. */
101 int line_gutter; /* Space around lines. */
102 int line_space; /* Space between lines. */
103 int line_width; /* Width of lines. */
105 struct font *fonts[OUTP_FONT_CNT];
106 int last_font; /* Index of last font set with setfont. */
109 /* Transform logical y-ordinate Y into a page ordinate. */
110 #define YT(Y) (this->length - (Y))
112 static bool handle_option (struct outp_driver *this, const char *key,
113 const struct string *val);
114 static void draw_headers (struct outp_driver *this);
116 static void write_ps_prologue (struct outp_driver *);
118 static char *quote_ps_name (const char *string);
120 static struct font *load_font (const char *string);
121 static void free_font (struct font *);
122 static void setup_font (struct outp_driver *this, struct font *, int index);
124 /* Driver initialization. */
127 ps_open_driver (struct outp_driver *this, struct substring options)
129 struct ps_driver_ext *x;
132 this->width = this->length = 0;
133 this->font_height = PSUS * 10 / 72;
135 this->ext = x = xmalloc (sizeof *x);
136 x->file_name = xstrdup ("pspp.ps");
138 x->draw_headers = true;
141 x->paper_width = PSUS * 17 / 2;
142 x->paper_length = PSUS * 11;
143 x->left_margin = PSUS / 2;
144 x->right_margin = PSUS / 2;
145 x->top_margin = PSUS / 2;
146 x->bottom_margin = PSUS / 2;
147 x->line_gutter = PSUS / 72;
148 x->line_space = PSUS / 72;
149 x->line_width = PSUS / 144;
150 for (i = 0; i < OUTP_FONT_CNT; i++)
153 outp_parse_options (options, handle_option, this);
155 x->file = fn_open (x->file_name, "w");
158 error (0, errno, _("opening PostScript output file \"%s\""),
165 this->width = x->paper_width;
166 this->length = x->paper_length;
170 this->width = x->paper_length;
171 this->length = x->paper_width;
173 this->width -= x->left_margin + x->right_margin;
174 this->length -= x->top_margin + x->bottom_margin;
177 int header_length = 3 * this->font_height;
178 this->length -= header_length;
179 x->top_margin += header_length;
182 for (i = 0; i < OUTP_FONT_CNT; i++)
183 if (x->fonts[i] == NULL)
185 const char *default_fonts[OUTP_FONT_CNT];
186 default_fonts[OUTP_FIXED] = "Courier.afm";
187 default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
188 default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
189 x->fonts[i] = load_font (default_fonts[i]);
190 if (x->fonts[i] == NULL)
194 if (this->length / this->font_height < 15)
196 error (0, 0, _("The defined PostScript page is not long "
197 "enough to hold margins and headers, plus least 15 "
198 "lines of the default fonts. In fact, there's only "
199 "room for %d lines of each font at the default size "
200 "of %d.%03d points."),
201 this->length / this->font_height,
202 this->font_height / 1000, this->font_height % 1000);
207 afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
208 * this->font_height / 1000;
209 this->prop_em_width =
210 afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
211 * this->font_height / 1000;
213 this->horiz_line_width[OUTP_L_NONE] = 0;
214 this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
215 this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
216 + 2 * x->line_width);
217 memcpy (this->vert_line_width, this->horiz_line_width,
218 sizeof this->vert_line_width);
220 write_ps_prologue (this);
225 this->class->close_driver (this);
230 ps_close_driver (struct outp_driver *this)
232 struct ps_driver_ext *x = this->ext;
242 ok = fn_close (x->file_name, x->file) == 0;
244 error (0, errno, _("closing PostScript output file \"%s\""), x->file_name);
246 for (i = 0; i < OUTP_FONT_CNT; i++)
247 free_font (x->fonts[i]);
253 /* Generic option types. */
267 /* All the options that the PostScript driver supports. */
268 static const struct outp_option option_tab[] =
270 {"output-file", output_file_arg,0},
271 {"paper-size", paper_size_arg, 0},
272 {"orientation", orientation_arg,0},
274 {"headers", boolean_arg, 1},
276 {"prop-font", string_arg, OUTP_PROPORTIONAL},
277 {"emph-font", string_arg, OUTP_EMPHASIS},
278 {"fixed-font", string_arg, OUTP_FIXED},
280 {"left-margin", pos_int_arg, 0},
281 {"right-margin", pos_int_arg, 1},
282 {"top-margin", pos_int_arg, 2},
283 {"bottom-margin", pos_int_arg, 3},
284 {"font-size", pos_int_arg, 4},
286 {"line-width", dimension_arg, 0},
287 {"line-gutter", dimension_arg, 1},
288 {"line-width", dimension_arg, 2},
293 handle_option (struct outp_driver *this, const char *key,
294 const struct string *val)
296 struct ps_driver_ext *x = this->ext;
298 char *value = ds_cstr (val);
300 switch (outp_match_keyword (key, option_tab, &subcat))
304 _("unknown configuration parameter `%s' for PostScript device "
307 case output_file_arg:
309 x->file_name = xstrdup (value);
312 outp_get_paper_size (value, &this->width, &this->length);
314 case orientation_arg:
315 if (!strcmp (value, "portrait"))
317 else if (!strcmp (value, "landscape"))
320 error (0, 0, _("unknown orientation `%s' (valid orientations are "
321 "`portrait' and `landscape')"), value);
324 if (!strcmp (value, "on") || !strcmp (value, "true")
325 || !strcmp (value, "yes") || atoi (value))
326 x->draw_headers = true;
327 else if (!strcmp (value, "off") || !strcmp (value, "false")
328 || !strcmp (value, "no") || !strcmp (value, "0"))
329 x->draw_headers = false;
332 error (0, 0, _("boolean value expected for %s"), key);
342 arg = strtol (value, &tail, 0);
343 if (arg < 1 || errno == ERANGE || *tail)
345 error (0, 0, _("positive integer value required for `%s'"), key);
348 if ((subcat == 4 || subcat == 5) && arg < 1000)
350 error (0, 0, _("default font size must be at least 1 point (value "
351 "of 1000 for key `%s')"), key);
357 x->left_margin = arg;
360 x->right_margin = arg;
366 x->bottom_margin = arg;
369 this->font_height = arg;
378 int dimension = outp_evaluate_dimension (value, NULL);
382 error (0, 0, _("value for `%s' must be a dimension of positive "
383 "length (i.e., `1in')"), key);
389 x->line_width = dimension;
392 x->line_gutter = dimension;
395 x->line_width = dimension;
404 struct font *font = load_font (value);
407 struct font **dst = &x->fonts[subcat];
421 /* Looks for a PostScript font file or config file in all the
422 appropriate places. Returns the file name on success, NULL on
425 find_ps_file (const char *name)
427 if (fn_is_absolute (name))
428 return xstrdup (name);
431 char *base_name = xasprintf ("psfonts/%s", name);
432 char *file_name = fn_search_path (base_name, config_path);
438 /* Basic file operations. */
440 /* Writes the PostScript prologue to file F. */
442 write_ps_prologue (struct outp_driver *this)
444 struct ps_driver_ext *x = this->ext;
445 size_t embedded_cnt, preloaded_cnt;
448 fputs ("%!PS-Adobe-3.0\n", x->file);
449 fputs ("%%Pages: (atend)\n", x->file);
451 embedded_cnt = preloaded_cnt = 0;
452 for (i = 0; i < OUTP_FONT_CNT; i++)
454 bool embed = x->fonts[i]->embed_fn != NULL;
455 embedded_cnt += embed;
456 preloaded_cnt += !embed;
458 if (preloaded_cnt > 0)
460 fputs ("%%DocumentNeededResources: font", x->file);
461 for (i = 0; i < OUTP_FONT_CNT; i++)
463 struct font *f = x->fonts[i];
464 if (f->embed_fn == NULL)
465 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
467 fputs ("\n", x->file);
469 if (embedded_cnt > 0)
471 fputs ("%%DocumentSuppliedResources: font", x->file);
472 for (i = 0; i < OUTP_FONT_CNT; i++)
474 struct font *f = x->fonts[i];
475 if (f->embed_fn != NULL)
476 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
478 fputs ("\n", x->file);
480 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
481 fprintf (x->file, "%%%%Creator: %s\n", version);
482 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
483 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
484 fprintf (x->file, "%%%%Orientation: %s\n",
485 x->portrait ? "Portrait" : "Landscape");
486 fputs ("%%EndComments\n", x->file);
487 fputs ("%%BeginDefaults\n", x->file);
488 fputs ("%%PageResources: font", x->file);
489 for (i = 0; i < OUTP_FONT_CNT; i++)
490 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
491 fputs ("\n", x->file);
492 fputs ("%%EndDefaults\n", x->file);
493 fputs ("%%BeginProlog\n", x->file);
494 fputs ("/ED{exch def}bind def\n", x->file);
495 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
496 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
497 fputs ("/S{show}bind def\n", x->file);
498 fputs ("/GS{glyphshow}def\n", x->file);
499 fputs ("/RF{\n", x->file);
500 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
501 fputs (" {\n", x->file);
502 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
503 fputs (" }forall\n", x->file);
504 fputs (" /Encoding ED\n", x->file);
505 fputs (" currentdict end\n", x->file);
506 fputs ("}bind def\n", x->file);
507 fputs ("/F{setfont}bind def\n", x->file);
508 fputs ("/EP{\n", x->file);
509 fputs (" pg restore\n", x->file);
510 fputs (" showpage\n", x->file);
511 fputs ("}bind def\n", x->file);
512 fputs ("/GB{\n", x->file);
513 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
514 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
516 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
517 fputs ("}bind def\n", x->file);
518 fputs ("/K{0 rmoveto}bind def\n", x->file);
519 fputs ("%%EndProlog\n", x->file);
520 fputs ("%%BeginSetup\n", x->file);
521 for (i = 0; i < OUTP_FONT_CNT; i++)
522 setup_font (this, x->fonts[i], i);
523 fputs ("%%EndSetup\n", x->file);
526 /* Returns STRING as a Postscript name, which is just '/'
527 followed by STRING unless characters need to be quoted.
528 The caller must free the string. */
530 quote_ps_name (const char *string)
534 for (cp = string; *cp != '\0'; cp++)
536 unsigned char c = *cp;
537 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
538 && (cp == string || !isdigit (c)))
540 struct string out = DS_EMPTY_INITIALIZER;
541 ds_put_char (&out, '<');
542 for (cp = string; *cp != '\0'; cp++)
545 ds_put_format (&out, "%02x", c);
547 ds_put_cstr (&out, ">cvn");
548 return ds_cstr (&out);
551 return xasprintf ("/%s", string);
555 ps_open_page (struct outp_driver *this)
557 struct ps_driver_ext *x = this->ext;
559 /* Assure page independence. */
566 "%%%%BeginPageSetup\n"
567 "/pg save def 0.001 dup scale\n",
568 x->page_number, x->page_number);
572 "%d 0 translate 90 rotate\n",
575 if (x->bottom_margin != 0 || x->left_margin != 0)
578 x->left_margin, x->bottom_margin);
581 "/LW %d def %d setlinewidth\n"
582 "%%%%EndPageSetup\n",
583 x->line_width, x->line_width);
590 ps_close_page (struct outp_driver *this)
592 struct ps_driver_ext *x = this->ext;
593 fputs ("%%PageTrailer\n"
599 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
610 /* Draws a line from (x0,y0) to (x1,y1). */
612 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
614 struct ps_driver_ext *ext = this->ext;
615 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
618 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
619 shortening it to X0...X1 if SHORTEN is true.
620 Draws a horizontal line X1...X3 at Y if RIGHT says so,
621 shortening it to X2...X3 if SHORTEN is true. */
623 horz_line (struct outp_driver *this,
624 int x0, int x1, int x2, int x3, int y,
625 enum outp_line_style left, enum outp_line_style right,
628 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
629 dump_line (this, x0, y, x3, y);
632 if (left != OUTP_L_NONE)
633 dump_line (this, x0, y, shorten ? x1 : x2, y);
634 if (right != OUTP_L_NONE)
635 dump_line (this, shorten ? x2 : x1, y, x3, y);
639 /* Draws a vertical line Y0...Y2 at X if TOP says so,
640 shortening it to Y0...Y1 if SHORTEN is true.
641 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
642 shortening it to Y2...Y3 if SHORTEN is true. */
644 vert_line (struct outp_driver *this,
645 int y0, int y1, int y2, int y3, int x,
646 enum outp_line_style top, enum outp_line_style bottom,
649 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
650 dump_line (this, x, y0, x, y3);
653 if (top != OUTP_L_NONE)
654 dump_line (this, x, y0, x, shorten ? y1 : y2);
655 if (bottom != OUTP_L_NONE)
656 dump_line (this, x, shorten ? y2 : y1, x, y3);
660 /* Draws a generalized intersection of lines in the rectangle
661 (X0,Y0)-(X3,Y3). The line coming from the top to the center
662 is of style TOP, from left to center of style LEFT, from
663 bottom to center of style BOTTOM, and from right to center of
666 ps_line (struct outp_driver *this,
667 int x0, int y0, int x3, int y3,
668 enum outp_line_style top, enum outp_line_style left,
669 enum outp_line_style bottom, enum outp_line_style right)
671 /* The algorithm here is somewhat subtle, to allow it to handle
672 all the kinds of intersections that we need.
674 Three additional ordinates are assigned along the x axis. The
675 first is xc, midway between x0 and x3. The others are x1 and
676 x2; for a single vertical line these are equal to xc, and for
677 a double vertical line they are the ordinates of the left and
678 right half of the double line.
680 yc, y1, and y2 are assigned similarly along the y axis.
682 The following diagram shows the coordinate system and output
683 for double top and bottom lines, single left line, and no
687 y0 ________________________
693 y1 = y2 = yc |######### # |
698 y3 |________#_____#_______|
700 struct ps_driver_ext *ext = this->ext;
702 /* Offset from center of each line in a pair of double lines. */
703 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
705 /* Are the lines along each axis single or double?
706 (It doesn't make sense to have different kinds of line on the
707 same axis, so we don't try to gracefully handle that case.) */
708 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
709 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
711 /* When horizontal lines are doubled,
712 the left-side line along y1 normally runs from x0 to x2,
713 and the right-side line along y1 from x3 to x1.
714 If the top-side line is also doubled, we shorten the y1 lines,
715 so that the left-side line runs only to x1,
716 and the right-side line only to x2.
717 Otherwise, the horizontal line at y = y1 below would cut off
718 the intersection, which looks ugly:
720 y0 ________________________
725 y1 |######### ########|
728 y2 |######################|
731 y3 |______________________|
732 It is more of a judgment call when the horizontal line is
733 single. We actually choose to cut off the line anyhow, as
734 shown in the first diagram above.
736 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
737 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
738 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
739 int horz_line_ofs = double_vert ? double_line_ofs : 0;
740 int xc = (x0 + x3) / 2;
741 int x1 = xc - horz_line_ofs;
742 int x2 = xc + horz_line_ofs;
744 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
745 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
746 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
747 int vert_line_ofs = double_horz ? double_line_ofs : 0;
748 int yc = (y0 + y3) / 2;
749 int y1 = yc - vert_line_ofs;
750 int y2 = yc + vert_line_ofs;
753 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
756 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
757 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
761 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
764 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
765 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
769 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
770 and with the given JUSTIFICATION for THIS driver. */
772 draw_text (struct outp_driver *this,
773 const char *string, int x, int y, int max_width,
774 enum outp_justification justification)
776 struct outp_text text;
779 text.font = OUTP_PROPORTIONAL;
780 text.justification = justification;
781 text.string = ss_cstr (string);
783 text.v = this->font_height;
786 this->class->text_metrics (this, &text, &width, NULL);
787 this->class->text_draw (this, &text);
791 /* Writes LEFT left-justified and RIGHT right-justified within
792 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
794 draw_header_line (struct outp_driver *this,
795 const char *left, const char *right,
796 int x0, int x1, int y)
800 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
801 + this->prop_em_width);
803 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
806 /* Draw top of page headers for THIS driver. */
808 draw_headers (struct outp_driver *this)
810 struct ps_driver_ext *ext = this->ext;
815 y = -3 * this->font_height;
816 x0 = this->prop_em_width;
817 x1 = this->width - this->prop_em_width;
820 fprintf (ext->file, "%d %d %d %d GB\n",
822 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
823 y += ext->line_width + ext->line_gutter;
825 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
826 r2 = xasprintf ("%s - %s", version, host_system);
828 draw_header_line (this, outp_title, r1, x0, x1, y);
829 y += this->font_height;
831 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
837 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
839 The characters are justified according to JUSTIFICATION in a
840 field that has WIDTH_LEFT space remaining after the characters
841 themselves are accounted for.
842 Before character I is written, its x-position is adjusted by
845 write_text (struct outp_driver *this,
848 enum outp_justification justification,
849 const struct afm_character **chars, int *kerns, size_t char_cnt,
852 struct ps_driver_ext *ext = this->ext;
853 struct afm *afm = ext->fonts[font]->metrics;
857 if (justification == OUTP_RIGHT)
859 else if (justification == OUTP_CENTER)
860 x0 += width_left / 2;
861 y0 += afm_get_ascent (afm) * this->font_height / 1000;
863 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
865 if (ext->last_font != font)
867 ext->last_font = font;
868 fprintf (ext->file, "F%d setfont\n", font);
871 ds_init_empty (&out);
872 for (i = 0; i < char_cnt; i = j)
874 for (j = i + 1; j < char_cnt; j++)
879 fprintf (ext->file, "%d K", kerns[i]);
882 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
885 fprintf (ext->file, "%sS\n", ds_cstr (&out));
892 fprintf (ext->file, "/%s GS\n", chars[i]->name);
900 /* State of a text formatting operation. */
904 const struct outp_text *text;
908 const struct afm_character **glyphs;
912 size_t glyph_cnt; /* Number of glyphs output. */
913 int width_left; /* Width left over. */
914 int height_left; /* Height left over. */
916 /* State as of last space. */
917 const char *space_char; /* Just past last space. */
918 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
919 int space_width_left; /* Width left over as of last space. */
922 int max_width; /* Widest line so far. */
925 /* Adjusts S to complete a line of text,
926 and draws the current line if appropriate. */
928 finish_line (struct outp_driver *this, struct text_state *s)
935 s->text->x, s->text->y + (s->text->v - s->height_left),
937 s->text->justification,
938 s->glyphs, s->glyph_kerns, s->glyph_cnt,
943 /* Update maximum width. */
944 width = s->text->h - s->width_left;
945 if (width > s->max_width)
946 s->max_width = width;
948 /* Move to next line. */
949 s->width_left = s->text->h;
950 s->height_left -= this->font_height;
952 /* No spaces on this line yet. */
953 s->space_char = NULL;
956 /* Format TEXT on THIS driver.
957 If DRAW is nonzero, draw the text.
958 The width of the widest line is stored into *WIDTH, if WIDTH
960 The total height of the text written is stored into *HEIGHT,
961 if HEIGHT is nonnull. */
963 text (struct outp_driver *this, const struct outp_text *text, bool draw,
964 int *width, int *height)
966 struct ps_driver_ext *ext = this->ext;
967 struct afm *afm = ext->fonts[text->font]->metrics;
976 s.glyph_kerns = NULL;
980 s.width_left = s.text->h;
981 s.height_left = s.text->v;
987 cp = ss_data (s.text->string);
988 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
990 const struct afm_character *cur;
996 finish_line (this, &s);
1001 /* Get character and resolve ligatures. */
1002 cur = afm_get_character (afm, *cp);
1003 while (++cp < ss_end (s.text->string))
1005 const struct afm_character *next = afm_get_character (afm, *cp);
1006 const struct afm_character *ligature = afm_get_ligature (cur, next);
1007 if (ligature == NULL)
1011 char_width = cur->width * this->font_height / 1000;
1013 /* Get kern adjustment. */
1014 if (s.glyph_cnt > 0)
1015 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1016 * this->font_height / 1000);
1020 /* Record the current status if this is a space character. */
1021 if (cur->code == ' ' && cp > ss_data (s.text->string))
1024 s.space_glyph_cnt = s.glyph_cnt;
1025 s.space_width_left = s.width_left;
1028 /* Enough room on this line? */
1029 if (char_width + kern_adjust > s.width_left)
1031 if (s.space_char == NULL)
1033 finish_line (this, &s);
1039 s.glyph_cnt = s.space_glyph_cnt;
1040 s.width_left = s.space_width_left;
1041 finish_line (this, &s);
1046 if (s.glyph_cnt >= glyph_cap)
1048 glyph_cap = 2 * (glyph_cap + 8);
1049 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1050 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1051 glyph_cap, sizeof *s.glyph_kerns);
1053 s.glyphs[s.glyph_cnt] = cur;
1054 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1057 s.width_left -= char_width + kern_adjust;
1059 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1060 finish_line (this, &s);
1063 *width = s.max_width;
1065 *height = text->v - s.height_left;
1067 free (s.glyph_kerns);
1071 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1072 int *width, int *height)
1074 text (this, t, false, width, height);
1078 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1080 assert (this->page_open);
1081 text (this, t, true, NULL, NULL);
1085 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
1090 struct ps_driver_ext *x = this->ext;
1091 char page_size[128];
1093 int x_origin, y_origin;
1095 ch->file = tmpfile ();
1096 if (ch->file == NULL)
1102 size = this->width < this->length ? this->width : this->length;
1103 x_origin = x->left_margin + (size - this->width) / 2;
1104 y_origin = x->bottom_margin + (size - this->length) / 2;
1106 snprintf (page_size, sizeof page_size,
1107 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
1108 (double) size / PSUS, (double) size / PSUS,
1109 (double) x_origin / PSUS, (double) y_origin / PSUS);
1111 ch->pl_params = pl_newplparams ();
1112 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
1113 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
1118 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
1121 struct ps_driver_ext *x = this->ext;
1123 static int doc_num = 0;
1125 outp_eject_page (this);
1128 "%d %d translate 1000 dup scale\n"
1130 "/showpage { } def\n"
1131 "0 setgray 0 setlinecap 1 setlinewidth\n"
1132 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
1133 "%%%%BeginDocument: %d\n",
1134 -x->left_margin, -x->bottom_margin,
1138 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
1142 fputs ("%%EndDocument\n"
1146 outp_close_page (this);
1150 static void embed_font (struct outp_driver *this, struct font *font);
1151 static void reencode_font (struct outp_driver *this, struct font *font);
1153 /* Loads and returns the font for STRING, which has the format
1154 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1155 PFA or PFB file's name, and ENC is the encoding file's name.
1156 PFA and ENC are optional.
1157 Returns a null pointer if unsuccessful. */
1158 static struct font *
1159 load_font (const char *string_)
1161 char *string = xstrdup (string_);
1163 char *position = string;
1165 char *afm_file_name;
1167 font = xmalloc (sizeof *font);
1168 font->metrics = NULL;
1169 font->embed_fn = NULL;
1170 font->encoding_fn = NULL;
1172 token = strsep (&position, ",");
1175 error (0, 0, _("\"%s\": bad font specification"), string);
1179 /* Read AFM file. */
1180 afm_file_name = find_ps_file (token);
1181 if (afm_file_name == NULL)
1183 error (0, 0, _("could not find AFM file \"%s\""), token);
1186 font->metrics = afm_open (afm_file_name);
1187 free (afm_file_name);
1188 if (font->metrics == NULL)
1191 /* Find font file to embed. */
1192 token = strsep (&position, ",");
1193 if (token != NULL && *token != '\0')
1195 font->embed_fn = find_ps_file (token);
1196 if (font->embed_fn == NULL)
1197 error (0, 0, _("could not find font \"%s\""), token);
1200 /* Find encoding. */
1201 token = strsep (&position, ",");
1202 if (token != NULL && *token == '\0')
1204 font->encoding_fn = find_ps_file (token);
1205 if (font->encoding_fn == NULL)
1206 error (0, 0, _("could not find encoding \"%s\""), token);
1220 free_font (struct font *font)
1224 afm_close (font->metrics);
1225 free (font->embed_fn);
1226 free (font->encoding_fn);
1231 /* Emits PostScript code to embed FONT (if necessary), scale it
1232 to the proper size, re-encode it (if necessary), and store the
1233 resulting font as an object named F#, where INDEX is
1234 substituted for #. */
1236 setup_font (struct outp_driver *this, struct font *font, int index)
1238 struct ps_driver_ext *x = this->ext;
1241 if (font->embed_fn != NULL)
1242 embed_font (this, font);
1244 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1245 afm_get_findfont_name (font->metrics));
1247 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1248 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1251 if (font->encoding_fn != NULL)
1252 reencode_font (this, font);
1254 fprintf (x->file, "/F%d ED\n", index);
1257 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1258 end-of-file or on error. */
1260 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1262 while (copy_bytes > 0)
1264 char buffer[BUFSIZ];
1265 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1266 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1267 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1268 if (write_bytes != chunk_bytes)
1270 copy_bytes -= chunk_bytes;
1274 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1275 end-of-file or on error. The bytes are translated into
1276 hexadecimal during copying and broken into lines with
1277 new-line characters. */
1279 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1283 for (i = 0; i < copy_bytes; i++)
1288 if (i > 0 && i % 36 == 0)
1290 fprintf (dst, "%02X", c);
1295 /* Embeds the given FONT into THIS driver's output stream. */
1297 embed_font (struct outp_driver *this, struct font *font)
1299 struct ps_driver_ext *x = this->ext;
1303 file = fopen (font->embed_fn, "rb");
1306 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1310 fprintf (x->file, "%%%%BeginResource: font %s\n",
1311 afm_get_findfont_name (font->metrics));
1317 /* PFA file. Copy literally. */
1318 copy_bytes_literally (file, x->file, ULONG_MAX);
1322 /* PFB file. Translate as specified in Adobe Technical
1324 while ((c = getc (file)) == 128)
1327 unsigned long length;
1333 length = getc (file);
1334 length |= (unsigned long) getc (file) << 8;
1335 length |= (unsigned long) getc (file) << 16;
1336 length |= (unsigned long) getc (file) << 24;
1339 copy_bytes_literally (file, x->file, length);
1341 copy_bytes_as_hex (file, x->file, length);
1346 if (freaderror (file))
1347 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1348 fputs ("%%EndResource\n", x->file);
1351 /* Re-encodes FONT according to the specified encoding. */
1353 reencode_font (struct outp_driver *this, struct font *font)
1355 struct ps_driver_ext *x = this->ext;
1366 file = fopen (font->encoding_fn, "r");
1369 error (errno, 0, _("cannot open font encoding file \"%s\""),
1374 for (i = 0; i < 256; i++)
1379 ds_init_empty (&line);
1380 while (ds_read_config_line (&line, &line_number, file))
1382 char *pschar, *code;
1383 char *save_ptr, *tail;
1386 if (ds_is_empty (&line) == 0)
1389 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1390 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1391 if (pschar == NULL || code == NULL)
1394 code_val = strtol (code, &tail, 0);
1397 error_at_line (0, 0, font->encoding_fn, line_number,
1398 _("invalid numeric format"));
1401 if (code_val < 0 || code_val > 255)
1403 if (tab[code_val] != 0)
1404 free (tab[code_val]);
1405 tab[code_val] = xstrdup (pschar);
1409 fputs ("[", x->file);
1410 for (i = 0; i < 256; i++)
1412 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1413 fprintf (x->file, "%s\n", name);
1417 fputs ("] RF\n", x->file);
1419 if (freaderror (file) != 0)
1420 error (errno, 0, _("closing Postscript encoding \"%s\""),
1424 /* PostScript driver class. */
1425 const struct outp_class postscript_class =
1443 ps_chart_initialise,