1 // Copyright 2009 The Archiveopteryx Developers <info@aox.org>
2 
3 #include "postscript.h"
4 
5 #include "file.h"
6 #include "error.h"
7 #include "class.h"
8 #include "function.h"
9 
10 
11 static const char * prologue =
12     "%!PS-Adobe-3.0\n"
13     "%%Creator: udoc, http://archiveopteryx.org/udoc/\n"
14     "%%PageOrder: Ascend\n"
15     "%%DocumentMedia:\n"
16     "%%BoundingBox: 0 0 595 841\n"
17     "%%DocumentData: Clean8Bit\n"
18     "%%Orientation: Portrait\n"
19     "%%EndComments\n"
20     "\n"
21     "%%BeginProlog\n"
22     "\n"
23     "/mm { 72 mul 25.4 div } bind def\n"
24     "\n"
25     "/lx 20 mm def\n"
26     "/rx 190 mm def\n"
27     "/dy 12 def\n"
28     "/ty 279 mm def\n"
29     "/by 25 mm def\n"
30     "/page 1 def\n"
31     "\n"
32     "/header\n" // shows page number
33     "{ page 10 string cvs dup stringwidth pop\n"
34     "    rx exch sub 285 mm moveto\n"
35     "    show\n"
36     "} bind def\n"
37     "/l\n" // shows a single line of text and moves the point down
38     "{\n"
39     " currentpoint 3 -1 roll show dy sub moveto\n"
40     "} bind def\n"
41     "\n"
42     "/s\n" // tos: word. shows a single word and one trailing space.
43     "{ dup stringwidth pop currentpoint pop add rx gt\n"
44     "     { currentpoint exch pop dy sub\n"
45     "       dup by lt { showpage pop ty /page page 1 add def header } if\n"
46     "       lx exch moveto } if\n"
47     "     show ( ) show \n"
48     "} bind def\n"
49     "\n"
50     "/p\n" // tos: paragraph. shows the paragraph within x boundaries lx-rx
51     "{ { ( ) search { s pop } { s exit } ifelse } loop\n"
52     "  lx currentpoint exch pop dy 2 mul sub moveto\n"
53     "} bind def\n"
54     "\n"
55     "%%EndProlog\n";
56 
57 static Postscript * current = 0;
58 
59 
60 /*! \class Postscript postscript.h
61 
62     The Postscript class generates output in postscript form. Plain
63     postscript level 1 is used, and all formatting is done on the
64     printer, even wordwrapping.
65 
66     At the moment, all output uses the same font. That's a bug. Have
67     to fix that.
68 */
69 
70 
71 
72 /*! Constructs an Postscript output function, opens \a f for writing
73     and writes the postscript prologue.
74 */
75 
Postscript(const char * f)76 Postscript::Postscript( const char * f )
77     : file( 0 )
78 {
79     file = new File( f, File::Write );
80     if ( !file->valid() ) {
81         (void)new Error( file, 0,
82                          "Postscript: Unable to open this file for writing" );
83         file = 0;
84         return;
85     }
86     ::current = this;
87     output( prologue );
88     output( "/Times findfont 9.5 scalefont setfont\n"
89             "header\n"
90             "lx ty moveto\n" );
91 }
92 
93 
94 /*! Destroys the writer and closes the file. */
95 
~Postscript()96 Postscript::~Postscript()
97 {
98     endParagraph();
99     output( "showpage\n" );
100     delete file;
101 }
102 
103 
104 /*! Returns a pointer to the current Postscript singleton. */
105 
current()106 Postscript * Postscript::current()
107 {
108     return ::current;
109 }
110 
111 
112 /*! As Output::startHeadline() */
113 
startHeadline(Intro *)114 void Postscript::startHeadline( Intro * )
115 {
116     endParagraph();
117 }
118 
119 
120 /*! As Output::startHeadline(). */
121 
startHeadline(Class *)122 void Postscript::startHeadline( Class * )
123 {
124     endParagraph();
125 }
126 
127 
128 /*! As Output::startHeadline(). */
129 
startHeadline(Function *)130 void Postscript::startHeadline( Function * )
131 {
132     endParagraph();
133 }
134 
135 
136 /*! Ends a paragraph, if one is being output. */
137 
endParagraph()138 void Postscript::endParagraph()
139 {
140     if ( para.isEmpty() )
141         return;
142 
143     EString r;
144     uint i = 0;
145     while ( i < para.length() ) {
146         if ( para[i] == '(' || para[i] == ')' || para[i] == '\\' )
147             r.append( "\\" );
148         r.append( para[i] );
149         i++;
150     }
151     output( "(" + r.simplified() + ") p\n" );
152     para = "";
153 }
154 
155 
156 /*! Outputs \a s to the destination file, taking care to escape
157     characters correctly, and to start a new paragraph if necessary.
158 */
159 
addText(const EString & s)160 void Postscript::addText( const EString & s )
161 {
162     para.append( s );
163 }
164 
165 
166 /*! Outputs \a s to the destination file, theorecically in
167   italics. Right now it's exactly as addText().
168 */
169 
addArgument(const EString & s)170 void Postscript::addArgument( const EString & s )
171 {
172     addText( s );
173 }
174 
175 
176 /*! Adds \a text to the destination file, if possible with the page
177     number where \a f is documented.
178 */
179 
addFunction(const EString & text,Function * f)180 void Postscript::addFunction( const EString & text, Function * f )
181 {
182     addText( text );
183     f = f;
184 }
185 
186 
187 /*! Adds \a text to the destination file, if possible with the page
188     number where \a c is documented.
189 
190 */
191 
addClass(const EString & text,Class * c)192 void Postscript::addClass( const EString & text, Class * c )
193 {
194     addText( text );
195     c = c;
196 }
197 
198 
199 /*! Writes \a s to the destination file as-is. */
200 
output(const EString & s)201 void Postscript::output( const EString & s )
202 {
203     if ( file )
204         file->write( s );
205 }
206