1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29 #include <libpspp/alloc.h>
30 #include <libpspp/bit-vector.h>
31 #include <libpspp/compiler.h>
32 #include <libpspp/freaderror.h>
33 #include <libpspp/hash.h>
34 #include <libpspp/message.h>
35 #include <libpspp/misc.h>
36 #include <libpspp/start-date.h>
37 #include <libpspp/version.h>
39 #include <data/filename.h>
53 #define _(msgid) gettext (msgid)
55 /* PostScript driver options: (defaults listed first)
59 paper-size=letter (see "papersize" file)
60 orientation=portrait|landscape
69 emph-font=Times-Italic
78 /* The number of `psus' (PostScript driver UnitS) per inch. */
81 /* A PostScript font. */
84 struct afm *metrics; /* Metrics. */
85 char *embed_fn; /* Name of file to embed. */
86 char *encoding_fn; /* Name of file with encoding. */
89 /* PostScript output driver extension record. */
92 char *file_name; /* Output file name. */
93 FILE *file; /* Output file. */
95 bool draw_headers; /* Draw headers at top of page? */
96 int page_number; /* Current page number. */
98 bool portrait; /* Portrait mode? */
99 int paper_width; /* Width of paper before dropping margins. */
100 int paper_length; /* Length of paper before dropping margins. */
101 int left_margin; /* Left margin in psus. */
102 int right_margin; /* Right margin in psus. */
103 int top_margin; /* Top margin in psus. */
104 int bottom_margin; /* Bottom margin in psus. */
106 int line_gutter; /* Space around lines. */
107 int line_space; /* Space between lines. */
108 int line_width; /* Width of lines. */
110 struct font *fonts[OUTP_FONT_CNT];
111 int last_font; /* Index of last font set with setfont. */
115 /* Transform logical y-ordinate Y into a page ordinate. */
116 #define YT(Y) (this->length - (Y))
118 static bool handle_option (struct outp_driver *this, const char *key,
119 const struct string *val);
120 static void draw_headers (struct outp_driver *this);
122 static void write_ps_prologue (struct outp_driver *);
124 static char *quote_ps_name (const char *string);
126 static struct font *load_font (const char *string);
127 static void free_font (struct font *);
128 static void setup_font (struct outp_driver *this, struct font *, int index);
130 /* Driver initialization. */
133 ps_open_driver (struct outp_driver *this, const char *options)
135 struct ps_driver_ext *x;
138 this->width = this->length = 0;
139 this->font_height = PSUS * 10 / 72;
141 this->ext = x = xmalloc (sizeof *x);
142 x->file_name = xstrdup ("pspp.ps");
144 x->draw_headers = true;
147 x->paper_width = PSUS * 17 / 2;
148 x->paper_length = PSUS * 11;
149 x->left_margin = PSUS / 2;
150 x->right_margin = PSUS / 2;
151 x->top_margin = PSUS / 2;
152 x->bottom_margin = PSUS / 2;
153 x->line_gutter = PSUS / 72;
154 x->line_space = PSUS / 72;
155 x->line_width = PSUS / 144;
156 for (i = 0; i < OUTP_FONT_CNT; i++)
159 outp_parse_options (options, handle_option, this);
161 x->file = fn_open (x->file_name, "w");
164 error (0, errno, _("opening PostScript output file \"%s\""),
171 this->width = x->paper_width;
172 this->length = x->paper_length;
176 this->width = x->paper_length;
177 this->length = x->paper_width;
179 this->width -= x->left_margin + x->right_margin;
180 this->length -= x->top_margin + x->bottom_margin;
183 int header_length = 3 * this->font_height;
184 this->length -= header_length;
185 x->top_margin += header_length;
188 for (i = 0; i < OUTP_FONT_CNT; i++)
189 if (x->fonts[i] == NULL)
191 const char *default_fonts[OUTP_FONT_CNT];
192 default_fonts[OUTP_FIXED] = "Courier.afm";
193 default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
194 default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
195 x->fonts[i] = load_font (default_fonts[i]);
196 if (x->fonts[i] == NULL)
200 if (this->length / this->font_height < 15)
202 error (0, 0, _("The defined PostScript page is not long "
203 "enough to hold margins and headers, plus least 15 "
204 "lines of the default fonts. In fact, there's only "
205 "room for %d lines of each font at the default size "
206 "of %d.%03d points."),
207 this->length / this->font_height,
208 this->font_height / 1000, this->font_height % 1000);
213 afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
214 * this->font_height / 1000;
215 this->prop_em_width =
216 afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
217 * this->font_height / 1000;
219 this->horiz_line_width[OUTP_L_NONE] = 0;
220 this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
221 this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
222 + 2 * x->line_width);
223 memcpy (this->vert_line_width, this->horiz_line_width,
224 sizeof this->vert_line_width);
226 write_ps_prologue (this);
231 this->class->close_driver (this);
236 ps_close_driver (struct outp_driver *this)
238 struct ps_driver_ext *x = this->ext;
248 ok = fn_close (x->file_name, x->file) == 0;
250 error (0, errno, _("closing PostScript output file \"%s\""), x->file_name);
252 for (i = 0; i < OUTP_FONT_CNT; i++)
253 free_font (x->fonts[i]);
259 /* Generic option types. */
273 /* All the options that the PostScript driver supports. */
274 static struct outp_option option_tab[] =
276 {"output-file", output_file_arg,0},
277 {"paper-size", paper_size_arg, 0},
278 {"orientation", orientation_arg,0},
280 {"headers", boolean_arg, 1},
282 {"prop-font", string_arg, OUTP_PROPORTIONAL},
283 {"emph-font", string_arg, OUTP_EMPHASIS},
284 {"fixed-font", string_arg, OUTP_FIXED},
286 {"left-margin", pos_int_arg, 0},
287 {"right-margin", pos_int_arg, 1},
288 {"top-margin", pos_int_arg, 2},
289 {"bottom-margin", pos_int_arg, 3},
290 {"font-size", pos_int_arg, 4},
292 {"line-width", dimension_arg, 0},
293 {"line-gutter", dimension_arg, 1},
294 {"line-width", dimension_arg, 2},
299 handle_option (struct outp_driver *this, const char *key,
300 const struct string *val)
302 struct ps_driver_ext *x = this->ext;
304 char *value = ds_c_str (val);
306 switch (outp_match_keyword (key, option_tab, &subcat))
310 _("unknown configuration parameter `%s' for PostScript device "
313 case output_file_arg:
315 x->file_name = xstrdup (value);
318 outp_get_paper_size (value, &this->width, &this->length);
320 case orientation_arg:
321 if (!strcmp (value, "portrait"))
323 else if (!strcmp (value, "landscape"))
326 error (0, 0, _("unknown orientation `%s' (valid orientations are "
327 "`portrait' and `landscape')"), value);
330 if (!strcmp (value, "on") || !strcmp (value, "true")
331 || !strcmp (value, "yes") || atoi (value))
332 x->draw_headers = true;
333 else if (!strcmp (value, "off") || !strcmp (value, "false")
334 || !strcmp (value, "no") || !strcmp (value, "0"))
335 x->draw_headers = false;
338 error (0, 0, _("boolean value expected for %s"), key);
348 arg = strtol (value, &tail, 0);
349 if (arg < 1 || errno == ERANGE || *tail)
351 error (0, 0, _("positive integer value required for `%s'"), key);
354 if ((subcat == 4 || subcat == 5) && arg < 1000)
356 error (0, 0, _("default font size must be at least 1 point (value "
357 "of 1000 for key `%s')"), key);
363 x->left_margin = arg;
366 x->right_margin = arg;
372 x->bottom_margin = arg;
375 this->font_height = arg;
384 int dimension = outp_evaluate_dimension (value, NULL);
388 error (0, 0, _("value for `%s' must be a dimension of positive "
389 "length (i.e., `1in')"), key);
395 x->line_width = dimension;
398 x->line_gutter = dimension;
401 x->line_width = dimension;
410 struct font *font = load_font (value);
413 struct font **dst = &x->fonts[subcat];
427 /* Looks for a PostScript font file or config file in all the
428 appropriate places. Returns the filename on success, NULL on
431 find_ps_file (const char *name)
433 if (fn_absolute_p (name))
434 return xstrdup (name);
437 char *base_name = xasprintf ("psfonts%c%s", DIR_SEPARATOR, name);
438 char *file_name = fn_search_path (base_name, config_path, NULL);
444 /* Basic file operations. */
446 /* Writes the PostScript prologue to file F. */
448 write_ps_prologue (struct outp_driver *this)
450 struct ps_driver_ext *x = this->ext;
451 size_t embedded_cnt, preloaded_cnt;
454 fputs ("%!PS-Adobe-3.0\n", x->file);
455 fputs ("%%Pages: (atend)\n", x->file);
457 embedded_cnt = preloaded_cnt = 0;
458 for (i = 0; i < OUTP_FONT_CNT; i++)
460 bool embed = x->fonts[i]->embed_fn != NULL;
461 embedded_cnt += embed;
462 preloaded_cnt += !embed;
464 if (preloaded_cnt > 0)
466 fputs ("%%DocumentNeededResources: font", x->file);
467 for (i = 0; i < OUTP_FONT_CNT; i++)
469 struct font *f = x->fonts[i];
470 if (f->embed_fn == NULL)
471 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
473 fputs ("\n", x->file);
475 if (embedded_cnt > 0)
477 fputs ("%%DocumentSuppliedResources: font", x->file);
478 for (i = 0; i < OUTP_FONT_CNT; i++)
480 struct font *f = x->fonts[i];
481 if (f->embed_fn != NULL)
482 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
484 fputs ("\n", x->file);
486 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
487 fprintf (x->file, "%%%%Creator: %s\n", version);
488 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
489 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
490 fprintf (x->file, "%%%%Orientation: %s\n",
491 x->portrait ? "Portrait" : "Landscape");
492 fputs ("%%EndComments\n", x->file);
493 fputs ("%%BeginDefaults\n", x->file);
494 fputs ("%%PageResources: font", x->file);
495 for (i = 0; i < OUTP_FONT_CNT; i++)
496 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
497 fputs ("\n", x->file);
498 fputs ("%%EndDefaults\n", x->file);
499 fputs ("%%BeginProlog\n", x->file);
500 fputs ("/ED{exch def}bind def\n", x->file);
501 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
502 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
503 fputs ("/S{show}bind def\n", x->file);
504 fputs ("/GS{glyphshow}def\n", x->file);
505 fputs ("/RF{\n", x->file);
506 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
507 fputs (" {\n", x->file);
508 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
509 fputs (" }forall\n", x->file);
510 fputs (" /Encoding ED\n", x->file);
511 fputs (" currentdict end\n", x->file);
512 fputs ("}bind def\n", x->file);
513 fputs ("/F{setfont}bind def\n", x->file);
514 fputs ("/EP{\n", x->file);
515 fputs (" pg restore\n", x->file);
516 fputs (" showpage\n", x->file);
517 fputs ("}bind def\n", x->file);
518 fputs ("/GB{\n", x->file);
519 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
520 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
522 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
523 fputs ("}bind def\n", x->file);
524 fputs ("/K{0 rmoveto}bind def\n", x->file);
525 fputs ("%%EndProlog\n", x->file);
526 fputs ("%%BeginSetup\n", x->file);
527 for (i = 0; i < OUTP_FONT_CNT; i++)
528 setup_font (this, x->fonts[i], i);
529 fputs ("%%EndSetup\n", x->file);
532 /* Returns STRING as a Postscript name, which is just '/'
533 followed by STRING unless characters need to be quoted.
534 The caller must free the string. */
536 quote_ps_name (const char *string)
540 for (cp = string; *cp != '\0'; cp++)
542 unsigned char c = *cp;
543 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
544 && (cp == string || !isdigit (c)))
546 struct string out = DS_INITIALIZER;
548 for (cp = string; *cp != '\0'; cp++)
551 ds_printf (&out, "%02x", c);
553 ds_puts (&out, ">cvn");
554 return ds_c_str (&out);
557 return xasprintf ("/%s", string);
561 ps_open_page (struct outp_driver *this)
563 struct ps_driver_ext *x = this->ext;
565 /* Assure page independence. */
572 "%%%%BeginPageSetup\n"
573 "/pg save def 0.001 dup scale\n",
574 x->page_number, x->page_number);
578 "%d 0 translate 90 rotate\n",
581 if (x->bottom_margin != 0 || x->left_margin != 0)
584 x->left_margin, x->bottom_margin);
587 "/LW %d def %d setlinewidth\n"
588 "%%%%EndPageSetup\n",
589 x->line_width, x->line_width);
596 ps_close_page (struct outp_driver *this)
598 struct ps_driver_ext *x = this->ext;
599 fputs ("%%PageTrailer\n"
605 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
617 /* Draws a line from (x0,y0) to (x1,y1). */
619 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
621 struct ps_driver_ext *ext = this->ext;
622 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
625 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
626 shortening it to X0...X1 if SHORTEN is true.
627 Draws a horizontal line X1...X3 at Y if RIGHT says so,
628 shortening it to X2...X3 if SHORTEN is true. */
630 horz_line (struct outp_driver *this,
631 int x0, int x1, int x2, int x3, int y,
632 enum outp_line_style left, enum outp_line_style right,
635 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
636 dump_line (this, x0, y, x3, y);
639 if (left != OUTP_L_NONE)
640 dump_line (this, x0, y, shorten ? x1 : x2, y);
641 if (right != OUTP_L_NONE)
642 dump_line (this, shorten ? x2 : x1, y, x3, y);
646 /* Draws a vertical line Y0...Y2 at X if TOP says so,
647 shortening it to Y0...Y1 if SHORTEN is true.
648 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
649 shortening it to Y2...Y3 if SHORTEN is true. */
651 vert_line (struct outp_driver *this,
652 int y0, int y1, int y2, int y3, int x,
653 enum outp_line_style top, enum outp_line_style bottom,
656 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
657 dump_line (this, x, y0, x, y3);
660 if (top != OUTP_L_NONE)
661 dump_line (this, x, y0, x, shorten ? y1 : y2);
662 if (bottom != OUTP_L_NONE)
663 dump_line (this, x, shorten ? y2 : y1, x, y3);
667 /* Draws a generalized intersection of lines in the rectangle
668 (X0,Y0)-(X3,Y3). The line coming from the top to the center
669 is of style TOP, from left to center of style LEFT, from
670 bottom to center of style BOTTOM, and from right to center of
673 ps_line (struct outp_driver *this,
674 int x0, int y0, int x3, int y3,
675 enum outp_line_style top, enum outp_line_style left,
676 enum outp_line_style bottom, enum outp_line_style right)
678 /* The algorithm here is somewhat subtle, to allow it to handle
679 all the kinds of intersections that we need.
681 Three additional ordinates are assigned along the x axis. The
682 first is xc, midway between x0 and x3. The others are x1 and
683 x2; for a single vertical line these are equal to xc, and for
684 a double vertical line they are the ordinates of the left and
685 right half of the double line.
687 yc, y1, and y2 are assigned similarly along the y axis.
689 The following diagram shows the coordinate system and output
690 for double top and bottom lines, single left line, and no
694 y0 ________________________
700 y1 = y2 = yc |######### # |
705 y3 |________#_____#_______|
707 struct ps_driver_ext *ext = this->ext;
709 /* Offset from center of each line in a pair of double lines. */
710 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
712 /* Are the lines along each axis single or double?
713 (It doesn't make sense to have different kinds of line on the
714 same axis, so we don't try to gracefully handle that case.) */
715 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
716 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
718 /* When horizontal lines are doubled,
719 the left-side line along y1 normally runs from x0 to x2,
720 and the right-side line along y1 from x3 to x1.
721 If the top-side line is also doubled, we shorten the y1 lines,
722 so that the left-side line runs only to x1,
723 and the right-side line only to x2.
724 Otherwise, the horizontal line at y = y1 below would cut off
725 the intersection, which looks ugly:
727 y0 ________________________
732 y1 |######### ########|
735 y2 |######################|
738 y3 |______________________|
739 It is more of a judgment call when the horizontal line is
740 single. We actually choose to cut off the line anyhow, as
741 shown in the first diagram above.
743 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
744 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
745 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
746 int horz_line_ofs = double_vert ? double_line_ofs : 0;
747 int xc = (x0 + x3) / 2;
748 int x1 = xc - horz_line_ofs;
749 int x2 = xc + horz_line_ofs;
751 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
752 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
753 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
754 int vert_line_ofs = double_horz ? double_line_ofs : 0;
755 int yc = (y0 + y3) / 2;
756 int y1 = yc - vert_line_ofs;
757 int y2 = yc + vert_line_ofs;
760 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
763 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
764 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
768 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
771 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
772 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
776 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
777 and with the given JUSTIFICATION for THIS driver. */
779 draw_text (struct outp_driver *this,
780 const char *string, int x, int y, int max_width,
781 enum outp_justification justification)
783 struct outp_text text;
786 text.font = OUTP_PROPORTIONAL;
787 text.justification = justification;
788 ls_init (&text.string, (char *) string, strlen (string));
790 text.v = this->font_height;
793 this->class->text_metrics (this, &text, &width, NULL);
794 this->class->text_draw (this, &text);
798 /* Writes LEFT left-justified and RIGHT right-justified within
799 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
801 draw_header_line (struct outp_driver *this,
802 const char *left, const char *right,
803 int x0, int x1, int y)
807 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
808 + this->prop_em_width);
810 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
813 /* Draw top of page headers for THIS driver. */
815 draw_headers (struct outp_driver *this)
817 struct ps_driver_ext *ext = this->ext;
822 y = -3 * this->font_height;
823 x0 = this->prop_em_width;
824 x1 = this->width - this->prop_em_width;
827 fprintf (ext->file, "%d %d %d %d GB\n",
829 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
830 y += ext->line_width + ext->line_gutter;
832 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
833 r2 = xasprintf ("%s - %s", version, host_system);
835 draw_header_line (this, outp_title, r1, x0, x1, y);
836 y += this->font_height;
838 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
844 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
846 The characters are justified according to JUSTIFICATION in a
847 field that has WIDTH_LEFT space remaining after the characters
848 themselves are accounted for.
849 Before character I is written, its x-position is adjusted by
852 write_text (struct outp_driver *this,
855 enum outp_justification justification,
856 const struct afm_character **chars, int *kerns, size_t char_cnt,
859 struct ps_driver_ext *ext = this->ext;
860 struct afm *afm = ext->fonts[font]->metrics;
864 if (justification == OUTP_RIGHT)
866 else if (justification == OUTP_CENTER)
867 x0 += width_left / 2;
868 y0 += afm_get_ascent (afm) * this->font_height / 1000;
870 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
872 if (ext->last_font != font)
874 ext->last_font = font;
875 fprintf (ext->file, "F%d setfont\n", font);
879 for (i = 0; i < char_cnt; i = j)
881 for (j = i + 1; j < char_cnt; j++)
886 fprintf (ext->file, "%d K", kerns[i]);
889 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
892 fprintf (ext->file, "%sS\n", ds_c_str (&out));
899 fprintf (ext->file, "/%s GS\n", chars[i]->name);
907 /* State of a text formatting operation. */
911 const struct outp_text *text;
915 const struct afm_character **glyphs;
919 size_t glyph_cnt; /* Number of glyphs output. */
920 int width_left; /* Width left over. */
921 int height_left; /* Height left over. */
923 /* State as of last space. */
924 const char *space_char; /* Just past last space. */
925 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
926 int space_width_left; /* Width left over as of last space. */
929 int max_width; /* Widest line so far. */
932 /* Adjusts S to complete a line of text,
933 and draws the current line if appropriate. */
935 finish_line (struct outp_driver *this, struct text_state *s)
942 s->text->x, s->text->y + (s->text->v - s->height_left),
944 s->text->justification,
945 s->glyphs, s->glyph_kerns, s->glyph_cnt,
950 /* Update maximum width. */
951 width = s->text->h - s->width_left;
952 if (width > s->max_width)
953 s->max_width = width;
955 /* Move to next line. */
956 s->width_left = s->text->h;
957 s->height_left -= this->font_height;
959 /* No spaces on this line yet. */
960 s->space_char = NULL;
963 /* Format TEXT on THIS driver.
964 If DRAW is nonzero, draw the text.
965 The width of the widest line is stored into *WIDTH, if WIDTH
967 The total height of the text written is stored into *HEIGHT,
968 if HEIGHT is nonnull. */
970 text (struct outp_driver *this, const struct outp_text *text, bool draw,
971 int *width, int *height)
973 struct ps_driver_ext *ext = this->ext;
974 struct afm *afm = ext->fonts[text->font]->metrics;
983 s.glyph_kerns = NULL;
987 s.width_left = s.text->h;
988 s.height_left = s.text->v;
994 cp = ls_c_str (&s.text->string);
995 while (s.height_left >= this->font_height && cp < ls_end (&s.text->string))
997 const struct afm_character *cur;
1003 finish_line (this, &s);
1008 /* Get character and resolve ligatures. */
1009 cur = afm_get_character (afm, *cp);
1010 while (++cp < ls_end (&s.text->string))
1012 const struct afm_character *next = afm_get_character (afm, *cp);
1013 const struct afm_character *ligature = afm_get_ligature (cur, next);
1014 if (ligature == NULL)
1018 char_width = cur->width * this->font_height / 1000;
1020 /* Get kern adjustment. */
1021 if (s.glyph_cnt > 0)
1022 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1023 * this->font_height / 1000);
1027 /* Record the current status if this is a space character. */
1028 if (cur->code == ' ' && cp > ls_c_str (&s.text->string))
1031 s.space_glyph_cnt = s.glyph_cnt;
1032 s.space_width_left = s.width_left;
1035 /* Enough room on this line? */
1036 if (char_width + kern_adjust > s.width_left)
1038 if (s.space_char == NULL)
1040 finish_line (this, &s);
1046 s.glyph_cnt = s.space_glyph_cnt;
1047 s.width_left = s.space_width_left;
1048 finish_line (this, &s);
1053 if (s.glyph_cnt >= glyph_cap)
1055 glyph_cap = 2 * (glyph_cap + 8);
1056 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1057 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1058 glyph_cap, sizeof *s.glyph_kerns);
1060 s.glyphs[s.glyph_cnt] = cur;
1061 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1064 s.width_left -= char_width + kern_adjust;
1066 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1067 finish_line (this, &s);
1070 *width = s.max_width;
1072 *height = text->v - s.height_left;
1074 free (s.glyph_kerns);
1078 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1079 int *width, int *height)
1081 text (this, t, false, width, height);
1085 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1087 assert (this->page_open);
1088 text (this, t, true, NULL, NULL);
1092 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
1097 struct ps_driver_ext *x = this->ext;
1098 char page_size[128];
1100 int x_origin, y_origin;
1102 ch->file = tmpfile ();
1103 if (ch->file == NULL)
1109 size = this->width < this->length ? this->width : this->length;
1110 x_origin = x->left_margin + (size - this->width) / 2;
1111 y_origin = x->bottom_margin + (size - this->length) / 2;
1113 snprintf (page_size, sizeof page_size,
1114 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
1115 (double) size / PSUS, (double) size / PSUS,
1116 (double) x_origin / PSUS, (double) y_origin / PSUS);
1118 ch->pl_params = pl_newplparams ();
1119 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
1120 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
1125 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
1128 struct ps_driver_ext *x = this->ext;
1130 static int doc_num = 0;
1132 outp_eject_page (this);
1135 "%d %d translate 1000 dup scale\n"
1137 "/showpage { } def\n"
1138 "0 setgray 0 setlinecap 1 setlinewidth\n"
1139 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
1140 "%%%%BeginDocument: %d\n",
1141 -x->left_margin, -x->bottom_margin,
1145 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
1149 fputs ("%%%%EndDocument\n"
1153 outp_close_page (this);
1157 static void embed_font (struct outp_driver *this, struct font *font);
1158 static void reencode_font (struct outp_driver *this, struct font *font);
1160 /* Loads and returns the font for STRING, which has the format
1161 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1162 PFA or PFB file's name, and ENC is the encoding file's name.
1163 PFA and ENC are optional.
1164 Returns a null pointer if unsuccessful. */
1165 static struct font *
1166 load_font (const char *string_)
1168 char *string = xstrdup (string_);
1170 char *position = string;
1172 char *afm_file_name;
1174 font = xmalloc (sizeof *font);
1175 font->metrics = NULL;
1176 font->embed_fn = NULL;
1177 font->encoding_fn = NULL;
1179 token = strsep (&position, ",");
1182 error (0, 0, _("\"%s\": bad font specification"), string);
1186 /* Read AFM file. */
1187 afm_file_name = find_ps_file (token);
1188 if (afm_file_name == NULL)
1190 error (0, 0, _("could not find AFM file \"%s\""), token);
1193 font->metrics = afm_open (afm_file_name);
1194 free (afm_file_name);
1195 if (font->metrics == NULL)
1198 /* Find font file to embed. */
1199 token = strsep (&position, ",");
1200 if (token != NULL && *token != '\0')
1202 font->embed_fn = find_ps_file (token);
1203 if (font->embed_fn == NULL)
1204 error (0, 0, _("could not find font \"%s\""), token);
1207 /* Find encoding. */
1208 token = strsep (&position, ",");
1209 if (token != NULL && *token == '\0')
1211 font->encoding_fn = find_ps_file (token);
1212 if (font->encoding_fn == NULL)
1213 error (0, 0, _("could not find encoding \"%s\""), token);
1227 free_font (struct font *font)
1231 afm_close (font->metrics);
1232 free (font->embed_fn);
1233 free (font->encoding_fn);
1238 /* Emits PostScript code to embed FONT (if necessary), scale it
1239 to the proper size, re-encode it (if necessary), and store the
1240 resulting font as an object named F#, where INDEX is
1241 substituted for #. */
1243 setup_font (struct outp_driver *this, struct font *font, int index)
1245 struct ps_driver_ext *x = this->ext;
1248 if (font->embed_fn != NULL)
1249 embed_font (this, font);
1251 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1252 afm_get_findfont_name (font->metrics));
1254 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1255 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1258 if (font->encoding_fn != NULL)
1259 reencode_font (this, font);
1261 fprintf (x->file, "/F%d ED\n", index);
1264 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1265 end-of-file or on error. */
1267 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1269 while (copy_bytes > 0)
1271 char buffer[BUFSIZ];
1272 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1273 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1274 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1275 if (write_bytes != chunk_bytes)
1277 copy_bytes -= chunk_bytes;
1281 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1282 end-of-file or on error. The bytes are translated into
1283 hexadecimal during copying and broken into lines with
1284 new-line characters. */
1286 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1290 for (i = 0; i < copy_bytes; i++)
1295 if (i > 0 && i % 36 == 0)
1297 fprintf (dst, "%02X", c);
1302 /* Embeds the given FONT into THIS driver's output stream. */
1304 embed_font (struct outp_driver *this, struct font *font)
1306 struct ps_driver_ext *x = this->ext;
1310 file = fopen (font->embed_fn, "rb");
1313 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1317 fprintf (x->file, "%%%%BeginResource: font %s\n",
1318 afm_get_findfont_name (font->metrics));
1324 /* PFA file. Copy literally. */
1325 copy_bytes_literally (file, x->file, ULONG_MAX);
1329 /* PFB file. Translate as specified in Adobe Technical
1331 while ((c = getc (file)) == 128)
1334 unsigned long length;
1340 length = getc (file);
1341 length |= (unsigned long) getc (file) << 8;
1342 length |= (unsigned long) getc (file) << 16;
1343 length |= (unsigned long) getc (file) << 24;
1346 copy_bytes_literally (file, x->file, length);
1348 copy_bytes_as_hex (file, x->file, length);
1353 if (freaderror (file))
1354 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1355 fputs ("%%EndResource\n", x->file);
1358 /* Re-encodes FONT according to the specified encoding. */
1360 reencode_font (struct outp_driver *this, struct font *font)
1362 struct ps_driver_ext *x = this->ext;
1373 file = fopen (font->encoding_fn, "r");
1376 error (errno, 0, _("cannot open font encoding file \"%s\""),
1381 for (i = 0; i < 256; i++)
1387 while (ds_get_config_line (file, &line, &line_number))
1389 char *pschar, *code;
1390 char *save_ptr, *tail;
1393 if (ds_is_empty (&line) == 0)
1396 pschar = strtok_r (ds_c_str (&line), " \t\r\n", &save_ptr);
1397 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1398 if (pschar == NULL || code == NULL)
1401 code_val = strtol (code, &tail, 0);
1404 error_at_line (0, 0, font->encoding_fn, line_number,
1405 _("invalid numeric format"));
1408 if (code_val < 0 || code_val > 255)
1410 if (tab[code_val] != 0)
1411 free (tab[code_val]);
1412 tab[code_val] = xstrdup (pschar);
1416 fputs ("[", x->file);
1417 for (i = 0; i < 256; i++)
1419 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1420 fprintf (x->file, "%s\n", name);
1424 fputs ("] RF\n", x->file);
1426 if (freaderror (file) != 0)
1427 error (errno, 0, "closing Postscript encoding \"%s\"", font->encoding_fn);
1430 /* PostScript driver class. */
1431 struct outp_class postscript_class =
1448 ps_chart_initialise,