1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 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, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 #include <libpspp/alloc.h>
29 #include <libpspp/assertion.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>
51 #define _(msgid) gettext (msgid)
53 /* PostScript driver options: (defaults listed first)
57 paper-size=letter (see "papersize" file)
58 orientation=portrait|landscape
67 emph-font=Times-Italic
76 /* The number of `psus' (PostScript driver UnitS) per inch. */
79 /* A PostScript font. */
82 struct afm *metrics; /* Metrics. */
83 char *embed_fn; /* Name of file to embed. */
84 char *encoding_fn; /* Name of file with encoding. */
87 /* PostScript output driver extension record. */
90 char *file_name; /* Output file name. */
91 FILE *file; /* Output file. */
93 bool draw_headers; /* Draw headers at top of page? */
94 int page_number; /* Current page number. */
96 bool portrait; /* Portrait mode? */
97 int paper_width; /* Width of paper before dropping margins. */
98 int paper_length; /* Length of paper before dropping margins. */
99 int left_margin; /* Left margin in psus. */
100 int right_margin; /* Right margin in psus. */
101 int top_margin; /* Top margin in psus. */
102 int bottom_margin; /* Bottom margin in psus. */
104 int line_gutter; /* Space around lines. */
105 int line_space; /* Space between lines. */
106 int line_width; /* Width of lines. */
108 struct font *fonts[OUTP_FONT_CNT];
109 int last_font; /* Index of last font set with setfont. */
112 /* Transform logical y-ordinate Y into a page ordinate. */
113 #define YT(Y) (this->length - (Y))
115 static bool handle_option (struct outp_driver *this, const char *key,
116 const struct string *val);
117 static void draw_headers (struct outp_driver *this);
119 static void write_ps_prologue (struct outp_driver *);
121 static char *quote_ps_name (const char *string);
123 static struct font *load_font (const char *string);
124 static void free_font (struct font *);
125 static void setup_font (struct outp_driver *this, struct font *, int index);
127 /* Driver initialization. */
130 ps_open_driver (struct outp_driver *this, struct substring options)
132 struct ps_driver_ext *x;
135 this->width = this->length = 0;
136 this->font_height = PSUS * 10 / 72;
138 this->ext = x = xmalloc (sizeof *x);
139 x->file_name = xstrdup ("pspp.ps");
141 x->draw_headers = true;
144 x->paper_width = PSUS * 17 / 2;
145 x->paper_length = PSUS * 11;
146 x->left_margin = PSUS / 2;
147 x->right_margin = PSUS / 2;
148 x->top_margin = PSUS / 2;
149 x->bottom_margin = PSUS / 2;
150 x->line_gutter = PSUS / 72;
151 x->line_space = PSUS / 72;
152 x->line_width = PSUS / 144;
153 for (i = 0; i < OUTP_FONT_CNT; i++)
156 outp_parse_options (options, handle_option, this);
158 x->file = fn_open (x->file_name, "w");
161 error (0, errno, _("opening PostScript output file \"%s\""),
168 this->width = x->paper_width;
169 this->length = x->paper_length;
173 this->width = x->paper_length;
174 this->length = x->paper_width;
176 this->width -= x->left_margin + x->right_margin;
177 this->length -= x->top_margin + x->bottom_margin;
180 int header_length = 3 * this->font_height;
181 this->length -= header_length;
182 x->top_margin += header_length;
185 for (i = 0; i < OUTP_FONT_CNT; i++)
186 if (x->fonts[i] == NULL)
188 const char *default_fonts[OUTP_FONT_CNT];
189 default_fonts[OUTP_FIXED] = "Courier.afm";
190 default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
191 default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
192 x->fonts[i] = load_font (default_fonts[i]);
193 if (x->fonts[i] == NULL)
197 if (this->length / this->font_height < 15)
199 error (0, 0, _("The defined PostScript page is not long "
200 "enough to hold margins and headers, plus least 15 "
201 "lines of the default fonts. In fact, there's only "
202 "room for %d lines of each font at the default size "
203 "of %d.%03d points."),
204 this->length / this->font_height,
205 this->font_height / 1000, this->font_height % 1000);
210 afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
211 * this->font_height / 1000;
212 this->prop_em_width =
213 afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
214 * this->font_height / 1000;
216 this->horiz_line_width[OUTP_L_NONE] = 0;
217 this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
218 this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
219 + 2 * x->line_width);
220 memcpy (this->vert_line_width, this->horiz_line_width,
221 sizeof this->vert_line_width);
223 write_ps_prologue (this);
228 this->class->close_driver (this);
233 ps_close_driver (struct outp_driver *this)
235 struct ps_driver_ext *x = this->ext;
245 ok = fn_close (x->file_name, x->file) == 0;
247 error (0, errno, _("closing PostScript output file \"%s\""), x->file_name);
249 for (i = 0; i < OUTP_FONT_CNT; i++)
250 free_font (x->fonts[i]);
256 /* Generic option types. */
270 /* All the options that the PostScript driver supports. */
271 static const struct outp_option option_tab[] =
273 {"output-file", output_file_arg,0},
274 {"paper-size", paper_size_arg, 0},
275 {"orientation", orientation_arg,0},
277 {"headers", boolean_arg, 1},
279 {"prop-font", string_arg, OUTP_PROPORTIONAL},
280 {"emph-font", string_arg, OUTP_EMPHASIS},
281 {"fixed-font", string_arg, OUTP_FIXED},
283 {"left-margin", pos_int_arg, 0},
284 {"right-margin", pos_int_arg, 1},
285 {"top-margin", pos_int_arg, 2},
286 {"bottom-margin", pos_int_arg, 3},
287 {"font-size", pos_int_arg, 4},
289 {"line-width", dimension_arg, 0},
290 {"line-gutter", dimension_arg, 1},
291 {"line-width", dimension_arg, 2},
296 handle_option (struct outp_driver *this, const char *key,
297 const struct string *val)
299 struct ps_driver_ext *x = this->ext;
301 char *value = ds_cstr (val);
303 switch (outp_match_keyword (key, option_tab, &subcat))
307 _("unknown configuration parameter `%s' for PostScript device "
310 case output_file_arg:
312 x->file_name = xstrdup (value);
315 outp_get_paper_size (value, &this->width, &this->length);
317 case orientation_arg:
318 if (!strcmp (value, "portrait"))
320 else if (!strcmp (value, "landscape"))
323 error (0, 0, _("unknown orientation `%s' (valid orientations are "
324 "`portrait' and `landscape')"), value);
327 if (!strcmp (value, "on") || !strcmp (value, "true")
328 || !strcmp (value, "yes") || atoi (value))
329 x->draw_headers = true;
330 else if (!strcmp (value, "off") || !strcmp (value, "false")
331 || !strcmp (value, "no") || !strcmp (value, "0"))
332 x->draw_headers = false;
335 error (0, 0, _("boolean value expected for %s"), key);
345 arg = strtol (value, &tail, 0);
346 if (arg < 1 || errno == ERANGE || *tail)
348 error (0, 0, _("positive integer value required for `%s'"), key);
351 if ((subcat == 4 || subcat == 5) && arg < 1000)
353 error (0, 0, _("default font size must be at least 1 point (value "
354 "of 1000 for key `%s')"), key);
360 x->left_margin = arg;
363 x->right_margin = arg;
369 x->bottom_margin = arg;
372 this->font_height = arg;
381 int dimension = outp_evaluate_dimension (value, NULL);
385 error (0, 0, _("value for `%s' must be a dimension of positive "
386 "length (i.e., `1in')"), key);
392 x->line_width = dimension;
395 x->line_gutter = dimension;
398 x->line_width = dimension;
407 struct font *font = load_font (value);
410 struct font **dst = &x->fonts[subcat];
424 /* Looks for a PostScript font file or config file in all the
425 appropriate places. Returns the file name on success, NULL on
428 find_ps_file (const char *name)
430 if (fn_is_absolute (name))
431 return xstrdup (name);
434 char *base_name = xasprintf ("psfonts/%s", name);
435 char *file_name = fn_search_path (base_name, config_path);
441 /* Basic file operations. */
443 /* Writes the PostScript prologue to file F. */
445 write_ps_prologue (struct outp_driver *this)
447 struct ps_driver_ext *x = this->ext;
448 size_t embedded_cnt, preloaded_cnt;
451 fputs ("%!PS-Adobe-3.0\n", x->file);
452 fputs ("%%Pages: (atend)\n", x->file);
454 embedded_cnt = preloaded_cnt = 0;
455 for (i = 0; i < OUTP_FONT_CNT; i++)
457 bool embed = x->fonts[i]->embed_fn != NULL;
458 embedded_cnt += embed;
459 preloaded_cnt += !embed;
461 if (preloaded_cnt > 0)
463 fputs ("%%DocumentNeededResources: font", x->file);
464 for (i = 0; i < OUTP_FONT_CNT; i++)
466 struct font *f = x->fonts[i];
467 if (f->embed_fn == NULL)
468 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
470 fputs ("\n", x->file);
472 if (embedded_cnt > 0)
474 fputs ("%%DocumentSuppliedResources: font", x->file);
475 for (i = 0; i < OUTP_FONT_CNT; i++)
477 struct font *f = x->fonts[i];
478 if (f->embed_fn != NULL)
479 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
481 fputs ("\n", x->file);
483 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
484 fprintf (x->file, "%%%%Creator: %s\n", version);
485 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
486 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
487 fprintf (x->file, "%%%%Orientation: %s\n",
488 x->portrait ? "Portrait" : "Landscape");
489 fputs ("%%EndComments\n", x->file);
490 fputs ("%%BeginDefaults\n", x->file);
491 fputs ("%%PageResources: font", x->file);
492 for (i = 0; i < OUTP_FONT_CNT; i++)
493 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
494 fputs ("\n", x->file);
495 fputs ("%%EndDefaults\n", x->file);
496 fputs ("%%BeginProlog\n", x->file);
497 fputs ("/ED{exch def}bind def\n", x->file);
498 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
499 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
500 fputs ("/S{show}bind def\n", x->file);
501 fputs ("/GS{glyphshow}def\n", x->file);
502 fputs ("/RF{\n", x->file);
503 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
504 fputs (" {\n", x->file);
505 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
506 fputs (" }forall\n", x->file);
507 fputs (" /Encoding ED\n", x->file);
508 fputs (" currentdict end\n", x->file);
509 fputs ("}bind def\n", x->file);
510 fputs ("/F{setfont}bind def\n", x->file);
511 fputs ("/EP{\n", x->file);
512 fputs (" pg restore\n", x->file);
513 fputs (" showpage\n", x->file);
514 fputs ("}bind def\n", x->file);
515 fputs ("/GB{\n", x->file);
516 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
517 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
519 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
520 fputs ("}bind def\n", x->file);
521 fputs ("/K{0 rmoveto}bind def\n", x->file);
522 fputs ("%%EndProlog\n", x->file);
523 fputs ("%%BeginSetup\n", x->file);
524 for (i = 0; i < OUTP_FONT_CNT; i++)
525 setup_font (this, x->fonts[i], i);
526 fputs ("%%EndSetup\n", x->file);
529 /* Returns STRING as a Postscript name, which is just '/'
530 followed by STRING unless characters need to be quoted.
531 The caller must free the string. */
533 quote_ps_name (const char *string)
537 for (cp = string; *cp != '\0'; cp++)
539 unsigned char c = *cp;
540 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
541 && (cp == string || !isdigit (c)))
543 struct string out = DS_EMPTY_INITIALIZER;
544 ds_put_char (&out, '<');
545 for (cp = string; *cp != '\0'; cp++)
548 ds_put_format (&out, "%02x", c);
550 ds_put_cstr (&out, ">cvn");
551 return ds_cstr (&out);
554 return xasprintf ("/%s", string);
558 ps_open_page (struct outp_driver *this)
560 struct ps_driver_ext *x = this->ext;
562 /* Assure page independence. */
569 "%%%%BeginPageSetup\n"
570 "/pg save def 0.001 dup scale\n",
571 x->page_number, x->page_number);
575 "%d 0 translate 90 rotate\n",
578 if (x->bottom_margin != 0 || x->left_margin != 0)
581 x->left_margin, x->bottom_margin);
584 "/LW %d def %d setlinewidth\n"
585 "%%%%EndPageSetup\n",
586 x->line_width, x->line_width);
593 ps_close_page (struct outp_driver *this)
595 struct ps_driver_ext *x = this->ext;
596 fputs ("%%PageTrailer\n"
602 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
613 /* Draws a line from (x0,y0) to (x1,y1). */
615 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
617 struct ps_driver_ext *ext = this->ext;
618 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
621 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
622 shortening it to X0...X1 if SHORTEN is true.
623 Draws a horizontal line X1...X3 at Y if RIGHT says so,
624 shortening it to X2...X3 if SHORTEN is true. */
626 horz_line (struct outp_driver *this,
627 int x0, int x1, int x2, int x3, int y,
628 enum outp_line_style left, enum outp_line_style right,
631 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
632 dump_line (this, x0, y, x3, y);
635 if (left != OUTP_L_NONE)
636 dump_line (this, x0, y, shorten ? x1 : x2, y);
637 if (right != OUTP_L_NONE)
638 dump_line (this, shorten ? x2 : x1, y, x3, y);
642 /* Draws a vertical line Y0...Y2 at X if TOP says so,
643 shortening it to Y0...Y1 if SHORTEN is true.
644 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
645 shortening it to Y2...Y3 if SHORTEN is true. */
647 vert_line (struct outp_driver *this,
648 int y0, int y1, int y2, int y3, int x,
649 enum outp_line_style top, enum outp_line_style bottom,
652 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
653 dump_line (this, x, y0, x, y3);
656 if (top != OUTP_L_NONE)
657 dump_line (this, x, y0, x, shorten ? y1 : y2);
658 if (bottom != OUTP_L_NONE)
659 dump_line (this, x, shorten ? y2 : y1, x, y3);
663 /* Draws a generalized intersection of lines in the rectangle
664 (X0,Y0)-(X3,Y3). The line coming from the top to the center
665 is of style TOP, from left to center of style LEFT, from
666 bottom to center of style BOTTOM, and from right to center of
669 ps_line (struct outp_driver *this,
670 int x0, int y0, int x3, int y3,
671 enum outp_line_style top, enum outp_line_style left,
672 enum outp_line_style bottom, enum outp_line_style right)
674 /* The algorithm here is somewhat subtle, to allow it to handle
675 all the kinds of intersections that we need.
677 Three additional ordinates are assigned along the x axis. The
678 first is xc, midway between x0 and x3. The others are x1 and
679 x2; for a single vertical line these are equal to xc, and for
680 a double vertical line they are the ordinates of the left and
681 right half of the double line.
683 yc, y1, and y2 are assigned similarly along the y axis.
685 The following diagram shows the coordinate system and output
686 for double top and bottom lines, single left line, and no
690 y0 ________________________
696 y1 = y2 = yc |######### # |
701 y3 |________#_____#_______|
703 struct ps_driver_ext *ext = this->ext;
705 /* Offset from center of each line in a pair of double lines. */
706 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
708 /* Are the lines along each axis single or double?
709 (It doesn't make sense to have different kinds of line on the
710 same axis, so we don't try to gracefully handle that case.) */
711 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
712 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
714 /* When horizontal lines are doubled,
715 the left-side line along y1 normally runs from x0 to x2,
716 and the right-side line along y1 from x3 to x1.
717 If the top-side line is also doubled, we shorten the y1 lines,
718 so that the left-side line runs only to x1,
719 and the right-side line only to x2.
720 Otherwise, the horizontal line at y = y1 below would cut off
721 the intersection, which looks ugly:
723 y0 ________________________
728 y1 |######### ########|
731 y2 |######################|
734 y3 |______________________|
735 It is more of a judgment call when the horizontal line is
736 single. We actually choose to cut off the line anyhow, as
737 shown in the first diagram above.
739 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
740 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
741 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
742 int horz_line_ofs = double_vert ? double_line_ofs : 0;
743 int xc = (x0 + x3) / 2;
744 int x1 = xc - horz_line_ofs;
745 int x2 = xc + horz_line_ofs;
747 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
748 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
749 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
750 int vert_line_ofs = double_horz ? double_line_ofs : 0;
751 int yc = (y0 + y3) / 2;
752 int y1 = yc - vert_line_ofs;
753 int y2 = yc + vert_line_ofs;
756 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
759 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
760 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
764 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
767 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
768 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
772 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
773 and with the given JUSTIFICATION for THIS driver. */
775 draw_text (struct outp_driver *this,
776 const char *string, int x, int y, int max_width,
777 enum outp_justification justification)
779 struct outp_text text;
782 text.font = OUTP_PROPORTIONAL;
783 text.justification = justification;
784 text.string = ss_cstr (string);
786 text.v = this->font_height;
789 this->class->text_metrics (this, &text, &width, NULL);
790 this->class->text_draw (this, &text);
794 /* Writes LEFT left-justified and RIGHT right-justified within
795 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
797 draw_header_line (struct outp_driver *this,
798 const char *left, const char *right,
799 int x0, int x1, int y)
803 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
804 + this->prop_em_width);
806 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
809 /* Draw top of page headers for THIS driver. */
811 draw_headers (struct outp_driver *this)
813 struct ps_driver_ext *ext = this->ext;
818 y = -3 * this->font_height;
819 x0 = this->prop_em_width;
820 x1 = this->width - this->prop_em_width;
823 fprintf (ext->file, "%d %d %d %d GB\n",
825 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
826 y += ext->line_width + ext->line_gutter;
828 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
829 r2 = xasprintf ("%s - %s", version, host_system);
831 draw_header_line (this, outp_title, r1, x0, x1, y);
832 y += this->font_height;
834 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
840 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
842 The characters are justified according to JUSTIFICATION in a
843 field that has WIDTH_LEFT space remaining after the characters
844 themselves are accounted for.
845 Before character I is written, its x-position is adjusted by
848 write_text (struct outp_driver *this,
851 enum outp_justification justification,
852 const struct afm_character **chars, int *kerns, size_t char_cnt,
855 struct ps_driver_ext *ext = this->ext;
856 struct afm *afm = ext->fonts[font]->metrics;
860 if (justification == OUTP_RIGHT)
862 else if (justification == OUTP_CENTER)
863 x0 += width_left / 2;
864 y0 += afm_get_ascent (afm) * this->font_height / 1000;
866 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
868 if (ext->last_font != font)
870 ext->last_font = font;
871 fprintf (ext->file, "F%d setfont\n", font);
874 ds_init_empty (&out);
875 for (i = 0; i < char_cnt; i = j)
877 for (j = i + 1; j < char_cnt; j++)
882 fprintf (ext->file, "%d K", kerns[i]);
885 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
888 fprintf (ext->file, "%sS\n", ds_cstr (&out));
895 fprintf (ext->file, "/%s GS\n", chars[i]->name);
903 /* State of a text formatting operation. */
907 const struct outp_text *text;
911 const struct afm_character **glyphs;
915 size_t glyph_cnt; /* Number of glyphs output. */
916 int width_left; /* Width left over. */
917 int height_left; /* Height left over. */
919 /* State as of last space. */
920 const char *space_char; /* Just past last space. */
921 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
922 int space_width_left; /* Width left over as of last space. */
925 int max_width; /* Widest line so far. */
928 /* Adjusts S to complete a line of text,
929 and draws the current line if appropriate. */
931 finish_line (struct outp_driver *this, struct text_state *s)
938 s->text->x, s->text->y + (s->text->v - s->height_left),
940 s->text->justification,
941 s->glyphs, s->glyph_kerns, s->glyph_cnt,
946 /* Update maximum width. */
947 width = s->text->h - s->width_left;
948 if (width > s->max_width)
949 s->max_width = width;
951 /* Move to next line. */
952 s->width_left = s->text->h;
953 s->height_left -= this->font_height;
955 /* No spaces on this line yet. */
956 s->space_char = NULL;
959 /* Format TEXT on THIS driver.
960 If DRAW is nonzero, draw the text.
961 The width of the widest line is stored into *WIDTH, if WIDTH
963 The total height of the text written is stored into *HEIGHT,
964 if HEIGHT is nonnull. */
966 text (struct outp_driver *this, const struct outp_text *text, bool draw,
967 int *width, int *height)
969 struct ps_driver_ext *ext = this->ext;
970 struct afm *afm = ext->fonts[text->font]->metrics;
979 s.glyph_kerns = NULL;
983 s.width_left = s.text->h;
984 s.height_left = s.text->v;
990 cp = ss_data (s.text->string);
991 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
993 const struct afm_character *cur;
999 finish_line (this, &s);
1004 /* Get character and resolve ligatures. */
1005 cur = afm_get_character (afm, *cp);
1006 while (++cp < ss_end (s.text->string))
1008 const struct afm_character *next = afm_get_character (afm, *cp);
1009 const struct afm_character *ligature = afm_get_ligature (cur, next);
1010 if (ligature == NULL)
1014 char_width = cur->width * this->font_height / 1000;
1016 /* Get kern adjustment. */
1017 if (s.glyph_cnt > 0)
1018 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1019 * this->font_height / 1000);
1023 /* Record the current status if this is a space character. */
1024 if (cur->code == ' ' && cp > ss_data (s.text->string))
1027 s.space_glyph_cnt = s.glyph_cnt;
1028 s.space_width_left = s.width_left;
1031 /* Enough room on this line? */
1032 if (char_width + kern_adjust > s.width_left)
1034 if (s.space_char == NULL)
1036 finish_line (this, &s);
1042 s.glyph_cnt = s.space_glyph_cnt;
1043 s.width_left = s.space_width_left;
1044 finish_line (this, &s);
1049 if (s.glyph_cnt >= glyph_cap)
1051 glyph_cap = 2 * (glyph_cap + 8);
1052 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1053 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1054 glyph_cap, sizeof *s.glyph_kerns);
1056 s.glyphs[s.glyph_cnt] = cur;
1057 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1060 s.width_left -= char_width + kern_adjust;
1062 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1063 finish_line (this, &s);
1066 *width = s.max_width;
1068 *height = text->v - s.height_left;
1070 free (s.glyph_kerns);
1074 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1075 int *width, int *height)
1077 text (this, t, false, width, height);
1081 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1083 assert (this->page_open);
1084 text (this, t, true, NULL, NULL);
1088 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
1093 struct ps_driver_ext *x = this->ext;
1094 char page_size[128];
1096 int x_origin, y_origin;
1098 ch->file = tmpfile ();
1099 if (ch->file == NULL)
1105 size = this->width < this->length ? this->width : this->length;
1106 x_origin = x->left_margin + (size - this->width) / 2;
1107 y_origin = x->bottom_margin + (size - this->length) / 2;
1109 snprintf (page_size, sizeof page_size,
1110 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
1111 (double) size / PSUS, (double) size / PSUS,
1112 (double) x_origin / PSUS, (double) y_origin / PSUS);
1114 ch->pl_params = pl_newplparams ();
1115 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
1116 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
1121 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
1124 struct ps_driver_ext *x = this->ext;
1126 static int doc_num = 0;
1128 outp_eject_page (this);
1131 "%d %d translate 1000 dup scale\n"
1133 "/showpage { } def\n"
1134 "0 setgray 0 setlinecap 1 setlinewidth\n"
1135 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
1136 "%%%%BeginDocument: %d\n",
1137 -x->left_margin, -x->bottom_margin,
1141 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
1145 fputs ("%%EndDocument\n"
1149 outp_close_page (this);
1153 static void embed_font (struct outp_driver *this, struct font *font);
1154 static void reencode_font (struct outp_driver *this, struct font *font);
1156 /* Loads and returns the font for STRING, which has the format
1157 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1158 PFA or PFB file's name, and ENC is the encoding file's name.
1159 PFA and ENC are optional.
1160 Returns a null pointer if unsuccessful. */
1161 static struct font *
1162 load_font (const char *string_)
1164 char *string = xstrdup (string_);
1166 char *position = string;
1168 char *afm_file_name;
1170 font = xmalloc (sizeof *font);
1171 font->metrics = NULL;
1172 font->embed_fn = NULL;
1173 font->encoding_fn = NULL;
1175 token = strsep (&position, ",");
1178 error (0, 0, _("\"%s\": bad font specification"), string);
1182 /* Read AFM file. */
1183 afm_file_name = find_ps_file (token);
1184 if (afm_file_name == NULL)
1186 error (0, 0, _("could not find AFM file \"%s\""), token);
1189 font->metrics = afm_open (afm_file_name);
1190 free (afm_file_name);
1191 if (font->metrics == NULL)
1194 /* Find font file to embed. */
1195 token = strsep (&position, ",");
1196 if (token != NULL && *token != '\0')
1198 font->embed_fn = find_ps_file (token);
1199 if (font->embed_fn == NULL)
1200 error (0, 0, _("could not find font \"%s\""), token);
1203 /* Find encoding. */
1204 token = strsep (&position, ",");
1205 if (token != NULL && *token == '\0')
1207 font->encoding_fn = find_ps_file (token);
1208 if (font->encoding_fn == NULL)
1209 error (0, 0, _("could not find encoding \"%s\""), token);
1223 free_font (struct font *font)
1227 afm_close (font->metrics);
1228 free (font->embed_fn);
1229 free (font->encoding_fn);
1234 /* Emits PostScript code to embed FONT (if necessary), scale it
1235 to the proper size, re-encode it (if necessary), and store the
1236 resulting font as an object named F#, where INDEX is
1237 substituted for #. */
1239 setup_font (struct outp_driver *this, struct font *font, int index)
1241 struct ps_driver_ext *x = this->ext;
1244 if (font->embed_fn != NULL)
1245 embed_font (this, font);
1247 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1248 afm_get_findfont_name (font->metrics));
1250 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1251 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1254 if (font->encoding_fn != NULL)
1255 reencode_font (this, font);
1257 fprintf (x->file, "/F%d ED\n", index);
1260 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1261 end-of-file or on error. */
1263 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1265 while (copy_bytes > 0)
1267 char buffer[BUFSIZ];
1268 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1269 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1270 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1271 if (write_bytes != chunk_bytes)
1273 copy_bytes -= chunk_bytes;
1277 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1278 end-of-file or on error. The bytes are translated into
1279 hexadecimal during copying and broken into lines with
1280 new-line characters. */
1282 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1286 for (i = 0; i < copy_bytes; i++)
1291 if (i > 0 && i % 36 == 0)
1293 fprintf (dst, "%02X", c);
1298 /* Embeds the given FONT into THIS driver's output stream. */
1300 embed_font (struct outp_driver *this, struct font *font)
1302 struct ps_driver_ext *x = this->ext;
1306 file = fopen (font->embed_fn, "rb");
1309 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1313 fprintf (x->file, "%%%%BeginResource: font %s\n",
1314 afm_get_findfont_name (font->metrics));
1320 /* PFA file. Copy literally. */
1321 copy_bytes_literally (file, x->file, ULONG_MAX);
1325 /* PFB file. Translate as specified in Adobe Technical
1327 while ((c = getc (file)) == 128)
1330 unsigned long length;
1336 length = getc (file);
1337 length |= (unsigned long) getc (file) << 8;
1338 length |= (unsigned long) getc (file) << 16;
1339 length |= (unsigned long) getc (file) << 24;
1342 copy_bytes_literally (file, x->file, length);
1344 copy_bytes_as_hex (file, x->file, length);
1349 if (freaderror (file))
1350 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1351 fputs ("%%EndResource\n", x->file);
1354 /* Re-encodes FONT according to the specified encoding. */
1356 reencode_font (struct outp_driver *this, struct font *font)
1358 struct ps_driver_ext *x = this->ext;
1369 file = fopen (font->encoding_fn, "r");
1372 error (errno, 0, _("cannot open font encoding file \"%s\""),
1377 for (i = 0; i < 256; i++)
1382 ds_init_empty (&line);
1383 while (ds_read_config_line (&line, &line_number, file))
1385 char *pschar, *code;
1386 char *save_ptr, *tail;
1389 if (ds_is_empty (&line) == 0)
1392 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1393 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1394 if (pschar == NULL || code == NULL)
1397 code_val = strtol (code, &tail, 0);
1400 error_at_line (0, 0, font->encoding_fn, line_number,
1401 _("invalid numeric format"));
1404 if (code_val < 0 || code_val > 255)
1406 if (tab[code_val] != 0)
1407 free (tab[code_val]);
1408 tab[code_val] = xstrdup (pschar);
1412 fputs ("[", x->file);
1413 for (i = 0; i < 256; i++)
1415 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1416 fprintf (x->file, "%s\n", name);
1420 fputs ("] RF\n", x->file);
1422 if (freaderror (file) != 0)
1423 error (errno, 0, _("closing Postscript encoding \"%s\""),
1427 /* PostScript driver class. */
1428 const struct outp_class postscript_class =
1445 ps_chart_initialise,