1 /*
2 drvLATEX2E.cpp : This file is part of pstoedit
3 Backend for Latex2E files
4 Contributed by: Scott Pakin <pakin@uiuc.edu>
5
6 Copyright (C) 1993-2000 Wolfgang Glunz, wglunz@geocities.com,
7 Scott Pakin, pakin@uiuc.edu
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23 */
24
25
26 #include "drvlatex2e.h"
27
28 //#ifdef HAVESTL
29 //#include I_fstream
30 //#include I_stdio
31 //#include I_stdlib
32
33 // If true, round all coordinates to the nearest integer.
34 // NOTE: This is a top-level variable, not a member of drvLATEX2E.
35 static bool integersonly;
36
37
38 // Constructor
derivedConstructor(drvLATEX2E)39 drvLATEX2E::derivedConstructor(drvLATEX2E) : constructBase,
40 buffer(tempFile.asOutput())
41 {
42 integersonly = false;
43 for (unsigned int i=0; i<d_argc; i++)
44 if (!strcmp(d_argv[i],"integers"))
45 integersonly = true;
46 }
47
48
49 // Destructor
~drvLATEX2E()50 drvLATEX2E::~drvLATEX2E() {
51 }
52
53
54 // Output a point.
55 // NOTE: This is a top-level function, not a member of drvLATEX2E.
operator <<(ostream & os,Point & pt)56 static ostream & operator<<(ostream & os, Point & pt) {
57 if (integersonly)
58 os << '(' << long(pt.x_+0.5) << ',' << long(pt.y_+0.5) << ')';
59 else
60 os << '(' << pt.x_ << ',' << pt.y_ << ')';
61 return os;
62 }
63
64
65 // Convert the coordinates on a path to LaTeX2e.
print_coords()66 void drvLATEX2E::print_coords()
67 {
68 Point *firstpoint = NULL; // Where "closepath" takes us back to
69 Point pointlist[3]; // Other points we care about
70 //old ostrstream outputline; // Current line of LaTeX2e to output
71
72 buffer.setf(ios::fixed, ios::floatfield); // TeX can't deal with scientific notation.
73 for (unsigned int n = 0; n < numberOfElementsInPath(); n++) {
74 const basedrawingelement & elem = pathElement(n);
75 switch (elem.getType()) {
76 // Store a new current point.
77 case moveto:
78 currentpoint = elem.getPoint(0);
79 scalepoint (currentpoint);
80 updatebbox (currentpoint);
81 if (!firstpoint)
82 firstpoint = new Point(currentpoint.x_,currentpoint.y_);
83 break;
84
85 /*
86 * Draw a line. If the line is vertical or horizontal (the easy
87 * cases), then use the picture-mode "\line" command. Otherwise,
88 * don't bother calculating slopes and GCDs and all the other crud
89 * that \line requires. Instead, take the easy way out and use a
90 * \qbezier command.
91 */
92 case lineto:
93 case closepath:
94 if (elem.getType() == lineto) {
95 pointlist[0] = elem.getPoint(0);
96 scalepoint (pointlist[0]);
97 updatebbox (pointlist[0]);
98 }
99 else {
100 pointlist[0] = *firstpoint;
101 delete firstpoint;
102 firstpoint = NULL;
103 }
104 if (pointlist[0].x_ == currentpoint.x_) { // Vertical line
105 if (pointlist[0].y_ == currentpoint.y_) // (and not a point)
106 break;
107 float distance = (float) fabs(pointlist[0].y_-currentpoint.y_);
108 buffer << " \\put" << currentpoint << "{\\line(0,"
109 << (pointlist[0].y_>currentpoint.y_ ? 1 : -1) << "){";
110 if (integersonly)
111 buffer << long(distance+0.5) << "}}";
112 else
113 buffer << distance << "}}";
114 }
115 else
116 if (pointlist[0].y_ == currentpoint.y_) { // Horizontal line
117 float distance = (float) fabs(pointlist[0].x_-currentpoint.x_);
118 buffer << " \\put" << currentpoint << "{\\line("
119 << (pointlist[0].x_>currentpoint.x_ ? 1 : -1) << ",0){";
120 if (integersonly)
121 buffer << long(distance+0.5) << "}}";
122 else
123 buffer << distance << "}}";
124 }
125 else // Diagonal line -- use a Bezier
126 buffer << " \\qbezier"
127 << currentpoint << pointlist[0] << pointlist[0];
128 buffer << endl;
129 currentpoint = pointlist[0];
130 break;
131
132 /*
133 * Draw a Bezier curve. This is trickier than in most pstoedit
134 * backends because LaTeX2e uses quadratic Beziers, while PostScript
135 * (and hence, pstoedit) use cubic Beziers. Fortunately, it's
136 * possible to convert from one to the other (more or less), and
137 * that's what we do.
138 */
139 case curveto: {
140 for (unsigned int cp = 0 ; cp < 3; cp++ ) {
141 pointlist[cp] = elem.getPoint(cp);
142 scalepoint (pointlist[cp]);
143 updatebbox (pointlist[cp]);
144 }
145 float midx = ((3*pointlist[0].x_-currentpoint.x_)/2 +
146 (3*pointlist[1].x_-pointlist[2].x_)/2) / 2;
147 float midy = ((3*pointlist[0].y_-currentpoint.y_)/2 +
148 (3*pointlist[1].y_-pointlist[2].y_)/2) / 2;
149 Point midpoint(midx,midy);
150 buffer << " \\qbezier"
151 << currentpoint << midpoint << pointlist[2] << endl;
152 currentpoint = pointlist[2];
153 }
154 break;
155
156 // We got a bad path element.
157 default:
158 errf << "\t\tFatal: unexpected case in drvlatex2e " << endl;
159 abort();
160 break;
161 }
162 }
163
164 // Finish up the path.
165 //old buffer << ends;
166 //old outputQ.push (outputline.str());
167 if (firstpoint)
168 delete firstpoint;
169 }
170
171
172 // Start of page (really: figure) -- initialize bounding box and "previous"
173 // versions of things.
open_page()174 void drvLATEX2E::open_page()
175 {
176 currentpoint.x_ = 0;
177 currentpoint.y_ = 0;
178 boundingbox[0].x_ = boundingbox[0].y_ = 1e+10;
179 boundingbox[1].x_ = boundingbox[1].y_ = -1e+10;
180 prevR = prevG = prevB = 0.0;
181 thicklines = false;
182 prevfontname = "";
183 prevfontsize = -1.0;
184 }
185
186
187 // End of page (really: figure) -- output a bounding box and the entire
188 // contents of the output queue.
close_page()189 void drvLATEX2E::close_page()
190 {
191 // Specify the picture's width and height and, optionally, the origin.
192 Point boxsize(boundingbox[1].x_-boundingbox[0].x_,
193 boundingbox[1].y_-boundingbox[0].y_);
194 outf << "\\begin{picture}" << boxsize;
195 if (boundingbox[0].x_ || boundingbox[0].y_)
196 outf << boundingbox[0];
197 outf << endl;
198
199 #if old
200 // Output everything in the queue.
201 while (!outputQ.empty()) {
202 outf << outputQ.front();
203 outputQ.pop();
204 }
205 #endif
206 // now we can copy the buffer the output
207 ifstream & inbuffer = tempFile.asInput();
208 copy_file(inbuffer,outf);
209 tempFile.asOutput();
210
211 // Close the picture environment.
212 outf << "\\end{picture}" << endl;
213
214 }
215
216
217 /*
218 * Output a piece of text. We rely on pstoedit's font-mapping routine to
219 * convert the name directly to parameters to \usefont.
220 */
show_text(const TextInfo & textinfo)221 void drvLATEX2E::show_text(const TextInfo & textinfo)
222 {
223 //old ostrstream outputline; // Current line of LaTeX2e to output
224
225 buffer.setf(ios::fixed, ios::floatfield); // TeX can't deal with scientific notation.
226
227 // Set the font and font size if (and only if) it's changed.
228 string fontname(textinfo.currentFontName.value());
229 if (fontname[0]!='{' && fontname!=prevfontname) {
230 errf << "Font \"" << fontname
231 << "\" is not of the form \"{encoding}{family}{series}{shape}\".\n"
232 << "(You may need to use the -fontmap option to point pstoedit to latex2e.fmp.)"
233 << endl;
234 prevfontname = fontname;
235 }
236 else
237 if (fontname != prevfontname) {
238 buffer << " \\usefont" << fontname << endl;
239 prevfontname = fontname;
240 }
241 float fontsize = textinfo.currentFontSize * 72.27f/72.0f;
242 if (fontsize != prevfontsize) {
243 buffer << " \\fontsize{";
244 if (integersonly) {
245 long longsize = long(fontsize+0.5);
246 buffer << longsize << "\\unitlength}{" << longsize;
247 }
248 else
249 buffer << fontsize << "\\unitlength}{" << fontsize;
250 buffer << "\\unitlength}\\selectfont" << endl;
251 prevfontsize = fontsize;
252 }
253
254 // Set the color if (and only if) it's changed.
255 // NOTE: Color requires the "color" package.
256 if (textinfo.currentR!=prevR || textinfo.currentG!=prevG || textinfo.currentB!=prevB) {
257 prevR = textinfo.currentR;
258 prevG = textinfo.currentG;
259 prevB = textinfo.currentB;
260 buffer << " \\color[rgb]{"
261 << prevR << ',' << prevG << ',' << prevB << '}' << endl;
262 }
263
264 // Scale the starting point and update the bounding box.
265 Point textpoint(textinfo.x,textinfo.y);
266 scalepoint (textpoint);
267 updatebbox (textpoint);
268
269 // Output the text string, optionally rotated.
270 // NOTE: Rotation requires the "rotation" package.
271 buffer << " \\put" << textpoint << '{';
272 if (textinfo.currentFontAngle)
273 if (integersonly)
274 buffer << "\\turnbox{" << long(textinfo.currentFontAngle+0.5) << "}{";
275 else
276 buffer << "\\turnbox{" << textinfo.currentFontAngle << "}{";
277 buffer << textinfo.thetext.value() << '}';
278 if (textinfo.currentFontAngle)
279 buffer << '}';
280
281 // Scale the ending point and update the bounding box.
282 currentpoint.x_ = textinfo.x_end;
283 currentpoint.y_ = textinfo.y_end;
284 scalepoint (currentpoint);
285 updatebbox (currentpoint);
286
287 // Finish up the text message.
288 buffer << endl ; // old << ends;
289 //old outputQ.push (outputline.str());
290 }
291
292
293 /*
294 * Convert a path to LaTeX2e picture format. LaTeX2e's picture mode has no
295 * way to fill a region, so we ignore fills. It can't do line caps, so we
296 * ignore those. Dashes would be a pain to implement (given that we're
297 * using \qbezier), so we ignore dash patterns. Line widths are either
298 * "thick" or "thin"; we separate the two at 1pt. Finally, colors are
299 * supported by all the common backends with the color package, so we can
300 * deal with that. */
show_path()301 void drvLATEX2E::show_path()
302 {
303 // Set the line width as best we can.
304 if (currentLineWidth() >= 1.0) {
305 if (!thicklines) {
306 buffer << (" \\thicklines\n");
307 thicklines = true;
308 }
309 }
310 else
311 if (thicklines) {
312 buffer << (" \\thinlines\n");
313 thicklines = false;
314 }
315
316 // Set the color if (and only if) it's changed.
317 if (currentR()!=prevR || currentG()!=prevG || currentB()!=prevB) {
318 //old ostrstream outputline;
319 buffer.setf(ios::fixed, ios::floatfield); // TeX can't deal with scientific notation.
320 prevR = currentR();
321 prevG = currentG();
322 prevB = currentB();
323 buffer << " \\color[rgb]{"
324 << prevR << ',' << prevG << ',' << prevB << '}'
325 << endl ; // old << ends;
326 //old buffer << (outputline.str());
327 }
328
329 // Output the path.
330 print_coords();
331 };
332
333
334 // Draw a rectangle with \framebox.
show_rectangle(const float llx,const float lly,const float urx,const float ury)335 void drvLATEX2E::show_rectangle(const float llx, const float lly, const float urx, const float ury)
336 {
337 //old ostrstream outputline;
338 buffer.setf(ios::fixed, ios::floatfield); // TeX can't deal with scientific notation.
339 Point ll(llx,lly);
340 Point ur(urx,ury);
341
342 scalepoint (ll);
343 updatebbox (ll);
344 scalepoint (ur);
345 updatebbox (ur);
346 Point framesize(ur.x_-ll.x_, ur.y_-ll.y_);
347 buffer << " \\put" << ll << "{\\framebox" << framesize << "{}}"
348 << endl ; // old << ends;
349 //old outputQ.push (outputline.str());
350 }
351
352
353 // LaTeX2e's picture mode doesn't support images, so do nothing.
show_image(const Image & imageinfo)354 void drvLATEX2E::show_image(const Image & imageinfo)
355 {
356 }
357
358
359 // Describe the LaTeX2e backend's capabilities.
360 static DriverDescriptionT<drvLATEX2E> D_latex2e("latex2e","LaTeX2e picture format","tex",
361
362 true, // backend supports subpathes
363 // if subpathes are supported, the backend must deal with
364 // sequences of the following form
365 // moveto (start of subpath)
366 // lineto (a line segment)
367 // lineto
368 // moveto (start of a new subpath)
369 // lineto (a line segment)
370 // lineto
371 //
372 // If this argument is set to false each subpath is drawn
373 // individually which might not necessarily represent
374 // the original drawing.
375
376 true, // backend supports curves
377 false, // backend supports elements which are filled and have edges
378 true, // backend supports text
379 false, // backend supports Images
380 DriverDescription::normalopen,
381 false); // if format supports multiple pages in one file
382
383 //#endif // HAVESTL
384