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/assertion.h>
31 #include <libpspp/bit-vector.h>
32 #include <libpspp/compiler.h>
33 #include <libpspp/freaderror.h>
34 #include <libpspp/hash.h>
35 #include <libpspp/misc.h>
36 #include <libpspp/start-date.h>
37 #include <libpspp/version.h>
39 #include <data/file-name.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. */
114 /* Transform logical y-ordinate Y into a page ordinate. */
115 #define YT(Y) (this->length - (Y))
117 static bool handle_option (struct outp_driver *this, const char *key,
118 const struct string *val);
119 static void draw_headers (struct outp_driver *this);
121 static void write_ps_prologue (struct outp_driver *);
123 static char *quote_ps_name (const char *string);
125 static struct font *load_font (const char *string);
126 static void free_font (struct font *);
127 static void setup_font (struct outp_driver *this, struct font *, int index);
129 /* Driver initialization. */
132 ps_open_driver (struct outp_driver *this, struct substring options)
134 struct ps_driver_ext *x;
137 this->width = this->length = 0;
138 this->font_height = PSUS * 10 / 72;
140 this->ext = x = xmalloc (sizeof *x);
141 x->file_name = xstrdup ("pspp.ps");
143 x->draw_headers = true;
146 x->paper_width = PSUS * 17 / 2;
147 x->paper_length = PSUS * 11;
148 x->left_margin = PSUS / 2;
149 x->right_margin = PSUS / 2;
150 x->top_margin = PSUS / 2;
151 x->bottom_margin = PSUS / 2;
152 x->line_gutter = PSUS / 72;
153 x->line_space = PSUS / 72;
154 x->line_width = PSUS / 144;
155 for (i = 0; i < OUTP_FONT_CNT; i++)
158 outp_parse_options (options, handle_option, this);
160 x->file = fn_open (x->file_name, "w");
163 error (0, errno, _("opening PostScript output file \"%s\""),
170 this->width = x->paper_width;
171 this->length = x->paper_length;
175 this->width = x->paper_length;
176 this->length = x->paper_width;
178 this->width -= x->left_margin + x->right_margin;
179 this->length -= x->top_margin + x->bottom_margin;
182 int header_length = 3 * this->font_height;
183 this->length -= header_length;
184 x->top_margin += header_length;
187 for (i = 0; i < OUTP_FONT_CNT; i++)
188 if (x->fonts[i] == NULL)
190 const char *default_fonts[OUTP_FONT_CNT];
191 default_fonts[OUTP_FIXED] = "Courier.afm";
192 default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
193 default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
194 x->fonts[i] = load_font (default_fonts[i]);
195 if (x->fonts[i] == NULL)
199 if (this->length / this->font_height < 15)
201 error (0, 0, _("The defined PostScript page is not long "
202 "enough to hold margins and headers, plus least 15 "
203 "lines of the default fonts. In fact, there's only "
204 "room for %d lines of each font at the default size "
205 "of %d.%03d points."),
206 this->length / this->font_height,
207 this->font_height / 1000, this->font_height % 1000);
212 afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
213 * this->font_height / 1000;
214 this->prop_em_width =
215 afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
216 * this->font_height / 1000;
218 this->horiz_line_width[OUTP_L_NONE] = 0;
219 this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
220 this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
221 + 2 * x->line_width);
222 memcpy (this->vert_line_width, this->horiz_line_width,
223 sizeof this->vert_line_width);
225 write_ps_prologue (this);
230 this->class->close_driver (this);
235 ps_close_driver (struct outp_driver *this)
237 struct ps_driver_ext *x = this->ext;
247 ok = fn_close (x->file_name, x->file) == 0;
249 error (0, errno, _("closing PostScript output file \"%s\""), x->file_name);
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, NULL);
387 error (0, 0, _("value for `%s' must be a dimension of positive "
388 "length (i.e., `1in')"), key);
394 x->line_width = dimension;
397 x->line_gutter = dimension;
400 x->line_width = dimension;
409 struct font *font = load_font (value);
412 struct font **dst = &x->fonts[subcat];
426 /* Looks for a PostScript font file or config file in all the
427 appropriate places. Returns the file name on success, NULL on
430 find_ps_file (const char *name)
432 if (fn_is_absolute (name))
433 return xstrdup (name);
436 char *base_name = xasprintf ("psfonts/%s", name);
437 char *file_name = fn_search_path (base_name, config_path, NULL);
443 /* Basic file operations. */
445 /* Writes the PostScript prologue to file F. */
447 write_ps_prologue (struct outp_driver *this)
449 struct ps_driver_ext *x = this->ext;
450 size_t embedded_cnt, preloaded_cnt;
453 fputs ("%!PS-Adobe-3.0\n", x->file);
454 fputs ("%%Pages: (atend)\n", x->file);
456 embedded_cnt = preloaded_cnt = 0;
457 for (i = 0; i < OUTP_FONT_CNT; i++)
459 bool embed = x->fonts[i]->embed_fn != NULL;
460 embedded_cnt += embed;
461 preloaded_cnt += !embed;
463 if (preloaded_cnt > 0)
465 fputs ("%%DocumentNeededResources: font", x->file);
466 for (i = 0; i < OUTP_FONT_CNT; i++)
468 struct font *f = x->fonts[i];
469 if (f->embed_fn == NULL)
470 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
472 fputs ("\n", x->file);
474 if (embedded_cnt > 0)
476 fputs ("%%DocumentSuppliedResources: font", x->file);
477 for (i = 0; i < OUTP_FONT_CNT; i++)
479 struct font *f = x->fonts[i];
480 if (f->embed_fn != NULL)
481 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
483 fputs ("\n", x->file);
485 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
486 fprintf (x->file, "%%%%Creator: %s\n", version);
487 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
488 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
489 fprintf (x->file, "%%%%Orientation: %s\n",
490 x->portrait ? "Portrait" : "Landscape");
491 fputs ("%%EndComments\n", x->file);
492 fputs ("%%BeginDefaults\n", x->file);
493 fputs ("%%PageResources: font", x->file);
494 for (i = 0; i < OUTP_FONT_CNT; i++)
495 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
496 fputs ("\n", x->file);
497 fputs ("%%EndDefaults\n", x->file);
498 fputs ("%%BeginProlog\n", x->file);
499 fputs ("/ED{exch def}bind def\n", x->file);
500 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
501 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
502 fputs ("/S{show}bind def\n", x->file);
503 fputs ("/GS{glyphshow}def\n", x->file);
504 fputs ("/RF{\n", x->file);
505 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
506 fputs (" {\n", x->file);
507 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
508 fputs (" }forall\n", x->file);
509 fputs (" /Encoding ED\n", x->file);
510 fputs (" currentdict end\n", x->file);
511 fputs ("}bind def\n", x->file);
512 fputs ("/F{setfont}bind def\n", x->file);
513 fputs ("/EP{\n", x->file);
514 fputs (" pg restore\n", x->file);
515 fputs (" showpage\n", x->file);
516 fputs ("}bind def\n", x->file);
517 fputs ("/GB{\n", x->file);
518 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
519 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
521 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
522 fputs ("}bind def\n", x->file);
523 fputs ("/K{0 rmoveto}bind def\n", x->file);
524 fputs ("%%EndProlog\n", x->file);
525 fputs ("%%BeginSetup\n", x->file);
526 for (i = 0; i < OUTP_FONT_CNT; i++)
527 setup_font (this, x->fonts[i], i);
528 fputs ("%%EndSetup\n", x->file);
531 /* Returns STRING as a Postscript name, which is just '/'
532 followed by STRING unless characters need to be quoted.
533 The caller must free the string. */
535 quote_ps_name (const char *string)
539 for (cp = string; *cp != '\0'; cp++)
541 unsigned char c = *cp;
542 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
543 && (cp == string || !isdigit (c)))
545 struct string out = DS_EMPTY_INITIALIZER;
546 ds_put_char (&out, '<');
547 for (cp = string; *cp != '\0'; cp++)
550 ds_put_format (&out, "%02x", c);
552 ds_put_cstr (&out, ">cvn");
553 return ds_cstr (&out);
556 return xasprintf ("/%s", string);
560 ps_open_page (struct outp_driver *this)
562 struct ps_driver_ext *x = this->ext;
564 /* Assure page independence. */
571 "%%%%BeginPageSetup\n"
572 "/pg save def 0.001 dup scale\n",
573 x->page_number, x->page_number);
577 "%d 0 translate 90 rotate\n",
580 if (x->bottom_margin != 0 || x->left_margin != 0)
583 x->left_margin, x->bottom_margin);
586 "/LW %d def %d setlinewidth\n"
587 "%%%%EndPageSetup\n",
588 x->line_width, x->line_width);
595 ps_close_page (struct outp_driver *this)
597 struct ps_driver_ext *x = this->ext;
598 fputs ("%%PageTrailer\n"
604 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
615 /* Draws a line from (x0,y0) to (x1,y1). */
617 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
619 struct ps_driver_ext *ext = this->ext;
620 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
623 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
624 shortening it to X0...X1 if SHORTEN is true.
625 Draws a horizontal line X1...X3 at Y if RIGHT says so,
626 shortening it to X2...X3 if SHORTEN is true. */
628 horz_line (struct outp_driver *this,
629 int x0, int x1, int x2, int x3, int y,
630 enum outp_line_style left, enum outp_line_style right,
633 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
634 dump_line (this, x0, y, x3, y);
637 if (left != OUTP_L_NONE)
638 dump_line (this, x0, y, shorten ? x1 : x2, y);
639 if (right != OUTP_L_NONE)
640 dump_line (this, shorten ? x2 : x1, y, x3, y);
644 /* Draws a vertical line Y0...Y2 at X if TOP says so,
645 shortening it to Y0...Y1 if SHORTEN is true.
646 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
647 shortening it to Y2...Y3 if SHORTEN is true. */
649 vert_line (struct outp_driver *this,
650 int y0, int y1, int y2, int y3, int x,
651 enum outp_line_style top, enum outp_line_style bottom,
654 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
655 dump_line (this, x, y0, x, y3);
658 if (top != OUTP_L_NONE)
659 dump_line (this, x, y0, x, shorten ? y1 : y2);
660 if (bottom != OUTP_L_NONE)
661 dump_line (this, x, shorten ? y2 : y1, x, y3);
665 /* Draws a generalized intersection of lines in the rectangle
666 (X0,Y0)-(X3,Y3). The line coming from the top to the center
667 is of style TOP, from left to center of style LEFT, from
668 bottom to center of style BOTTOM, and from right to center of
671 ps_line (struct outp_driver *this,
672 int x0, int y0, int x3, int y3,
673 enum outp_line_style top, enum outp_line_style left,
674 enum outp_line_style bottom, enum outp_line_style right)
676 /* The algorithm here is somewhat subtle, to allow it to handle
677 all the kinds of intersections that we need.
679 Three additional ordinates are assigned along the x axis. The
680 first is xc, midway between x0 and x3. The others are x1 and
681 x2; for a single vertical line these are equal to xc, and for
682 a double vertical line they are the ordinates of the left and
683 right half of the double line.
685 yc, y1, and y2 are assigned similarly along the y axis.
687 The following diagram shows the coordinate system and output
688 for double top and bottom lines, single left line, and no
692 y0 ________________________
698 y1 = y2 = yc |######### # |
703 y3 |________#_____#_______|
705 struct ps_driver_ext *ext = this->ext;
707 /* Offset from center of each line in a pair of double lines. */
708 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
710 /* Are the lines along each axis single or double?
711 (It doesn't make sense to have different kinds of line on the
712 same axis, so we don't try to gracefully handle that case.) */
713 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
714 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
716 /* When horizontal lines are doubled,
717 the left-side line along y1 normally runs from x0 to x2,
718 and the right-side line along y1 from x3 to x1.
719 If the top-side line is also doubled, we shorten the y1 lines,
720 so that the left-side line runs only to x1,
721 and the right-side line only to x2.
722 Otherwise, the horizontal line at y = y1 below would cut off
723 the intersection, which looks ugly:
725 y0 ________________________
730 y1 |######### ########|
733 y2 |######################|
736 y3 |______________________|
737 It is more of a judgment call when the horizontal line is
738 single. We actually choose to cut off the line anyhow, as
739 shown in the first diagram above.
741 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
742 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
743 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
744 int horz_line_ofs = double_vert ? double_line_ofs : 0;
745 int xc = (x0 + x3) / 2;
746 int x1 = xc - horz_line_ofs;
747 int x2 = xc + horz_line_ofs;
749 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
750 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
751 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
752 int vert_line_ofs = double_horz ? double_line_ofs : 0;
753 int yc = (y0 + y3) / 2;
754 int y1 = yc - vert_line_ofs;
755 int y2 = yc + vert_line_ofs;
758 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
761 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
762 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
766 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
769 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
770 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
774 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
775 and with the given JUSTIFICATION for THIS driver. */
777 draw_text (struct outp_driver *this,
778 const char *string, int x, int y, int max_width,
779 enum outp_justification justification)
781 struct outp_text text;
784 text.font = OUTP_PROPORTIONAL;
785 text.justification = justification;
786 text.string = ss_cstr (string);
788 text.v = this->font_height;
791 this->class->text_metrics (this, &text, &width, NULL);
792 this->class->text_draw (this, &text);
796 /* Writes LEFT left-justified and RIGHT right-justified within
797 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
799 draw_header_line (struct outp_driver *this,
800 const char *left, const char *right,
801 int x0, int x1, int y)
805 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
806 + this->prop_em_width);
808 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
811 /* Draw top of page headers for THIS driver. */
813 draw_headers (struct outp_driver *this)
815 struct ps_driver_ext *ext = this->ext;
820 y = -3 * this->font_height;
821 x0 = this->prop_em_width;
822 x1 = this->width - this->prop_em_width;
825 fprintf (ext->file, "%d %d %d %d GB\n",
827 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
828 y += ext->line_width + ext->line_gutter;
830 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
831 r2 = xasprintf ("%s - %s", version, host_system);
833 draw_header_line (this, outp_title, r1, x0, x1, y);
834 y += this->font_height;
836 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
842 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
844 The characters are justified according to JUSTIFICATION in a
845 field that has WIDTH_LEFT space remaining after the characters
846 themselves are accounted for.
847 Before character I is written, its x-position is adjusted by
850 write_text (struct outp_driver *this,
853 enum outp_justification justification,
854 const struct afm_character **chars, int *kerns, size_t char_cnt,
857 struct ps_driver_ext *ext = this->ext;
858 struct afm *afm = ext->fonts[font]->metrics;
862 if (justification == OUTP_RIGHT)
864 else if (justification == OUTP_CENTER)
865 x0 += width_left / 2;
866 y0 += afm_get_ascent (afm) * this->font_height / 1000;
868 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
870 if (ext->last_font != font)
872 ext->last_font = font;
873 fprintf (ext->file, "F%d setfont\n", font);
876 ds_init_empty (&out);
877 for (i = 0; i < char_cnt; i = j)
879 for (j = i + 1; j < char_cnt; j++)
884 fprintf (ext->file, "%d K", kerns[i]);
887 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
890 fprintf (ext->file, "%sS\n", ds_cstr (&out));
897 fprintf (ext->file, "/%s GS\n", chars[i]->name);
905 /* State of a text formatting operation. */
909 const struct outp_text *text;
913 const struct afm_character **glyphs;
917 size_t glyph_cnt; /* Number of glyphs output. */
918 int width_left; /* Width left over. */
919 int height_left; /* Height left over. */
921 /* State as of last space. */
922 const char *space_char; /* Just past last space. */
923 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
924 int space_width_left; /* Width left over as of last space. */
927 int max_width; /* Widest line so far. */
930 /* Adjusts S to complete a line of text,
931 and draws the current line if appropriate. */
933 finish_line (struct outp_driver *this, struct text_state *s)
940 s->text->x, s->text->y + (s->text->v - s->height_left),
942 s->text->justification,
943 s->glyphs, s->glyph_kerns, s->glyph_cnt,
948 /* Update maximum width. */
949 width = s->text->h - s->width_left;
950 if (width > s->max_width)
951 s->max_width = width;
953 /* Move to next line. */
954 s->width_left = s->text->h;
955 s->height_left -= this->font_height;
957 /* No spaces on this line yet. */
958 s->space_char = NULL;
961 /* Format TEXT on THIS driver.
962 If DRAW is nonzero, draw the text.
963 The width of the widest line is stored into *WIDTH, if WIDTH
965 The total height of the text written is stored into *HEIGHT,
966 if HEIGHT is nonnull. */
968 text (struct outp_driver *this, const struct outp_text *text, bool draw,
969 int *width, int *height)
971 struct ps_driver_ext *ext = this->ext;
972 struct afm *afm = ext->fonts[text->font]->metrics;
981 s.glyph_kerns = NULL;
985 s.width_left = s.text->h;
986 s.height_left = s.text->v;
992 cp = ss_data (s.text->string);
993 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
995 const struct afm_character *cur;
1001 finish_line (this, &s);
1006 /* Get character and resolve ligatures. */
1007 cur = afm_get_character (afm, *cp);
1008 while (++cp < ss_end (s.text->string))
1010 const struct afm_character *next = afm_get_character (afm, *cp);
1011 const struct afm_character *ligature = afm_get_ligature (cur, next);
1012 if (ligature == NULL)
1016 char_width = cur->width * this->font_height / 1000;
1018 /* Get kern adjustment. */
1019 if (s.glyph_cnt > 0)
1020 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1021 * this->font_height / 1000);
1025 /* Record the current status if this is a space character. */
1026 if (cur->code == ' ' && cp > ss_data (s.text->string))
1029 s.space_glyph_cnt = s.glyph_cnt;
1030 s.space_width_left = s.width_left;
1033 /* Enough room on this line? */
1034 if (char_width + kern_adjust > s.width_left)
1036 if (s.space_char == NULL)
1038 finish_line (this, &s);
1044 s.glyph_cnt = s.space_glyph_cnt;
1045 s.width_left = s.space_width_left;
1046 finish_line (this, &s);
1051 if (s.glyph_cnt >= glyph_cap)
1053 glyph_cap = 2 * (glyph_cap + 8);
1054 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1055 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1056 glyph_cap, sizeof *s.glyph_kerns);
1058 s.glyphs[s.glyph_cnt] = cur;
1059 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1062 s.width_left -= char_width + kern_adjust;
1064 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1065 finish_line (this, &s);
1068 *width = s.max_width;
1070 *height = text->v - s.height_left;
1072 free (s.glyph_kerns);
1076 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1077 int *width, int *height)
1079 text (this, t, false, width, height);
1083 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1085 assert (this->page_open);
1086 text (this, t, true, NULL, NULL);
1090 ps_chart_initialise (struct outp_driver *this UNUSED, struct chart *ch)
1095 struct ps_driver_ext *x = this->ext;
1096 char page_size[128];
1098 int x_origin, y_origin;
1100 ch->file = tmpfile ();
1101 if (ch->file == NULL)
1107 size = this->width < this->length ? this->width : this->length;
1108 x_origin = x->left_margin + (size - this->width) / 2;
1109 y_origin = x->bottom_margin + (size - this->length) / 2;
1111 snprintf (page_size, sizeof page_size,
1112 "a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
1113 (double) size / PSUS, (double) size / PSUS,
1114 (double) x_origin / PSUS, (double) y_origin / PSUS);
1116 ch->pl_params = pl_newplparams ();
1117 pl_setplparam (ch->pl_params, "PAGESIZE", page_size);
1118 ch->lp = pl_newpl_r ("ps", NULL, ch->file, stderr, ch->pl_params);
1123 ps_chart_finalise (struct outp_driver *this UNUSED, struct chart *ch UNUSED)
1126 struct ps_driver_ext *x = this->ext;
1128 static int doc_num = 0;
1130 outp_eject_page (this);
1133 "%d %d translate 1000 dup scale\n"
1135 "/showpage { } def\n"
1136 "0 setgray 0 setlinecap 1 setlinewidth\n"
1137 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
1138 "%%%%BeginDocument: %d\n",
1139 -x->left_margin, -x->bottom_margin,
1143 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, ch->file), x->file))
1147 fputs ("%%EndDocument\n"
1151 outp_close_page (this);
1155 static void embed_font (struct outp_driver *this, struct font *font);
1156 static void reencode_font (struct outp_driver *this, struct font *font);
1158 /* Loads and returns the font for STRING, which has the format
1159 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1160 PFA or PFB file's name, and ENC is the encoding file's name.
1161 PFA and ENC are optional.
1162 Returns a null pointer if unsuccessful. */
1163 static struct font *
1164 load_font (const char *string_)
1166 char *string = xstrdup (string_);
1168 char *position = string;
1170 char *afm_file_name;
1172 font = xmalloc (sizeof *font);
1173 font->metrics = NULL;
1174 font->embed_fn = NULL;
1175 font->encoding_fn = NULL;
1177 token = strsep (&position, ",");
1180 error (0, 0, _("\"%s\": bad font specification"), string);
1184 /* Read AFM file. */
1185 afm_file_name = find_ps_file (token);
1186 if (afm_file_name == NULL)
1188 error (0, 0, _("could not find AFM file \"%s\""), token);
1191 font->metrics = afm_open (afm_file_name);
1192 free (afm_file_name);
1193 if (font->metrics == NULL)
1196 /* Find font file to embed. */
1197 token = strsep (&position, ",");
1198 if (token != NULL && *token != '\0')
1200 font->embed_fn = find_ps_file (token);
1201 if (font->embed_fn == NULL)
1202 error (0, 0, _("could not find font \"%s\""), token);
1205 /* Find encoding. */
1206 token = strsep (&position, ",");
1207 if (token != NULL && *token == '\0')
1209 font->encoding_fn = find_ps_file (token);
1210 if (font->encoding_fn == NULL)
1211 error (0, 0, _("could not find encoding \"%s\""), token);
1225 free_font (struct font *font)
1229 afm_close (font->metrics);
1230 free (font->embed_fn);
1231 free (font->encoding_fn);
1236 /* Emits PostScript code to embed FONT (if necessary), scale it
1237 to the proper size, re-encode it (if necessary), and store the
1238 resulting font as an object named F#, where INDEX is
1239 substituted for #. */
1241 setup_font (struct outp_driver *this, struct font *font, int index)
1243 struct ps_driver_ext *x = this->ext;
1246 if (font->embed_fn != NULL)
1247 embed_font (this, font);
1249 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1250 afm_get_findfont_name (font->metrics));
1252 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1253 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1256 if (font->encoding_fn != NULL)
1257 reencode_font (this, font);
1259 fprintf (x->file, "/F%d ED\n", index);
1262 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1263 end-of-file or on error. */
1265 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1267 while (copy_bytes > 0)
1269 char buffer[BUFSIZ];
1270 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1271 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1272 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1273 if (write_bytes != chunk_bytes)
1275 copy_bytes -= chunk_bytes;
1279 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1280 end-of-file or on error. The bytes are translated into
1281 hexadecimal during copying and broken into lines with
1282 new-line characters. */
1284 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1288 for (i = 0; i < copy_bytes; i++)
1293 if (i > 0 && i % 36 == 0)
1295 fprintf (dst, "%02X", c);
1300 /* Embeds the given FONT into THIS driver's output stream. */
1302 embed_font (struct outp_driver *this, struct font *font)
1304 struct ps_driver_ext *x = this->ext;
1308 file = fopen (font->embed_fn, "rb");
1311 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1315 fprintf (x->file, "%%%%BeginResource: font %s\n",
1316 afm_get_findfont_name (font->metrics));
1322 /* PFA file. Copy literally. */
1323 copy_bytes_literally (file, x->file, ULONG_MAX);
1327 /* PFB file. Translate as specified in Adobe Technical
1329 while ((c = getc (file)) == 128)
1332 unsigned long length;
1338 length = getc (file);
1339 length |= (unsigned long) getc (file) << 8;
1340 length |= (unsigned long) getc (file) << 16;
1341 length |= (unsigned long) getc (file) << 24;
1344 copy_bytes_literally (file, x->file, length);
1346 copy_bytes_as_hex (file, x->file, length);
1351 if (freaderror (file))
1352 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1353 fputs ("%%EndResource\n", x->file);
1356 /* Re-encodes FONT according to the specified encoding. */
1358 reencode_font (struct outp_driver *this, struct font *font)
1360 struct ps_driver_ext *x = this->ext;
1371 file = fopen (font->encoding_fn, "r");
1374 error (errno, 0, _("cannot open font encoding file \"%s\""),
1379 for (i = 0; i < 256; i++)
1384 ds_init_empty (&line);
1385 while (ds_read_config_line (&line, &line_number, file))
1387 char *pschar, *code;
1388 char *save_ptr, *tail;
1391 if (ds_is_empty (&line) == 0)
1394 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1395 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1396 if (pschar == NULL || code == NULL)
1399 code_val = strtol (code, &tail, 0);
1402 error_at_line (0, 0, font->encoding_fn, line_number,
1403 _("invalid numeric format"));
1406 if (code_val < 0 || code_val > 255)
1408 if (tab[code_val] != 0)
1409 free (tab[code_val]);
1410 tab[code_val] = xstrdup (pschar);
1414 fputs ("[", x->file);
1415 for (i = 0; i < 256; i++)
1417 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1418 fprintf (x->file, "%s\n", name);
1422 fputs ("] RF\n", x->file);
1424 if (freaderror (file) != 0)
1425 error (errno, 0, "closing Postscript encoding \"%s\"", font->encoding_fn);
1428 /* PostScript driver class. */
1429 const struct outp_class postscript_class =
1446 ps_chart_initialise,