1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2006, 2007, 2009 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 <data/file-name.h>
28 #include <libpspp/assertion.h>
29 #include <libpspp/bit-vector.h>
30 #include <libpspp/compiler.h>
31 #include <libpspp/freaderror.h>
32 #include <libpspp/hash.h>
33 #include <libpspp/misc.h>
34 #include <libpspp/start-date.h>
35 #include <libpspp/version.h>
36 #include <output/afm.h>
37 #include <output/chart-provider.h>
38 #include <output/chart.h>
39 #include <output/manager.h>
40 #include <output/output.h>
48 #define _(msgid) gettext (msgid)
50 /* PostScript driver options: (defaults listed first)
54 paper-size=letter (see "papersize" file)
55 orientation=portrait|landscape
64 emph-font=Times-Italic
73 /* The number of `psus' (PostScript driver UnitS) per inch. */
76 /* A PostScript font. */
79 struct afm *metrics; /* Metrics. */
80 char *embed_fn; /* Name of file to embed. */
81 char *encoding_fn; /* Name of file with encoding. */
84 /* PostScript output driver extension record. */
87 char *file_name; /* Output file name. */
88 FILE *file; /* Output file. */
90 bool draw_headers; /* Draw headers at top of page? */
91 int page_number; /* Current page number. */
93 bool portrait; /* Portrait mode? */
94 int paper_width; /* Width of paper before dropping margins. */
95 int paper_length; /* Length of paper before dropping margins. */
96 int left_margin; /* Left margin in psus. */
97 int right_margin; /* Right margin in psus. */
98 int top_margin; /* Top margin in psus. */
99 int bottom_margin; /* Bottom margin in psus. */
101 int line_gutter; /* Space around lines. */
102 int line_space; /* Space between lines. */
103 int line_width; /* Width of lines. */
105 struct font *fonts[OUTP_FONT_CNT];
106 int last_font; /* Index of last font set with setfont. */
108 int doc_num; /* %%DocumentNumber counter. */
111 /* Transform logical y-ordinate Y into a page ordinate. */
112 #define YT(Y) (this->length - (Y))
114 static bool handle_option (void *this, const char *key,
115 const struct string *val);
116 static void draw_headers (struct outp_driver *this);
118 static void write_ps_prologue (struct outp_driver *);
120 static char *quote_ps_name (const char *string);
122 static struct font *load_font (const char *string);
123 static void free_font (struct font *);
124 static void setup_font (struct outp_driver *this, struct font *, int index);
126 /* Driver initialization. */
129 ps_open_driver (const char *name, int types, struct substring options)
131 struct outp_driver *this;
132 struct ps_driver_ext *x;
135 this = outp_allocate_driver (&postscript_class, name, types);
136 this->width = this->length = 0;
137 this->font_height = PSUS * 10 / 72;
139 this->ext = x = xmalloc (sizeof *x);
140 x->file_name = xstrdup ("pspp.ps");
142 x->draw_headers = true;
145 outp_get_paper_size ("", &x->paper_width, &x->paper_length);
146 x->left_margin = PSUS / 2;
147 x->right_margin = PSUS / 2;
148 x->top_margin = PSUS / 2;
149 x->bottom_margin = PSUS / 2;
150 x->line_gutter = PSUS / 72;
151 x->line_space = PSUS / 72;
152 x->line_width = PSUS / 144;
153 for (i = 0; i < OUTP_FONT_CNT; i++)
157 outp_parse_options (this->name, options, handle_option, this);
159 x->file = fn_open (x->file_name, "w");
162 error (0, errno, _("opening PostScript output file \"%s\""),
169 this->width = x->paper_width;
170 this->length = x->paper_length;
174 this->width = x->paper_length;
175 this->length = x->paper_width;
177 this->width -= x->left_margin + x->right_margin;
178 this->length -= x->top_margin + x->bottom_margin;
181 int header_length = 3 * this->font_height;
182 this->length -= header_length;
183 x->top_margin += header_length;
186 for (i = 0; i < OUTP_FONT_CNT; i++)
187 if (x->fonts[i] == NULL)
189 const char *default_fonts[OUTP_FONT_CNT];
190 default_fonts[OUTP_FIXED] = "Courier.afm";
191 default_fonts[OUTP_PROPORTIONAL] = "Times-Roman.afm";
192 default_fonts[OUTP_EMPHASIS] = "Times-Italic.afm";
193 x->fonts[i] = load_font (default_fonts[i]);
194 if (x->fonts[i] == NULL)
198 if (this->length / this->font_height < 15)
200 error (0, 0, _("The defined PostScript page is not long "
201 "enough to hold margins and headers, plus least 15 "
202 "lines of the default fonts. In fact, there's only "
203 "room for %d lines of each font at the default size "
204 "of %d.%03d points."),
205 this->length / this->font_height,
206 this->font_height / 1000, this->font_height % 1000);
211 afm_get_character (x->fonts[OUTP_FIXED]->metrics, '0')->width
212 * this->font_height / 1000;
213 this->prop_em_width =
214 afm_get_character (x->fonts[OUTP_PROPORTIONAL]->metrics, '0')->width
215 * this->font_height / 1000;
217 this->horiz_line_width[OUTP_L_NONE] = 0;
218 this->horiz_line_width[OUTP_L_SINGLE] = 2 * x->line_gutter + x->line_width;
219 this->horiz_line_width[OUTP_L_DOUBLE] = (2 * x->line_gutter + x->line_space
220 + 2 * x->line_width);
221 memcpy (this->vert_line_width, this->horiz_line_width,
222 sizeof this->vert_line_width);
224 write_ps_prologue (this);
226 outp_register_driver (this);
230 this->class->close_driver (this);
231 outp_free_driver (this);
236 ps_close_driver (struct outp_driver *this)
238 struct ps_driver_ext *x = this->ext;
250 ok = fn_close (x->file_name, x->file) == 0;
252 error (0, errno, _("closing PostScript output file \"%s\""),
257 for (i = 0; i < OUTP_FONT_CNT; i++)
258 free_font (x->fonts[i]);
264 /* Generic option types. */
278 /* All the options that the PostScript driver supports. */
279 static const struct outp_option option_tab[] =
281 {"output-file", output_file_arg,0},
282 {"paper-size", paper_size_arg, 0},
283 {"orientation", orientation_arg,0},
285 {"headers", boolean_arg, 1},
287 {"prop-font", string_arg, OUTP_PROPORTIONAL},
288 {"emph-font", string_arg, OUTP_EMPHASIS},
289 {"fixed-font", string_arg, OUTP_FIXED},
291 {"left-margin", pos_int_arg, 0},
292 {"right-margin", pos_int_arg, 1},
293 {"top-margin", pos_int_arg, 2},
294 {"bottom-margin", pos_int_arg, 3},
295 {"font-size", pos_int_arg, 4},
297 {"line-width", dimension_arg, 0},
298 {"line-gutter", dimension_arg, 1},
299 {"line-width", dimension_arg, 2},
304 handle_option (void *this_, const char *key,
305 const struct string *val)
307 struct outp_driver *this = this_;
308 struct ps_driver_ext *x = this->ext;
310 char *value = ds_cstr (val);
312 switch (outp_match_keyword (key, option_tab, &subcat))
316 _("unknown configuration parameter `%s' for PostScript device "
319 case output_file_arg:
321 x->file_name = xstrdup (value);
324 outp_get_paper_size (value, &this->width, &this->length);
326 case orientation_arg:
327 if (!strcmp (value, "portrait"))
329 else if (!strcmp (value, "landscape"))
332 error (0, 0, _("unknown orientation `%s' (valid orientations are "
333 "`portrait' and `landscape')"), value);
336 if (!strcmp (value, "on") || !strcmp (value, "true")
337 || !strcmp (value, "yes") || atoi (value))
338 x->draw_headers = true;
339 else if (!strcmp (value, "off") || !strcmp (value, "false")
340 || !strcmp (value, "no") || !strcmp (value, "0"))
341 x->draw_headers = false;
344 error (0, 0, _("boolean value expected for %s"), key);
354 arg = strtol (value, &tail, 0);
355 if (arg < 1 || errno == ERANGE || *tail)
357 error (0, 0, _("positive integer value required for `%s'"), key);
360 if ((subcat == 4 || subcat == 5) && arg < 1000)
362 error (0, 0, _("default font size must be at least 1 point (value "
363 "of 1000 for key `%s')"), key);
369 x->left_margin = arg;
372 x->right_margin = arg;
378 x->bottom_margin = arg;
381 this->font_height = arg;
390 int dimension = outp_evaluate_dimension (value);
397 x->line_width = dimension;
400 x->line_gutter = dimension;
403 x->line_width = dimension;
412 struct font *font = load_font (value);
415 struct font **dst = &x->fonts[subcat];
429 /* Looks for a PostScript font file or config file in all the
430 appropriate places. Returns the file name on success, NULL on
433 find_ps_file (const char *name)
435 if (fn_is_absolute (name))
436 return xstrdup (name);
439 char *base_name = xasprintf ("psfonts/%s", name);
440 char *file_name = fn_search_path (base_name, config_path);
446 /* Basic file operations. */
448 /* Writes the PostScript prologue to file F. */
450 write_ps_prologue (struct outp_driver *this)
452 struct ps_driver_ext *x = this->ext;
453 size_t embedded_cnt, preloaded_cnt;
456 fputs ("%!PS-Adobe-3.0\n", x->file);
457 fputs ("%%Pages: (atend)\n", x->file);
459 embedded_cnt = preloaded_cnt = 0;
460 for (i = 0; i < OUTP_FONT_CNT; i++)
462 bool embed = x->fonts[i]->embed_fn != NULL;
463 embedded_cnt += embed;
464 preloaded_cnt += !embed;
466 if (preloaded_cnt > 0)
468 fputs ("%%DocumentNeededResources: font", x->file);
469 for (i = 0; i < OUTP_FONT_CNT; i++)
471 struct font *f = x->fonts[i];
472 if (f->embed_fn == NULL)
473 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
475 fputs ("\n", x->file);
477 if (embedded_cnt > 0)
479 fputs ("%%DocumentSuppliedResources: font", x->file);
480 for (i = 0; i < OUTP_FONT_CNT; i++)
482 struct font *f = x->fonts[i];
483 if (f->embed_fn != NULL)
484 fprintf (x->file, " %s", afm_get_findfont_name (f->metrics));
486 fputs ("\n", x->file);
488 fputs ("%%Copyright: This prologue is public domain.\n", x->file);
489 fprintf (x->file, "%%%%Creator: %s\n", version);
490 fprintf (x->file, "%%%%DocumentMedia: Plain %g %g 75 white ()\n",
491 x->paper_width / (PSUS / 72.0), x->paper_length / (PSUS / 72.0));
492 fprintf (x->file, "%%%%Orientation: %s\n",
493 x->portrait ? "Portrait" : "Landscape");
494 fputs ("%%EndComments\n", x->file);
495 fputs ("%%BeginDefaults\n", x->file);
496 fputs ("%%PageResources: font", x->file);
497 for (i = 0; i < OUTP_FONT_CNT; i++)
498 fprintf (x->file, " %s", afm_get_findfont_name (x->fonts[i]->metrics));
499 fputs ("\n", x->file);
500 fputs ("%%EndDefaults\n", x->file);
501 fputs ("%%BeginProlog\n", x->file);
502 fputs ("/ED{exch def}bind def\n", x->file);
503 fputs ("/L{moveto lineto stroke}bind def\n", x->file);
504 fputs ("/D{moveto lineto moveto lineto stroke}bind def\n", x->file);
505 fputs ("/S{show}bind def\n", x->file);
506 fputs ("/GS{glyphshow}def\n", x->file);
507 fputs ("/RF{\n", x->file);
508 fputs (" exch dup maxlength 1 add dict begin\n", x->file);
509 fputs (" {\n", x->file);
510 fputs (" 1 index/FID ne{def}{pop pop}ifelse\n", x->file);
511 fputs (" }forall\n", x->file);
512 fputs (" /Encoding ED\n", x->file);
513 fputs (" currentdict end\n", x->file);
514 fputs ("}bind def\n", x->file);
515 fputs ("/F{setfont}bind def\n", x->file);
516 fputs ("/EP{\n", x->file);
517 fputs (" pg restore\n", x->file);
518 fputs (" showpage\n", x->file);
519 fputs ("}bind def\n", x->file);
520 fputs ("/GB{\n", x->file);
521 fputs (" /y2 ED/x2 ED/y1 ED/x1 ED\n", x->file);
522 fputs (" x1 y1 moveto x2 y1 lineto x2 y2 lineto x1 y2 lineto closepath\n",
524 fputs (" gsave 0.9 setgray fill grestore stroke\n", x->file);
525 fputs ("}bind def\n", x->file);
526 fputs ("/K{0 rmoveto}bind def\n", x->file);
527 fputs ("%%EndProlog\n", x->file);
528 fputs ("%%BeginSetup\n", x->file);
529 for (i = 0; i < OUTP_FONT_CNT; i++)
530 setup_font (this, x->fonts[i], i);
531 fputs ("%%EndSetup\n", x->file);
534 /* Returns STRING as a Postscript name, which is just '/'
535 followed by STRING unless characters need to be quoted.
536 The caller must free the string. */
538 quote_ps_name (const char *string)
542 for (cp = string; *cp != '\0'; cp++)
544 unsigned char c = *cp;
545 if (!isalpha (c) && strchr ("^_|!$&:;.,-+", c) == NULL
546 && (cp == string || !isdigit (c)))
548 struct string out = DS_EMPTY_INITIALIZER;
549 ds_put_char (&out, '<');
550 for (cp = string; *cp != '\0'; cp++)
553 ds_put_format (&out, "%02x", c);
555 ds_put_cstr (&out, ">cvn");
556 return ds_cstr (&out);
559 return xasprintf ("/%s", string);
563 ps_open_page (struct outp_driver *this)
565 struct ps_driver_ext *x = this->ext;
567 /* Assure page independence. */
574 "%%%%BeginPageSetup\n"
575 "/pg save def 0.001 dup scale\n",
576 x->page_number, x->page_number);
580 "%d 0 translate 90 rotate\n",
583 if (x->bottom_margin != 0 || x->left_margin != 0)
586 x->left_margin, x->bottom_margin);
589 "/LW %d def %d setlinewidth\n"
590 "%%%%EndPageSetup\n",
591 x->line_width, x->line_width);
598 ps_close_page (struct outp_driver *this)
600 struct ps_driver_ext *x = this->ext;
601 fputs ("%%PageTrailer\n"
607 ps_output_chart (struct outp_driver *this, const struct chart *chart)
609 struct ps_driver_ext *x = this->ext;
610 plPlotterParams *params;
611 int x_origin, y_origin;
618 /* Create temporary file for chart. */
622 error (0, errno, _("failed to create temporary file"));
626 /* Create plotter for chart. */
627 size = this->width < this->length ? this->width : this->length;
628 x_origin = x->left_margin + (size - this->width) / 2;
629 y_origin = x->bottom_margin + (size - this->length) / 2;
630 page_size = xasprintf ("a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
631 (double) size / PSUS, (double) size / PSUS,
632 (double) x_origin / PSUS, (double) y_origin / PSUS);
634 params = pl_newplparams ();
635 pl_setplparam (params, "PAGESIZE", page_size);
637 lp = pl_newpl_r ("ps", 0, file, stderr, params);
638 pl_deleteplparams (params);
646 /* Draw chart and free plotter. */
647 chart_draw (chart, lp);
650 /* Write prologue for chart. */
651 outp_eject_page (this);
654 "%d %d translate 1000 dup scale\n"
656 "/showpage { } def\n"
657 "0 setgray 0 setlinecap 1 setlinewidth\n"
658 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
659 "%%%%BeginDocument: %d\n",
660 -x->left_margin, -x->bottom_margin,
663 /* Copy chart into output file. */
665 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, file), x->file))
669 /* Write epilogue for chart. */
670 fputs ("%%EndDocument\n"
674 outp_close_page (this);
678 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
687 /* Draws a line from (x0,y0) to (x1,y1). */
689 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
691 struct ps_driver_ext *ext = this->ext;
692 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
695 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
696 shortening it to X0...X1 if SHORTEN is true.
697 Draws a horizontal line X1...X3 at Y if RIGHT says so,
698 shortening it to X2...X3 if SHORTEN is true. */
700 horz_line (struct outp_driver *this,
701 int x0, int x1, int x2, int x3, int y,
702 enum outp_line_style left, enum outp_line_style right,
705 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
706 dump_line (this, x0, y, x3, y);
709 if (left != OUTP_L_NONE)
710 dump_line (this, x0, y, shorten ? x1 : x2, y);
711 if (right != OUTP_L_NONE)
712 dump_line (this, shorten ? x2 : x1, y, x3, y);
716 /* Draws a vertical line Y0...Y2 at X if TOP says so,
717 shortening it to Y0...Y1 if SHORTEN is true.
718 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
719 shortening it to Y2...Y3 if SHORTEN is true. */
721 vert_line (struct outp_driver *this,
722 int y0, int y1, int y2, int y3, int x,
723 enum outp_line_style top, enum outp_line_style bottom,
726 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
727 dump_line (this, x, y0, x, y3);
730 if (top != OUTP_L_NONE)
731 dump_line (this, x, y0, x, shorten ? y1 : y2);
732 if (bottom != OUTP_L_NONE)
733 dump_line (this, x, shorten ? y2 : y1, x, y3);
737 /* Draws a generalized intersection of lines in the rectangle
738 (X0,Y0)-(X3,Y3). The line coming from the top to the center
739 is of style TOP, from left to center of style LEFT, from
740 bottom to center of style BOTTOM, and from right to center of
743 ps_line (struct outp_driver *this,
744 int x0, int y0, int x3, int y3,
745 enum outp_line_style top, enum outp_line_style left,
746 enum outp_line_style bottom, enum outp_line_style right)
748 /* The algorithm here is somewhat subtle, to allow it to handle
749 all the kinds of intersections that we need.
751 Three additional ordinates are assigned along the x axis. The
752 first is xc, midway between x0 and x3. The others are x1 and
753 x2; for a single vertical line these are equal to xc, and for
754 a double vertical line they are the ordinates of the left and
755 right half of the double line.
757 yc, y1, and y2 are assigned similarly along the y axis.
759 The following diagram shows the coordinate system and output
760 for double top and bottom lines, single left line, and no
764 y0 ________________________
770 y1 = y2 = yc |######### # |
775 y3 |________#_____#_______|
777 struct ps_driver_ext *ext = this->ext;
779 /* Offset from center of each line in a pair of double lines. */
780 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
782 /* Are the lines along each axis single or double?
783 (It doesn't make sense to have different kinds of line on the
784 same axis, so we don't try to gracefully handle that case.) */
785 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
786 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
788 /* When horizontal lines are doubled,
789 the left-side line along y1 normally runs from x0 to x2,
790 and the right-side line along y1 from x3 to x1.
791 If the top-side line is also doubled, we shorten the y1 lines,
792 so that the left-side line runs only to x1,
793 and the right-side line only to x2.
794 Otherwise, the horizontal line at y = y1 below would cut off
795 the intersection, which looks ugly:
797 y0 ________________________
802 y1 |######### ########|
805 y2 |######################|
808 y3 |______________________|
809 It is more of a judgment call when the horizontal line is
810 single. We actually choose to cut off the line anyhow, as
811 shown in the first diagram above.
813 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
814 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
815 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
816 int horz_line_ofs = double_vert ? double_line_ofs : 0;
817 int xc = (x0 + x3) / 2;
818 int x1 = xc - horz_line_ofs;
819 int x2 = xc + horz_line_ofs;
821 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
822 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
823 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
824 int vert_line_ofs = double_horz ? double_line_ofs : 0;
825 int yc = (y0 + y3) / 2;
826 int y1 = yc - vert_line_ofs;
827 int y2 = yc + vert_line_ofs;
830 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
833 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
834 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
838 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
841 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
842 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
846 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
847 and with the given JUSTIFICATION for THIS driver. */
849 draw_text (struct outp_driver *this,
850 const char *string, int x, int y, int max_width,
851 enum outp_justification justification)
853 struct outp_text text;
856 text.font = OUTP_PROPORTIONAL;
857 text.justification = justification;
858 text.string = ss_cstr (string);
860 text.v = this->font_height;
863 this->class->text_metrics (this, &text, &width, NULL);
864 this->class->text_draw (this, &text);
868 /* Writes LEFT left-justified and RIGHT right-justified within
869 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
871 draw_header_line (struct outp_driver *this,
872 const char *left, const char *right,
873 int x0, int x1, int y)
877 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
878 + this->prop_em_width);
880 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
883 /* Draw top of page headers for THIS driver. */
885 draw_headers (struct outp_driver *this)
887 struct ps_driver_ext *ext = this->ext;
892 y = -3 * this->font_height;
893 x0 = this->prop_em_width;
894 x1 = this->width - this->prop_em_width;
897 fprintf (ext->file, "%d %d %d %d GB\n",
899 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
900 y += ext->line_width + ext->line_gutter;
902 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
903 r2 = xasprintf ("%s - %s", version, host_system);
905 draw_header_line (this, outp_title, r1, x0, x1, y);
906 y += this->font_height;
908 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
914 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
916 The characters are justified according to JUSTIFICATION in a
917 field that has WIDTH_LEFT space remaining after the characters
918 themselves are accounted for.
919 Before character I is written, its x-position is adjusted by
922 write_text (struct outp_driver *this,
925 enum outp_justification justification,
926 const struct afm_character **chars, int *kerns, size_t char_cnt,
929 struct ps_driver_ext *ext = this->ext;
930 struct afm *afm = ext->fonts[font]->metrics;
934 if (justification == OUTP_RIGHT)
936 else if (justification == OUTP_CENTER)
937 x0 += width_left / 2;
938 y0 += afm_get_ascent (afm) * this->font_height / 1000;
940 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
942 if (ext->last_font != font)
944 ext->last_font = font;
945 fprintf (ext->file, "F%d setfont\n", font);
948 ds_init_empty (&out);
949 for (i = 0; i < char_cnt; i = j)
951 for (j = i + 1; j < char_cnt; j++)
956 fprintf (ext->file, "%d K", kerns[i]);
959 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
962 fprintf (ext->file, "%sS\n", ds_cstr (&out));
969 fprintf (ext->file, "/%s GS\n", chars[i]->name);
977 /* State of a text formatting operation. */
981 const struct outp_text *text;
985 const struct afm_character **glyphs;
989 size_t glyph_cnt; /* Number of glyphs output. */
990 int width_left; /* Width left over. */
991 int height_left; /* Height left over. */
993 /* State as of last space. */
994 const char *space_char; /* Just past last space. */
995 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
996 int space_width_left; /* Width left over as of last space. */
999 int max_width; /* Widest line so far. */
1002 /* Adjusts S to complete a line of text,
1003 and draws the current line if appropriate. */
1005 finish_line (struct outp_driver *this, struct text_state *s)
1012 s->text->x, s->text->y + (s->text->v - s->height_left),
1014 s->text->justification,
1015 s->glyphs, s->glyph_kerns, s->glyph_cnt,
1020 /* Update maximum width. */
1021 width = s->text->h - s->width_left;
1022 if (width > s->max_width)
1023 s->max_width = width;
1025 /* Move to next line. */
1026 s->width_left = s->text->h;
1027 s->height_left -= this->font_height;
1029 /* No spaces on this line yet. */
1030 s->space_char = NULL;
1033 /* Format TEXT on THIS driver.
1034 If DRAW is nonzero, draw the text.
1035 The width of the widest line is stored into *WIDTH, if WIDTH
1037 The total height of the text written is stored into *HEIGHT,
1038 if HEIGHT is nonnull. */
1040 text (struct outp_driver *this, const struct outp_text *text, bool draw,
1041 int *width, int *height)
1043 struct ps_driver_ext *ext = this->ext;
1044 struct afm *afm = ext->fonts[text->font]->metrics;
1047 struct text_state s;
1053 s.glyph_kerns = NULL;
1057 s.width_left = s.text->h;
1058 s.height_left = s.text->v;
1064 cp = ss_data (s.text->string);
1065 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
1067 const struct afm_character *cur;
1073 finish_line (this, &s);
1078 /* Get character and resolve ligatures. */
1079 cur = afm_get_character (afm, *cp);
1080 while (++cp < ss_end (s.text->string))
1082 const struct afm_character *next = afm_get_character (afm, *cp);
1083 const struct afm_character *ligature = afm_get_ligature (cur, next);
1084 if (ligature == NULL)
1088 char_width = cur->width * this->font_height / 1000;
1090 /* Get kern adjustment. */
1091 if (s.glyph_cnt > 0)
1092 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1093 * this->font_height / 1000);
1097 /* Record the current status if this is a space character. */
1098 if (cur->code == ' ' && cp > ss_data (s.text->string))
1101 s.space_glyph_cnt = s.glyph_cnt;
1102 s.space_width_left = s.width_left;
1105 /* Enough room on this line? */
1106 if (char_width + kern_adjust > s.width_left)
1108 if (s.space_char == NULL)
1110 finish_line (this, &s);
1116 s.glyph_cnt = s.space_glyph_cnt;
1117 s.width_left = s.space_width_left;
1118 finish_line (this, &s);
1123 if (s.glyph_cnt >= glyph_cap)
1125 glyph_cap = 2 * (glyph_cap + 8);
1126 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1127 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1128 glyph_cap, sizeof *s.glyph_kerns);
1130 s.glyphs[s.glyph_cnt] = cur;
1131 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1134 s.width_left -= char_width + kern_adjust;
1136 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1137 finish_line (this, &s);
1140 *width = s.max_width;
1142 *height = text->v - s.height_left;
1144 free (s.glyph_kerns);
1148 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1149 int *width, int *height)
1151 text (this, t, false, width, height);
1155 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1157 assert (this->page_open);
1158 text (this, t, true, NULL, NULL);
1161 static void embed_font (struct outp_driver *this, struct font *font);
1162 static void reencode_font (struct outp_driver *this, struct font *font);
1164 /* Loads and returns the font for STRING, which has the format
1165 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1166 PFA or PFB file's name, and ENC is the encoding file's name.
1167 PFA and ENC are optional.
1168 Returns a null pointer if unsuccessful. */
1169 static struct font *
1170 load_font (const char *string_)
1172 char *string = xstrdup (string_);
1174 char *position = string;
1176 char *afm_file_name;
1178 font = xmalloc (sizeof *font);
1179 font->metrics = NULL;
1180 font->embed_fn = NULL;
1181 font->encoding_fn = NULL;
1183 token = strsep (&position, ",");
1186 error (0, 0, _("\"%s\": bad font specification"), string);
1190 /* Read AFM file. */
1191 afm_file_name = find_ps_file (token);
1192 if (afm_file_name == NULL)
1194 error (0, 0, _("could not find AFM file \"%s\""), token);
1197 font->metrics = afm_open (afm_file_name);
1198 free (afm_file_name);
1199 if (font->metrics == NULL)
1202 /* Find font file to embed. */
1203 token = strsep (&position, ",");
1204 if (token != NULL && *token != '\0')
1206 font->embed_fn = find_ps_file (token);
1207 if (font->embed_fn == NULL)
1208 error (0, 0, _("could not find font \"%s\""), token);
1211 /* Find encoding. */
1212 token = strsep (&position, ",");
1213 if (token != NULL && *token == '\0')
1215 font->encoding_fn = find_ps_file (token);
1216 if (font->encoding_fn == NULL)
1217 error (0, 0, _("could not find encoding \"%s\""), token);
1231 free_font (struct font *font)
1235 afm_close (font->metrics);
1236 free (font->embed_fn);
1237 free (font->encoding_fn);
1242 /* Emits PostScript code to embed FONT (if necessary), scale it
1243 to the proper size, re-encode it (if necessary), and store the
1244 resulting font as an object named F#, where INDEX is
1245 substituted for #. */
1247 setup_font (struct outp_driver *this, struct font *font, int index)
1249 struct ps_driver_ext *x = this->ext;
1252 if (font->embed_fn != NULL)
1253 embed_font (this, font);
1255 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1256 afm_get_findfont_name (font->metrics));
1258 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1259 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1262 if (font->encoding_fn != NULL)
1263 reencode_font (this, font);
1265 fprintf (x->file, "/F%d ED\n", index);
1268 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1269 end-of-file or on error. */
1271 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1273 while (copy_bytes > 0)
1275 char buffer[BUFSIZ];
1276 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1277 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1278 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1279 if (write_bytes != chunk_bytes)
1281 copy_bytes -= chunk_bytes;
1285 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1286 end-of-file or on error. The bytes are translated into
1287 hexadecimal during copying and broken into lines with
1288 new-line characters. */
1290 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1294 for (i = 0; i < copy_bytes; i++)
1299 if (i > 0 && i % 36 == 0)
1301 fprintf (dst, "%02X", c);
1306 /* Embeds the given FONT into THIS driver's output stream. */
1308 embed_font (struct outp_driver *this, struct font *font)
1310 struct ps_driver_ext *x = this->ext;
1314 file = fopen (font->embed_fn, "rb");
1317 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1321 fprintf (x->file, "%%%%BeginResource: font %s\n",
1322 afm_get_findfont_name (font->metrics));
1328 /* PFA file. Copy literally. */
1329 copy_bytes_literally (file, x->file, ULONG_MAX);
1333 /* PFB file. Translate as specified in Adobe Technical
1335 while ((c = getc (file)) == 128)
1338 unsigned long length;
1344 length = getc (file);
1345 length |= (unsigned long) getc (file) << 8;
1346 length |= (unsigned long) getc (file) << 16;
1347 length |= (unsigned long) getc (file) << 24;
1350 copy_bytes_literally (file, x->file, length);
1352 copy_bytes_as_hex (file, x->file, length);
1357 if (freaderror (file))
1358 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1359 fputs ("%%EndResource\n", x->file);
1362 /* Re-encodes FONT according to the specified encoding. */
1364 reencode_font (struct outp_driver *this, struct font *font)
1366 struct ps_driver_ext *x = this->ext;
1377 file = fopen (font->encoding_fn, "r");
1380 error (errno, 0, _("cannot open font encoding file \"%s\""),
1385 for (i = 0; i < 256; i++)
1390 ds_init_empty (&line);
1391 while (ds_read_config_line (&line, &line_number, file))
1393 char *pschar, *code;
1394 char *save_ptr, *tail;
1397 if (ds_is_empty (&line) == 0)
1400 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1401 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1402 if (pschar == NULL || code == NULL)
1405 code_val = strtol (code, &tail, 0);
1408 error_at_line (0, 0, font->encoding_fn, line_number,
1409 _("invalid numeric format"));
1412 if (code_val < 0 || code_val > 255)
1414 if (tab[code_val] != 0)
1415 free (tab[code_val]);
1416 tab[code_val] = xstrdup (pschar);
1420 fputs ("[", x->file);
1421 for (i = 0; i < 256; i++)
1423 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1424 fprintf (x->file, "%s\n", name);
1428 fputs ("] RF\n", x->file);
1430 if (freaderror (file) != 0)
1431 error (errno, 0, _("closing Postscript encoding \"%s\""),
1435 /* PostScript driver class. */
1436 const struct outp_class postscript_class =