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