1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2007 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/>. */
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>
49 #define _(msgid) gettext (msgid)
51 /* PostScript driver options: (defaults listed first)
55 paper-size=letter (see "papersize" file)
56 orientation=portrait|landscape
65 emph-font=Times-Italic
74 /* The number of `psus' (PostScript driver UnitS) per inch. */
77 /* A PostScript font. */
80 struct afm *metrics; /* Metrics. */
81 char *embed_fn; /* Name of file to embed. */
82 char *encoding_fn; /* Name of file with encoding. */
85 /* PostScript output driver extension record. */
88 char *file_name; /* Output file name. */
89 FILE *file; /* Output file. */
91 bool draw_headers; /* Draw headers at top of page? */
92 int page_number; /* Current page number. */
94 bool portrait; /* Portrait mode? */
95 int paper_width; /* Width of paper before dropping margins. */
96 int paper_length; /* Length of paper before dropping margins. */
97 int left_margin; /* Left margin in psus. */
98 int right_margin; /* Right margin in psus. */
99 int top_margin; /* Top margin in psus. */
100 int bottom_margin; /* Bottom margin in psus. */
102 int line_gutter; /* Space around lines. */
103 int line_space; /* Space between lines. */
104 int line_width; /* Width of lines. */
106 struct font *fonts[OUTP_FONT_CNT];
107 int last_font; /* Index of last font set with setfont. */
110 /* Transform logical y-ordinate Y into a page ordinate. */
111 #define YT(Y) (this->length - (Y))
113 static bool handle_option (struct outp_driver *this, const char *key,
114 const struct string *val);
115 static void draw_headers (struct outp_driver *this);
117 static void write_ps_prologue (struct outp_driver *);
119 static char *quote_ps_name (const char *string);
121 static struct font *load_font (const char *string);
122 static void free_font (struct font *);
123 static void setup_font (struct outp_driver *this, struct font *, int index);
125 /* Driver initialization. */
128 ps_open_driver (struct outp_driver *this, struct substring options)
130 struct ps_driver_ext *x;
133 this->width = this->length = 0;
134 this->font_height = PSUS * 10 / 72;
136 this->ext = x = xmalloc (sizeof *x);
137 x->file_name = xstrdup ("pspp.ps");
139 x->draw_headers = true;
142 outp_get_paper_size ("", &x->paper_width, &x->paper_length);
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;
244 ok = fn_close (x->file_name, x->file) == 0;
246 error (0, errno, _("closing PostScript output file \"%s\""),
251 for (i = 0; i < OUTP_FONT_CNT; i++)
252 free_font (x->fonts[i]);
258 /* Generic option types. */
272 /* All the options that the PostScript driver supports. */
273 static const struct outp_option option_tab[] =
275 {"output-file", output_file_arg,0},
276 {"paper-size", paper_size_arg, 0},
277 {"orientation", orientation_arg,0},
279 {"headers", boolean_arg, 1},
281 {"prop-font", string_arg, OUTP_PROPORTIONAL},
282 {"emph-font", string_arg, OUTP_EMPHASIS},
283 {"fixed-font", string_arg, OUTP_FIXED},
285 {"left-margin", pos_int_arg, 0},
286 {"right-margin", pos_int_arg, 1},
287 {"top-margin", pos_int_arg, 2},
288 {"bottom-margin", pos_int_arg, 3},
289 {"font-size", pos_int_arg, 4},
291 {"line-width", dimension_arg, 0},
292 {"line-gutter", dimension_arg, 1},
293 {"line-width", dimension_arg, 2},
298 handle_option (struct outp_driver *this, const char *key,
299 const struct string *val)
301 struct ps_driver_ext *x = this->ext;
303 char *value = ds_cstr (val);
305 switch (outp_match_keyword (key, option_tab, &subcat))
309 _("unknown configuration parameter `%s' for PostScript device "
312 case output_file_arg:
314 x->file_name = xstrdup (value);
317 outp_get_paper_size (value, &this->width, &this->length);
319 case orientation_arg:
320 if (!strcmp (value, "portrait"))
322 else if (!strcmp (value, "landscape"))
325 error (0, 0, _("unknown orientation `%s' (valid orientations are "
326 "`portrait' and `landscape')"), value);
329 if (!strcmp (value, "on") || !strcmp (value, "true")
330 || !strcmp (value, "yes") || atoi (value))
331 x->draw_headers = true;
332 else if (!strcmp (value, "off") || !strcmp (value, "false")
333 || !strcmp (value, "no") || !strcmp (value, "0"))
334 x->draw_headers = false;
337 error (0, 0, _("boolean value expected for %s"), key);
347 arg = strtol (value, &tail, 0);
348 if (arg < 1 || errno == ERANGE || *tail)
350 error (0, 0, _("positive integer value required for `%s'"), key);
353 if ((subcat == 4 || subcat == 5) && arg < 1000)
355 error (0, 0, _("default font size must be at least 1 point (value "
356 "of 1000 for key `%s')"), key);
362 x->left_margin = arg;
365 x->right_margin = arg;
371 x->bottom_margin = arg;
374 this->font_height = arg;
383 int dimension = outp_evaluate_dimension (value);
390 x->line_width = dimension;
393 x->line_gutter = dimension;
396 x->line_width = dimension;
405 struct font *font = load_font (value);
408 struct font **dst = &x->fonts[subcat];
422 /* Looks for a PostScript font file or config file in all the
423 appropriate places. Returns the file name on success, NULL on
426 find_ps_file (const char *name)
428 if (fn_is_absolute (name))
429 return xstrdup (name);
432 char *base_name = xasprintf ("psfonts/%s", name);
433 char *file_name = fn_search_path (base_name, config_path);
439 /* Basic file operations. */
441 /* Writes the PostScript prologue to file F. */
443 write_ps_prologue (struct outp_driver *this)
445 struct ps_driver_ext *x = this->ext;
446 size_t embedded_cnt, preloaded_cnt;
449 fputs ("%!PS-Adobe-3.0\n", x->file);
450 fputs ("%%Pages: (atend)\n", x->file);
452 embedded_cnt = preloaded_cnt = 0;
453 for (i = 0; i < OUTP_FONT_CNT; i++)
455 bool embed = x->fonts[i]->embed_fn != NULL;
456 embedded_cnt += embed;
457 preloaded_cnt += !embed;
459 if (preloaded_cnt > 0)
461 fputs ("%%DocumentNeededResources: font", x->file);
462 for (i = 0; i < OUTP_FONT_CNT; i++)
464 struct font *f = x->fonts[i];
465 if (f->embed_fn == NULL)
466 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
468 fputs ("\n", x->file);
470 if (embedded_cnt > 0)
472 fputs ("%%DocumentSuppliedResources: font", x->file);
473 for (i = 0; i < OUTP_FONT_CNT; i++)
475 struct font *f = x->fonts[i];
476 if (f->embed_fn != NULL)
477 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
479 fputs ("\n", x->file);
481 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
482 fprintf (x->file, "%%%%Creator: %s\n", version);
483 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
484 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
485 fprintf (x->file, "%%%%Orientation: %s\n",
486 x->portrait ? "Portrait" : "Landscape");
487 fputs ("%%EndComments\n", x->file);
488 fputs ("%%BeginDefaults\n", x->file);
489 fputs ("%%PageResources: font", x->file);
490 for (i = 0; i < OUTP_FONT_CNT; i++)
491 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
492 fputs ("\n", x->file);
493 fputs ("%%EndDefaults\n", x->file);
494 fputs ("%%BeginProlog\n", x->file);
495 fputs ("/ED{exch def}bind def\n", x->file);
496 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
497 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
498 fputs ("/S{show}bind def\n", x->file);
499 fputs ("/GS{glyphshow}def\n", x->file);
500 fputs ("/RF{\n", x->file);
501 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
502 fputs (" {\n", x->file);
503 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
504 fputs (" }forall\n", x->file);
505 fputs (" /Encoding ED\n", x->file);
506 fputs (" currentdict end\n", x->file);
507 fputs ("}bind def\n", x->file);
508 fputs ("/F{setfont}bind def\n", x->file);
509 fputs ("/EP{\n", x->file);
510 fputs (" pg restore\n", x->file);
511 fputs (" showpage\n", x->file);
512 fputs ("}bind def\n", x->file);
513 fputs ("/GB{\n", x->file);
514 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
515 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
517 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
518 fputs ("}bind def\n", x->file);
519 fputs ("/K{0 rmoveto}bind def\n", x->file);
520 fputs ("%%EndProlog\n", x->file);
521 fputs ("%%BeginSetup\n", x->file);
522 for (i = 0; i < OUTP_FONT_CNT; i++)
523 setup_font (this, x->fonts[i], i);
524 fputs ("%%EndSetup\n", x->file);
527 /* Returns STRING as a Postscript name, which is just '/'
528 followed by STRING unless characters need to be quoted.
529 The caller must free the string. */
531 quote_ps_name (const char *string)
535 for (cp = string; *cp != '\0'; cp++)
537 unsigned char c = *cp;
538 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
539 && (cp == string || !isdigit (c)))
541 struct string out = DS_EMPTY_INITIALIZER;
542 ds_put_char (&out, '<');
543 for (cp = string; *cp != '\0'; cp++)
546 ds_put_format (&out, "%02x", c);
548 ds_put_cstr (&out, ">cvn");
549 return ds_cstr (&out);
552 return xasprintf ("/%s", string);
556 ps_open_page (struct outp_driver *this)
558 struct ps_driver_ext *x = this->ext;
560 /* Assure page independence. */
567 "%%%%BeginPageSetup\n"
568 "/pg save def 0.001 dup scale\n",
569 x->page_number, x->page_number);
573 "%d 0 translate 90 rotate\n",
576 if (x->bottom_margin != 0 || x->left_margin != 0)
579 x->left_margin, x->bottom_margin);
582 "/LW %d def %d setlinewidth\n"
583 "%%%%EndPageSetup\n",
584 x->line_width, x->line_width);
591 ps_close_page (struct outp_driver *this)
593 struct ps_driver_ext *x = this->ext;
594 fputs ("%%PageTrailer\n"
600 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
611 /* Draws a line from (x0,y0) to (x1,y1). */
613 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
615 struct ps_driver_ext *ext = this->ext;
616 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
619 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
620 shortening it to X0...X1 if SHORTEN is true.
621 Draws a horizontal line X1...X3 at Y if RIGHT says so,
622 shortening it to X2...X3 if SHORTEN is true. */
624 horz_line (struct outp_driver *this,
625 int x0, int x1, int x2, int x3, int y,
626 enum outp_line_style left, enum outp_line_style right,
629 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
630 dump_line (this, x0, y, x3, y);
633 if (left != OUTP_L_NONE)
634 dump_line (this, x0, y, shorten ? x1 : x2, y);
635 if (right != OUTP_L_NONE)
636 dump_line (this, shorten ? x2 : x1, y, x3, y);
640 /* Draws a vertical line Y0...Y2 at X if TOP says so,
641 shortening it to Y0...Y1 if SHORTEN is true.
642 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
643 shortening it to Y2...Y3 if SHORTEN is true. */
645 vert_line (struct outp_driver *this,
646 int y0, int y1, int y2, int y3, int x,
647 enum outp_line_style top, enum outp_line_style bottom,
650 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
651 dump_line (this, x, y0, x, y3);
654 if (top != OUTP_L_NONE)
655 dump_line (this, x, y0, x, shorten ? y1 : y2);
656 if (bottom != OUTP_L_NONE)
657 dump_line (this, x, shorten ? y2 : y1, x, y3);
661 /* Draws a generalized intersection of lines in the rectangle
662 (X0,Y0)-(X3,Y3). The line coming from the top to the center
663 is of style TOP, from left to center of style LEFT, from
664 bottom to center of style BOTTOM, and from right to center of
667 ps_line (struct outp_driver *this,
668 int x0, int y0, int x3, int y3,
669 enum outp_line_style top, enum outp_line_style left,
670 enum outp_line_style bottom, enum outp_line_style right)
672 /* The algorithm here is somewhat subtle, to allow it to handle
673 all the kinds of intersections that we need.
675 Three additional ordinates are assigned along the x axis. The
676 first is xc, midway between x0 and x3. The others are x1 and
677 x2; for a single vertical line these are equal to xc, and for
678 a double vertical line they are the ordinates of the left and
679 right half of the double line.
681 yc, y1, and y2 are assigned similarly along the y axis.
683 The following diagram shows the coordinate system and output
684 for double top and bottom lines, single left line, and no
688 y0 ________________________
694 y1 = y2 = yc |######### # |
699 y3 |________#_____#_______|
701 struct ps_driver_ext *ext = this->ext;
703 /* Offset from center of each line in a pair of double lines. */
704 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
706 /* Are the lines along each axis single or double?
707 (It doesn't make sense to have different kinds of line on the
708 same axis, so we don't try to gracefully handle that case.) */
709 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
710 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
712 /* When horizontal lines are doubled,
713 the left-side line along y1 normally runs from x0 to x2,
714 and the right-side line along y1 from x3 to x1.
715 If the top-side line is also doubled, we shorten the y1 lines,
716 so that the left-side line runs only to x1,
717 and the right-side line only to x2.
718 Otherwise, the horizontal line at y = y1 below would cut off
719 the intersection, which looks ugly:
721 y0 ________________________
726 y1 |######### ########|
729 y2 |######################|
732 y3 |______________________|
733 It is more of a judgment call when the horizontal line is
734 single. We actually choose to cut off the line anyhow, as
735 shown in the first diagram above.
737 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
738 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
739 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
740 int horz_line_ofs = double_vert ? double_line_ofs : 0;
741 int xc = (x0 + x3) / 2;
742 int x1 = xc - horz_line_ofs;
743 int x2 = xc + horz_line_ofs;
745 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
746 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
747 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
748 int vert_line_ofs = double_horz ? double_line_ofs : 0;
749 int yc = (y0 + y3) / 2;
750 int y1 = yc - vert_line_ofs;
751 int y2 = yc + vert_line_ofs;
754 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
757 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
758 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
762 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
765 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
766 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
770 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
771 and with the given JUSTIFICATION for THIS driver. */
773 draw_text (struct outp_driver *this,
774 const char *string, int x, int y, int max_width,
775 enum outp_justification justification)
777 struct outp_text text;
780 text.font = OUTP_PROPORTIONAL;
781 text.justification = justification;
782 text.string = ss_cstr (string);
784 text.v = this->font_height;
787 this->class->text_metrics (this, &text, &width, NULL);
788 this->class->text_draw (this, &text);
792 /* Writes LEFT left-justified and RIGHT right-justified within
793 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
795 draw_header_line (struct outp_driver *this,
796 const char *left, const char *right,
797 int x0, int x1, int y)
801 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
802 + this->prop_em_width);
804 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
807 /* Draw top of page headers for THIS driver. */
809 draw_headers (struct outp_driver *this)
811 struct ps_driver_ext *ext = this->ext;
816 y = -3 * this->font_height;
817 x0 = this->prop_em_width;
818 x1 = this->width - this->prop_em_width;
821 fprintf (ext->file, "%d %d %d %d GB\n",
823 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
824 y += ext->line_width + ext->line_gutter;
826 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
827 r2 = xasprintf ("%s - %s", version, host_system);
829 draw_header_line (this, outp_title, r1, x0, x1, y);
830 y += this->font_height;
832 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
838 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
840 The characters are justified according to JUSTIFICATION in a
841 field that has WIDTH_LEFT space remaining after the characters
842 themselves are accounted for.
843 Before character I is written, its x-position is adjusted by
846 write_text (struct outp_driver *this,
849 enum outp_justification justification,
850 const struct afm_character **chars, int *kerns, size_t char_cnt,
853 struct ps_driver_ext *ext = this->ext;
854 struct afm *afm = ext->fonts[font]->metrics;
858 if (justification == OUTP_RIGHT)
860 else if (justification == OUTP_CENTER)
861 x0 += width_left / 2;
862 y0 += afm_get_ascent (afm) * this->font_height / 1000;
864 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
866 if (ext->last_font != font)
868 ext->last_font = font;
869 fprintf (ext->file, "F%d setfont\n", font);
872 ds_init_empty (&out);
873 for (i = 0; i < char_cnt; i = j)
875 for (j = i + 1; j < char_cnt; j++)
880 fprintf (ext->file, "%d K", kerns[i]);
883 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
886 fprintf (ext->file, "%sS\n", ds_cstr (&out));
893 fprintf (ext->file, "/%s GS\n", chars[i]->name);
901 /* State of a text formatting operation. */
905 const struct outp_text *text;
909 const struct afm_character **glyphs;
913 size_t glyph_cnt; /* Number of glyphs output. */
914 int width_left; /* Width left over. */
915 int height_left; /* Height left over. */
917 /* State as of last space. */
918 const char *space_char; /* Just past last space. */
919 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
920 int space_width_left; /* Width left over as of last space. */
923 int max_width; /* Widest line so far. */
926 /* Adjusts S to complete a line of text,
927 and draws the current line if appropriate. */
929 finish_line (struct outp_driver *this, struct text_state *s)
936 s->text->x, s->text->y + (s->text->v - s->height_left),
938 s->text->justification,
939 s->glyphs, s->glyph_kerns, s->glyph_cnt,
944 /* Update maximum width. */
945 width = s->text->h - s->width_left;
946 if (width > s->max_width)
947 s->max_width = width;
949 /* Move to next line. */
950 s->width_left = s->text->h;
951 s->height_left -= this->font_height;
953 /* No spaces on this line yet. */
954 s->space_char = NULL;
957 /* Format TEXT on THIS driver.
958 If DRAW is nonzero, draw the text.
959 The width of the widest line is stored into *WIDTH, if WIDTH
961 The total height of the text written is stored into *HEIGHT,
962 if HEIGHT is nonnull. */
964 text (struct outp_driver *this, const struct outp_text *text, bool draw,
965 int *width, int *height)
967 struct ps_driver_ext *ext = this->ext;
968 struct afm *afm = ext->fonts[text->font]->metrics;
977 s.glyph_kerns = NULL;
981 s.width_left = s.text->h;
982 s.height_left = s.text->v;
988 cp = ss_data (s.text->string);
989 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
991 const struct afm_character *cur;
997 finish_line (this, &s);
1002 /* Get character and resolve ligatures. */
1003 cur = afm_get_character (afm, *cp);
1004 while (++cp < ss_end (s.text->string))
1006 const struct afm_character *next = afm_get_character (afm, *cp);
1007 const struct afm_character *ligature = afm_get_ligature (cur, next);
1008 if (ligature == NULL)
1012 char_width = cur->width * this->font_height / 1000;
1014 /* Get kern adjustment. */
1015 if (s.glyph_cnt > 0)
1016 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1017 * this->font_height / 1000);
1021 /* Record the current status if this is a space character. */
1022 if (cur->code == ' ' && cp > ss_data (s.text->string))
1025 s.space_glyph_cnt = s.glyph_cnt;
1026 s.space_width_left = s.width_left;
1029 /* Enough room on this line? */
1030 if (char_width + kern_adjust > s.width_left)
1032 if (s.space_char == NULL)
1034 finish_line (this, &s);
1040 s.glyph_cnt = s.space_glyph_cnt;
1041 s.width_left = s.space_width_left;
1042 finish_line (this, &s);
1047 if (s.glyph_cnt >= glyph_cap)
1049 glyph_cap = 2 * (glyph_cap + 8);
1050 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1051 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1052 glyph_cap, sizeof *s.glyph_kerns);
1054 s.glyphs[s.glyph_cnt] = cur;
1055 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1058 s.width_left -= char_width + kern_adjust;
1060 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1061 finish_line (this, &s);
1064 *width = s.max_width;
1066 *height = text->v - s.height_left;
1068 free (s.glyph_kerns);
1072 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1073 int *width, int *height)
1075 text (this, t, false, width, height);
1079 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1081 assert (this->page_open);
1082 text (this, t, true, NULL, NULL);
1086 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
1091 struct ps_driver_ext *x = this->ext;
1092 char page_size[128];
1094 int x_origin, y_origin;
1096 ch->file = tmpfile ();
1097 if (ch->file == NULL)
1103 size = this->width < this->length ? this->width : this->length;
1104 x_origin = x->left_margin + (size - this->width) / 2;
1105 y_origin = x->bottom_margin + (size - this->length) / 2;
1107 snprintf (page_size, sizeof page_size,
1108 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
1109 (double) size / PSUS, (double) size / PSUS,
1110 (double) x_origin / PSUS, (double) y_origin / PSUS);
1112 ch->pl_params = pl_newplparams ();
1113 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
1114 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
1119 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
1122 struct ps_driver_ext *x = this->ext;
1124 static int doc_num = 0;
1126 outp_eject_page (this);
1129 "%d %d translate 1000 dup scale\n"
1131 "/showpage { } def\n"
1132 "0 setgray 0 setlinecap 1 setlinewidth\n"
1133 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
1134 "%%%%BeginDocument: %d\n",
1135 -x->left_margin, -x->bottom_margin,
1139 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
1143 fputs ("%%EndDocument\n"
1147 outp_close_page (this);
1151 static void embed_font (struct outp_driver *this, struct font *font);
1152 static void reencode_font (struct outp_driver *this, struct font *font);
1154 /* Loads and returns the font for STRING, which has the format
1155 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1156 PFA or PFB file's name, and ENC is the encoding file's name.
1157 PFA and ENC are optional.
1158 Returns a null pointer if unsuccessful. */
1159 static struct font *
1160 load_font (const char *string_)
1162 char *string = xstrdup (string_);
1164 char *position = string;
1166 char *afm_file_name;
1168 font = xmalloc (sizeof *font);
1169 font->metrics = NULL;
1170 font->embed_fn = NULL;
1171 font->encoding_fn = NULL;
1173 token = strsep (&position, ",");
1176 error (0, 0, _("\"%s\": bad font specification"), string);
1180 /* Read AFM file. */
1181 afm_file_name = find_ps_file (token);
1182 if (afm_file_name == NULL)
1184 error (0, 0, _("could not find AFM file \"%s\""), token);
1187 font->metrics = afm_open (afm_file_name);
1188 free (afm_file_name);
1189 if (font->metrics == NULL)
1192 /* Find font file to embed. */
1193 token = strsep (&position, ",");
1194 if (token != NULL && *token != '\0')
1196 font->embed_fn = find_ps_file (token);
1197 if (font->embed_fn == NULL)
1198 error (0, 0, _("could not find font \"%s\""), token);
1201 /* Find encoding. */
1202 token = strsep (&position, ",");
1203 if (token != NULL && *token == '\0')
1205 font->encoding_fn = find_ps_file (token);
1206 if (font->encoding_fn == NULL)
1207 error (0, 0, _("could not find encoding \"%s\""), token);
1221 free_font (struct font *font)
1225 afm_close (font->metrics);
1226 free (font->embed_fn);
1227 free (font->encoding_fn);
1232 /* Emits PostScript code to embed FONT (if necessary), scale it
1233 to the proper size, re-encode it (if necessary), and store the
1234 resulting font as an object named F#, where INDEX is
1235 substituted for #. */
1237 setup_font (struct outp_driver *this, struct font *font, int index)
1239 struct ps_driver_ext *x = this->ext;
1242 if (font->embed_fn != NULL)
1243 embed_font (this, font);
1245 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1246 afm_get_findfont_name (font->metrics));
1248 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1249 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1252 if (font->encoding_fn != NULL)
1253 reencode_font (this, font);
1255 fprintf (x->file, "/F%d ED\n", index);
1258 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1259 end-of-file or on error. */
1261 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1263 while (copy_bytes > 0)
1265 char buffer[BUFSIZ];
1266 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1267 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1268 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1269 if (write_bytes != chunk_bytes)
1271 copy_bytes -= chunk_bytes;
1275 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1276 end-of-file or on error. The bytes are translated into
1277 hexadecimal during copying and broken into lines with
1278 new-line characters. */
1280 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1284 for (i = 0; i < copy_bytes; i++)
1289 if (i > 0 && i % 36 == 0)
1291 fprintf (dst, "%02X", c);
1296 /* Embeds the given FONT into THIS driver's output stream. */
1298 embed_font (struct outp_driver *this, struct font *font)
1300 struct ps_driver_ext *x = this->ext;
1304 file = fopen (font->embed_fn, "rb");
1307 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1311 fprintf (x->file, "%%%%BeginResource: font %s\n",
1312 afm_get_findfont_name (font->metrics));
1318 /* PFA file. Copy literally. */
1319 copy_bytes_literally (file, x->file, ULONG_MAX);
1323 /* PFB file. Translate as specified in Adobe Technical
1325 while ((c = getc (file)) == 128)
1328 unsigned long length;
1334 length = getc (file);
1335 length |= (unsigned long) getc (file) << 8;
1336 length |= (unsigned long) getc (file) << 16;
1337 length |= (unsigned long) getc (file) << 24;
1340 copy_bytes_literally (file, x->file, length);
1342 copy_bytes_as_hex (file, x->file, length);
1347 if (freaderror (file))
1348 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1349 fputs ("%%EndResource\n", x->file);
1352 /* Re-encodes FONT according to the specified encoding. */
1354 reencode_font (struct outp_driver *this, struct font *font)
1356 struct ps_driver_ext *x = this->ext;
1367 file = fopen (font->encoding_fn, "r");
1370 error (errno, 0, _("cannot open font encoding file \"%s\""),
1375 for (i = 0; i < 256; i++)
1380 ds_init_empty (&line);
1381 while (ds_read_config_line (&line, &line_number, file))
1383 char *pschar, *code;
1384 char *save_ptr, *tail;
1387 if (ds_is_empty (&line) == 0)
1390 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1391 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1392 if (pschar == NULL || code == NULL)
1395 code_val = strtol (code, &tail, 0);
1398 error_at_line (0, 0, font->encoding_fn, line_number,
1399 _("invalid numeric format"));
1402 if (code_val < 0 || code_val > 255)
1404 if (tab[code_val] != 0)
1405 free (tab[code_val]);
1406 tab[code_val] = xstrdup (pschar);
1410 fputs ("[", x->file);
1411 for (i = 0; i < 256; i++)
1413 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1414 fprintf (x->file, "%s\n", name);
1418 fputs ("] RF\n", x->file);
1420 if (freaderror (file) != 0)
1421 error (errno, 0, _("closing Postscript encoding \"%s\""),
1425 /* PostScript driver class. */
1426 const struct outp_class postscript_class =
1444 ps_chart_initialise,