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_submit (struct outp_driver *this UNUSED, struct som_entity *s)
616 /* Draws a line from (x0,y0) to (x1,y1). */
618 dump_line (struct outp_driver *this, int x0, int y0, int x1, int y1)
620 struct ps_driver_ext *ext = this->ext;
621 fprintf (ext->file, "%d %d %d %d L\n", x0, YT (y0), x1, YT (y1));
624 /* Draws a horizontal line X0...X2 at Y if LEFT says so,
625 shortening it to X0...X1 if SHORTEN is true.
626 Draws a horizontal line X1...X3 at Y if RIGHT says so,
627 shortening it to X2...X3 if SHORTEN is true. */
629 horz_line (struct outp_driver *this,
630 int x0, int x1, int x2, int x3, int y,
631 enum outp_line_style left, enum outp_line_style right,
634 if (left != OUTP_L_NONE && right != OUTP_L_NONE && !shorten)
635 dump_line (this, x0, y, x3, y);
638 if (left != OUTP_L_NONE)
639 dump_line (this, x0, y, shorten ? x1 : x2, y);
640 if (right != OUTP_L_NONE)
641 dump_line (this, shorten ? x2 : x1, y, x3, y);
645 /* Draws a vertical line Y0...Y2 at X if TOP says so,
646 shortening it to Y0...Y1 if SHORTEN is true.
647 Draws a vertical line Y1...Y3 at X if BOTTOM says so,
648 shortening it to Y2...Y3 if SHORTEN is true. */
650 vert_line (struct outp_driver *this,
651 int y0, int y1, int y2, int y3, int x,
652 enum outp_line_style top, enum outp_line_style bottom,
655 if (top != OUTP_L_NONE && bottom != OUTP_L_NONE && !shorten)
656 dump_line (this, x, y0, x, y3);
659 if (top != OUTP_L_NONE)
660 dump_line (this, x, y0, x, shorten ? y1 : y2);
661 if (bottom != OUTP_L_NONE)
662 dump_line (this, x, shorten ? y2 : y1, x, y3);
666 /* Draws a generalized intersection of lines in the rectangle
667 (X0,Y0)-(X3,Y3). The line coming from the top to the center
668 is of style TOP, from left to center of style LEFT, from
669 bottom to center of style BOTTOM, and from right to center of
672 ps_line (struct outp_driver *this,
673 int x0, int y0, int x3, int y3,
674 enum outp_line_style top, enum outp_line_style left,
675 enum outp_line_style bottom, enum outp_line_style right)
677 /* The algorithm here is somewhat subtle, to allow it to handle
678 all the kinds of intersections that we need.
680 Three additional ordinates are assigned along the x axis. The
681 first is xc, midway between x0 and x3. The others are x1 and
682 x2; for a single vertical line these are equal to xc, and for
683 a double vertical line they are the ordinates of the left and
684 right half of the double line.
686 yc, y1, and y2 are assigned similarly along the y axis.
688 The following diagram shows the coordinate system and output
689 for double top and bottom lines, single left line, and no
693 y0 ________________________
699 y1 = y2 = yc |######### # |
704 y3 |________#_____#_______|
706 struct ps_driver_ext *ext = this->ext;
708 /* Offset from center of each line in a pair of double lines. */
709 int double_line_ofs = (ext->line_space + ext->line_width) / 2;
711 /* Are the lines along each axis single or double?
712 (It doesn't make sense to have different kinds of line on the
713 same axis, so we don't try to gracefully handle that case.) */
714 bool double_vert = top == OUTP_L_DOUBLE || bottom == OUTP_L_DOUBLE;
715 bool double_horz = left == OUTP_L_DOUBLE || right == OUTP_L_DOUBLE;
717 /* When horizontal lines are doubled,
718 the left-side line along y1 normally runs from x0 to x2,
719 and the right-side line along y1 from x3 to x1.
720 If the top-side line is also doubled, we shorten the y1 lines,
721 so that the left-side line runs only to x1,
722 and the right-side line only to x2.
723 Otherwise, the horizontal line at y = y1 below would cut off
724 the intersection, which looks ugly:
726 y0 ________________________
731 y1 |######### ########|
734 y2 |######################|
737 y3 |______________________|
738 It is more of a judgment call when the horizontal line is
739 single. We actually choose to cut off the line anyhow, as
740 shown in the first diagram above.
742 bool shorten_y1_lines = top == OUTP_L_DOUBLE;
743 bool shorten_y2_lines = bottom == OUTP_L_DOUBLE;
744 bool shorten_yc_line = shorten_y1_lines && shorten_y2_lines;
745 int horz_line_ofs = double_vert ? double_line_ofs : 0;
746 int xc = (x0 + x3) / 2;
747 int x1 = xc - horz_line_ofs;
748 int x2 = xc + horz_line_ofs;
750 bool shorten_x1_lines = left == OUTP_L_DOUBLE;
751 bool shorten_x2_lines = right == OUTP_L_DOUBLE;
752 bool shorten_xc_line = shorten_x1_lines && shorten_x2_lines;
753 int vert_line_ofs = double_horz ? double_line_ofs : 0;
754 int yc = (y0 + y3) / 2;
755 int y1 = yc - vert_line_ofs;
756 int y2 = yc + vert_line_ofs;
759 horz_line (this, x0, x1, x2, x3, yc, left, right, shorten_yc_line);
762 horz_line (this, x0, x1, x2, x3, y1, left, right, shorten_y1_lines);
763 horz_line (this, x0, x1, x2, x3, y2, left, right, shorten_y2_lines);
767 vert_line (this, y0, y1, y2, y3, xc, top, bottom, shorten_xc_line);
770 vert_line (this, y0, y1, y2, y3, x1, top, bottom, shorten_x1_lines);
771 vert_line (this, y0, y1, y2, y3, x2, top, bottom, shorten_x2_lines);
775 /* Writes STRING at location (X,Y) trimmed to the given MAX_WIDTH
776 and with the given JUSTIFICATION for THIS driver. */
778 draw_text (struct outp_driver *this,
779 const char *string, int x, int y, int max_width,
780 enum outp_justification justification)
782 struct outp_text text;
785 text.font = OUTP_PROPORTIONAL;
786 text.justification = justification;
787 text.string = ss_cstr (string);
789 text.v = this->font_height;
792 this->class->text_metrics (this, &text, &width, NULL);
793 this->class->text_draw (this, &text);
797 /* Writes LEFT left-justified and RIGHT right-justified within
798 (X0...X1) at Y. LEFT or RIGHT or both may be null. */
800 draw_header_line (struct outp_driver *this,
801 const char *left, const char *right,
802 int x0, int x1, int y)
806 right_width = (draw_text (this, right, x0, y, x1 - x0, OUTP_RIGHT)
807 + this->prop_em_width);
809 draw_text (this, left, x0, y, x1 - x0 - right_width, OUTP_LEFT);
812 /* Draw top of page headers for THIS driver. */
814 draw_headers (struct outp_driver *this)
816 struct ps_driver_ext *ext = this->ext;
821 y = -3 * this->font_height;
822 x0 = this->prop_em_width;
823 x1 = this->width - this->prop_em_width;
826 fprintf (ext->file, "%d %d %d %d GB\n",
828 this->width, YT (y + 2 * this->font_height + ext->line_gutter));
829 y += ext->line_width + ext->line_gutter;
831 r1 = xasprintf (_("%s - Page %d"), get_start_date (), ext->page_number);
832 r2 = xasprintf ("%s - %s", version, host_system);
834 draw_header_line (this, outp_title, r1, x0, x1, y);
835 y += this->font_height;
837 draw_header_line (this, outp_subtitle, r2, x0, x1, y);
843 /* Writes the CHAR_CNT characters in CHARS at (X0,Y0), using the
845 The characters are justified according to JUSTIFICATION in a
846 field that has WIDTH_LEFT space remaining after the characters
847 themselves are accounted for.
848 Before character I is written, its x-position is adjusted by
851 write_text (struct outp_driver *this,
854 enum outp_justification justification,
855 const struct afm_character **chars, int *kerns, size_t char_cnt,
858 struct ps_driver_ext *ext = this->ext;
859 struct afm *afm = ext->fonts[font]->metrics;
863 if (justification == OUTP_RIGHT)
865 else if (justification == OUTP_CENTER)
866 x0 += width_left / 2;
867 y0 += afm_get_ascent (afm) * this->font_height / 1000;
869 fprintf (ext->file, "\n%d %d moveto\n", x0, YT (y0));
871 if (ext->last_font != font)
873 ext->last_font = font;
874 fprintf (ext->file, "F%d setfont\n", font);
877 ds_init_empty (&out);
878 for (i = 0; i < char_cnt; i = j)
880 for (j = i + 1; j < char_cnt; j++)
885 fprintf (ext->file, "%d K", kerns[i]);
888 size_t encoded = afm_encode_string (afm, chars + i, j - i, &out);
891 fprintf (ext->file, "%sS\n", ds_cstr (&out));
898 fprintf (ext->file, "/%s GS\n", chars[i]->name);
906 /* State of a text formatting operation. */
910 const struct outp_text *text;
914 const struct afm_character **glyphs;
918 size_t glyph_cnt; /* Number of glyphs output. */
919 int width_left; /* Width left over. */
920 int height_left; /* Height left over. */
922 /* State as of last space. */
923 const char *space_char; /* Just past last space. */
924 size_t space_glyph_cnt; /* Number of glyphs as of last space. */
925 int space_width_left; /* Width left over as of last space. */
928 int max_width; /* Widest line so far. */
931 /* Adjusts S to complete a line of text,
932 and draws the current line if appropriate. */
934 finish_line (struct outp_driver *this, struct text_state *s)
941 s->text->x, s->text->y + (s->text->v - s->height_left),
943 s->text->justification,
944 s->glyphs, s->glyph_kerns, s->glyph_cnt,
949 /* Update maximum width. */
950 width = s->text->h - s->width_left;
951 if (width > s->max_width)
952 s->max_width = width;
954 /* Move to next line. */
955 s->width_left = s->text->h;
956 s->height_left -= this->font_height;
958 /* No spaces on this line yet. */
959 s->space_char = NULL;
962 /* Format TEXT on THIS driver.
963 If DRAW is nonzero, draw the text.
964 The width of the widest line is stored into *WIDTH, if WIDTH
966 The total height of the text written is stored into *HEIGHT,
967 if HEIGHT is nonnull. */
969 text (struct outp_driver *this, const struct outp_text *text, bool draw,
970 int *width, int *height)
972 struct ps_driver_ext *ext = this->ext;
973 struct afm *afm = ext->fonts[text->font]->metrics;
982 s.glyph_kerns = NULL;
986 s.width_left = s.text->h;
987 s.height_left = s.text->v;
993 cp = ss_data (s.text->string);
994 while (s.height_left >= this->font_height && cp < ss_end (s.text->string))
996 const struct afm_character *cur;
1002 finish_line (this, &s);
1007 /* Get character and resolve ligatures. */
1008 cur = afm_get_character (afm, *cp);
1009 while (++cp < ss_end (s.text->string))
1011 const struct afm_character *next = afm_get_character (afm, *cp);
1012 const struct afm_character *ligature = afm_get_ligature (cur, next);
1013 if (ligature == NULL)
1017 char_width = cur->width * this->font_height / 1000;
1019 /* Get kern adjustment. */
1020 if (s.glyph_cnt > 0)
1021 kern_adjust = (afm_get_kern_adjustment (s.glyphs[s.glyph_cnt - 1], cur)
1022 * this->font_height / 1000);
1026 /* Record the current status if this is a space character. */
1027 if (cur->code == ' ' && cp > ss_data (s.text->string))
1030 s.space_glyph_cnt = s.glyph_cnt;
1031 s.space_width_left = s.width_left;
1034 /* Enough room on this line? */
1035 if (char_width + kern_adjust > s.width_left)
1037 if (s.space_char == NULL)
1039 finish_line (this, &s);
1045 s.glyph_cnt = s.space_glyph_cnt;
1046 s.width_left = s.space_width_left;
1047 finish_line (this, &s);
1052 if (s.glyph_cnt >= glyph_cap)
1054 glyph_cap = 2 * (glyph_cap + 8);
1055 s.glyphs = xnrealloc (s.glyphs, glyph_cap, sizeof *s.glyphs);
1056 s.glyph_kerns = xnrealloc (s.glyph_kerns,
1057 glyph_cap, sizeof *s.glyph_kerns);
1059 s.glyphs[s.glyph_cnt] = cur;
1060 s.glyph_kerns[s.glyph_cnt] = kern_adjust;
1063 s.width_left -= char_width + kern_adjust;
1065 if (s.height_left >= this->font_height && s.glyph_cnt > 0)
1066 finish_line (this, &s);
1069 *width = s.max_width;
1071 *height = text->v - s.height_left;
1073 free (s.glyph_kerns);
1077 ps_text_metrics (struct outp_driver *this, const struct outp_text *t,
1078 int *width, int *height)
1080 text (this, t, false, width, height);
1084 ps_text_draw (struct outp_driver *this, const struct outp_text *t)
1086 assert (this->page_open);
1087 text (this, t, true, NULL, NULL);
1090 static void embed_font (struct outp_driver *this, struct font *font);
1091 static void reencode_font (struct outp_driver *this, struct font *font);
1093 /* Loads and returns the font for STRING, which has the format
1094 "AFM,PFA,ENC", where AFM is the AFM file's name, PFA is the
1095 PFA or PFB file's name, and ENC is the encoding file's name.
1096 PFA and ENC are optional.
1097 Returns a null pointer if unsuccessful. */
1098 static struct font *
1099 load_font (const char *string_)
1101 char *string = xstrdup (string_);
1103 char *position = string;
1105 char *afm_file_name;
1107 font = xmalloc (sizeof *font);
1108 font->metrics = NULL;
1109 font->embed_fn = NULL;
1110 font->encoding_fn = NULL;
1112 token = strsep (&position, ",");
1115 error (0, 0, _("\"%s\": bad font specification"), string);
1119 /* Read AFM file. */
1120 afm_file_name = find_ps_file (token);
1121 if (afm_file_name == NULL)
1123 error (0, 0, _("could not find AFM file \"%s\""), token);
1126 font->metrics = afm_open (afm_file_name);
1127 free (afm_file_name);
1128 if (font->metrics == NULL)
1131 /* Find font file to embed. */
1132 token = strsep (&position, ",");
1133 if (token != NULL && *token != '\0')
1135 font->embed_fn = find_ps_file (token);
1136 if (font->embed_fn == NULL)
1137 error (0, 0, _("could not find font \"%s\""), token);
1140 /* Find encoding. */
1141 token = strsep (&position, ",");
1142 if (token != NULL && *token == '\0')
1144 font->encoding_fn = find_ps_file (token);
1145 if (font->encoding_fn == NULL)
1146 error (0, 0, _("could not find encoding \"%s\""), token);
1160 free_font (struct font *font)
1164 afm_close (font->metrics);
1165 free (font->embed_fn);
1166 free (font->encoding_fn);
1171 /* Emits PostScript code to embed FONT (if necessary), scale it
1172 to the proper size, re-encode it (if necessary), and store the
1173 resulting font as an object named F#, where INDEX is
1174 substituted for #. */
1176 setup_font (struct outp_driver *this, struct font *font, int index)
1178 struct ps_driver_ext *x = this->ext;
1181 if (font->embed_fn != NULL)
1182 embed_font (this, font);
1184 fprintf (x->file, "%%%%IncludeResource: font %s\n",
1185 afm_get_findfont_name (font->metrics));
1187 ps_name = quote_ps_name (afm_get_findfont_name (font->metrics));
1188 fprintf (x->file, "%s findfont %d scalefont\n", ps_name, this->font_height);
1191 if (font->encoding_fn != NULL)
1192 reencode_font (this, font);
1194 fprintf (x->file, "/F%d ED\n", index);
1197 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1198 end-of-file or on error. */
1200 copy_bytes_literally (FILE *src, FILE *dst, unsigned long copy_bytes)
1202 while (copy_bytes > 0)
1204 char buffer[BUFSIZ];
1205 unsigned long chunk_bytes = MIN (copy_bytes, sizeof buffer);
1206 size_t read_bytes = fread (buffer, 1, chunk_bytes, src);
1207 size_t write_bytes = fwrite (buffer, 1, read_bytes, dst);
1208 if (write_bytes != chunk_bytes)
1210 copy_bytes -= chunk_bytes;
1214 /* Copies up to COPY_BYTES bytes from SRC to DST, stopping at
1215 end-of-file or on error. The bytes are translated into
1216 hexadecimal during copying and broken into lines with
1217 new-line characters. */
1219 copy_bytes_as_hex (FILE *src, FILE *dst, unsigned long copy_bytes)
1223 for (i = 0; i < copy_bytes; i++)
1228 if (i > 0 && i % 36 == 0)
1230 fprintf (dst, "%02X", c);
1235 /* Embeds the given FONT into THIS driver's output stream. */
1237 embed_font (struct outp_driver *this, struct font *font)
1239 struct ps_driver_ext *x = this->ext;
1243 file = fopen (font->embed_fn, "rb");
1246 error (errno, 0, _("cannot open font file \"%s\""), font->embed_fn);
1250 fprintf (x->file, "%%%%BeginResource: font %s\n",
1251 afm_get_findfont_name (font->metrics));
1257 /* PFA file. Copy literally. */
1258 copy_bytes_literally (file, x->file, ULONG_MAX);
1262 /* PFB file. Translate as specified in Adobe Technical
1264 while ((c = getc (file)) == 128)
1267 unsigned long length;
1273 length = getc (file);
1274 length |= (unsigned long) getc (file) << 8;
1275 length |= (unsigned long) getc (file) << 16;
1276 length |= (unsigned long) getc (file) << 24;
1279 copy_bytes_literally (file, x->file, length);
1281 copy_bytes_as_hex (file, x->file, length);
1286 if (freaderror (file))
1287 error (errno, 0, _("reading font file \"%s\""), font->embed_fn);
1288 fputs ("%%EndResource\n", x->file);
1291 /* Re-encodes FONT according to the specified encoding. */
1293 reencode_font (struct outp_driver *this, struct font *font)
1295 struct ps_driver_ext *x = this->ext;
1306 file = fopen (font->encoding_fn, "r");
1309 error (errno, 0, _("cannot open font encoding file \"%s\""),
1314 for (i = 0; i < 256; i++)
1319 ds_init_empty (&line);
1320 while (ds_read_config_line (&line, &line_number, file))
1322 char *pschar, *code;
1323 char *save_ptr, *tail;
1326 if (ds_is_empty (&line) == 0)
1329 pschar = strtok_r (ds_cstr (&line), " \t\r\n", &save_ptr);
1330 code = strtok_r (NULL, " \t\r\n", &save_ptr);
1331 if (pschar == NULL || code == NULL)
1334 code_val = strtol (code, &tail, 0);
1337 error_at_line (0, 0, font->encoding_fn, line_number,
1338 _("invalid numeric format"));
1341 if (code_val < 0 || code_val > 255)
1343 if (tab[code_val] != 0)
1344 free (tab[code_val]);
1345 tab[code_val] = xstrdup (pschar);
1349 fputs ("[", x->file);
1350 for (i = 0; i < 256; i++)
1352 char *name = quote_ps_name (tab[i] != NULL ? tab[i] : ".notdef");
1353 fprintf (x->file, "%s\n", name);
1357 fputs ("] RF\n", x->file);
1359 if (freaderror (file) != 0)
1360 error (errno, 0, _("closing Postscript encoding \"%s\""),
1364 /* PostScript driver class. */
1365 const struct outp_class postscript_class =
1377 NULL, /* output_chart */