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>
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. */
113 /* Transform logical y-ordinate Y into a page ordinate. */
114 #define YT(Y) (this->length - (Y))
116 static bool handle_option (struct outp_driver *this, const char *key,
117 const struct string *val);
118 static void draw_headers (struct outp_driver *this);
120 static void write_ps_prologue (struct outp_driver *);
122 static char *quote_ps_name (const char *string);
124 static struct font *load_font (const char *string);
125 static void free_font (struct font *);
126 static void setup_font (struct outp_driver *this, struct font *, int index);
128 /* Driver initialization. */
131 ps_open_driver (struct outp_driver *this, struct substring options)
133 struct ps_driver_ext *x;
136 this->width = this->length = 0;
137 this->font_height = PSUS * 10 / 72;
139 this->ext = x = xmalloc (sizeof *x);
140 x->file_name = xstrdup ("pspp.ps");
142 x->draw_headers = true;
145 x->paper_width = PSUS * 17 / 2;
146 x->paper_length = PSUS * 11;
147 x->left_margin = PSUS / 2;
148 x->right_margin = PSUS / 2;
149 x->top_margin = PSUS / 2;
150 x->bottom_margin = PSUS / 2;
151 x->line_gutter = PSUS / 72;
152 x->line_space = PSUS / 72;
153 x->line_width = PSUS / 144;
154 for (i = 0; i < OUTP_FONT_CNT; i++)
157 outp_parse_options (options, handle_option, this);
159 x->file = fn_open (x->file_name, "w");
162 error (0, errno, _("opening PostScript output file \"%s\""),
169 this->width = x->paper_width;
170 this->length = x->paper_length;
174 this->width = x->paper_length;
175 this->length = x->paper_width;
177 this->width -= x->left_margin + x->right_margin;
178 this->length -= x->top_margin + x->bottom_margin;
181 int header_length = 3 * this->font_height;
182 this->length -= header_length;
183 x->top_margin += header_length;
186 for (i = 0; i < OUTP_FONT_CNT; i++)
187 if (x->fonts[i] == NULL)
189 const char *default_fonts[OUTP_FONT_CNT];
190 default_fonts[OUTP_FIXED] = "Courier.afm";
191 default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
192 default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
193 x->fonts[i] = load_font (default_fonts[i]);
194 if (x->fonts[i] == NULL)
198 if (this->length / this->font_height < 15)
200 error (0, 0, _("The defined PostScript page is not long "
201 "enough to hold margins and headers, plus least 15 "
202 "lines of the default fonts. In fact, there's only "
203 "room for %d lines of each font at the default size "
204 "of %d.%03d points."),
205 this->length / this->font_height,
206 this->font_height / 1000, this->font_height % 1000);
211 afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
212 * this->font_height / 1000;
213 this->prop_em_width =
214 afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
215 * this->font_height / 1000;
217 this->horiz_line_width[OUTP_L_NONE] = 0;
218 this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
219 this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
220 + 2 * x->line_width);
221 memcpy (this->vert_line_width, this->horiz_line_width,
222 sizeof this->vert_line_width);
224 write_ps_prologue (this);
229 this->class->close_driver (this);
234 ps_close_driver (struct outp_driver *this)
236 struct ps_driver_ext *x = this->ext;
246 ok = fn_close (x->file_name, x->file) == 0;
248 error (0, errno, _("closing PostScript output file \"%s\""), x->file_name);
250 for (i = 0; i < OUTP_FONT_CNT; i++)
251 free_font (x->fonts[i]);
257 /* Generic option types. */
271 /* All the options that the PostScript driver supports. */
272 static const struct outp_option option_tab[] =
274 {"output-file", output_file_arg,0},
275 {"paper-size", paper_size_arg, 0},
276 {"orientation", orientation_arg,0},
278 {"headers", boolean_arg, 1},
280 {"prop-font", string_arg, OUTP_PROPORTIONAL},
281 {"emph-font", string_arg, OUTP_EMPHASIS},
282 {"fixed-font", string_arg, OUTP_FIXED},
284 {"left-margin", pos_int_arg, 0},
285 {"right-margin", pos_int_arg, 1},
286 {"top-margin", pos_int_arg, 2},
287 {"bottom-margin", pos_int_arg, 3},
288 {"font-size", pos_int_arg, 4},
290 {"line-width", dimension_arg, 0},
291 {"line-gutter", dimension_arg, 1},
292 {"line-width", dimension_arg, 2},
297 handle_option (struct outp_driver *this, const char *key,
298 const struct string *val)
300 struct ps_driver_ext *x = this->ext;
302 char *value = ds_cstr (val);
304 switch (outp_match_keyword (key, option_tab, &subcat))
308 _("unknown configuration parameter `%s' for PostScript device "
311 case output_file_arg:
313 x->file_name = xstrdup (value);
316 outp_get_paper_size (value, &this->width, &this->length);
318 case orientation_arg:
319 if (!strcmp (value, "portrait"))
321 else if (!strcmp (value, "landscape"))
324 error (0, 0, _("unknown orientation `%s' (valid orientations are "
325 "`portrait' and `landscape')"), value);
328 if (!strcmp (value, "on") || !strcmp (value, "true")
329 || !strcmp (value, "yes") || atoi (value))
330 x->draw_headers = true;
331 else if (!strcmp (value, "off") || !strcmp (value, "false")
332 || !strcmp (value, "no") || !strcmp (value, "0"))
333 x->draw_headers = false;
336 error (0, 0, _("boolean value expected for %s"), key);
346 arg = strtol (value, &tail, 0);
347 if (arg < 1 || errno == ERANGE || *tail)
349 error (0, 0, _("positive integer value required for `%s'"), key);
352 if ((subcat == 4 || subcat == 5) && arg < 1000)
354 error (0, 0, _("default font size must be at least 1 point (value "
355 "of 1000 for key `%s')"), key);
361 x->left_margin = arg;
364 x->right_margin = arg;
370 x->bottom_margin = arg;
373 this->font_height = arg;
382 int dimension = outp_evaluate_dimension (value, NULL);
386 error (0, 0, _("value for `%s' must be a dimension of positive "
387 "length (i.e., `1in')"), key);
393 x->line_width = dimension;
396 x->line_gutter = dimension;
399 x->line_width = dimension;
408 struct font *font = load_font (value);
411 struct font **dst = &x->fonts[subcat];
425 /* Looks for a PostScript font file or config file in all the
426 appropriate places. Returns the file name on success, NULL on
429 find_ps_file (const char *name)
431 if (fn_is_absolute (name))
432 return xstrdup (name);
435 char *base_name = xasprintf ("psfonts/%s", name);
436 char *file_name = fn_search_path (base_name, config_path);
442 /* Basic file operations. */
444 /* Writes the PostScript prologue to file F. */
446 write_ps_prologue (struct outp_driver *this)
448 struct ps_driver_ext *x = this->ext;
449 size_t embedded_cnt, preloaded_cnt;
452 fputs ("%!PS-Adobe-3.0\n", x->file);
453 fputs ("%%Pages: (atend)\n", x->file);
455 embedded_cnt = preloaded_cnt = 0;
456 for (i = 0; i < OUTP_FONT_CNT; i++)
458 bool embed = x->fonts[i]->embed_fn != NULL;
459 embedded_cnt += embed;
460 preloaded_cnt += !embed;
462 if (preloaded_cnt > 0)
464 fputs ("%%DocumentNeededResources: font", x->file);
465 for (i = 0; i < OUTP_FONT_CNT; i++)
467 struct font *f = x->fonts[i];
468 if (f->embed_fn == NULL)
469 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
471 fputs ("\n", x->file);
473 if (embedded_cnt > 0)
475 fputs ("%%DocumentSuppliedResources: font", x->file);
476 for (i = 0; i < OUTP_FONT_CNT; i++)
478 struct font *f = x->fonts[i];
479 if (f->embed_fn != NULL)
480 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
482 fputs ("\n", x->file);
484 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
485 fprintf (x->file, "%%%%Creator: %s\n", version);
486 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
487 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
488 fprintf (x->file, "%%%%Orientation: %s\n",
489 x->portrait ? "Portrait" : "Landscape");
490 fputs ("%%EndComments\n", x->file);
491 fputs ("%%BeginDefaults\n", x->file);
492 fputs ("%%PageResources: font", x->file);
493 for (i = 0; i < OUTP_FONT_CNT; i++)
494 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
495 fputs ("\n", x->file);
496 fputs ("%%EndDefaults\n", x->file);
497 fputs ("%%BeginProlog\n", x->file);
498 fputs ("/ED{exch def}bind def\n", x->file);
499 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
500 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
501 fputs ("/S{show}bind def\n", x->file);
502 fputs ("/GS{glyphshow}def\n", x->file);
503 fputs ("/RF{\n", x->file);
504 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
505 fputs (" {\n", x->file);
506 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
507 fputs (" }forall\n", x->file);
508 fputs (" /Encoding ED\n", x->file);
509 fputs (" currentdict end\n", x->file);
510 fputs ("}bind def\n", x->file);
511 fputs ("/F{setfont}bind def\n", x->file);
512 fputs ("/EP{\n", x->file);
513 fputs (" pg restore\n", x->file);
514 fputs (" showpage\n", x->file);
515 fputs ("}bind def\n", x->file);
516 fputs ("/GB{\n", x->file);
517 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
518 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
520 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
521 fputs ("}bind def\n", x->file);
522 fputs ("/K{0 rmoveto}bind def\n", x->file);
523 fputs ("%%EndProlog\n", x->file);
524 fputs ("%%BeginSetup\n", x->file);
525 for (i = 0; i < OUTP_FONT_CNT; i++)
526 setup_font (this, x->fonts[i], i);
527 fputs ("%%EndSetup\n", x->file);
530 /* Returns STRING as a Postscript name, which is just '/'
531 followed by STRING unless characters need to be quoted.
532 The caller must free the string. */
534 quote_ps_name (const char *string)
538 for (cp = string; *cp != '\0'; cp++)
540 unsigned char c = *cp;
541 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
542 && (cp == string || !isdigit (c)))
544 struct string out = DS_EMPTY_INITIALIZER;
545 ds_put_char (&out, '<');
546 for (cp = string; *cp != '\0'; cp++)
549 ds_put_format (&out, "%02x", c);
551 ds_put_cstr (&out, ">cvn");
552 return ds_cstr (&out);
555 return xasprintf ("/%s", string);
559 ps_open_page (struct outp_driver *this)
561 struct ps_driver_ext *x = this->ext;
563 /* Assure page independence. */
570 "%%%%BeginPageSetup\n"
571 "/pg save def 0.001 dup scale\n",
572 x->page_number, x->page_number);
576 "%d 0 translate 90 rotate\n",
579 if (x->bottom_margin != 0 || x->left_margin != 0)
582 x->left_margin, x->bottom_margin);
585 "/LW %d def %d setlinewidth\n"
586 "%%%%EndPageSetup\n",
587 x->line_width, x->line_width);
594 ps_close_page (struct outp_driver *this)
596 struct ps_driver_ext *x = this->ext;
597 fputs ("%%PageTrailer\n"
603 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
614 /* Draws a line from (x0,y0) to (x1,y1). */
616 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
618 struct ps_driver_ext *ext = this->ext;
619 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
622 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
623 shortening it to X0...X1 if SHORTEN is true.
624 Draws a horizontal line X1...X3 at Y if RIGHT says so,
625 shortening it to X2...X3 if SHORTEN is true. */
627 horz_line (struct outp_driver *this,
628 int x0, int x1, int x2, int x3, int y,
629 enum outp_line_style left, enum outp_line_style right,
632 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
633 dump_line (this, x0, y, x3, y);
636 if (left != OUTP_L_NONE)
637 dump_line (this, x0, y, shorten ? x1 : x2, y);
638 if (right != OUTP_L_NONE)
639 dump_line (this, shorten ? x2 : x1, y, x3, y);
643 /* Draws a vertical line Y0...Y2 at X if TOP says so,
644 shortening it to Y0...Y1 if SHORTEN is true.
645 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
646 shortening it to Y2...Y3 if SHORTEN is true. */
648 vert_line (struct outp_driver *this,
649 int y0, int y1, int y2, int y3, int x,
650 enum outp_line_style top, enum outp_line_style bottom,
653 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
654 dump_line (this, x, y0, x, y3);
657 if (top != OUTP_L_NONE)
658 dump_line (this, x, y0, x, shorten ? y1 : y2);
659 if (bottom != OUTP_L_NONE)
660 dump_line (this, x, shorten ? y2 : y1, x, y3);
664 /* Draws a generalized intersection of lines in the rectangle
665 (X0,Y0)-(X3,Y3). The line coming from the top to the center
666 is of style TOP, from left to center of style LEFT, from
667 bottom to center of style BOTTOM, and from right to center of
670 ps_line (struct outp_driver *this,
671 int x0, int y0, int x3, int y3,
672 enum outp_line_style top, enum outp_line_style left,
673 enum outp_line_style bottom, enum outp_line_style right)
675 /* The algorithm here is somewhat subtle, to allow it to handle
676 all the kinds of intersections that we need.
678 Three additional ordinates are assigned along the x axis. The
679 first is xc, midway between x0 and x3. The others are x1 and
680 x2; for a single vertical line these are equal to xc, and for
681 a double vertical line they are the ordinates of the left and
682 right half of the double line.
684 yc, y1, and y2 are assigned similarly along the y axis.
686 The following diagram shows the coordinate system and output
687 for double top and bottom lines, single left line, and no
691 y0 ________________________
697 y1 = y2 = yc |######### # |
702 y3 |________#_____#_______|
704 struct ps_driver_ext *ext = this->ext;
706 /* Offset from center of each line in a pair of double lines. */
707 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
709 /* Are the lines along each axis single or double?
710 (It doesn't make sense to have different kinds of line on the
711 same axis, so we don't try to gracefully handle that case.) */
712 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
713 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
715 /* When horizontal lines are doubled,
716 the left-side line along y1 normally runs from x0 to x2,
717 and the right-side line along y1 from x3 to x1.
718 If the top-side line is also doubled, we shorten the y1 lines,
719 so that the left-side line runs only to x1,
720 and the right-side line only to x2.
721 Otherwise, the horizontal line at y = y1 below would cut off
722 the intersection, which looks ugly:
724 y0 ________________________
729 y1 |######### ########|
732 y2 |######################|
735 y3 |______________________|
736 It is more of a judgment call when the horizontal line is
737 single. We actually choose to cut off the line anyhow, as
738 shown in the first diagram above.
740 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
741 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
742 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
743 int horz_line_ofs = double_vert ? double_line_ofs : 0;
744 int xc = (x0 + x3) / 2;
745 int x1 = xc - horz_line_ofs;
746 int x2 = xc + horz_line_ofs;
748 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
749 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
750 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
751 int vert_line_ofs = double_horz ? double_line_ofs : 0;
752 int yc = (y0 + y3) / 2;
753 int y1 = yc - vert_line_ofs;
754 int y2 = yc + vert_line_ofs;
757 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
760 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
761 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
765 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
768 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
769 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
773 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
774 and with the given JUSTIFICATION for THIS driver. */
776 draw_text (struct outp_driver *this,
777 const char *string, int x, int y, int max_width,
778 enum outp_justification justification)
780 struct outp_text text;
783 text.font = OUTP_PROPORTIONAL;
784 text.justification = justification;
785 text.string = ss_cstr (string);
787 text.v = this->font_height;
790 this->class->text_metrics (this, &text, &width, NULL);
791 this->class->text_draw (this, &text);
795 /* Writes LEFT left-justified and RIGHT right-justified within
796 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
798 draw_header_line (struct outp_driver *this,
799 const char *left, const char *right,
800 int x0, int x1, int y)
804 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
805 + this->prop_em_width);
807 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
810 /* Draw top of page headers for THIS driver. */
812 draw_headers (struct outp_driver *this)
814 struct ps_driver_ext *ext = this->ext;
819 y = -3 * this->font_height;
820 x0 = this->prop_em_width;
821 x1 = this->width - this->prop_em_width;
824 fprintf (ext->file, "%d %d %d %d GB\n",
826 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
827 y += ext->line_width + ext->line_gutter;
829 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
830 r2 = xasprintf ("%s - %s", version, host_system);
832 draw_header_line (this, outp_title, r1, x0, x1, y);
833 y += this->font_height;
835 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
841 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
843 The characters are justified according to JUSTIFICATION in a
844 field that has WIDTH_LEFT space remaining after the characters
845 themselves are accounted for.
846 Before character I is written, its x-position is adjusted by
849 write_text (struct outp_driver *this,
852 enum outp_justification justification,
853 const struct afm_character **chars, int *kerns, size_t char_cnt,
856 struct ps_driver_ext *ext = this->ext;
857 struct afm *afm = ext->fonts[font]->metrics;
861 if (justification == OUTP_RIGHT)
863 else if (justification == OUTP_CENTER)
864 x0 += width_left / 2;
865 y0 += afm_get_ascent (afm) * this->font_height / 1000;
867 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
869 if (ext->last_font != font)
871 ext->last_font = font;
872 fprintf (ext->file, "F%d setfont\n", font);
875 ds_init_empty (&out);
876 for (i = 0; i < char_cnt; i = j)
878 for (j = i + 1; j < char_cnt; j++)
883 fprintf (ext->file, "%d K", kerns[i]);
886 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
889 fprintf (ext->file, "%sS\n", ds_cstr (&out));
896 fprintf (ext->file, "/%s GS\n", chars[i]->name);
904 /* State of a text formatting operation. */
908 const struct outp_text *text;
912 const struct afm_character **glyphs;
916 size_t glyph_cnt; /* Number of glyphs output. */
917 int width_left; /* Width left over. */
918 int height_left; /* Height left over. */
920 /* State as of last space. */
921 const char *space_char; /* Just past last space. */
922 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
923 int space_width_left; /* Width left over as of last space. */
926 int max_width; /* Widest line so far. */
929 /* Adjusts S to complete a line of text,
930 and draws the current line if appropriate. */
932 finish_line (struct outp_driver *this, struct text_state *s)
939 s->text->x, s->text->y + (s->text->v - s->height_left),
941 s->text->justification,
942 s->glyphs, s->glyph_kerns, s->glyph_cnt,
947 /* Update maximum width. */
948 width = s->text->h - s->width_left;
949 if (width > s->max_width)
950 s->max_width = width;
952 /* Move to next line. */
953 s->width_left = s->text->h;
954 s->height_left -= this->font_height;
956 /* No spaces on this line yet. */
957 s->space_char = NULL;
960 /* Format TEXT on THIS driver.
961 If DRAW is nonzero, draw the text.
962 The width of the widest line is stored into *WIDTH, if WIDTH
964 The total height of the text written is stored into *HEIGHT,
965 if HEIGHT is nonnull. */
967 text (struct outp_driver *this, const struct outp_text *text, bool draw,
968 int *width, int *height)
970 struct ps_driver_ext *ext = this->ext;
971 struct afm *afm = ext->fonts[text->font]->metrics;
980 s.glyph_kerns = NULL;
984 s.width_left = s.text->h;
985 s.height_left = s.text->v;
991 cp = ss_data (s.text->string);
992 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
994 const struct afm_character *cur;
1000 finish_line (this, &s);
1005 /* Get character and resolve ligatures. */
1006 cur = afm_get_character (afm, *cp);
1007 while (++cp < ss_end (s.text->string))
1009 const struct afm_character *next = afm_get_character (afm, *cp);
1010 const struct afm_character *ligature = afm_get_ligature (cur, next);
1011 if (ligature == NULL)
1015 char_width = cur->width * this->font_height / 1000;
1017 /* Get kern adjustment. */
1018 if (s.glyph_cnt > 0)
1019 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1020 * this->font_height / 1000);
1024 /* Record the current status if this is a space character. */
1025 if (cur->code == ' ' && cp > ss_data (s.text->string))
1028 s.space_glyph_cnt = s.glyph_cnt;
1029 s.space_width_left = s.width_left;
1032 /* Enough room on this line? */
1033 if (char_width + kern_adjust > s.width_left)
1035 if (s.space_char == NULL)
1037 finish_line (this, &s);
1043 s.glyph_cnt = s.space_glyph_cnt;
1044 s.width_left = s.space_width_left;
1045 finish_line (this, &s);
1050 if (s.glyph_cnt >= glyph_cap)
1052 glyph_cap = 2 * (glyph_cap + 8);
1053 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1054 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1055 glyph_cap, sizeof *s.glyph_kerns);
1057 s.glyphs[s.glyph_cnt] = cur;
1058 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1061 s.width_left -= char_width + kern_adjust;
1063 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1064 finish_line (this, &s);
1067 *width = s.max_width;
1069 *height = text->v - s.height_left;
1071 free (s.glyph_kerns);
1075 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1076 int *width, int *height)
1078 text (this, t, false, width, height);
1082 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1084 assert (this->page_open);
1085 text (this, t, true, NULL, NULL);
1089 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
1094 struct ps_driver_ext *x = this->ext;
1095 char page_size[128];
1097 int x_origin, y_origin;
1099 ch->file = tmpfile ();
1100 if (ch->file == NULL)
1106 size = this->width < this->length ? this->width : this->length;
1107 x_origin = x->left_margin + (size - this->width) / 2;
1108 y_origin = x->bottom_margin + (size - this->length) / 2;
1110 snprintf (page_size, sizeof page_size,
1111 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
1112 (double) size / PSUS, (double) size / PSUS,
1113 (double) x_origin / PSUS, (double) y_origin / PSUS);
1115 ch->pl_params = pl_newplparams ();
1116 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
1117 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
1122 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
1125 struct ps_driver_ext *x = this->ext;
1127 static int doc_num = 0;
1129 outp_eject_page (this);
1132 "%d %d translate 1000 dup scale\n"
1134 "/showpage { } def\n"
1135 "0 setgray 0 setlinecap 1 setlinewidth\n"
1136 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
1137 "%%%%BeginDocument: %d\n",
1138 -x->left_margin, -x->bottom_margin,
1142 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
1146 fputs ("%%EndDocument\n"
1150 outp_close_page (this);
1154 static void embed_font (struct outp_driver *this, struct font *font);
1155 static void reencode_font (struct outp_driver *this, struct font *font);
1157 /* Loads and returns the font for STRING, which has the format
1158 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1159 PFA or PFB file's name, and ENC is the encoding file's name.
1160 PFA and ENC are optional.
1161 Returns a null pointer if unsuccessful. */
1162 static struct font *
1163 load_font (const char *string_)
1165 char *string = xstrdup (string_);
1167 char *position = string;
1169 char *afm_file_name;
1171 font = xmalloc (sizeof *font);
1172 font->metrics = NULL;
1173 font->embed_fn = NULL;
1174 font->encoding_fn = NULL;
1176 token = strsep (&position, ",");
1179 error (0, 0, _("\"%s\": bad font specification"), string);
1183 /* Read AFM file. */
1184 afm_file_name = find_ps_file (token);
1185 if (afm_file_name == NULL)
1187 error (0, 0, _("could not find AFM file \"%s\""), token);
1190 font->metrics = afm_open (afm_file_name);
1191 free (afm_file_name);
1192 if (font->metrics == NULL)
1195 /* Find font file to embed. */
1196 token = strsep (&position, ",");
1197 if (token != NULL && *token != '\0')
1199 font->embed_fn = find_ps_file (token);
1200 if (font->embed_fn == NULL)
1201 error (0, 0, _("could not find font \"%s\""), token);
1204 /* Find encoding. */
1205 token = strsep (&position, ",");
1206 if (token != NULL && *token == '\0')
1208 font->encoding_fn = find_ps_file (token);
1209 if (font->encoding_fn == NULL)
1210 error (0, 0, _("could not find encoding \"%s\""), token);
1224 free_font (struct font *font)
1228 afm_close (font->metrics);
1229 free (font->embed_fn);
1230 free (font->encoding_fn);
1235 /* Emits PostScript code to embed FONT (if necessary), scale it
1236 to the proper size, re-encode it (if necessary), and store the
1237 resulting font as an object named F#, where INDEX is
1238 substituted for #. */
1240 setup_font (struct outp_driver *this, struct font *font, int index)
1242 struct ps_driver_ext *x = this->ext;
1245 if (font->embed_fn != NULL)
1246 embed_font (this, font);
1248 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1249 afm_get_findfont_name (font->metrics));
1251 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1252 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1255 if (font->encoding_fn != NULL)
1256 reencode_font (this, font);
1258 fprintf (x->file, "/F%d ED\n", index);
1261 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1262 end-of-file or on error. */
1264 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1266 while (copy_bytes > 0)
1268 char buffer[BUFSIZ];
1269 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1270 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1271 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1272 if (write_bytes != chunk_bytes)
1274 copy_bytes -= chunk_bytes;
1278 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1279 end-of-file or on error. The bytes are translated into
1280 hexadecimal during copying and broken into lines with
1281 new-line characters. */
1283 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1287 for (i = 0; i < copy_bytes; i++)
1292 if (i > 0 && i % 36 == 0)
1294 fprintf (dst, "%02X", c);
1299 /* Embeds the given FONT into THIS driver's output stream. */
1301 embed_font (struct outp_driver *this, struct font *font)
1303 struct ps_driver_ext *x = this->ext;
1307 file = fopen (font->embed_fn, "rb");
1310 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1314 fprintf (x->file, "%%%%BeginResource: font %s\n",
1315 afm_get_findfont_name (font->metrics));
1321 /* PFA file. Copy literally. */
1322 copy_bytes_literally (file, x->file, ULONG_MAX);
1326 /* PFB file. Translate as specified in Adobe Technical
1328 while ((c = getc (file)) == 128)
1331 unsigned long length;
1337 length = getc (file);
1338 length |= (unsigned long) getc (file) << 8;
1339 length |= (unsigned long) getc (file) << 16;
1340 length |= (unsigned long) getc (file) << 24;
1343 copy_bytes_literally (file, x->file, length);
1345 copy_bytes_as_hex (file, x->file, length);
1350 if (freaderror (file))
1351 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1352 fputs ("%%EndResource\n", x->file);
1355 /* Re-encodes FONT according to the specified encoding. */
1357 reencode_font (struct outp_driver *this, struct font *font)
1359 struct ps_driver_ext *x = this->ext;
1370 file = fopen (font->encoding_fn, "r");
1373 error (errno, 0, _("cannot open font encoding file \"%s\""),
1378 for (i = 0; i < 256; i++)
1383 ds_init_empty (&line);
1384 while (ds_read_config_line (&line, &line_number, file))
1386 char *pschar, *code;
1387 char *save_ptr, *tail;
1390 if (ds_is_empty (&line) == 0)
1393 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1394 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1395 if (pschar == NULL || code == NULL)
1398 code_val = strtol (code, &tail, 0);
1401 error_at_line (0, 0, font->encoding_fn, line_number,
1402 _("invalid numeric format"));
1405 if (code_val < 0 || code_val > 255)
1407 if (tab[code_val] != 0)
1408 free (tab[code_val]);
1409 tab[code_val] = xstrdup (pschar);
1413 fputs ("[", x->file);
1414 for (i = 0; i < 256; i++)
1416 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1417 fprintf (x->file, "%s\n", name);
1421 fputs ("] RF\n", x->file);
1423 if (freaderror (file) != 0)
1424 error (errno, 0, "closing Postscript encoding \"%s\"", font->encoding_fn);
1427 /* PostScript driver class. */
1428 const struct outp_class postscript_class =
1445 ps_chart_initialise,