PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pstex.c
Go to the documentation of this file.
1 // $Id$
2 //
3 // PLplot pstex (Postscript/LaTeX) device driver.
4 //
5 
6 #include "plDevs.h"
7 
8 #ifdef PLD_pstex
9 
10 #include "plplotP.h"
11 #include "drivers.h"
12 #include "ps.h"
13 
14 // Device info
15 PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_pstex =
16  "pstex:Combined Postscript/LaTeX files:0:pstex:41:pstex\n";
17 
18 //--------------------------------------------------------------------------
19 // plD_init_pstex()
20 //
21 // Initialize device.
22 //--------------------------------------------------------------------------
23 
24 
25 static void parse_str( const char *str, char *dest );
26 static void proc_str( PLStream *pls, EscText *args );
27 static int color = 1;
28 
29 static DrvOpt pstex_options[] = { { "color", DRV_INT, &color,
30  "Color Postscript/LaTeX (color=1|0)" },
31  { NULL, DRV_INT, NULL, NULL} };
32 
34 {
35 #ifndef ENABLE_DYNDRIVERS
36  pdt->pl_MenuStr = "Combined Postscript/LaTeX files";
37  pdt->pl_DevName = "pstex";
38 #endif
40  pdt->pl_seq = 41;
44  pdt->pl_eop = (plD_eop_fp) plD_eop_ps;
49 }
50 
51 void
53 {
54  char *ofile;
55  size_t len;
56  PSDev *dev;
57  FILE *fp;
58 
59  plParseDrvOpts( pstex_options );
60  if ( color )
61  plD_init_psc( pls ); // init color postscript driver
62  else
63  plD_init_psm( pls ); // init monochrome postscript driver
64 
65  dev = (PSDev *) pls->dev;
66 
67  pls->dev_text = 1; // want to draw text
68  pls->dev_unicode = 0; // don't want unicode
69 
70  // open latex output file
71  len = strlen( pls->FileName ) + 3;
72  ofile = (char *) malloc( sizeof ( char ) * len );
73  snprintf( ofile, len, "%s_t", pls->FileName );
74  fp = fopen( ofile, "w" );
75  free( ofile );
76  dev->fp = fp;
77 
78  fprintf( fp, "\\begin{picture}(0,0)(0,0)%%\n" );
79  fprintf( fp, "\\includegraphics[scale=1.,clip]{%s}%%\n", pls->FileName );
80  fprintf( fp, "\\end{picture}%%\n" );
81 // fprintf(fp,"\\setlength{\\unitlength}{%fbp}%%\n", 72./25.4/pls->xpmm);
82  fprintf( fp, "\\setlength{\\unitlength}{%fbp}%%\n", 1.0 / ENLARGE );
83  fprintf( fp, "\\begingroup\\makeatletter\\ifx\\SetFigFont\\undefined%%\n" );
84  fprintf( fp, "\\gdef\\SetFigFont#1#2#3#4#5{%%\n" );
85  fprintf( fp, "\\reset@font\\fontsize{#1}{#2pt}%%\n" );
86  fprintf( fp, "\\fontfamily{#3}\\fontseries{#4}\\fontshape{#5}%%\n" );
87  fprintf( fp, "\\selectfont}%%\n" );
88  fprintf( fp, "\\fi\\endgroup%%\n" );
89 
90  dev->cur_pos = ftell( fp );
91  fprintf( fp, "\\begin{picture}(xxxxxx,xxxxxx)(xxxxxx,xxxxxx)%%\n" );
92 }
93 
94 void
95 plD_esc_pstex( PLStream *pls, PLINT op, void *ptr )
96 {
97  switch ( op )
98  {
99  case PLESC_HAS_TEXT:
100  proc_str( pls, ptr );
101  break;
102  default:
103  plD_esc_ps( pls, op, ptr );
104  }
105 }
106 
107 void
108 plD_bop_pstex( PLStream *pls )
109 {
110  plD_bop_ps( pls );
111  plGetFam( pls );
112 }
113 
114 void
115 plD_tidy_pstex( PLStream *pls )
116 {
117  PSDev *dev = (PSDev *) pls->dev;
118  PLFLT scale;
119  FILE *fp;
120 
121  plD_tidy_ps( pls );
122 
123  scale = pls->xpmm * 25.4 / 72.;
124 
125  fp = dev->fp;
126  fprintf( fp, "\\end{picture}\n" );
127 
128  fseek( fp, dev->cur_pos, SEEK_SET );
129  fprintf( fp, "\\begin{picture}(%d,%d)(%d,%d)%%\n%%",
130  ROUND( ( dev->urx - dev->llx ) * scale ),
131  ROUND( ( dev->ury - dev->lly ) * scale ),
132  ROUND( ( dev->llx - XOFFSET ) * scale ),
133  ROUND( ( dev->lly - YOFFSET ) * scale ) );
134 
135  plCloseFile( pls );
136 }
137 
138 void
139 proc_str( PLStream *pls, EscText *args )
140 {
141  PLFLT *t = args->xform;
142  PLFLT a1, alpha, ft_ht, angle;
143  char cptr[256], jst, ref;
144  PSDev *dev = (PSDev *) pls->dev;
145  PLINT clxmin, clxmax, clymin, clymax;
146  FILE *fp;
147 
148  fp = dev->fp;
149 
150  // font height
151  ft_ht = 1.6 * pls->chrht * 72.0 / 25.4; /* ft_ht in points. ht is in mm */
152 
153  // calculate baseline text angle
154  angle = ( (PLFLT) ( ORIENTATION - 1 ) + pls->diorot ) * 90.;
155  a1 = acos( t[0] ) * 180. / PI;
156  if ( t[2] > 0. )
157  alpha = a1 - angle - 90.;
158  else
159  alpha = 360. - a1 - angle - 90.;
160 
161  // parse string for format (escape) characters
162  parse_str( args->string, cptr );
163 
164  //
165  // Reference point (center baseline of string, not latex character reference point).
166  // If base = 0, it is aligned with the center of the text box
167  // If base = 1, it is aligned with the baseline of the text box
168  // If base = 2, it is aligned with the top of the text box
169  // Currently plplot only uses base=0
170  //
171 
172  if ( args->base == 2 ) // not supported by plplot
173  ref = 't';
174  else if ( args->base == 1 )
175  ref = 'b';
176  else
177  ref = 'c';
178 
179  //
180  // Text justification. Left, center and right justification, which
181  // are the more common options, are supported; variable justification is
182  // only approximate, based on plplot computation of it's string lenght
183  //
184 
185  if ( args->just == 0.5 )
186  jst = 'c';
187  else if ( args->just == 1. )
188  jst = 'r';
189  else
190  {
191  jst = 'l';
192  args->x = args->refx; // use hints provided by plplot
193  args->y = args->refy;
194  }
195 
196  // apply transformations
197  difilt( &args->x, &args->y, 1, &clxmin, &clxmax, &clymin, &clymax );
198 
199  // check clip limits. For now, only the reference point of the string is checked;
200  // but the the whole string should be checked -- using a postscript construct
201  // such as gsave/clip/grestore. This method can also be applied to the xfig and
202  // pstex drivers. Zoom side effect: the font size must be adjusted!
203 
204  if ( args->x < clxmin || args->x > clxmax || args->y < clymin || args->y > clymax )
205  return;
206 
207  plRotPhy( ORIENTATION, dev->xmin, dev->ymin, dev->xmax, dev->ymax,
208  &( args->x ), &( args->y ) );
209 
210 #ifdef DEBUG
211  fprintf( fp, "\\put(%d,%d){\\circle{10}}\n",
212  args->x, args->y );
213 #endif
214 
215  fprintf( fp, "\\put(%d,%d){\\rotatebox{%.1f}{\\makebox(0,0)[%c%c]{\\SetFigFont{%.1f}{12}",
216  args->x, args->y, alpha, jst, ref, ft_ht );
217 
218  //
219  // font family, serie and shape. Currently not supported by plplot
220  //
221  // Use current font instead:
222  // 1: Normal font (latex document default font)
223  // 2: Roman font
224  // 3: Italic font (most probably latex slanted)
225  // 4: Script font (latex sans serif)
226  //
227 
228  switch ( pls->cfont )
229  {
230  case ( 1 ): fprintf( fp, "{\\familydefault}" ); break;
231  case ( 2 ): fprintf( fp, "{\\rmdefault}" ); break;
232  case ( 3 ): fprintf( fp, "{\\itdefault}" ); break;
233  case ( 4 ): fprintf( fp, "{\\sfdefault}" ); break;
234  default: fprintf( fp, "{\\familydefault}" );
235  }
236 
237  fprintf( fp, "{\\mddefault}{\\updefault}\n" );
238 
239  // font color.
240 
241  if ( color )
242  fprintf( fp, "\\special{ps: %.3f %.3f %.3f setrgbcolor}{",
243  pls->curcolor.r / 255., pls->curcolor.g / 255., pls->curcolor.b / 255. );
244  else
245  fprintf( fp, "\\special{ps: 0 0 0 setrgbcolor}{" );
246 
247  fprintf( fp, "%% Your text follows:\n" );
248  fprintf( fp, "%s\n", cptr );
249  fprintf( fp, "}}}}" );
250 
251  //
252  // keep ps driver happy -- needed for background and orientation.
253  // arghhh! can't calculate it, as I only have the string reference
254  // point, not its extent!
255  // Quick (and final?) *hack*, ASSUME that no more than a char height
256  // extents after/before the string reference point.
257  //
258 
259  dev->llx = MIN( dev->llx, args->x - ft_ht * 25.4 / 72. * pls->xpmm );
260  dev->lly = MIN( dev->lly, args->y - ft_ht * 25.4 / 72. * pls->ypmm );
261  dev->urx = MAX( dev->urx, args->x + ft_ht * 25.4 / 72. * pls->xpmm );
262  dev->ury = MAX( dev->ury, args->y + ft_ht * 25.4 / 72. * pls->ypmm );
263 }
264 
265 void
266 parse_str( const char *str, char *dest )
267 {
268  int n, opened = 0, raised = 0, overline = 0, underline = 0, fontset = 0, math = 0;
269  char *tp = dest, c, esc;
270  char greek[] = "abgGdDezyhHiklLmncCopPrsStuUfFxqQwW";
271  char *mathgreek[] = { "alpha", "beta", "gamma", "Gamma", "delta", "Delta",
272  "epsilon", "zeta", "eta", "theta", "Theta", "iota",
273  "kappa", "lambda", "Lambda", "mu", "nu", "xi", "Xi",
274  "o", "pi", "Pi", "rho", "sigma", "Sigma","tau",
275  "upsilon", "Upsilon", "phi", "Phi", "chi",
276  "psi", "Psi", "omega", "Omega" };
277 
278  plgesc( &esc );
279 
280  while ( *str )
281  {
282  if ( *str != esc )
283  {
284  *tp++ = *str++;
285  continue;
286  }
287  str++;
288 
289  switch ( *str++ )
290  {
291  case 'u': // up one level
292  if ( raised < 0 )
293  {
294  *tp++ = '}';
295  opened--;
296  }
297  else
298  {
299  n = sprintf( tp, "\\raisebox{%.2fex}{", 0.6 );
300  tp += n; opened++;
301  }
302  raised++;
303  break;
304 
305  case 'd': // down one level
306  if ( raised > 0 )
307  {
308  *tp++ = '}';
309  opened--;
310  }
311  else
312  {
313  n = sprintf( tp, "\\raisebox{%.2fex}{", -0.6 );
314  tp += n; opened++;
315  }
316  raised--;
317  break;
318 
319  case 'b': // backspace
320  n = sprintf( tp, "\\hspace{-1em}" );
321  tp += n;
322  break;
323 
324  case '+': // toggles overline mode. Side effect, enter math mode.
325  if ( overline )
326  {
327  if ( --math )
328  *tp++ = '}';
329  else
330  {
331  n = sprintf( tp, "}$" );
332  tp += n;
333  }
334  overline--; opened--;
335  }
336  else
337  {
338  if ( !math )
339  *tp++ = '$';
340 
341  n = sprintf( tp, "\\overline{" );
342  tp += n; overline++; opened++; math++;
343  }
344  break;
345 
346  case '-': // toggles underline mode. Side effect, enter math mode.
347  if ( underline )
348  {
349  if ( --math )
350  *tp++ = '}';
351  else
352  {
353  n = sprintf( tp, "}$" );
354  tp += n;
355  }
356  underline--; opened--;
357  }
358  else
359  {
360  if ( !math )
361  *tp++ = '$';
362 
363  n = sprintf( tp, "\\underline{" );
364  tp += n; underline++; opened++; math++;
365  }
366  break;
367 
368  case 'g': // greek letter corresponding to roman letter x
369  c = *str++;
370  n = plP_strpos( greek, c );
371  if ( n != -1 )
372  {
373  if ( !math )
374  *tp++ = '$';
375 
376  *tp++ = '\\';
377  strcpy( tp, mathgreek[n] );
378  if ( isupper( c ) )
379  *tp = toupper( *tp );
380  tp += strlen( mathgreek[n] );
381  if ( !math )
382  *tp++ = '$';
383  }
384  else
385  *tp++ = c;
386 
387  break;
388 
389  case '(': // Hershey symbol number (nnn) (any number of digits) FIXME ???
390  plwarn( "'g(...)' text escape sequence not processed." );
391  while ( *str++ != ')' )
392  ;
393  break;
394 
395  case 'f': // switch font
396 
397  switch ( *str++ )
398  {
399  case 'n': // Normal
400  while ( fontset-- )
401  {
402  *tp++ = '}';
403  opened--;
404  }
405 
406  if ( math )
407  {
408  *tp++ = '$';
409  math = 0;
410  }
411 
412  n = sprintf( tp, "\\normalfont " );
413  tp += n;
414  break;
415 
416  case 'r': // Roman
417  if ( math )
418  n = sprintf( tp, "\\mathrm{" );
419  else
420  n = sprintf( tp, "\\textrm{" );
421 
422  tp += n; opened++; fontset++;
423  break;
424 
425  case 'i': // Italic
426  if ( math )
427  n = sprintf( tp, "\\mathit{" );
428  else
429  n = sprintf( tp, "\\textit{" );
430 
431  tp += n; opened++; fontset++;
432  break;
433 
434  case 's': // Script. Don't, use sans serif
435  if ( math )
436  n = sprintf( tp, "\\mathsf{" );
437  else
438  n = sprintf( tp, "\\textsf{" );
439 
440  tp += n; opened++; fontset++;
441  break;
442  }
443 
444  default:
445  if ( *str == esc )
446  *tp++ = esc;
447  }
448  }
449 
450  while ( opened-- )
451  *tp++ = '}';
452  *tp = '\0';
453 }
454 
455 #else
456 int
458 {
459  return 0;
460 }
461 
462 #endif // PLD_pstexdev