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 struct chart_geometry geom;
611 plPlotterParams *params;
612 int x_origin, y_origin;
619 /* Create temporary file for chart. */
623 error (0, errno, _("failed to create temporary file"));
627 /* Create plotter for chart. */
628 size = this->width < this->length ? this->width : this->length;
629 x_origin = x->left_margin + (size - this->width) / 2;
630 y_origin = x->bottom_margin + (size - this->length) / 2;
631 page_size = xasprintf ("a,xsize=%.3f,ysize=%.3f,xorigin=%.3f,yorigin=%.3f",
632 (double) size / PSUS, (double) size / PSUS,
633 (double) x_origin / PSUS, (double) y_origin / PSUS);
635 params = pl_newplparams ();
636 pl_setplparam (params, "PAGESIZE", page_size);
638 lp = pl_newpl_r ("ps", 0, file, stderr, params);
639 pl_deleteplparams (params);
647 /* Draw chart and free plotter. */
648 chart_geometry_init (lp, &geom, 1000.0, 1000.0);
649 chart_draw (chart, lp, &geom);
650 chart_geometry_free (lp);
653 /* Write prologue for chart. */
654 outp_eject_page (this);
657 "%d %d translate 1000 dup scale\n"
659 "/showpage { } def\n"
660 "0 setgray 0 setlinecap 1 setlinewidth\n"
661 "0 setlinejoin 10 setmiterlimit [ ] 0 setdash newpath clear\n"
662 "%%%%BeginDocument: %d\n",
663 -x->left_margin, -x->bottom_margin,
666 /* Copy chart into output file. */
668 while (fwrite (buf, 1, fread (buf, 1, sizeof buf, file), x->file))
672 /* Write epilogue for chart. */
673 fputs ("%%EndDocument\n"
677 outp_close_page (this);
681 ps_submit (struct outp_driver *this UNUSED, struct som_entity *s)
690 /* Draws a line from (x0,y0) to (x1,y1). */
692 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
694 struct ps_driver_ext *ext = this->ext;
695 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
698 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
699 shortening it to X0...X1 if SHORTEN is true.
700 Draws a horizontal line X1...X3 at Y if RIGHT says so,
701 shortening it to X2...X3 if SHORTEN is true. */
703 horz_line (struct outp_driver *this,
704 int x0, int x1, int x2, int x3, int y,
705 enum outp_line_style left, enum outp_line_style right,
708 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
709 dump_line (this, x0, y, x3, y);
712 if (left != OUTP_L_NONE)
713 dump_line (this, x0, y, shorten ? x1 : x2, y);
714 if (right != OUTP_L_NONE)
715 dump_line (this, shorten ? x2 : x1, y, x3, y);
719 /* Draws a vertical line Y0...Y2 at X if TOP says so,
720 shortening it to Y0...Y1 if SHORTEN is true.
721 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
722 shortening it to Y2...Y3 if SHORTEN is true. */
724 vert_line (struct outp_driver *this,
725 int y0, int y1, int y2, int y3, int x,
726 enum outp_line_style top, enum outp_line_style bottom,
729 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
730 dump_line (this, x, y0, x, y3);
733 if (top != OUTP_L_NONE)
734 dump_line (this, x, y0, x, shorten ? y1 : y2);
735 if (bottom != OUTP_L_NONE)
736 dump_line (this, x, shorten ? y2 : y1, x, y3);
740 /* Draws a generalized intersection of lines in the rectangle
741 (X0,Y0)-(X3,Y3). The line coming from the top to the center
742 is of style TOP, from left to center of style LEFT, from
743 bottom to center of style BOTTOM, and from right to center of
746 ps_line (struct outp_driver *this,
747 int x0, int y0, int x3, int y3,
748 enum outp_line_style top, enum outp_line_style left,
749 enum outp_line_style bottom, enum outp_line_style right)
751 /* The algorithm here is somewhat subtle, to allow it to handle
752 all the kinds of intersections that we need.
754 Three additional ordinates are assigned along the x axis. The
755 first is xc, midway between x0 and x3. The others are x1 and
756 x2; for a single vertical line these are equal to xc, and for
757 a double vertical line they are the ordinates of the left and
758 right half of the double line.
760 yc, y1, and y2 are assigned similarly along the y axis.
762 The following diagram shows the coordinate system and output
763 for double top and bottom lines, single left line, and no
767 y0 ________________________
773 y1 = y2 = yc |######### # |
778 y3 |________#_____#_______|
780 struct ps_driver_ext *ext = this->ext;
782 /* Offset from center of each line in a pair of double lines. */
783 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
785 /* Are the lines along each axis single or double?
786 (It doesn't make sense to have different kinds of line on the
787 same axis, so we don't try to gracefully handle that case.) */
788 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
789 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
791 /* When horizontal lines are doubled,
792 the left-side line along y1 normally runs from x0 to x2,
793 and the right-side line along y1 from x3 to x1.
794 If the top-side line is also doubled, we shorten the y1 lines,
795 so that the left-side line runs only to x1,
796 and the right-side line only to x2.
797 Otherwise, the horizontal line at y = y1 below would cut off
798 the intersection, which looks ugly:
800 y0 ________________________
805 y1 |######### ########|
808 y2 |######################|
811 y3 |______________________|
812 It is more of a judgment call when the horizontal line is
813 single. We actually choose to cut off the line anyhow, as
814 shown in the first diagram above.
816 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
817 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
818 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
819 int horz_line_ofs = double_vert ? double_line_ofs : 0;
820 int xc = (x0 + x3) / 2;
821 int x1 = xc - horz_line_ofs;
822 int x2 = xc + horz_line_ofs;
824 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
825 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
826 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
827 int vert_line_ofs = double_horz ? double_line_ofs : 0;
828 int yc = (y0 + y3) / 2;
829 int y1 = yc - vert_line_ofs;
830 int y2 = yc + vert_line_ofs;
833 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
836 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
837 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
841 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
844 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
845 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
849 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
850 and with the given JUSTIFICATION for THIS driver. */
852 draw_text (struct outp_driver *this,
853 const char *string, int x, int y, int max_width,
854 enum outp_justification justification)
856 struct outp_text text;
859 text.font = OUTP_PROPORTIONAL;
860 text.justification = justification;
861 text.string = ss_cstr (string);
863 text.v = this->font_height;
866 this->class->text_metrics (this, &text, &width, NULL);
867 this->class->text_draw (this, &text);
871 /* Writes LEFT left-justified and RIGHT right-justified within
872 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
874 draw_header_line (struct outp_driver *this,
875 const char *left, const char *right,
876 int x0, int x1, int y)
880 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
881 + this->prop_em_width);
883 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
886 /* Draw top of page headers for THIS driver. */
888 draw_headers (struct outp_driver *this)
890 struct ps_driver_ext *ext = this->ext;
895 y = -3 * this->font_height;
896 x0 = this->prop_em_width;
897 x1 = this->width - this->prop_em_width;
900 fprintf (ext->file, "%d %d %d %d GB\n",
902 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
903 y += ext->line_width + ext->line_gutter;
905 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
906 r2 = xasprintf ("%s - %s", version, host_system);
908 draw_header_line (this, outp_title, r1, x0, x1, y);
909 y += this->font_height;
911 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
917 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
919 The characters are justified according to JUSTIFICATION in a
920 field that has WIDTH_LEFT space remaining after the characters
921 themselves are accounted for.
922 Before character I is written, its x-position is adjusted by
925 write_text (struct outp_driver *this,
928 enum outp_justification justification,
929 const struct afm_character **chars, int *kerns, size_t char_cnt,
932 struct ps_driver_ext *ext = this->ext;
933 struct afm *afm = ext->fonts[font]->metrics;
937 if (justification == OUTP_RIGHT)
939 else if (justification == OUTP_CENTER)
940 x0 += width_left / 2;
941 y0 += afm_get_ascent (afm) * this->font_height / 1000;
943 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
945 if (ext->last_font != font)
947 ext->last_font = font;
948 fprintf (ext->file, "F%d setfont\n", font);
951 ds_init_empty (&out);
952 for (i = 0; i < char_cnt; i = j)
954 for (j = i + 1; j < char_cnt; j++)
959 fprintf (ext->file, "%d K", kerns[i]);
962 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
965 fprintf (ext->file, "%sS\n", ds_cstr (&out));
972 fprintf (ext->file, "/%s GS\n", chars[i]->name);
980 /* State of a text formatting operation. */
984 const struct outp_text *text;
988 const struct afm_character **glyphs;
992 size_t glyph_cnt; /* Number of glyphs output. */
993 int width_left; /* Width left over. */
994 int height_left; /* Height left over. */
996 /* State as of last space. */
997 const char *space_char; /* Just past last space. */
998 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
999 int space_width_left; /* Width left over as of last space. */
1002 int max_width; /* Widest line so far. */
1005 /* Adjusts S to complete a line of text,
1006 and draws the current line if appropriate. */
1008 finish_line (struct outp_driver *this, struct text_state *s)
1015 s->text->x, s->text->y + (s->text->v - s->height_left),
1017 s->text->justification,
1018 s->glyphs, s->glyph_kerns, s->glyph_cnt,
1023 /* Update maximum width. */
1024 width = s->text->h - s->width_left;
1025 if (width > s->max_width)
1026 s->max_width = width;
1028 /* Move to next line. */
1029 s->width_left = s->text->h;
1030 s->height_left -= this->font_height;
1032 /* No spaces on this line yet. */
1033 s->space_char = NULL;
1036 /* Format TEXT on THIS driver.
1037 If DRAW is nonzero, draw the text.
1038 The width of the widest line is stored into *WIDTH, if WIDTH
1040 The total height of the text written is stored into *HEIGHT,
1041 if HEIGHT is nonnull. */
1043 text (struct outp_driver *this, const struct outp_text *text, bool draw,
1044 int *width, int *height)
1046 struct ps_driver_ext *ext = this->ext;
1047 struct afm *afm = ext->fonts[text->font]->metrics;
1050 struct text_state s;
1056 s.glyph_kerns = NULL;
1060 s.width_left = s.text->h;
1061 s.height_left = s.text->v;
1067 cp = ss_data (s.text->string);
1068 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
1070 const struct afm_character *cur;
1076 finish_line (this, &s);
1081 /* Get character and resolve ligatures. */
1082 cur = afm_get_character (afm, *cp);
1083 while (++cp < ss_end (s.text->string))
1085 const struct afm_character *next = afm_get_character (afm, *cp);
1086 const struct afm_character *ligature = afm_get_ligature (cur, next);
1087 if (ligature == NULL)
1091 char_width = cur->width * this->font_height / 1000;
1093 /* Get kern adjustment. */
1094 if (s.glyph_cnt > 0)
1095 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1096 * this->font_height / 1000);
1100 /* Record the current status if this is a space character. */
1101 if (cur->code == ' ' && cp > ss_data (s.text->string))
1104 s.space_glyph_cnt = s.glyph_cnt;
1105 s.space_width_left = s.width_left;
1108 /* Enough room on this line? */
1109 if (char_width + kern_adjust > s.width_left)
1111 if (s.space_char == NULL)
1113 finish_line (this, &s);
1119 s.glyph_cnt = s.space_glyph_cnt;
1120 s.width_left = s.space_width_left;
1121 finish_line (this, &s);
1126 if (s.glyph_cnt >= glyph_cap)
1128 glyph_cap = 2 * (glyph_cap + 8);
1129 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1130 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1131 glyph_cap, sizeof *s.glyph_kerns);
1133 s.glyphs[s.glyph_cnt] = cur;
1134 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1137 s.width_left -= char_width + kern_adjust;
1139 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1140 finish_line (this, &s);
1143 *width = s.max_width;
1145 *height = text->v - s.height_left;
1147 free (s.glyph_kerns);
1151 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1152 int *width, int *height)
1154 text (this, t, false, width, height);
1158 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1160 assert (this->page_open);
1161 text (this, t, true, NULL, NULL);
1164 static void embed_font (struct outp_driver *this, struct font *font);
1165 static void reencode_font (struct outp_driver *this, struct font *font);
1167 /* Loads and returns the font for STRING, which has the format
1168 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1169 PFA or PFB file's name, and ENC is the encoding file's name.
1170 PFA and ENC are optional.
1171 Returns a null pointer if unsuccessful. */
1172 static struct font *
1173 load_font (const char *string_)
1175 char *string = xstrdup (string_);
1177 char *position = string;
1179 char *afm_file_name;
1181 font = xmalloc (sizeof *font);
1182 font->metrics = NULL;
1183 font->embed_fn = NULL;
1184 font->encoding_fn = NULL;
1186 token = strsep (&position, ",");
1189 error (0, 0, _("\"%s\": bad font specification"), string);
1193 /* Read AFM file. */
1194 afm_file_name = find_ps_file (token);
1195 if (afm_file_name == NULL)
1197 error (0, 0, _("could not find AFM file \"%s\""), token);
1200 font->metrics = afm_open (afm_file_name);
1201 free (afm_file_name);
1202 if (font->metrics == NULL)
1205 /* Find font file to embed. */
1206 token = strsep (&position, ",");
1207 if (token != NULL && *token != '\0')
1209 font->embed_fn = find_ps_file (token);
1210 if (font->embed_fn == NULL)
1211 error (0, 0, _("could not find font \"%s\""), token);
1214 /* Find encoding. */
1215 token = strsep (&position, ",");
1216 if (token != NULL && *token == '\0')
1218 font->encoding_fn = find_ps_file (token);
1219 if (font->encoding_fn == NULL)
1220 error (0, 0, _("could not find encoding \"%s\""), token);
1234 free_font (struct font *font)
1238 afm_close (font->metrics);
1239 free (font->embed_fn);
1240 free (font->encoding_fn);
1245 /* Emits PostScript code to embed FONT (if necessary), scale it
1246 to the proper size, re-encode it (if necessary), and store the
1247 resulting font as an object named F#, where INDEX is
1248 substituted for #. */
1250 setup_font (struct outp_driver *this, struct font *font, int index)
1252 struct ps_driver_ext *x = this->ext;
1255 if (font->embed_fn != NULL)
1256 embed_font (this, font);
1258 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1259 afm_get_findfont_name (font->metrics));
1261 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1262 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1265 if (font->encoding_fn != NULL)
1266 reencode_font (this, font);
1268 fprintf (x->file, "/F%d ED\n", index);
1271 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1272 end-of-file or on error. */
1274 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1276 while (copy_bytes > 0)
1278 char buffer[BUFSIZ];
1279 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1280 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1281 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1282 if (write_bytes != chunk_bytes)
1284 copy_bytes -= chunk_bytes;
1288 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1289 end-of-file or on error. The bytes are translated into
1290 hexadecimal during copying and broken into lines with
1291 new-line characters. */
1293 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1297 for (i = 0; i < copy_bytes; i++)
1302 if (i > 0 && i % 36 == 0)
1304 fprintf (dst, "%02X", c);
1309 /* Embeds the given FONT into THIS driver's output stream. */
1311 embed_font (struct outp_driver *this, struct font *font)
1313 struct ps_driver_ext *x = this->ext;
1317 file = fopen (font->embed_fn, "rb");
1320 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1324 fprintf (x->file, "%%%%BeginResource: font %s\n",
1325 afm_get_findfont_name (font->metrics));
1331 /* PFA file. Copy literally. */
1332 copy_bytes_literally (file, x->file, ULONG_MAX);
1336 /* PFB file. Translate as specified in Adobe Technical
1338 while ((c = getc (file)) == 128)
1341 unsigned long length;
1347 length = getc (file);
1348 length |= (unsigned long) getc (file) << 8;
1349 length |= (unsigned long) getc (file) << 16;
1350 length |= (unsigned long) getc (file) << 24;
1353 copy_bytes_literally (file, x->file, length);
1355 copy_bytes_as_hex (file, x->file, length);
1360 if (freaderror (file))
1361 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1362 fputs ("%%EndResource\n", x->file);
1365 /* Re-encodes FONT according to the specified encoding. */
1367 reencode_font (struct outp_driver *this, struct font *font)
1369 struct ps_driver_ext *x = this->ext;
1380 file = fopen (font->encoding_fn, "r");
1383 error (errno, 0, _("cannot open font encoding file \"%s\""),
1388 for (i = 0; i < 256; i++)
1393 ds_init_empty (&line);
1394 while (ds_read_config_line (&line, &line_number, file))
1396 char *pschar, *code;
1397 char *save_ptr, *tail;
1400 if (ds_is_empty (&line) == 0)
1403 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1404 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1405 if (pschar == NULL || code == NULL)
1408 code_val = strtol (code, &tail, 0);
1411 error_at_line (0, 0, font->encoding_fn, line_number,
1412 _("invalid numeric format"));
1415 if (code_val < 0 || code_val > 255)
1417 if (tab[code_val] != 0)
1418 free (tab[code_val]);
1419 tab[code_val] = xstrdup (pschar);
1423 fputs ("[", x->file);
1424 for (i = 0; i < 256; i++)
1426 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1427 fprintf (x->file, "%s\n", name);
1431 fputs ("] RF\n", x->file);
1433 if (freaderror (file) != 0)
1434 error (errno, 0, _("closing Postscript encoding \"%s\""),
1438 /* PostScript driver class. */
1439 const struct outp_class postscript_class =