2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 * PostScript documentation:
24 * http://www.adobe.com/products/postscript/pdfs/PLRM.pdf
25 * http://partners.adobe.com/asn/developer/pdfs/tn/5001.DSC_Spec.pdf
29 #include "stringclass.h"
37 #ifdef NEED_DECLARATION_PUTENV
39 int putenv(const char *);
41 #endif /* NEED_DECLARATION_PUTENV */
43 extern "C" const char *Version_string
;
45 // search path defaults to the current directory
46 search_path
include_search_path(0, 0, 0, 1);
48 static int landscape_flag
= 0;
49 static int manual_feed_flag
= 0;
50 static int ncopies
= 1;
51 static int linewidth
= -1;
52 // Non-zero means generate PostScript code that guesses the paper
53 // length using the imageable area.
54 static int guess_flag
= 0;
55 static double user_paper_length
= 0;
56 static double user_paper_width
= 0;
58 // Non-zero if -b was specified on the command line.
60 unsigned broken_flags
= 0;
62 // Non-zero means we need the CMYK extension for PostScript Level 1
63 static int cmyk_flag
= 0;
65 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
66 #define MAX_LINE_LENGTH 72
69 const char *const dict_name
= "grops";
70 const char *const defs_dict_name
= "DEFS";
71 const int DEFS_DICT_SPARE
= 50;
73 double degrees(double r
)
78 double radians(double d
)
83 // This is used for testing whether a character should be output in the
84 // PostScript file using \nnn, so we really want the character to be
87 inline int is_ascii(char c
)
89 return (unsigned char)c
< 0200;
92 ps_output::ps_output(FILE *f
, int n
)
93 : fp(f
), col(0), max_line_length(n
), need_space(0), fixed_point(0)
97 ps_output
&ps_output::set_file(FILE *f
)
104 ps_output
&ps_output::copy_file(FILE *infp
)
107 while ((c
= getc(infp
)) != EOF
)
112 ps_output
&ps_output::end_line()
122 ps_output
&ps_output::special(const char *s
)
124 if (s
== 0 || *s
== '\0')
131 if (strchr(s
, '\0')[-1] != '\n')
137 ps_output
&ps_output::simple_comment(const char *s
)
150 ps_output
&ps_output::begin_comment(const char *s
)
161 ps_output
&ps_output::end_comment()
171 ps_output
&ps_output::comment_arg(const char *s
)
174 if (col
+ len
+ 1 > max_line_length
) {
185 ps_output
&ps_output::set_fixed_point(int n
)
187 assert(n
>= 0 && n
<= 10);
192 ps_output
&ps_output::put_delimiter(char c
)
194 if (col
+ 1 > max_line_length
) {
204 ps_output
&ps_output::put_string(const char *s
, int n
)
208 for (i
= 0; i
< n
; i
++) {
210 if (is_ascii(c
) && csprint(c
)) {
211 if (c
== '(' || c
== ')' || c
== '\\')
220 if (col
+ n
*2 + 2 > max_line_length
&& n
*2 + 2 <= max_line_length
) {
224 if (col
+ 1 > max_line_length
) {
230 for (i
= 0; i
< n
; i
++) {
231 if (col
+ 2 > max_line_length
) {
235 fprintf(fp
, "%02x", s
[i
] & 0377);
242 if (col
+ len
+ 2 > max_line_length
&& len
+ 2 <= max_line_length
) {
246 if (col
+ 2 > max_line_length
) {
252 for (i
= 0; i
< n
; i
++) {
254 if (is_ascii(c
) && csprint(c
)) {
255 if (c
== '(' || c
== ')' || c
== '\\')
262 if (col
+ len
+ 1 > max_line_length
) {
276 fprintf(fp
, "\\%03o", c
& 0377);
290 ps_output
&ps_output::put_number(int n
)
292 char buf
[1 + INT_DIGITS
+ 1];
293 sprintf(buf
, "%d", n
);
294 int len
= strlen(buf
);
295 if (col
> 0 && col
+ len
+ need_space
> max_line_length
) {
310 ps_output
&ps_output::put_fix_number(int i
)
312 const char *p
= if_to_a(i
, fixed_point
);
314 if (col
> 0 && col
+ len
+ need_space
> max_line_length
) {
329 ps_output
&ps_output::put_float(double d
)
332 sprintf(buf
, "%.4f", d
);
333 int last
= strlen(buf
) - 1;
334 while (buf
[last
] == '0')
336 if (buf
[last
] == '.')
339 if (col
> 0 && col
+ last
+ need_space
> max_line_length
) {
354 ps_output
&ps_output::put_symbol(const char *s
)
357 if (col
> 0 && col
+ len
+ need_space
> max_line_length
) {
372 ps_output
&ps_output::put_color(unsigned int c
)
375 sprintf(buf
, "%.3g", double(c
) / color::MAX_COLOR_VAL
);
376 int len
= strlen(buf
);
377 if (col
> 0 && col
+ len
+ need_space
> max_line_length
) {
392 ps_output
&ps_output::put_literal_symbol(const char *s
)
395 if (col
> 0 && col
+ len
+ 1 > max_line_length
) {
406 class ps_font
: public font
{
407 ps_font(const char *);
411 char *reencoded_name
;
413 void handle_unknown_font_command(const char *command
, const char *arg
,
414 const char *filename
, int lineno
);
415 static ps_font
*load_ps_font(const char *);
418 ps_font
*ps_font::load_ps_font(const char *s
)
420 ps_font
*f
= new ps_font(s
);
428 ps_font::ps_font(const char *nm
)
429 : font(nm
), encoding_index(-1), encoding(0), reencoded_name(0)
436 a_delete reencoded_name
;
439 void ps_font::handle_unknown_font_command(const char *command
, const char *arg
,
440 const char *filename
, int lineno
)
442 if (strcmp(command
, "encoding") == 0) {
444 error_with_file_and_line(filename
, lineno
,
445 "`encoding' command requires an argument");
447 encoding
= strsave(arg
);
451 static void handle_unknown_desc_command(const char *command
, const char *arg
,
452 const char *filename
, int lineno
)
454 if (strcmp(command
, "broken") == 0) {
456 error_with_file_and_line(filename
, lineno
,
457 "`broken' command requires an argument");
459 broken_flags
= atoi(arg
);
468 const char *glyphs
[256];
471 subencoding(font
*, unsigned int, int, subencoding
*);
475 subencoding::subencoding(font
*f
, unsigned int n
, int ix
, subencoding
*s
)
476 : p(f
), num(n
), idx(ix
), subfont(0), next(s
)
478 for (int i
= 0; i
< 256; i
++)
482 subencoding::~subencoding()
494 style(font
*, subencoding
*, int, int, int);
495 int operator==(const style
&) const;
496 int operator!=(const style
&) const;
499 style::style() : f(0)
503 style::style(font
*p
, subencoding
*s
, int sz
, int h
, int sl
)
504 : f(p
), sub(s
), point_size(sz
), height(h
), slant(sl
)
508 int style::operator==(const style
&s
) const
512 && point_size
== s
.point_size
513 && height
== s
.height
514 && slant
== s
.slant
);
517 int style::operator!=(const style
&s
) const
519 return !(*this == s
);
522 class ps_printer
: public printer
{
526 int space_char_index
;
530 enum { SBUF_SIZE
= 256 };
531 char sbuf
[SBUF_SIZE
];
536 int sbuf_space_width
;
537 int sbuf_space_count
;
538 int sbuf_space_diff_count
;
542 color sbuf_color
; // the current PS color
544 subencoding
*subencodings
;
547 int output_draw_point_size
;
549 int output_line_thickness
;
550 unsigned char output_space_code
;
551 enum { MAX_DEFINED_STYLES
= 50 };
552 style defined_styles
[MAX_DEFINED_STYLES
];
554 int next_encoding_index
;
555 int next_subencoding_index
;
562 void set_style(const style
&);
563 void set_space_code(unsigned char c
);
564 int set_encoding_index(ps_font
*);
565 subencoding
*set_subencoding(font
*, int, unsigned char *);
566 char *get_subfont(subencoding
*, const char *);
567 void do_exec(char *, const environment
*);
568 void do_import(char *, const environment
*);
569 void do_def(char *, const environment
*);
570 void do_mdef(char *, const environment
*);
571 void do_file(char *, const environment
*);
572 void do_invis(char *, const environment
*);
573 void do_endinvis(char *, const environment
*);
574 void set_line_thickness_and_color(const environment
*);
575 void fill_path(const environment
*);
577 void encode_subfont(subencoding
*);
578 void define_encoding(const char *, int);
579 void reencode_font(ps_font
*);
580 void set_color(color
*c
, int fill
= 0);
582 const char *media_name();
590 void set_char(int i
, font
*f
, const environment
*env
, int w
,
592 void draw(int code
, int *p
, int np
, const environment
*env
);
593 void begin_page(int);
595 void special(char *arg
, const environment
*env
, char type
);
596 font
*make_font(const char *);
601 ps_printer::ps_printer(double pl
)
602 : out(0, MAX_LINE_LENGTH
),
610 next_encoding_index(0),
611 next_subencoding_index(0),
616 out
.set_file(tempfp
);
618 linewidth
= DEFAULT_LINEWIDTH
;
620 fatal("horizontal resolution must be 1");
622 fatal("vertical resolution must be 1");
623 if (font::res
% (font::sizescale
*72) != 0)
624 fatal("res must be a multiple of 72*sizescale");
627 while (r
% 10 == 0) {
632 out
.set_fixed_point(point
);
633 space_char_index
= font::name_to_index("space");
635 paper_length
= font::paperlength
;
637 paper_length
= int(pl
* font::res
+ 0.5);
638 if (paper_length
== 0)
639 paper_length
= 11 * font::res
;
640 equalise_spaces
= font::res
>= 72000;
643 int ps_printer::set_encoding_index(ps_font
*f
)
645 if (f
->encoding_index
>= 0)
646 return f
->encoding_index
;
647 for (font_pointer_list
*p
= font_list
; p
; p
= p
->next
)
649 char *encoding
= ((ps_font
*)p
->p
)->encoding
;
650 int encoding_index
= ((ps_font
*)p
->p
)->encoding_index
;
651 if (encoding
!= 0 && encoding_index
>= 0
652 && strcmp(f
->encoding
, encoding
) == 0) {
653 return f
->encoding_index
= encoding_index
;
656 return f
->encoding_index
= next_encoding_index
++;
659 subencoding
*ps_printer::set_subencoding(font
*f
, int i
, unsigned char *codep
)
661 unsigned int idx
= f
->get_code(i
);
663 unsigned int num
= idx
>> 8;
667 for (p
= subencodings
; p
; p
= p
->next
)
668 if (p
->p
== f
&& p
->num
== num
)
671 p
= subencodings
= new subencoding(f
, num
, next_subencoding_index
++,
673 p
->glyphs
[*codep
] = f
->get_special_device_encoding(i
);
677 char *ps_printer::get_subfont(subencoding
*sub
, const char *stem
)
681 char *tem
= new char[strlen(stem
) + 2 + INT_DIGITS
+ 1];
682 sprintf(tem
, "%s@@%d", stem
, next_subencoding_index
);
688 void ps_printer::set_char(int i
, font
*f
, const environment
*env
, int w
,
691 if (i
== space_char_index
|| invis_count
> 0)
694 subencoding
*sub
= set_subencoding(f
, i
, &code
);
695 style
sty(f
, sub
, env
->size
, env
->height
, env
->slant
);
696 if (sty
.slant
!= 0) {
697 if (sty
.slant
> 80 || sty
.slant
< -80) {
698 error("silly slant `%1' degrees", sty
.slant
);
703 if (sbuf_len
< SBUF_SIZE
705 && sbuf_vpos
== env
->vpos
706 && sbuf_color
== *env
->col
) {
707 if (sbuf_end_hpos
== env
->hpos
) {
708 sbuf
[sbuf_len
++] = code
;
709 sbuf_end_hpos
+= w
+ sbuf_kern
;
712 if (sbuf_len
== 1 && sbuf_kern
== 0) {
713 sbuf_kern
= env
->hpos
- sbuf_end_hpos
;
714 sbuf_end_hpos
= env
->hpos
+ sbuf_kern
+ w
;
715 sbuf
[sbuf_len
++] = code
;
718 /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
719 starting a new string. */
720 if (sbuf_len
< SBUF_SIZE
- 1 && env
->hpos
>= sbuf_end_hpos
721 && (sbuf_kern
== 0 || sbuf_end_hpos
- sbuf_kern
!= env
->hpos
)) {
722 if (sbuf_space_code
< 0) {
723 if (f
->contains(space_char_index
)) {
724 sbuf_space_code
= f
->get_code(space_char_index
);
725 sbuf_space_width
= env
->hpos
- sbuf_end_hpos
;
726 sbuf_end_hpos
= env
->hpos
+ w
+ sbuf_kern
;
727 sbuf
[sbuf_len
++] = sbuf_space_code
;
728 sbuf
[sbuf_len
++] = code
;
734 int diff
= env
->hpos
- sbuf_end_hpos
- sbuf_space_width
;
735 if (diff
== 0 || (equalise_spaces
&& (diff
== 1 || diff
== -1))) {
736 sbuf_end_hpos
= env
->hpos
+ w
+ sbuf_kern
;
737 sbuf
[sbuf_len
++] = sbuf_space_code
;
738 sbuf
[sbuf_len
++] = code
;
741 sbuf_space_diff_count
++;
743 sbuf_space_diff_count
--;
753 sbuf_end_hpos
= env
->hpos
+ w
;
754 sbuf_start_hpos
= env
->hpos
;
755 sbuf_vpos
= env
->vpos
;
757 sbuf_space_code
= -1;
758 sbuf_space_width
= 0;
759 sbuf_space_count
= sbuf_space_diff_count
= 0;
761 if (sbuf_color
!= *env
->col
)
765 static char *make_encoding_name(int encoding_index
)
767 static char buf
[3 + INT_DIGITS
+ 1];
768 sprintf(buf
, "ENC%d", encoding_index
);
772 static char *make_subencoding_name(int subencoding_index
)
774 static char buf
[6 + INT_DIGITS
+ 1];
775 sprintf(buf
, "SUBENC%d", subencoding_index
);
779 const char *const WS
= " \t\n\r";
781 void ps_printer::define_encoding(const char *encoding
, int encoding_index
)
785 for (i
= 0; i
< 256; i
++)
788 FILE *fp
= font::open_file(encoding
, &path
);
790 fatal("can't open encoding file `%1'", encoding
);
792 const int BUFFER_SIZE
= 512;
793 char buf
[BUFFER_SIZE
];
794 while (fgets(buf
, BUFFER_SIZE
, fp
) != 0) {
798 if (*p
!= '#' && *p
!= '\0' && (p
= strtok(buf
, WS
)) != 0) {
799 char *q
= strtok(0, WS
);
800 int n
= 0; // pacify compiler
801 if (q
== 0 || sscanf(q
, "%d", &n
) != 1 || n
< 0 || n
>= 256)
802 fatal_with_file_and_line(path
, lineno
, "bad second field");
803 vec
[n
] = new char[strlen(p
) + 1];
809 out
.put_literal_symbol(make_encoding_name(encoding_index
))
811 for (i
= 0; i
< 256; i
++) {
813 out
.put_literal_symbol(".notdef");
815 out
.put_literal_symbol(vec
[i
]);
819 out
.put_delimiter(']')
824 void ps_printer::reencode_font(ps_font
*f
)
826 out
.put_literal_symbol(f
->reencoded_name
)
827 .put_symbol(make_encoding_name(f
->encoding_index
))
828 .put_literal_symbol(f
->get_internal_name())
832 void ps_printer::encode_fonts()
834 if (next_encoding_index
== 0)
836 char *done_encoding
= new char[next_encoding_index
];
837 for (int i
= 0; i
< next_encoding_index
; i
++)
838 done_encoding
[i
] = 0;
839 for (font_pointer_list
*f
= font_list
; f
; f
= f
->next
) {
840 int encoding_index
= ((ps_font
*)f
->p
)->encoding_index
;
841 if (encoding_index
>= 0) {
842 assert(encoding_index
< next_encoding_index
);
843 if (!done_encoding
[encoding_index
]) {
844 done_encoding
[encoding_index
] = 1;
845 define_encoding(((ps_font
*)f
->p
)->encoding
, encoding_index
);
847 reencode_font((ps_font
*)f
->p
);
850 a_delete done_encoding
;
853 void ps_printer::encode_subfont(subencoding
*sub
)
855 assert(sub
->glyphs
!= 0);
856 out
.put_literal_symbol(make_subencoding_name(sub
->idx
))
858 for (int i
= 0; i
< 256; i
++)
861 out
.put_literal_symbol(sub
->glyphs
[i
]);
863 out
.put_literal_symbol(".notdef");
865 out
.put_delimiter(']')
869 void ps_printer::set_style(const style
&sty
)
871 char buf
[1 + INT_DIGITS
+ 1];
872 for (int i
= 0; i
< ndefined_styles
; i
++)
873 if (sty
== defined_styles
[i
]) {
874 sprintf(buf
, "F%d", i
);
878 if (ndefined_styles
>= MAX_DEFINED_STYLES
)
880 sprintf(buf
, "F%d", ndefined_styles
);
881 out
.put_literal_symbol(buf
);
882 const char *psname
= sty
.f
->get_internal_name();
884 fatal("no internalname specified for font `%1'", sty
.f
->get_name());
885 char *encoding
= ((ps_font
*)sty
.f
)->encoding
;
888 char *s
= ((ps_font
*)sty
.f
)->reencoded_name
;
890 int ei
= set_encoding_index((ps_font
*)sty
.f
);
891 char *tem
= new char[strlen(psname
) + 1 + INT_DIGITS
+ 1];
892 sprintf(tem
, "%s@%d", psname
, ei
);
894 ((ps_font
*)sty
.f
)->reencoded_name
= tem
;
901 psname
= get_subfont(sty
.sub
, psname
);
902 out
.put_fix_number((font::res
/(72*font::sizescale
))*sty
.point_size
);
903 if (sty
.height
!= 0 || sty
.slant
!= 0) {
904 int h
= sty
.height
== 0 ? sty
.point_size
: sty
.height
;
905 h
*= font::res
/(72*font::sizescale
);
906 int c
= int(h
*tan(radians(sty
.slant
)) + .5);
907 out
.put_fix_number(c
)
909 .put_literal_symbol(psname
)
913 out
.put_literal_symbol(psname
)
916 defined_styles
[ndefined_styles
++] = sty
;
919 void ps_printer::set_color(color
*col
, int fill
)
922 unsigned int components
[4];
924 color_scheme cs
= col
->get_components(components
);
925 s
[0] = fill
? 'F' : 'C';
928 case DEFAULT
: // black
939 col
->get_cmyk(&Cyan
, &Magenta
, &Yellow
, &Black
);
957 void ps_printer::set_space_code(unsigned char c
)
959 out
.put_literal_symbol("SC")
964 void ps_printer::end_of_line()
967 // this ensures that we do an absolute motion to the beginning of a line
968 output_vpos
= output_hpos
= -1;
971 void ps_printer::flush_sbuf()
983 if (output_style
!= sbuf_style
) {
984 set_style(sbuf_style
);
985 output_style
= sbuf_style
;
988 if (output_hpos
< 0 || output_vpos
< 0)
991 if (output_hpos
!= sbuf_start_hpos
)
993 if (output_vpos
!= sbuf_vpos
) {
995 motion
= RELATIVE_HV
;
1000 if (sbuf_space_code
>= 0) {
1001 int w
= sbuf_style
.f
->get_width(space_char_index
, sbuf_style
.point_size
);
1002 if (w
+ sbuf_kern
!= sbuf_space_width
) {
1003 if (sbuf_space_code
!= output_space_code
) {
1004 set_space_code(sbuf_space_code
);
1005 output_space_code
= sbuf_space_code
;
1008 extra_space
= sbuf_space_width
- w
- sbuf_kern
;
1009 if (sbuf_space_diff_count
> sbuf_space_count
/2)
1011 else if (sbuf_space_diff_count
< -(sbuf_space_count
/2))
1016 out
.put_fix_number(extra_space
);
1018 out
.put_fix_number(sbuf_kern
);
1019 out
.put_string(sbuf
, sbuf_len
);
1020 char command_array
[] = {'A', 'B', 'C', 'D',
1024 'Q', 'R', 'S', 'T'};
1026 sym
[0] = command_array
[motion
*4 + space_flag
+ 2*(sbuf_kern
!= 0)];
1032 out
.put_fix_number(sbuf_start_hpos
)
1033 .put_fix_number(sbuf_vpos
);
1036 out
.put_fix_number(sbuf_start_hpos
- output_hpos
);
1039 out
.put_fix_number(sbuf_vpos
- output_vpos
);
1042 out
.put_fix_number(sbuf_start_hpos
- output_hpos
)
1043 .put_fix_number(sbuf_vpos
- output_vpos
);
1048 out
.put_symbol(sym
);
1049 output_hpos
= sbuf_end_hpos
;
1050 output_vpos
= sbuf_vpos
;
1054 void ps_printer::set_line_thickness_and_color(const environment
*env
)
1056 if (line_thickness
< 0) {
1057 if (output_draw_point_size
!= env
->size
) {
1058 // we ought to check for overflow here
1059 int lw
= ((font::res
/(72*font::sizescale
))*linewidth
*env
->size
)/1000;
1060 out
.put_fix_number(lw
)
1062 output_draw_point_size
= env
->size
;
1063 output_line_thickness
= -1;
1067 if (output_line_thickness
!= line_thickness
) {
1068 out
.put_fix_number(line_thickness
)
1070 output_line_thickness
= line_thickness
;
1071 output_draw_point_size
= -1;
1074 if (sbuf_color
!= *env
->col
)
1075 set_color(env
->col
);
1078 void ps_printer::fill_path(const environment
*env
)
1080 if (sbuf_color
== *env
->fill
)
1081 out
.put_symbol("FL");
1083 set_color(env
->fill
, 1);
1086 void ps_printer::draw(int code
, int *p
, int np
, const environment
*env
)
1088 if (invis_count
> 0)
1097 // troff adds an extra argument to C
1098 if (np
!= 1 && !(code
== 'C' && np
== 2)) {
1099 error("1 argument required for circle");
1102 out
.put_fix_number(env
->hpos
+ p
[0]/2)
1103 .put_fix_number(env
->vpos
)
1104 .put_fix_number(p
[0]/2)
1109 set_line_thickness_and_color(env
);
1110 out
.put_symbol("ST");
1115 error("2 arguments required for line");
1118 set_line_thickness_and_color(env
);
1119 out
.put_fix_number(p
[0] + env
->hpos
)
1120 .put_fix_number(p
[1] + env
->vpos
)
1121 .put_fix_number(env
->hpos
)
1122 .put_fix_number(env
->vpos
)
1130 error("2 arguments required for ellipse");
1133 out
.put_fix_number(p
[0])
1134 .put_fix_number(p
[1])
1135 .put_fix_number(env
->hpos
+ p
[0]/2)
1136 .put_fix_number(env
->vpos
)
1141 set_line_thickness_and_color(env
);
1142 out
.put_symbol("ST");
1151 error("even number of arguments required for polygon");
1155 error("no arguments for polygon");
1158 out
.put_fix_number(env
->hpos
)
1159 .put_fix_number(env
->vpos
)
1161 for (int i
= 0; i
< np
; i
+= 2)
1162 out
.put_fix_number(p
[i
])
1163 .put_fix_number(p
[i
+1])
1165 out
.put_symbol("CL");
1169 set_line_thickness_and_color(env
);
1170 out
.put_symbol("ST");
1177 error("even number of arguments required for spline");
1181 error("no arguments for spline");
1184 out
.put_fix_number(env
->hpos
)
1185 .put_fix_number(env
->vpos
)
1187 out
.put_fix_number(p
[0]/2)
1188 .put_fix_number(p
[1]/2)
1190 /* tnum/tden should be between 0 and 1; the closer it is to 1
1191 the tighter the curve will be to the guiding lines; 2/3
1192 is the standard value */
1195 for (int i
= 0; i
< np
- 2; i
+= 2) {
1196 out
.put_fix_number((p
[i
]*tnum
)/(2*tden
))
1197 .put_fix_number((p
[i
+ 1]*tnum
)/(2*tden
))
1198 .put_fix_number(p
[i
]/2 + (p
[i
+ 2]*(tden
- tnum
))/(2*tden
))
1199 .put_fix_number(p
[i
+ 1]/2 + (p
[i
+ 3]*(tden
- tnum
))/(2*tden
))
1200 .put_fix_number((p
[i
] - p
[i
]/2) + p
[i
+ 2]/2)
1201 .put_fix_number((p
[i
+ 1] - p
[i
+ 1]/2) + p
[i
+ 3]/2)
1204 out
.put_fix_number(p
[np
- 2] - p
[np
- 2]/2)
1205 .put_fix_number(p
[np
- 1] - p
[np
- 1]/2)
1207 set_line_thickness_and_color(env
);
1208 out
.put_symbol("ST");
1214 error("4 arguments required for arc");
1217 set_line_thickness_and_color(env
);
1219 if (adjust_arc_center(p
, c
))
1220 out
.put_fix_number(env
->hpos
+ int(c
[0]))
1221 .put_fix_number(env
->vpos
+ int(c
[1]))
1222 .put_fix_number(int(sqrt(c
[0]*c
[0] + c
[1]*c
[1])))
1223 .put_float(degrees(atan2(-c
[1], -c
[0])))
1224 .put_float(degrees(atan2(p
[1] + p
[3] - c
[1], p
[0] + p
[2] - c
[0])))
1227 out
.put_fix_number(p
[0] + p
[2] + env
->hpos
)
1228 .put_fix_number(p
[1] + p
[3] + env
->vpos
)
1229 .put_fix_number(env
->hpos
)
1230 .put_fix_number(env
->vpos
)
1236 line_thickness
= -1;
1238 // troff gratuitously adds an extra 0
1239 if (np
!= 1 && np
!= 2) {
1240 error("0 or 1 argument required for thickness");
1243 line_thickness
= p
[0];
1247 error("unrecognised drawing command `%1'", char(code
));
1250 output_hpos
= output_vpos
= -1;
1253 const char *ps_printer::media_name()
1258 int ps_printer::media_width()
1262 * Although paper size is defined as real numbers, it seems to be
1263 * a common convention to round to the nearest postscript unit.
1264 * For example, a4 is really 595.276 by 841.89 but we use 595 by 842.
1266 * This is probably a good compromise, especially since the
1267 * Postscript definition specifies that media
1268 * matching should be done within a tolerance of 5 units.
1270 return int(user_paper_width
? user_paper_width
*72.0 + 0.5
1271 : font::paperwidth
*72.0/font::res
+ 0.5);
1274 int ps_printer::media_height()
1276 return int(user_paper_length
? user_paper_length
*72.0 + 0.5
1277 : paper_length
*72.0/font::res
+ 0.5);
1280 void ps_printer::media_set()
1283 * The setpagedevice implies an erasepage and initgraphics, and
1284 * must thus precede any descriptions for a particular page.
1287 * This does not work with ps2pdf when there are included eps
1288 * segments that contain PageSize/setpagedevice.
1289 * This might be a bug in ghostscript -- must be investigated.
1290 * Using setpagedevice in an .eps is really the wrong concept, anyway.
1293 * For the future, this is really the place to insert other
1294 * media selection features, like:
1308 if (!(broken_flags
& (USE_PS_ADOBE_2_0
|NO_PAPERSIZE
))) {
1309 out
.begin_comment("BeginFeature:")
1310 .comment_arg("*PageSize")
1311 .comment_arg(media_name())
1313 int w
= media_width();
1314 int h
= media_height();
1316 // warning to user is done elsewhere
1317 fprintf(out
.get_file(),
1318 "<< /PageSize [ %d %d ] /ImagingBBox null >> setpagedevice\n",
1320 out
.simple_comment("EndFeature");
1324 void ps_printer::begin_page(int n
)
1326 out
.begin_comment("Page:")
1327 .comment_arg(i_to_a(n
));
1328 out
.comment_arg(i_to_a(++pages_output
))
1331 output_space_code
= 32;
1332 output_draw_point_size
= -1;
1333 output_line_thickness
= -1;
1334 output_hpos
= output_vpos
= -1;
1335 ndefined_styles
= 0;
1336 out
.simple_comment("BeginPageSetup");
1341 * may decide to do this once per page
1346 out
.put_symbol("BP")
1347 .simple_comment("EndPageSetup");
1348 if (sbuf_color
!= default_color
)
1349 set_color(&sbuf_color
);
1352 void ps_printer::end_page(int)
1355 set_color(&default_color
);
1356 out
.put_symbol("EP");
1357 if (invis_count
!= 0) {
1358 error("missing `endinvis' command");
1363 font
*ps_printer::make_font(const char *nm
)
1365 return ps_font::load_ps_font(nm
);
1368 ps_printer::~ps_printer()
1370 out
.simple_comment("Trailer")
1372 .simple_comment("EOF");
1373 if (fseek(tempfp
, 0L, 0) < 0)
1374 fatal("fseek on temporary file failed");
1375 fputs("%!PS-Adobe-", stdout
);
1376 fputs((broken_flags
& USE_PS_ADOBE_2_0
) ? "2.0" : "3.0", stdout
);
1378 out
.set_file(stdout
);
1380 out
.begin_comment("Extensions:")
1381 .comment_arg("CMYK")
1383 out
.begin_comment("Creator:")
1384 .comment_arg("groff")
1385 .comment_arg("version")
1386 .comment_arg(Version_string
)
1389 fputs("%%CreationDate: ", out
.get_file());
1390 #ifdef LONG_FOR_TIME_T
1396 fputs(ctime(&t
), out
.get_file());
1398 for (font_pointer_list
*f
= font_list
; f
; f
= f
->next
) {
1399 ps_font
*psf
= (ps_font
*)(f
->p
);
1400 rm
.need_font(psf
->get_internal_name());
1402 rm
.print_header_comments(out
);
1403 out
.begin_comment("Pages:")
1404 .comment_arg(i_to_a(pages_output
))
1406 out
.begin_comment("PageOrder:")
1407 .comment_arg("Ascend")
1409 if (!(broken_flags
& NO_PAPERSIZE
)) {
1410 int w
= media_width();
1411 int h
= media_height();
1413 fprintf(out
.get_file(),
1414 "%%%%DocumentMedia: %s %d %d %d %s %s\n",
1415 media_name(), // tag name of media
1418 0, // weight in g/m2
1419 "()", // paper color, e.g. white
1420 "()" // preprinted form type
1424 // see ps_printer::ps_printer
1425 warning("bad paper height, defaulting to 11i");
1427 warning("bad paper width");
1430 out
.begin_comment("Orientation:")
1431 .comment_arg(landscape_flag
? "Landscape" : "Portrait")
1435 fprintf(out
.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies
);
1437 out
.simple_comment("EndComments");
1438 if (!(broken_flags
& NO_PAPERSIZE
)) {
1439 /* gv works fine without this one, but it really should be there. */
1440 out
.simple_comment("BeginDefaults");
1441 fprintf(out
.get_file(), "%%%%PageMedia: %s\n", media_name());
1442 out
.simple_comment("EndDefaults");
1444 out
.simple_comment("BeginProlog");
1445 rm
.output_prolog(out
);
1446 if (!(broken_flags
& NO_SETUP_SECTION
)) {
1447 out
.simple_comment("EndProlog");
1448 out
.simple_comment("BeginSetup");
1452 * Define paper (i.e., media) size for entire document here.
1453 * This allows ps2pdf to correctly determine page size, for instance.
1457 rm
.document_setup(out
);
1458 out
.put_symbol(dict_name
)
1459 .put_symbol("begin");
1461 ndefs
+= DEFS_DICT_SPARE
;
1462 out
.put_literal_symbol(defs_dict_name
)
1463 .put_number(ndefs
+ 1)
1466 out
.put_symbol(defs_dict_name
)
1467 .put_symbol("begin");
1468 out
.put_literal_symbol("u")
1476 out
.special(defs
.contents());
1477 out
.put_symbol("end");
1479 out
.put_literal_symbol("#copies")
1480 .put_number(ncopies
)
1482 out
.put_literal_symbol("RES")
1485 out
.put_literal_symbol("PL");
1487 out
.put_symbol("PLG");
1489 out
.put_fix_number(paper_length
);
1490 out
.put_symbol("def");
1491 out
.put_literal_symbol("LS")
1492 .put_symbol(landscape_flag
? "true" : "false")
1494 if (manual_feed_flag
) {
1495 out
.begin_comment("BeginFeature:")
1496 .comment_arg("*ManualFeed")
1497 .comment_arg("True")
1499 .put_symbol("MANUAL")
1500 .simple_comment("EndFeature");
1503 while (subencodings
) {
1504 subencoding
*tem
= subencodings
;
1505 subencodings
= subencodings
->next
;
1506 encode_subfont(tem
);
1507 out
.put_literal_symbol(tem
->subfont
)
1508 .put_symbol(make_subencoding_name(tem
->idx
))
1509 .put_literal_symbol(tem
->p
->get_internal_name())
1513 out
.simple_comment((broken_flags
& NO_SETUP_SECTION
)
1517 out
.copy_file(tempfp
);
1521 void ps_printer::special(char *arg
, const environment
*env
, char type
)
1525 typedef void (ps_printer::*SPECIAL_PROCP
)(char *, const environment
*);
1530 { "exec", &ps_printer::do_exec
},
1531 { "def", &ps_printer::do_def
},
1532 { "mdef", &ps_printer::do_mdef
},
1533 { "import", &ps_printer::do_import
},
1534 { "file", &ps_printer::do_file
},
1535 { "invis", &ps_printer::do_invis
},
1536 { "endinvis", &ps_printer::do_endinvis
},
1539 for (p
= arg
; *p
== ' ' || *p
== '\n'; p
++)
1542 for (; *p
!= '\0' && *p
!= ':' && *p
!= ' ' && *p
!= '\n'; p
++)
1544 if (*p
== '\0' || strncmp(tag
, "ps", p
- tag
) != 0) {
1545 error("X command without `ps:' tag ignored");
1549 for (; *p
== ' ' || *p
== '\n'; p
++)
1552 for (; *p
!= '\0' && *p
!= ' ' && *p
!= '\n'; p
++)
1554 if (*command
== '\0') {
1555 error("empty X command ignored");
1558 for (unsigned int i
= 0; i
< sizeof(proc_table
)/sizeof(proc_table
[0]); i
++)
1559 if (strncmp(command
, proc_table
[i
].name
, p
- command
) == 0) {
1560 (this->*(proc_table
[i
].proc
))(p
, env
);
1563 error("X command `%1' not recognised", command
);
1566 // A conforming PostScript document must not have lines longer
1567 // than 255 characters (excluding line termination characters).
1569 static int check_line_lengths(const char *p
)
1572 const char *end
= strchr(p
, '\n');
1574 end
= strchr(p
, '\0');
1584 void ps_printer::do_exec(char *arg
, const environment
*env
)
1587 while (csspace(*arg
))
1590 error("missing argument to X exec command");
1593 if (!check_line_lengths(arg
)) {
1594 error("lines in X exec command must not be more than 255 characters long");
1597 out
.put_fix_number(env
->hpos
)
1598 .put_fix_number(env
->vpos
)
1599 .put_symbol("EBEGIN")
1601 .put_symbol("EEND");
1602 output_hpos
= output_vpos
= -1;
1604 output_draw_point_size
= -1;
1605 output_line_thickness
= -1;
1606 ndefined_styles
= 0;
1611 void ps_printer::do_file(char *arg
, const environment
*env
)
1614 while (csspace(*arg
))
1617 error("missing argument to X file command");
1620 const char *filename
= arg
;
1623 } while (*arg
!= '\0' && *arg
!= ' ' && *arg
!= '\n');
1624 out
.put_fix_number(env
->hpos
)
1625 .put_fix_number(env
->vpos
)
1626 .put_symbol("EBEGIN");
1627 rm
.import_file(filename
, out
);
1628 out
.put_symbol("EEND");
1629 output_hpos
= output_vpos
= -1;
1631 output_draw_point_size
= -1;
1632 output_line_thickness
= -1;
1633 ndefined_styles
= 0;
1638 void ps_printer::do_def(char *arg
, const environment
*)
1641 while (csspace(*arg
))
1643 if (!check_line_lengths(arg
)) {
1644 error("lines in X def command must not be more than 255 characters long");
1648 if (*arg
!= '\0' && strchr(arg
, '\0')[-1] != '\n')
1653 // Like def, but the first argument says how many definitions it contains.
1655 void ps_printer::do_mdef(char *arg
, const environment
*)
1659 int n
= (int)strtol(arg
, &p
, 10);
1660 if (n
== 0 && p
== arg
) {
1661 error("first argument to X mdef must be an integer");
1665 error("out of range argument `%1' to X mdef command", int(n
));
1669 while (csspace(*arg
))
1671 if (!check_line_lengths(arg
)) {
1672 error("lines in X mdef command must not be more than 255 characters long");
1676 if (*arg
!= '\0' && strchr(arg
, '\0')[-1] != '\n')
1681 void ps_printer::do_import(char *arg
, const environment
*env
)
1684 while (*arg
== ' ' || *arg
== '\n')
1687 for (p
= arg
; *p
!= '\0' && *p
!= ' ' && *p
!= '\n'; p
++)
1693 while (nparms
< 6) {
1695 long n
= strtol(p
, &end
, 10);
1696 if (n
== 0 && end
== p
)
1698 parms
[nparms
++] = int(n
);
1701 if (csalpha(*p
) && (p
[1] == '\0' || p
[1] == ' ' || p
[1] == '\n')) {
1702 error("scaling indicators not allowed in arguments for X import command");
1705 while (*p
== ' ' || *p
== '\n')
1709 error("too few arguments for X import command");
1711 error("invalid argument `%1' for X import command", p
);
1715 error("superfluous argument `%1' for X import command", p
);
1722 int desired_width
= parms
[4];
1723 int desired_height
= parms
[5];
1724 if (desired_width
<= 0) {
1725 error("bad width argument `%1' for X import command: must be > 0",
1729 if (nparms
== 6 && desired_height
<= 0) {
1730 error("bad height argument `%1' for X import command: must be > 0",
1735 error("llx and urx arguments for X import command must not be equal");
1739 error("lly and ury arguments for X import command must not be equal");
1743 int old_wid
= urx
- llx
;
1744 int old_ht
= ury
- lly
;
1749 desired_height
= int(desired_width
*(double(old_ht
)/double(old_wid
)) + .5);
1751 if (env
->vpos
- desired_height
< 0)
1752 warning("top of imported graphic is above the top of the page");
1755 .put_fix_number(desired_width
)
1756 .put_number(urx
- llx
)
1757 .put_fix_number(-desired_height
)
1758 .put_number(ury
- lly
)
1759 .put_fix_number(env
->hpos
)
1760 .put_fix_number(env
->vpos
)
1761 .put_symbol("PBEGIN");
1762 rm
.import_file(arg
, out
);
1763 // do this here just in case application defines PEND
1764 out
.put_symbol("end")
1765 .put_symbol("PEND");
1768 void ps_printer::do_invis(char *, const environment
*)
1773 void ps_printer::do_endinvis(char *, const environment
*)
1775 if (invis_count
== 0)
1776 error("unbalanced `endinvis' command");
1781 printer
*make_printer()
1783 return new ps_printer(user_paper_length
);
1786 static void usage(FILE *stream
);
1788 int main(int argc
, char **argv
)
1790 setlocale(LC_NUMERIC
, "C");
1791 program_name
= argv
[0];
1793 static char stderr_buf
[BUFSIZ
];
1794 setbuf(stderr
, stderr_buf
);
1796 static const struct option long_options
[] = {
1797 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
1798 { "version", no_argument
, 0, 'v' },
1801 while ((c
= getopt_long(argc
, argv
, "b:c:F:gI:lmp:P:vw:", long_options
, NULL
))
1806 broken_flags
= atoi(optarg
);
1810 if (sscanf(optarg
, "%d", &ncopies
) != 1 || ncopies
<= 0) {
1811 error("bad number of copies `%s'", optarg
);
1816 font::command_line_font_dir(optarg
);
1822 include_search_path
.command_line_dir(optarg
);
1828 manual_feed_flag
= 1;
1831 if (!font::scan_papersize(optarg
, 0,
1832 &user_paper_length
, &user_paper_width
))
1833 error("invalid custom paper size `%1' ignored", optarg
);
1836 env
= "GROPS_PROLOGUE";
1840 if (putenv(strsave(env
.contents())))
1841 fatal("putenv failed");
1844 printf("GNU grops (groff) version %s\n", Version_string
);
1848 if (sscanf(optarg
, "%d", &linewidth
) != 1 || linewidth
< 0) {
1849 error("bad linewidth `%1'", optarg
);
1853 case CHAR_MAX
+ 1: // --help
1864 font::set_unknown_desc_command_handler(handle_unknown_desc_command
);
1865 SET_BINARY(fileno(stdout
));
1869 for (int i
= optind
; i
< argc
; i
++)
1875 static void usage(FILE *stream
)
1878 "usage: %s [-glmv] [-b n] [-c n] [-w n] [-I dir] [-P prologue]\n"
1879 " [-F dir] [files ...]\n",