PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plsym.c
Go to the documentation of this file.
1 // Point, symbol, and string plotting routines.
2 // Also font management code. See the description of plLibOpen() for
3 // the search path used in finding the font files.
4 //
5 // Copyright (C) 1992 Geoffrey Furnish
6 // Copyright (C) 1993, 1994, 1995, 2000, 2001, 2002 Maurice LeBrun
7 // Copyright (C) 2000-2014 Alan W. Irwin
8 // Copyright (C) 2001, 2003, 2004 Rafael Laboissiere
9 // Copyright (C) 2002 Vincent Darley
10 // Copyright (C) 2004 Andrew Ross
11 // Copyright (C) 2007 Hazen Babcock
12 //
13 // This file is part of PLplot.
14 //
15 // PLplot is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU Library General Public License as published
17 // by the Free Software Foundation; either version 2 of the License, or
18 // (at your option) any later version.
19 //
20 // PLplot is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU Library General Public License for more details.
24 //
25 // You should have received a copy of the GNU Library General Public License
26 // along with PLplot; if not, write to the Free Software
27 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
28 //
29 
34 
35 #ifndef __PLSYM_H__
36 #define __PLSYM_H__
37 
38 #include "plplotP.h"
39 #include <float.h>
40 #include <ctype.h>
41 #include "plhershey-unicode.h"
42 
43 // Declarations
44 
45 static short int *fntlkup;
46 static short int *fntindx;
47 static signed char *fntbffr;
48 static short int numberfonts, numberchars;
49 static short int indxleng;
50 
51 static short fontloaded = 0;
52 // moved to plstr.h, plsc->cfont static PLINT font = 1; current font
53 
54 #define PLMAXSTR 300
55 #define STLEN 250
56 
57 static const char font_types[] = "nris";
58 
59 static short symbol_buffer[PLMAXSTR];
60 static signed char xygrid[STLEN];
61 
62 int hershey2unicode( int in );
63 
64 // Static function prototypes
65 
66 static void
67 pldeco( short int **sym, PLINT *length, const char *text );
68 
69 static void
70 plchar( signed char *xygrid, PLFLT *xform, PLINT base, PLINT oline, PLINT uline,
71  PLINT refx, PLINT refy, PLFLT scale, PLFLT xpmm, PLFLT ypmm,
72  PLFLT *p_xorg, PLFLT *p_yorg, PLFLT *p_width );
73 
74 static PLINT
75 plcvec( PLINT ch, signed char **xygr );
76 
77 static void
78 plhrsh( PLINT ch, PLINT x, PLINT y );
79 
80 static void
81 plhrsh2( PLINT ch, PLINT x, PLINT y );
82 
83 //--------------------------------------------------------------------------
98 //--------------------------------------------------------------------------
99 
100 void
101 c_plstring( PLINT n, const PLFLT *x, const PLFLT *y, const char *string )
102 {
103  PLINT i;
104  for ( i = 0; i < n; i++ )
105  {
106  c_plptex( x[i], y[i], 1., 0., 0.5, string );
107  }
108 }
109 
110 //--------------------------------------------------------------------------
118 //--------------------------------------------------------------------------
119 
120 void
121 c_plsym( PLINT n, const PLFLT *x, const PLFLT *y, PLINT code )
122 {
123  PLINT i;
124  PLFLT xt, yt;
125 
126  if ( plsc->level < 3 )
127  {
128  plabort( "plsym: Please set up window first" );
129  return;
130  }
131  if ( code < 0 )
132  {
133  plabort( "plsym: Invalid code" );
134  return;
135  }
136 
137  for ( i = 0; i < n; i++ )
138  {
139  TRANSFORM( x[i], y[i], &xt, &yt );
140  plhrsh( code, plP_wcpcx( xt ), plP_wcpcy( yt ) );
141  }
142 }
143 
144 //--------------------------------------------------------------------------
162 //--------------------------------------------------------------------------
163 
164 void
165 c_plpoin( PLINT n, const PLFLT *x, const PLFLT *y, PLINT code )
166 {
167  PLINT i, sym, ifont = plsc->cfont;
168  PLFLT xt, yt;
169 
170  if ( plsc->level < 3 )
171  {
172  plabort( "plpoin: Please set up window first" );
173  return;
174  }
175  if ( code < -1 || code > 127 )
176  {
177  plabort( "plpoin: Invalid code" );
178  return;
179  }
180 
181  if ( code == -1 )
182  {
183  for ( i = 0; i < n; i++ )
184  {
185  TRANSFORM( x[i], y[i], &xt, &yt );
186  pljoin( xt, yt, xt, yt );
187  }
188  }
189  else
190  {
191  if ( ifont > numberfonts )
192  ifont = 1;
193  sym = *( fntlkup + ( ifont - 1 ) * numberchars + code );
194  // One-time diagnostic output.
195  // fprintf(stdout, "plploin code, sym = %d, %d\n", code, sym);
196 
197  for ( i = 0; i < n; i++ )
198  {
199  TRANSFORM( x[i], y[i], &xt, &yt );
200  plhrsh( sym, plP_wcpcx( xt ), plP_wcpcy( yt ) );
201  }
202  }
203 }
204 
205 //--------------------------------------------------------------------------
225 //--------------------------------------------------------------------------
226 
227 void
228 c_plpoin3( PLINT n, const PLFLT *x, const PLFLT *y, const PLFLT *z, PLINT code )
229 {
230  PLINT i, sym, ifont = plsc->cfont;
231  PLFLT u, v;
232  PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale;
233 
234  if ( plsc->level < 3 )
235  {
236  plabort( "plpoin3: Please set up window first" );
237  return;
238  }
239  if ( code < -1 || code > 127 )
240  {
241  plabort( "plpoin3: Invalid code" );
242  return;
243  }
244 
245  plP_gdom( &xmin, &xmax, &ymin, &ymax );
246  plP_grange( &zscale, &zmin, &zmax );
247 
248  if ( code == -1 )
249  {
250  for ( i = 0; i < n; i++ )
251  {
252  if ( x[i] >= xmin && x[i] <= xmax &&
253  y[i] >= ymin && y[i] <= ymax &&
254  z[i] >= zmin && z[i] <= zmax )
255  {
256  u = plP_wcpcx( plP_w3wcx( x[i], y[i], z[i] ) );
257  v = plP_wcpcy( plP_w3wcy( x[i], y[i], z[i] ) );
258  plP_movphy( (PLINT) u, (PLINT) v );
259  plP_draphy( (PLINT) u, (PLINT) v );
260  }
261  }
262  }
263  else
264  {
265  if ( ifont > numberfonts )
266  ifont = 1;
267  sym = *( fntlkup + ( ifont - 1 ) * numberchars + code );
268 
269  for ( i = 0; i < n; i++ )
270  {
271  if ( x[i] >= xmin && x[i] <= xmax &&
272  y[i] >= ymin && y[i] <= ymax &&
273  z[i] >= zmin && z[i] <= zmax )
274  {
275  u = plP_wcpcx( plP_w3wcx( x[i], y[i], z[i] ) );
276  v = plP_wcpcy( plP_w3wcy( x[i], y[i], z[i] ) );
277  plhrsh( sym, (PLINT) u, (PLINT) v );
278  }
279  }
280  }
281 }
282 
283 //--------------------------------------------------------------------------
301 //--------------------------------------------------------------------------
302 
303 void
304 c_plstring3( PLINT n, const PLFLT *x, const PLFLT *y, const PLFLT *z, const char * string )
305 {
306  PLINT i;
307  PLFLT u, v;
308  PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale;
309 
310  if ( plsc->level < 3 )
311  {
312  plabort( "plstring3: Please set up window first" );
313  return;
314  }
315 
316  plP_gdom( &xmin, &xmax, &ymin, &ymax );
317  plP_grange( &zscale, &zmin, &zmax );
318 
319  for ( i = 0; i < n; i++ )
320  {
321  if ( x[i] >= xmin && x[i] <= xmax &&
322  y[i] >= ymin && y[i] <= ymax &&
323  z[i] >= zmin && z[i] <= zmax )
324  {
325  u = plP_w3wcx( x[i], y[i], z[i] );
326  v = plP_w3wcy( x[i], y[i], z[i] );
327  c_plptex( u, v, 1., 0., 0.5, string );
328  }
329  }
330 }
331 
332 //--------------------------------------------------------------------------
333 // static void plhrsh(PLINT ch, PLINT x, PLINT y)
334 // PLINT ch - hershey code to plot
335 // PLINT x - device-world x coordinate of hershey character
336 // PLINT y - device-world y coordinate of hershey character
337 //
338 // Writes the Hershey symbol "ch" centred at the physical coordinate (x,y).
339 // This function is now just a "spoof" front end to the old plhersh,
340 // which has now been renamed to plhrsh2(). All this function does is
341 // decide whether or not we should render natively as unicode, and then
342 // convert between hershey and unicode.
343 //
344 // If the function KNOWS there isn't a unicode equivalent, then it will
345 // try to render it as a hershey font. Understandably, this might make
346 // testing out the unicode functions a little tricky, so if you want
347 // to disable this behaviour, recompile with PL_TEST_FOR_MISSING_GLYPHS
348 // defined.
349 //--------------------------------------------------------------------------
350 
351 static void
352 plhrsh( PLINT ch, PLINT x, PLINT y )
353 {
354  EscText args;
355  int idx;
356  PLUNICODE unicode_char;
357 
358  // Check to see if the device understands unicode and wants to draw
359  // symbols.
360  //
361  if ( ( plsc->dev_text ) && ( plsc->dev_unicode ) && ( !plsc->dev_hrshsym ) )
362  {
363  idx = plhershey2unicode( ch ); // Get the index in the lookup table
364  unicode_char = hershey_to_unicode_lookup_table[idx].Unicode;
365 
366  //
367  // Test to see if there is a defined unicode glyph for this hershey code;
368  // if there isn't, then we pass the glyph to plhersh, and have it
369  // rendered the old fashioned way.
370  // Otherwise, we let the driver render it as unicode
371  //
372 
373  if ( ( unicode_char == 0 ) || ( idx == -1 ) )
374  {
375 #ifndef PL_TEST_FOR_MISSING_GLYPHS
376  plhrsh2( ch, x, y );
377 #endif
378  }
379  else
380  {
381  PLUNICODE plhrsh_unicode_buffer[3], fci;
382  PLFLT xform[] = { 1.0, 0.0, 0.0, 1.0 };
383  char esc;
384  args.unicode_char = unicode_char;
386  // Comment out to fix problem with ps, psttf drivers
387  //args.base = 1;
388  args.base = 0;
389  args.just = .5;
390  args.xform = 0;
391  args.x = x;
392  args.y = y;
393  args.string = NULL; // Since we are using unicode, we want this to be NULL
394  // "array method"
395  plgesc( &esc );
396  args.xform = xform;
397  args.unicode_array_len = 1;
398  plhrsh_unicode_buffer[0] = unicode_char;
399  // watch out for escape character and unescape it by appending
400  // one extra.
401  if ( unicode_char == (PLUNICODE) esc )
402  {
403  args.unicode_array_len = 2;
404  plhrsh_unicode_buffer[1] = unicode_char;
405  }
406 
407  // No need to change font back since only one character.
408  args.unicode_array = &plhrsh_unicode_buffer[0]; // Get address of the unicode buffer (even though it is currently static)
409 
410  plsc->original_chrht = plsc->chrht;
411  plsc->original_chrdef = plsc->chrdef;
412  plsc->chrht = plsc->symht;
413  plsc->chrdef = plsc->symdef;
414 
415  if ( plsc->alt_unicode )
416  {
417  plgfci( &fci );
418  args.n_fci = fci;
419  args.n_char = unicode_char;
420  plP_esc( PLESC_BEGIN_TEXT, &args );
421  plP_esc( PLESC_TEXT_CHAR, &args );
422  plP_esc( PLESC_END_TEXT, &args );
423  }
424  else
425  {
426  plP_esc( PLESC_HAS_TEXT, &args );
427  }
428 
429  plsc->chrht = plsc->original_chrht;
430  plsc->chrdef = plsc->original_chrdef;
431  }
432  }
433  else
434  {
435  plhrsh2( ch, x, y );
436  }
437 }
438 
439 //--------------------------------------------------------------------------
440 // void plhrsh2()
441 //
442 // Writes the Hershey symbol "ch" centred at the physical coordinate (x,y).
443 //--------------------------------------------------------------------------
444 
445 static void
447 {
448  PLINT cx, cy, k, penup, style;
449  signed char *vxygrid = 0;
450  PLFLT scale, xscale, yscale;
451  PLINT llx[STLEN], lly[STLEN], l = 0;
452 
453  penup = 1;
454  scale = 0.05 * plsc->symht;
455 
456  if ( !plcvec( ch, &vxygrid ) )
457  {
458  plP_movphy( x, y );
459  return;
460  }
461 
462 // Line style must be continuous
463 
464  style = plsc->nms;
465  plsc->nms = 0;
466 
467 // Compute how many physical pixels correspond to a character pixel
468 
469  xscale = scale * plsc->xpmm;
470  yscale = scale * plsc->ypmm;
471 
472  k = 4;
473  for (;; )
474  {
475  cx = vxygrid[k++];
476  cy = vxygrid[k++];
477  if ( cx == 64 && cy == 64 )
478  {
479  if ( l )
480  {
481  plP_draphy_poly( llx, lly, l );
482  l = 0;
483  }
484  plP_movphy( x, y );
485  plsc->nms = style;
486  return;
487  }
488  else if ( cx == 64 && cy == 0 )
489  penup = 1;
490  else
491  {
492  if ( penup == 1 )
493  {
494  if ( l )
495  {
496  plP_draphy_poly( llx, lly, l );
497  l = 0;
498  }
499  llx[l] = ROUND( x + xscale * cx );
500  lly[l++] = ROUND( y + yscale * cy );
501  plP_movphy( llx[l - 1], lly[l - 1] );
502  penup = 0;
503  }
504  else
505  {
506  llx[l] = ROUND( x + xscale * cx );
507  lly[l++] = ROUND( y + yscale * cy );
508  }
509  }
510  }
511 }
512 
513 //--------------------------------------------------------------------------
514 // void pllab()
515 //
516 // Simple routine for labelling graphs.
517 //--------------------------------------------------------------------------
518 
519 void
520 c_pllab( const char *xlabel, const char *ylabel, const char *tlabel )
521 {
522  if ( plsc->level < 2 )
523  {
524  plabort( "pllab: Please set up viewport first" );
525  return;
526  }
527 
528  plmtex( "t", (PLFLT) 2.0, (PLFLT) 0.5, (PLFLT) 0.5, tlabel );
529  plmtex( "b", (PLFLT) 3.2, (PLFLT) 0.5, (PLFLT) 0.5, xlabel );
530  plmtex( "l", (PLFLT) 5.0, (PLFLT) 0.5, (PLFLT) 0.5, ylabel );
531 }
532 
533 //--------------------------------------------------------------------------
534 // void plmtex()
535 //
536 // Prints out "text" at specified position relative to viewport
537 // (may be inside or outside)
538 //
539 // side String which is one of the following:
540 // B or b : Bottom of viewport
541 // T or t : Top of viewport
542 // BV or bv : Bottom of viewport, vertical text
543 // TV or tv : Top of viewport, vertical text
544 // L or l : Left of viewport
545 // R or r : Right of viewport
546 // LV or lv : Left of viewport, vertical text
547 // RV or rv : Right of viewport, vertical text
548 //
549 // disp Displacement from specified edge of viewport, measured outwards from
550 // the viewport in units of the current character height. The
551 // centerlines of the characters are aligned with the specified
552 // position.
553 //
554 // pos Position of the reference point of the string relative to the
555 // viewport edge, ranging from 0.0 (left-hand edge) to 1.0 (right-hand
556 // edge)
557 //
558 // just Justification of string relative to reference point
559 // just = 0.0 => left hand edge of string is at reference
560 // just = 1.0 => right hand edge of string is at reference
561 // just = 0.5 => center of string is at reference
562 //--------------------------------------------------------------------------
563 
564 void
565 c_plmtex( const char *side, PLFLT disp, PLFLT pos, PLFLT just,
566  const char *text )
567 {
568  PLINT clpxmi, clpxma, clpymi, clpyma;
569  PLINT vert, refx, refy, x, y;
570  PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, shift, xform[4];
571  PLFLT chrdef, chrht;
572  PLFLT dispx, dispy;
573 
574  if ( plsc->level < 2 )
575  {
576  plabort( "plmtex: Please set up viewport first" );
577  return;
578  }
579 
580 // Open clip limits to subpage limits
581 
582  plP_gclp( &clpxmi, &clpxma, &clpymi, &clpyma ); // get and store current clip limits
583  plP_sclp( plsc->sppxmi, plsc->sppxma, plsc->sppymi, plsc->sppyma );
584 
585  if ( plP_stindex( side, "BV" ) != -1 || plP_stindex( side, "bv" ) != -1 )
586  {
587  vert = 1;
588  xdv = plsc->vpdxmi + ( plsc->vpdxma - plsc->vpdxmi ) * pos;
589  ydv = plsc->vpdymi;
590  dispx = 0;
591  dispy = -disp;
592  }
593  else if ( plP_stindex( side, "TV" ) != -1 || plP_stindex( side, "tv" ) != -1 )
594  {
595  vert = 1;
596  xdv = plsc->vpdxmi + ( plsc->vpdxma - plsc->vpdxmi ) * pos;
597  ydv = plsc->vpdyma;
598  dispx = 0;
599  dispy = disp;
600  }
601  else if ( plP_stsearch( side, 'b' ) )
602  {
603  vert = 0;
604  xdv = plsc->vpdxmi + ( plsc->vpdxma - plsc->vpdxmi ) * pos;
605  ydv = plsc->vpdymi;
606  dispx = 0;
607  dispy = -disp;
608  }
609  else if ( plP_stsearch( side, 't' ) )
610  {
611  vert = 0;
612  xdv = plsc->vpdxmi + ( plsc->vpdxma - plsc->vpdxmi ) * pos;
613  ydv = plsc->vpdyma;
614  dispx = 0;
615  dispy = disp;
616  }
617  else if ( plP_stindex( side, "LV" ) != -1 || plP_stindex( side, "lv" ) != -1 )
618  {
619  vert = 0;
620  xdv = plsc->vpdxmi;
621  ydv = plsc->vpdymi + ( plsc->vpdyma - plsc->vpdymi ) * pos;
622  dispx = -disp;
623  dispy = 0;
624  }
625  else if ( plP_stindex( side, "RV" ) != -1 || plP_stindex( side, "rv" ) != -1 )
626  {
627  vert = 0;
628  xdv = plsc->vpdxma;
629  ydv = plsc->vpdymi + ( plsc->vpdyma - plsc->vpdymi ) * pos;
630  dispx = disp;
631  dispy = 0;
632  }
633  else if ( plP_stsearch( side, 'l' ) )
634  {
635  vert = 1;
636  xdv = plsc->vpdxmi;
637  ydv = plsc->vpdymi + ( plsc->vpdyma - plsc->vpdymi ) * pos;
638  dispx = -disp;
639  dispy = 0;
640  }
641  else if ( plP_stsearch( side, 'r' ) )
642  {
643  vert = 1;
644  xdv = plsc->vpdxma;
645  ydv = plsc->vpdymi + ( plsc->vpdyma - plsc->vpdymi ) * pos;
646  dispx = disp;
647  dispy = 0;
648  }
649  else
650  {
651  plP_sclp( clpxmi, clpxma, clpymi, clpyma ); // restore initial clip limits
652  return;
653  }
654 
655 // Transformation matrix
656 
657  if ( vert != 0 )
658  {
659  xform[0] = 0.0;
660  xform[1] = -1.0;
661  xform[2] = 1.0;
662  xform[3] = 0.0;
663  }
664  else
665  {
666  xform[0] = 1.0;
667  xform[1] = 0.0;
668  xform[2] = 0.0;
669  xform[3] = 1.0;
670  }
671 
672 // Convert to physical units (mm) and compute shifts
673 
674  plgchr( &chrdef, &chrht );
675  shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just;
676 
677  xmm = plP_dcmmx( xdv ) + dispx * chrht;
678  ymm = plP_dcmmy( ydv ) + dispy * chrht;
679  refxmm = xmm - shift * xform[0];
680  refymm = ymm - shift * xform[2];
681 
682 // Convert to device units (pixels) and call text plotter
683 
684  x = plP_mmpcx( xmm );
685  y = plP_mmpcy( ymm );
686  refx = plP_mmpcx( refxmm );
687  refy = plP_mmpcy( refymm );
688 
689  plP_text( 0, just, xform, x, y, refx, refy, text );
690  plP_sclp( clpxmi, clpxma, clpymi, clpyma ); // restore clip limits
691 }
692 
693 //--------------------------------------------------------------------------
694 // void plptex()
695 //
696 // Prints out "text" at world cooordinate (wx,wy). The text may be
697 // at any angle "angle" relative to the horizontal. The parameter
698 // "just" adjusts the horizontal justification of the string:
699 // just = 0.0 => left hand edge of string is at (wx,wy)
700 // just = 1.0 => right hand edge of string is at (wx,wy)
701 // just = 0.5 => center of string is at (wx,wy) etc.
702 //--------------------------------------------------------------------------
703 
704 void
705 c_plptex( PLFLT wx, PLFLT wy, PLFLT dx, PLFLT dy, PLFLT just, const char *text )
706 {
707  PLINT x, y, refx, refy;
708  PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, shift, cc, ss;
709  PLFLT xform[4], diag;
710  PLFLT chrdef, chrht;
711  PLFLT dispx, dispy;
712  PLFLT wxt, wyt, dxt, dyt;
713 
714  if ( plsc->level < 3 )
715  {
716  plabort( "plptex: Please set up window first" );
717  return;
718  }
719 
720  // Transform both the origin and offset values
721  TRANSFORM( wx, wy, &wxt, &wyt );
722  TRANSFORM( wx + dx, wy + dy, &dxt, &dyt );
723  dxt = dxt - wxt;
724  dyt = dyt - wyt;
725  if ( dxt == 0.0 && dyt == 0.0 )
726  {
727  dxt = 1.0;
728  dyt = 0.0;
729  }
730 
731  cc = plsc->wmxscl * dxt;
732  ss = plsc->wmyscl * dyt;
733  diag = sqrt( cc * cc + ss * ss );
734  cc /= diag;
735  ss /= diag;
736 
737  xform[0] = cc;
738  xform[1] = -ss;
739  xform[2] = ss;
740  xform[3] = cc;
741 
742  xdv = plP_wcdcx( wxt );
743  ydv = plP_wcdcy( wyt );
744 
745  dispx = 0.;
746  dispy = 0.;
747 
748 // Convert to physical units (mm) and compute shifts
749 
750  plgchr( &chrdef, &chrht );
751  shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just;
752 
753  xmm = plP_dcmmx( xdv ) + dispx * chrht;
754  ymm = plP_dcmmy( ydv ) + dispy * chrht;
755  refxmm = xmm - shift * xform[0];
756  refymm = ymm - shift * xform[2];
757 
758  x = plP_mmpcx( xmm );
759  y = plP_mmpcy( ymm );
760  refx = plP_mmpcx( refxmm );
761  refy = plP_mmpcy( refymm );
762 
763  plP_text( 0, just, xform, x, y, refx, refy, text );
764 }
765 
766 //--------------------------------------------------------------------------
767 // void plstr()
768 //
769 // Prints out a "string" at reference position with physical coordinates
770 // (refx,refy). The coordinates of the vectors defining the string are
771 // passed through the linear mapping defined by the 2 x 2 matrix xform()
772 // before being plotted. The reference position is at the left-hand edge of
773 // the string. If base = 1, it is aligned with the baseline of the string.
774 // If base = 0, it is aligned with the center of the character box.
775 //
776 // Note, all calculations are done in terms of millimetres. These are scaled
777 // as necessary before plotting the string on the page.
778 //--------------------------------------------------------------------------
779 
780 void
781 plstr( PLINT base, PLFLT *xform, PLINT refx, PLINT refy, const char *string )
782 {
783  short int *symbol;
784  signed char *vxygrid = 0;
785 
786  PLINT ch, i, length, level = 0, style, oline = 0, uline = 0;
787  PLFLT width = 0., xorg = 0., yorg = 0., def, ht, dscale, scale;
788  PLFLT old_sscale, sscale, old_soffset, soffset;
789 
790  plgchr( &def, &ht );
791  dscale = 0.05 * ht;
792  scale = dscale;
793 
794 // Line style must be continuous
795 
796  style = plsc->nms;
797  plsc->nms = 0;
798 
799  pldeco( &symbol, &length, string );
800 
801  for ( i = 0; i < length; i++ )
802  {
803  ch = symbol[i];
804  if ( ch == -1 ) // superscript
805  {
806  plP_script_scale( TRUE, &level,
807  &old_sscale, &sscale, &old_soffset, &soffset );
808  yorg = 16.0 * dscale * soffset;
809  scale = dscale * sscale;
810  }
811  else if ( ch == -2 ) // subscript
812  {
813  plP_script_scale( FALSE, &level,
814  &old_sscale, &sscale, &old_soffset, &soffset );
815  yorg = -16.0 * dscale * soffset;
816  scale = dscale * sscale;
817  }
818  else if ( ch == -3 ) // back-char
819  xorg -= width * scale;
820  else if ( ch == -4 ) // toogle overline
821  oline = !oline;
822  else if ( ch == -5 ) // toogle underline
823  uline = !uline;
824  else
825  {
826  if ( plcvec( ch, &vxygrid ) )
827  plchar( vxygrid, xform, base, oline, uline, refx, refy, scale,
828  plsc->xpmm, plsc->ypmm, &xorg, &yorg, &width );
829  }
830  }
831  plsc->nms = style;
832 }
833 
834 //--------------------------------------------------------------------------
835 // plchar()
836 //
837 // Plots out a given stroke font character.
838 //--------------------------------------------------------------------------
839 
840 static void
841 plchar( signed char *vxygrid, PLFLT *xform, PLINT base, PLINT oline, PLINT uline,
842  PLINT refx, PLINT refy, PLFLT scale, PLFLT xpmm, PLFLT ypmm,
843  PLFLT *p_xorg, PLFLT *p_yorg, PLFLT *p_width )
844 {
845  PLINT xbase, ybase, ydisp, lx, ly, cx, cy;
846  PLINT k, penup;
847  PLFLT x, y;
848  PLINT llx[STLEN], lly[STLEN], l = 0;
849 
850  xbase = vxygrid[2];
851  *p_width = vxygrid[3] - xbase;
852  if ( base == 0 )
853  {
854  ybase = 0;
855  ydisp = vxygrid[0];
856  }
857  else
858  {
859  ybase = vxygrid[0];
860  ydisp = 0;
861  }
862  k = 4;
863  penup = 1;
864 
865  for (;; )
866  {
867  cx = vxygrid[k++];
868  cy = vxygrid[k++];
869  if ( cx == 64 && cy == 64 )
870  {
871  if ( l )
872  {
873  plP_draphy_poly( llx, lly, l );
874  l = 0;
875  }
876  break;
877  }
878  if ( cx == 64 && cy == 0 )
879  {
880  if ( l )
881  {
882  plP_draphy_poly( llx, lly, l );
883  l = 0;
884  }
885  penup = 1;
886  }
887  else
888  {
889  x = *p_xorg + ( cx - xbase ) * scale;
890  y = *p_yorg + ( cy - ybase ) * scale;
891  lx = refx + ROUND( xpmm * ( xform[0] * x + xform[1] * y ) );
892  ly = refy + ROUND( ypmm * ( xform[2] * x + xform[3] * y ) );
893  if ( penup == 1 )
894  {
895  if ( l )
896  {
897  plP_draphy_poly( llx, lly, l );
898  l = 0;
899  }
900  llx[l] = lx;
901  lly[l++] = ly; // store 1st point !
902  plP_movphy( lx, ly );
903  penup = 0;
904  }
905  else
906  {
907  llx[l] = lx;
908  lly[l++] = ly;
909  }
910  }
911  }
912 
913  if ( oline )
914  {
915  x = *p_xorg;
916  y = *p_yorg + ( 30 + ydisp ) * scale;
917  lx = refx + ROUND( xpmm * ( xform[0] * x + xform[1] * y ) );
918  ly = refy + ROUND( ypmm * ( xform[2] * x + xform[3] * y ) );
919  plP_movphy( lx, ly );
920  x = *p_xorg + *p_width * scale;
921  lx = refx + ROUND( xpmm * ( xform[0] * x + xform[1] * y ) );
922  ly = refy + ROUND( ypmm * ( xform[2] * x + xform[3] * y ) );
923  plP_draphy( lx, ly );
924  }
925  if ( uline )
926  {
927  x = *p_xorg;
928  y = *p_yorg + ( -5 + ydisp ) * scale;
929  lx = refx + ROUND( xpmm * ( xform[0] * x + xform[1] * y ) );
930  ly = refy + ROUND( ypmm * ( xform[2] * x + xform[3] * y ) );
931  plP_movphy( lx, ly );
932  x = *p_xorg + *p_width * scale;
933  lx = refx + ROUND( xpmm * ( xform[0] * x + xform[1] * y ) );
934  ly = refy + ROUND( ypmm * ( xform[2] * x + xform[3] * y ) );
935  plP_draphy( lx, ly );
936  }
937  *p_xorg = *p_xorg + *p_width * scale;
938 }
939 
940 //--------------------------------------------------------------------------
941 // PLFLT plstrl()
942 //
943 // Computes the length of a string in mm, including escape sequences.
944 //--------------------------------------------------------------------------
945 
946 PLFLT
947 plstrl( const char *string )
948 {
949  short int *symbol;
950  signed char *vxygrid = 0;
951  PLINT ch, i, length, level = 0;
952  PLFLT width = 0., xorg = 0., dscale, scale, def, ht;
953 
954  // If the driver will compute string lengths for us then we ask
955  // it do so by setting get_string_length flag. When this is set
956  // the driver will set the string_length variable instead of
957  // actually rendering the string.
958  //
959  // TODO:
960  // Is plmtex the best string diplay routine to use?
961  // Will this work for buffered plots?
962 
963  if ( plsc->has_string_length )
964  {
965  plsc->get_string_length = 1;
966  c_plmtex( "t", 0.0, 0.0, 0.0, string );
967  plsc->get_string_length = 0;
968  return (PLFLT) plsc->string_length;
969  }
970 
971 
972  plgchr( &def, &ht );
973  dscale = 0.05 * ht;
974  scale = dscale;
975  pldeco( &symbol, &length, string );
976 
977  for ( i = 0; i < length; i++ )
978  {
979  ch = symbol[i];
980  if ( ch == -1 )
981  {
982  level++;
983  scale = dscale * pow( 0.75, (double) ABS( level ) );
984  }
985  else if ( ch == -2 )
986  {
987  level--;
988  scale = dscale * pow( 0.75, (double) ABS( level ) );
989  }
990  else if ( ch == -3 )
991  xorg -= width * scale;
992  else if ( ch == -4 || ch == -5 )
993  ;
994  else
995  {
996  if ( plcvec( ch, &vxygrid ) )
997  {
998  width = vxygrid[3] - vxygrid[2];
999  xorg += width * scale;
1000  }
1001  }
1002  }
1003  return (PLFLT) xorg;
1004 }
1005 
1006 //--------------------------------------------------------------------------
1007 // PLINT plcvec()
1008 //
1009 // Gets the character digitisation of Hershey table entry "char".
1010 // Returns 1 if there is a valid entry.
1011 //--------------------------------------------------------------------------
1012 
1013 static PLINT
1014 plcvec( PLINT ch, signed char **xygr )
1015 {
1016  PLINT k = 0, ib;
1017  signed char x, y;
1018 
1019  ch--;
1020  if ( ch < 0 || ch >= indxleng )
1021  return (PLINT) 0;
1022  ib = fntindx[ch] - 2;
1023  if ( ib == -2 )
1024  return (PLINT) 0;
1025 
1026  do
1027  {
1028  ib++;
1029  x = fntbffr[2 * ib];
1030  y = fntbffr[2 * ib + 1];
1031  xygrid[k++] = x;
1032  xygrid[k++] = y;
1033  } while ( ( x != 64 || y != 64 ) && k <= ( STLEN - 2 ) );
1034 
1035  if ( k == ( STLEN - 1 ) )
1036  {
1037  // This is bad if we get here
1038  xygrid[k] = 64;
1039  xygrid[k] = 64;
1040  }
1041 
1042  *xygr = xygrid;
1043  return (PLINT) 1;
1044 }
1045 
1046 //--------------------------------------------------------------------------
1047 // void pldeco()
1048 //
1049 // Decode a character string, and return an array of float integer symbol
1050 // numbers. This routine is responsible for interpreting all escape sequences.
1051 // At present the following escape sequences are defined (the letter following
1052 // the <esc> may be either upper or lower case):
1053 //
1054 // <esc>u : up one level (returns -1)
1055 // <esc>d : down one level (returns -2)
1056 // <esc>b : backspace (returns -3)
1057 // <esc>+ : toggles overline mode (returns -4)
1058 // <esc>- : toggles underline mode (returns -5)
1059 // <esc><esc> : <esc>
1060 // <esc>gx : greek letter corresponding to roman letter x
1061 // <esc>fn : switch to Normal font
1062 // <esc>fr : switch to Roman font
1063 // <esc>fi : switch to Italic font
1064 // <esc>fs : switch to Script font
1065 // <esc>(nnn) : Hershey symbol number nnn (any number of digits)
1066 //
1067 // The escape character defaults to '#', but can be changed to any of
1068 // [!#$%&*@^~] via a call to plsesc.
1069 //--------------------------------------------------------------------------
1070 
1071 static void
1072 pldeco( short int **symbol, PLINT *length, const char *text )
1073 {
1074  PLINT ch, ifont = plsc->cfont, ig, j = 0, lentxt = (PLINT) strlen( text );
1075  char test, esc;
1076  short int *sym = symbol_buffer;
1077 
1078 // Initialize parameters.
1079 
1080  *length = 0;
1081  *symbol = symbol_buffer;
1082  plgesc( &esc );
1083  if ( ifont > numberfonts )
1084  ifont = 1;
1085 
1086 // Get next character; treat non-printing characters as spaces.
1087 
1088  while ( j < lentxt )
1089  {
1090  if ( *length >= PLMAXSTR )
1091  return;
1092  test = text[j++];
1093  ch = test;
1094  if ( ch < 0 || ch > 175 )
1095  ch = 32;
1096 
1097  // Test for escape sequence (#)
1098 
1099  if ( ch == esc && ( lentxt - j ) >= 1 )
1100  {
1101  test = text[j++];
1102  if ( test == esc )
1103  sym[( *length )++] = *( fntlkup + ( ifont - 1 ) * numberchars + ch );
1104 
1105  else if ( test == 'u' || test == 'U' )
1106  sym[( *length )++] = -1;
1107 
1108  else if ( test == 'd' || test == 'D' )
1109  sym[( *length )++] = -2;
1110 
1111  else if ( test == 'b' || test == 'B' )
1112  sym[( *length )++] = -3;
1113 
1114  else if ( test == '+' )
1115  sym[( *length )++] = -4;
1116 
1117  else if ( test == '-' )
1118  sym[( *length )++] = -5;
1119 
1120  else if ( test == '(' )
1121  {
1122  sym[*length] = 0;
1123  while ( '0' <= text[j] && text[j] <= '9' )
1124  {
1125  sym[*length] = (short) ( (int) sym[*length] * 10 + text[j] - '0' );
1126  j++;
1127  }
1128  ( *length )++;
1129  if ( text[j] == ')' )
1130  j++;
1131  }
1132  else if ( test == 'f' || test == 'F' )
1133  {
1134  test = text[j++];
1135  ifont = 1 + plP_strpos( font_types,
1136  isupper( test ) ? tolower( test ) : test );
1137  if ( ifont == 0 || ifont > numberfonts )
1138  ifont = 1;
1139  }
1140  else if ( test == 'g' || test == 'G' )
1141  {
1142  test = text[j++];
1143  ig = plP_strpos( plP_greek_mnemonic, test ) + 1;
1144  // This accesses the Hershey glyphs using the same
1145  // "ascii" index as plpoin. So the order of the Greek
1146  // glyphs in this case depends on the subhersh[0-3]
1147  // indices in fonts/font11.c which for lower-case epsilon,
1148  // theta, and phi substitutes (684, 685, and 686) for
1149  // (631, 634, and 647) in the compact case and (2184,
1150  // 2185, and 2186) for (2131, 2134, and 2147) in the
1151  // extended case.
1152  sym[( *length )++] =
1153  *( fntlkup + ( ifont - 1 ) * numberchars + 127 + ig );
1154  }
1155  else
1156  {
1157  ;
1158  }
1159  }
1160  else
1161  {
1162  // Decode character.
1163  // >>PC<< removed increment from following expression to fix
1164  // compiler bug
1165 
1166  sym[( *length )] = *( fntlkup + ( ifont - 1 ) * numberchars + ch );
1167  ( *length )++;
1168  }
1169  }
1170 }
1171 
1172 //--------------------------------------------------------------------------
1173 // PLINT plP_strpos()
1174 //
1175 // Searches string str for first occurence of character chr. If found
1176 // the position of the character in the string is returned (the first
1177 // character has position 0). If the character is not found a -1 is
1178 // returned.
1179 //--------------------------------------------------------------------------
1180 
1181 PLINT
1182 plP_strpos( const char *str, int chr )
1183 {
1184  char *temp;
1185 
1186  if ( ( temp = strchr( str, chr ) ) )
1187  return (PLINT) ( temp - str );
1188  else
1189  return (PLINT) -1;
1190 }
1191 
1192 //--------------------------------------------------------------------------
1193 // PLINT plP_stindex()
1194 //
1195 // Similar to strpos, but searches for occurence of string str2.
1196 //--------------------------------------------------------------------------
1197 
1198 PLINT
1199 plP_stindex( const char *str1, const char *str2 )
1200 {
1201  PLINT base, str1ind, str2ind;
1202 
1203  for ( base = 0; *( str1 + base ) != '\0'; base++ )
1204  {
1205  for ( str1ind = base, str2ind = 0; *( str2 + str2ind ) != '\0' &&
1206  *( str2 + str2ind ) == *( str1 + str1ind ); str1ind++, str2ind++ )
1207  ;
1208 
1209  if ( *( str2 + str2ind ) == '\0' )
1210  return (PLINT) base;
1211  }
1212  return (PLINT) -1; // search failed
1213 }
1214 
1215 //--------------------------------------------------------------------------
1216 // PLBOOL plP_stsearch()
1217 //
1218 // Searches string str for character chr (case insensitive).
1219 //--------------------------------------------------------------------------
1220 
1221 PLBOOL
1222 plP_stsearch( const char *str, int chr )
1223 {
1224  if ( strchr( str, chr ) )
1225  return TRUE;
1226  else if ( strchr( str, toupper( chr ) ) )
1227  return TRUE;
1228  else
1229  return FALSE;
1230 }
1231 
1232 //--------------------------------------------------------------------------
1266 
1267 void
1268 plP_script_scale( PLBOOL ifupper, PLINT *level,
1269  PLFLT *old_scale, PLFLT *scale,
1270  PLFLT *old_offset, PLFLT *offset )
1271 {
1272  if ( *level == 0 )
1273  {
1274  *old_scale = 1.;
1275  *old_offset = 0.;
1276  }
1277  else
1278  {
1279  *old_scale = *scale;
1280  *old_offset = *offset;
1281  }
1282  if ( ( *level >= 0 && ifupper ) || ( *level <= 0 && !ifupper ) )
1283  {
1284  // If superscript of subscript moves further away from centerline....
1285  *scale = 0.75 * *old_scale;
1286  *offset = *old_offset + *old_scale;
1287  }
1288  else
1289  {
1290  // If superscript of subscript moves closer to centerline....
1291  *scale = *old_scale / 0.75;
1292  *offset = *old_offset - *scale;
1293  }
1294  if ( ifupper )
1295  ( *level )++;
1296  else
1297  ( *level )--;
1298 }
1299 
1300 //--------------------------------------------------------------------------
1301 // void c_plfont(ifont)
1302 //
1303 // Sets the global font flag to 'ifont'.
1304 //--------------------------------------------------------------------------
1305 
1306 void
1307 c_plfont( PLINT ifont )
1308 {
1309  PLUNICODE fci = PL_FCI_MARK;
1310  if ( plsc->level < 1 )
1311  {
1312  plabort( "plfont: Please call plinit first" );
1313  return;
1314  }
1315  if ( ifont < 1 || ifont > 4 )
1316  {
1317  plabort( "plfont: Invalid font" );
1318  return;
1319  }
1320 
1321  plsc->cfont = ifont;
1322 
1323  // Provide some degree of forward compatibility if dealing with
1324  // unicode font. But better procedure is to call plsfci directly rather
1325  // than using this lame Hershey font interface.
1326  //
1327  switch ( ifont )
1328  {
1329  case 1:
1330  // normal = (medium, upright, sans serif)
1332  plsfci( fci );
1333  break;
1334  // roman = (medium, upright, serif)
1335  case 2:
1337  plsfci( fci );
1338  break;
1339  // italic = (medium, italic, serif)
1340  case 3:
1343  plsfci( fci );
1344  break;
1345  // script = (medium, upright, script)
1346  case 4:
1348  plsfci( fci );
1349  break;
1350  }
1351 }
1352 
1353 //--------------------------------------------------------------------------
1354 // void plfntld(fnt)
1355 //
1356 // Loads either the standard or extended font.
1357 //--------------------------------------------------------------------------
1358 
1359 void
1361 {
1362  static PLINT charset;
1363  short bffrleng;
1364  PDFstrm *pdfs;
1365 
1366  if ( fontloaded && ( charset == fnt ) )
1367  return;
1368 
1369  plfontrel();
1370  fontloaded = 1;
1371  charset = fnt;
1372 
1373  if ( fnt )
1374  pdfs = plLibOpenPdfstrm( PL_XFONT );
1375  else
1376  pdfs = plLibOpenPdfstrm( PL_SFONT );
1377 
1378  if ( pdfs == NULL )
1379  plexit( "Unable to either (1) open/find or (2) allocate memory for the font file" );
1380 
1381 // Read fntlkup[]
1382 
1383  pdf_rd_2bytes( pdfs, (U_SHORT *) &bffrleng );
1384  numberfonts = bffrleng / 256;
1385  numberchars = bffrleng & 0xff;
1386  bffrleng = (short) ( numberfonts * numberchars );
1387  fntlkup = (short int *) malloc( (size_t) bffrleng * sizeof ( short int ) );
1388  if ( !fntlkup )
1389  plexit( "plfntld: Out of memory while allocating font buffer." );
1390 
1391  pdf_rd_2nbytes( pdfs, (U_SHORT *) fntlkup, bffrleng );
1392 
1393 // Read fntindx[]
1394 
1395  pdf_rd_2bytes( pdfs, (U_SHORT *) &indxleng );
1396  fntindx = (short int *) malloc( (size_t) indxleng * sizeof ( short int ) );
1397  if ( !fntindx )
1398  plexit( "plfntld: Out of memory while allocating font buffer." );
1399 
1400  pdf_rd_2nbytes( pdfs, (U_SHORT *) fntindx, indxleng );
1401 
1402 // Read fntbffr[]
1403 // Since this is an array of char, there are no endian problems
1404 
1405  pdf_rd_2bytes( pdfs, (U_SHORT *) &bffrleng );
1406  fntbffr = (signed char *) malloc( 2 * (size_t) bffrleng * sizeof ( signed char ) );
1407  if ( !fntbffr )
1408  plexit( "plfntld: Out of memory while allocating font buffer." );
1409 
1410 #if PLPLOT_USE_TCL_CHANNELS
1411  pdf_rdx( fntbffr, sizeof ( signed char ) * (size_t) ( 2 * bffrleng ), pdfs );
1412 #else
1413  plio_fread( (void *) fntbffr, (size_t) sizeof ( signed char ),
1414  (size_t) ( 2 * bffrleng ), pdfs->file );
1415 #endif
1416 
1417 // Done
1418 
1419  pdf_close( pdfs );
1420 }
1421 
1422 //--------------------------------------------------------------------------
1423 // void plfontrel()
1424 //
1425 // Release memory for fonts.
1426 //--------------------------------------------------------------------------
1427 
1428 void
1429 plfontrel( void )
1430 {
1431  if ( fontloaded )
1432  {
1433  free_mem( fntindx )
1434  free_mem( fntbffr )
1435  free_mem( fntlkup )
1436  fontloaded = 0;
1437  }
1438 }
1439 
1440 //--------------------------------------------------------------------------
1441 // int plhershey2unicode ( int in )
1442 //
1443 // Function searches for in, the input hershey code, in a lookup table and
1444 // returns the corresponding index in that table.
1445 // Using this index you can work out the unicode equivalent as well as
1446 // the closest approximate to the font-face. If the returned index is
1447 // -1 then no match was possible.
1448 //
1449 // Two versions of the function exist, a simple linear search version,
1450 // and a more complex, but significantly faster, binary search version.
1451 // If there seem to be problems with the binary search method, the brain-dead
1452 // linear search can be enabled by defining SIMPLE_BUT_SAFE_HERSHEY_LOOKUP
1453 // at compile time.
1454 //--------------------------------------------------------------------------
1455 
1456 int plhershey2unicode( int in )
1457 {
1458 #ifdef SIMPLE_BUT_SAFE_HERSHEY_LOOKUP
1459  int ret = -1;
1460  int i;
1461 
1462  for ( i = 0; ( i < number_of_entries_in_hershey_to_unicode_table ) && ( ret == -1 ); i++ )
1463  {
1464  if ( hershey_to_unicode_lookup_table[i].Hershey == in )
1465  ret = i;
1466  }
1467 
1468  return ( ret );
1469 
1470 #else
1471 
1472  int jlo = -1, jmid, jhi = number_of_entries_in_hershey_to_unicode_table;
1473  while ( jhi - jlo > 1 )
1474  {
1475  // Note that although jlo or jhi can be just outside valid
1476  // range (see initialization above) because of while condition
1477  // jlo < jmid < jhi and jmid must be in valid range.
1478  //
1479  jmid = ( jlo + jhi ) / 2;
1480  // convert hershey_to_unicode_lookup_table[jmid].Hershey to signed
1481  // integer since we don't loose information - the number range
1482  // is from 1 and 2932 at the moment
1483  if ( in > (int) ( hershey_to_unicode_lookup_table[jmid].Hershey ) )
1484  jlo = jmid;
1485  else if ( in < (int) ( hershey_to_unicode_lookup_table[jmid].Hershey ) )
1486  jhi = jmid;
1487  else
1488  // We have found it!
1489  // in == hershey_to_unicode_lookup_table[jmid].Hershey
1490  //
1491  return ( jmid );
1492  }
1493  // jlo is invalid or it is valid and in > hershey_to_unicode_lookup_table[jlo].Hershey.
1494  // jhi is invalid or it is valid and in < hershey_to_unicode_lookup_table[jhi].Hershey.
1495  // All these conditions together imply in cannot be found in
1496  // hershey_to_unicode_lookup_table[j].Hershey, for all j.
1497  //
1498  return ( -1 );
1499 #endif
1500 }
1501 
1502 //--------------------------------------------------------------------------
1503 // char *
1504 // plP_FCI2FontName ( PLUNICODE fci,
1505 // const FCI_to_FontName_Table lookup[], const int nlookup)
1506 //
1507 // Function takes an input FCI (font characterization integer) index,
1508 // looks through the lookup table (which must be sorted by PLUNICODE fci),
1509 // then returns the corresponding pointer to a valid font name. If the FCI
1510 // index is not present the returned value is NULL.
1511 //--------------------------------------------------------------------------
1512 
1513 const char *
1515  const FCI_to_FontName_Table lookup[], const int nlookup )
1516 {
1517  int jlo = -1, jmid, jhi = nlookup;
1518  while ( jhi - jlo > 1 )
1519  {
1520  // Note that although jlo or jhi can be just outside valid
1521  // range (see initialization above) because of while condition
1522  // jlo < jmid < jhi and jmid must be in valid range.
1523  //
1524  jmid = ( jlo + jhi ) / 2;
1525  if ( fci > lookup[jmid].fci )
1526  jlo = jmid;
1527  else if ( fci < lookup[jmid].fci )
1528  jhi = jmid;
1529  else
1530  // We have found it!
1531  // fci == lookup[jmid].fci
1532  //
1533  return (const char *) ( lookup[jmid].pfont );
1534  }
1535  // jlo is invalid or it is valid and fci > lookup[jlo].Unicode.
1536  // jhi is invalid or it is valid and fci < lookup[jhi].Unicode.
1537  // All these conditions together imply fci index cannot be found in lookup.
1538  // Mark lookup failure with NULL pointer.
1539  //
1540  return ( NULL );
1541 }
1542 
1543 //--------------------------------------------------------------------------
1544 // void plmtex3()
1545 //
1546 // This is the 3d equivalent of plmtex(). It prints out "text" at specified
1547 // position relative to viewport (may be inside or outside)
1548 //
1549 // side String contains one or more of the following characters
1550 // x,y,z : Specify which axis is to be labeled
1551 // p,s : Label the "primary" or the "secondary" axis. The "primary" axis
1552 // being somewhat arbitrary, but basically it is the one that you'd
1553 // expect to labeled in a 3d graph of standard orientation. Example:
1554 // for z this would be the left hand axis.
1555 // v : draw the text perpendicular to the axis.
1556 //
1557 // disp Displacement from specified edge of axis, measured outwards from
1558 // the axis in units of the current character height. The
1559 // centerlines of the characters are aligned with the specified
1560 // position.
1561 //
1562 // pos Position of the reference point of the string relative to the
1563 // axis ends, ranging from 0.0 (left-hand end) to 1.0 (right-hand
1564 // end)
1565 //
1566 // just Justification of string relative to reference point
1567 // just = 0.0 => left hand edge of string is at reference
1568 // just = 1.0 => right hand edge of string is at reference
1569 // just = 0.5 => center of string is at reference
1570 //
1571 // All calculations are done in physical coordinates.
1572 //
1573 //--------------------------------------------------------------------------
1574 
1575 void
1576 c_plmtex3( const char *side, PLFLT disp, PLFLT pos, PLFLT just, const char *text )
1577 {
1578  // local storage
1579  PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale;
1580  PLFLT chrdef, chrht;
1581 
1582  // calculated
1583  PLFLT xpc, ypc, xrefpc, yrefpc;
1584  PLFLT epx1 = 0.0, epy1 = 0.0, epx2 = 0.0, epy2 = 0.0, epx3 = 0.0, epy3 = 0.0;
1585  PLFLT dispx, dispy, xform[4];
1586  PLFLT shift, theta, temp;
1587 
1588  // check that the plotting environment is set up
1589  if ( plsc->level < 3 )
1590  {
1591  plabort( "plmtex3: Please set up window first" );
1592  return;
1593  }
1594 
1595  // get plotting environment information
1596  plP_gdom( &xmin, &xmax, &ymin, &ymax );
1597  plP_grange( &zscale, &zmin, &zmax );
1598  plgchr( &chrdef, &chrht );
1599 
1600  // handle x/y axises
1601  if ( ( plP_stindex( side, "x" ) != -1 ) || ( plP_stindex( side, "y" ) != -1 ) )
1602  {
1603  // get the locations of the end points of the relevant axis
1604 
1605  // x axis label
1606  if ( plP_stindex( side, "x" ) != -1 )
1607  {
1608  // primary
1609  if ( plP_stindex( side, "p" ) != -1 )
1610  {
1611  epx1 = plP_wcpcx( plP_w3wcx( xmin, ymin, zmin ) );
1612  epy1 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmin ) );
1613  epx2 = plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) );
1614  epy2 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmin ) );
1615  }
1616  else
1617  {
1618  epx1 = plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) );
1619  epy1 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmin ) );
1620  epx2 = plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) );
1621  epy2 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmin ) );
1622  }
1623  }
1624  else
1625  {
1626  if ( plP_stindex( side, "p" ) != -1 )
1627  {
1628  epx1 = plP_wcpcx( plP_w3wcx( xmin, ymin, zmin ) );
1629  epy1 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmin ) );
1630  epx2 = plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) );
1631  epy2 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmin ) );
1632  }
1633  else
1634  {
1635  epx1 = plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) );
1636  epy1 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmin ) );
1637  epx2 = plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) );
1638  epy2 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmin ) );
1639  }
1640  }
1641 
1642  // text always goes from left to right
1643  if ( epx1 > epx2 )
1644  {
1645  temp = epx1;
1646  epx1 = epx2;
1647  epx2 = temp;
1648  temp = epy1;
1649  epy1 = epy2;
1650  epy2 = temp;
1651  // recalculate position assuming the user specified
1652  // it in the min -> max direction of the axis.
1653  pos = 1.0 - pos;
1654  }
1655 
1656  // calculate location of text center point
1657 
1658  // 1. calculate the angle of the axis we are to
1659  // draw the text on relative to the horizontal
1660 
1661  if ( ( epx2 - epx1 ) != 0.0 )
1662  {
1663  theta = atan( ( epy2 - epy1 ) / ( epx2 - epx1 ) );
1664  }
1665  else
1666  {
1667  if ( epy2 > epy1 )
1668  {
1669  theta = 0.5 * PI;
1670  }
1671  else
1672  {
1673  theta = -0.5 * PI;
1674  }
1675  }
1676 
1677  // 2. calculate the perpendicular vector
1678 
1679  dispy = disp * chrht;
1680 
1681  // 3. calculate x & y center points
1682 
1683  xpc = pos * ( epx2 - epx1 ) + epx1;
1684  ypc = pos * ( epy2 - epy1 ) + epy1;
1685 
1686  // 4. compute reference point
1687  // It appears that drivers that cannot handle text justification
1688  // use this as the starting point of the string.
1689  // Calculations must be done in millimeters for this part
1690  // so we convert to mm, do the calculation and convert back.
1691  // The calculation is also dependent of the orientation
1692  // (perpendicular or parallel) of the text.
1693 
1694  xpc = plP_dcmmx( plP_pcdcx( (PLINT) xpc ) );
1695  ypc = plP_dcmmy( plP_pcdcy( (PLINT) ypc ) ) - dispy;
1696 
1697  shift = plstrl( text ) * just;
1698 
1699  if ( plP_stindex( side, "v" ) != -1 )
1700  {
1701  xrefpc = xpc;
1702  yrefpc = ypc - shift;
1703  }
1704  else
1705  {
1706  xrefpc = xpc - cos( theta ) * shift;
1707  yrefpc = ypc - sin( theta ) * shift;
1708  }
1709 
1710  xpc = plP_mmpcx( xpc );
1711  ypc = plP_mmpcy( ypc );
1712  xrefpc = plP_mmpcx( xrefpc );
1713  yrefpc = plP_mmpcy( yrefpc );
1714 
1715  // 5. compute transform matrix & draw text
1716 
1717  // perpendicular, rotate 90 degrees & shear
1718 
1719  if ( plP_stindex( side, "v" ) != -1 )
1720  {
1721  xform[0] = 0.0;
1722  xform[1] = -cos( theta );
1723  xform[2] = 1.0;
1724  xform[3] = -sin( theta );
1725  plP_text( 0, just, xform, (PLINT) xpc, (PLINT) ypc, (PLINT) xrefpc, (PLINT) yrefpc, text );
1726  }
1727 
1728  // parallel, rotate & shear by angle
1729  else
1730  {
1731  xform[0] = cos( theta );
1732  xform[1] = 0.0;
1733  xform[2] = sin( theta );
1734  xform[3] = 1.0;
1735 
1736  plP_text( 0, just, xform, (PLINT) xpc, (PLINT) ypc, (PLINT) xrefpc, (PLINT) yrefpc, text );
1737  }
1738  }
1739 
1740  // handle z axises
1741  if ( plP_stindex( side, "z" ) != -1 )
1742  {
1743  // Find the left most of the 4 z axis options for "primary"
1744  // Also find the location of frontmost point in the graph,
1745  // which will be needed to calculate at what angle to shear
1746  // the text.
1747 
1748  if ( plP_stindex( side, "p" ) != -1 )
1749  {
1750  epx1 = plP_wcpcx( plP_w3wcx( xmin, ymin, zmin ) );
1751  epy1 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmin ) );
1752  epy2 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmax ) );
1753  epx3 = plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) );
1754  epy3 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmin ) );
1755 
1756  if ( plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) ) < epx1 )
1757  {
1758  epx1 = plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) );
1759  epy1 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmin ) );
1760  epy2 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmax ) );
1761  epx3 = plP_wcpcx( plP_w3wcx( xmin, ymin, zmin ) );
1762  epy3 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmin ) );
1763  }
1764 
1765  if ( plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) ) < epx1 )
1766  {
1767  epx1 = plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) );
1768  epy1 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmin ) );
1769  epy2 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmax ) );
1770  epx3 = plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) );
1771  epy3 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmin ) );
1772  }
1773 
1774  if ( plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) ) < epx1 )
1775  {
1776  epx1 = plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) );
1777  epy1 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmin ) );
1778  epy2 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmax ) );
1779  epx3 = plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) );
1780  epy3 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmin ) );
1781  }
1782  }
1783 
1784  // find the right most of the 4 z axis options for "primary"
1785  if ( plP_stindex( side, "s" ) != -1 )
1786  {
1787  epx1 = plP_wcpcx( plP_w3wcx( xmin, ymin, zmin ) );
1788  epy1 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmin ) );
1789  epy2 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmax ) );
1790  epx3 = plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) );
1791  epy3 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmin ) );
1792 
1793  if ( plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) ) > epx1 )
1794  {
1795  epx1 = plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) );
1796  epy1 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmin ) );
1797  epy2 = plP_wcpcy( plP_w3wcy( xmin, ymax, zmax ) );
1798  epx3 = plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) );
1799  epy3 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmin ) );
1800  }
1801 
1802  if ( plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) ) > epx1 )
1803  {
1804  epx1 = plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) );
1805  epy1 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmin ) );
1806  epy2 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmax ) );
1807  epx3 = plP_wcpcx( plP_w3wcx( xmin, ymin, zmin ) );
1808  epy3 = plP_wcpcy( plP_w3wcy( xmin, ymin, zmin ) );
1809  }
1810 
1811  if ( plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) ) > epx1 )
1812  {
1813  epx1 = plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) );
1814  epy1 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmin ) );
1815  epy2 = plP_wcpcy( plP_w3wcy( xmax, ymax, zmax ) );
1816  epx3 = plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) );
1817  epy3 = plP_wcpcy( plP_w3wcy( xmax, ymin, zmin ) );
1818  }
1819  }
1820 
1821  // Calculate location of text center point.
1822  // This is very similiar for the z axis.
1823 
1824  // primary and secondary have to be handled separately here
1825 
1826  if ( plP_stindex( side, "p" ) != -1 )
1827  {
1828  // 1. Calculate the angle of the axis we are to
1829  // draw the text on relative to the horizontal.
1830 
1831  if ( ( epx3 - epx1 ) != 0.0 )
1832  {
1833  theta = atan( ( epy3 - epy1 ) / ( epx3 - epx1 ) );
1834  }
1835  else
1836  {
1837  if ( epy3 > epy1 )
1838  {
1839  theta = 0.5 * PI;
1840  }
1841  else
1842  {
1843  theta = -0.5 * PI;
1844  }
1845  }
1846 
1847  // 2. Calculate the perpendicular vector.
1848 
1849  dispx = -cos( theta ) * disp * chrht;
1850  dispy = -sin( theta ) * disp * chrht;
1851  }
1852  else
1853  {
1854  if ( ( epx1 - epx3 ) != 0.0 )
1855  {
1856  theta = -atan( ( epy3 - epy1 ) / ( epx1 - epx3 ) );
1857  }
1858  else
1859  {
1860  if ( epy3 > epy1 )
1861  {
1862  theta = -0.5 * PI;
1863  }
1864  else
1865  {
1866  theta = 0.5 * PI;
1867  }
1868  }
1869 
1870  dispx = cos( theta ) * disp * chrht;
1871  dispy = sin( theta ) * disp * chrht;
1872  }
1873 
1874  // 3. Calculate x & y center points.
1875 
1876  xpc = epx1;
1877  ypc = pos * ( epy2 - epy1 ) + epy1;
1878 
1879  // 4. Compute the reference point.
1880 
1881  xpc = plP_dcmmx( plP_pcdcx( (PLINT) xpc ) ) + dispx;
1882  ypc = plP_dcmmy( plP_pcdcy( (PLINT) ypc ) ) + dispy;
1883 
1884  shift = plstrl( text ) * just;
1885 
1886  if ( plP_stindex( side, "v" ) != -1 )
1887  {
1888  xrefpc = xpc - cos( theta ) * shift;
1889  yrefpc = ypc - sin( theta ) * shift;
1890  }
1891  else
1892  {
1893  xrefpc = xpc;
1894  yrefpc = ypc - shift;
1895  }
1896 
1897  xpc = plP_mmpcx( xpc );
1898  ypc = plP_mmpcy( ypc );
1899  xrefpc = plP_mmpcx( xrefpc );
1900  yrefpc = plP_mmpcy( yrefpc );
1901 
1902  // 5. Compute transform matrix & draw text.
1903 
1904  if ( plP_stindex( side, "v" ) != -1 )
1905  {
1906  xform[0] = cos( theta );
1907  xform[1] = 0.0;
1908  xform[2] = sin( theta );
1909  xform[3] = 1.0;
1910 
1911  plP_text( 0, just, xform, (PLINT) xpc, (PLINT) ypc, (PLINT) xrefpc, (PLINT) yrefpc, text );
1912  }
1913 
1914  else
1915  {
1916  xform[0] = 0.0;
1917  xform[1] = -cos( theta );
1918  xform[2] = 1.0;
1919  xform[3] = -sin( theta );
1920 
1921  plP_text( 0, just, xform, (PLINT) xpc, (PLINT) ypc, (PLINT) xrefpc, (PLINT) yrefpc, text );
1922  }
1923  }
1924 }
1925 
1926 //--------------------------------------------------------------------------
1927 // void plptex3()
1928 //
1929 // Prints out "text" at world cooordinate (wx,wy,wz).
1930 //
1931 // The text is drawn parallel to the line between (wx,wy,wz) and
1932 // (wx+dx,wy+dy,wz+dz).
1933 //
1934 // The text is sheared so that it is "vertically" parallel to the
1935 // line between (wx,wy,wz) and (wx+sx, wy+sy, wz+sz). If sx=sy=sz=0 then
1936 // the text is simply rotated to parallel to the baseline.
1937 //
1938 // "just" adjusts the horizontal justification of the string:
1939 // just = 0.0 => left hand edge of string is at (wx,wy)
1940 // just = 1.0 => right hand edge of string is at (wx,wy)
1941 // just = 0.5 => center of string is at (wx,wy) etc.
1942 //
1943 // Calculations are done in physical coordinates.
1944 //
1945 //--------------------------------------------------------------------------
1946 
1947 void
1949  PLFLT sx, PLFLT sy, PLFLT sz, PLFLT just, const char *text )
1950 {
1951  PLFLT xpc, ypc, xrefpc, yrefpc, xdpc, ydpc, xspc, yspc, ld, ls, cp, shift;
1952  PLFLT x_o, y_o, z_o, x_dx, y_dy, z_dz;
1953  PLFLT theta, phi, stride, xform[6], affineL[6], cosphi;
1954 
1955  // check that the plotting environment is set up
1956  if ( plsc->level < 3 )
1957  {
1958  plabort( "plptex3: Please set up window first" );
1959  return;
1960  }
1961 
1962  // compute text x,y location in physical coordinates
1963  xpc = plP_wcpcx( plP_w3wcx( wx, wy, wz ) );
1964  ypc = plP_wcpcy( plP_w3wcy( wx, wy, wz ) );
1965 
1966  // determine angle to rotate text in the x-y plane
1967  xdpc = plP_wcpcx( plP_w3wcx( wx + dx, wy + dy, wz + dz ) );
1968  ydpc = plP_wcpcy( plP_w3wcy( wx + dx, wy + dy, wz + dz ) );
1969  theta = atan2( ydpc - ypc, xdpc - xpc );
1970 
1971  // Determine angle to shear text in the x-y plane. This is a little
1972  // messy, but basically the idea is:
1973  //
1974  // Compute the dot product of the vector d and the vector s to
1975  // determine the angle between them (acos(t) = d . s / |d| |s|).
1976  // Then because acos will return a number from 0.0 to PI, i.e.
1977  // only in quadrants 1 or 2, compute the cross product of the
1978  // two vectors. If this is negative then the angle is adjusted
1979  // 0.0 to -PI.
1980 
1981  if ( ( sx == 0.0 ) && ( sy == 0.0 ) && ( sz == 0.0 ) )
1982  {
1983  phi = 0.0;
1984  }
1985  else
1986  {
1987  xspc = plP_wcpcx( plP_w3wcx( wx + sx, wy + sy, wz + sz ) );
1988  yspc = plP_wcpcy( plP_w3wcy( wx + sx, wy + sy, wz + sz ) );
1989  ld = sqrt( ( xpc - xdpc ) * ( xpc - xdpc ) + ( ypc - ydpc ) * ( ypc - ydpc ) );
1990  ls = sqrt( ( xpc - xspc ) * ( xpc - xspc ) + ( ypc - yspc ) * ( ypc - yspc ) );
1991  phi = acos( ( ( xdpc - xpc ) * ( xspc - xpc ) + ( ydpc - ypc ) * ( yspc - ypc ) ) / ( ld * ls ) );
1992  cp = ( xdpc - xpc ) * ( yspc - ypc ) - ( ydpc - ypc ) * ( xspc - xpc );
1993  if ( cp < 0.0 )
1994  {
1995  phi = -phi;
1996  }
1997  phi = 0.5 * PI - phi;
1998  }
1999 
2000  // Determine how to adjust the "stride" of the text to make it
2001  // appear that it is going into (or out of) the page. Basically
2002  // scale the x baseline of the text by the normalized length of
2003  // the d vector projected into the x-y plane.
2004  x_o = plP_w3wcx( wx, wy, wz );
2005  y_o = plP_w3wcy( wx, wy, wz );
2006  z_o = plP_w3wcz( wx, wy, wz );
2007  x_dx = x_o - plP_w3wcx( wx + dx, wy + dy, wz + dz );
2008  y_dy = y_o - plP_w3wcy( wx + dx, wy + dy, wz + dz );
2009  z_dz = z_o - plP_w3wcz( wx + dx, wy + dy, wz + dz );
2010 
2011  stride = sqrt( x_dx * x_dx + y_dy * y_dy );
2012  stride = stride / sqrt( x_dx * x_dx + y_dy * y_dy + z_dz * z_dz );
2013 
2014  // compute the reference point
2015  xpc = plP_dcmmx( plP_pcdcx( (PLINT) xpc ) );
2016  ypc = plP_dcmmy( plP_pcdcy( (PLINT) ypc ) );
2017 
2018  shift = plstrl( text ) * just;
2019  xrefpc = xpc - cos( theta ) * shift * stride;
2020  yrefpc = ypc - sin( theta ) * shift * stride;
2021 
2022  xpc = plP_mmpcx( xpc );
2023  ypc = plP_mmpcy( ypc );
2024  xrefpc = plP_mmpcx( xrefpc );
2025  yrefpc = plP_mmpcy( yrefpc );
2026 
2027  // compute the transform
2028  // This affine transformation corresponds to transforming from old
2029  // coordinates to new coordinates by rotating axes, y shearing
2030  // or (y skewing), and scaling.
2031  // Comment out the explicit xform calculations because we use
2032  // the affine utilities for that calculation instead.
2033  //
2034  // xform[0] = cos( theta ) * stride;
2035  // xform[1] = cos( theta ) * sin( phi ) - sin( theta ) * cos( phi );
2036  // xform[2] = sin( theta ) * stride;
2037  // xform[3] = sin( theta ) * sin( phi ) + cos( theta ) * cos( phi );
2038  //
2039  plP_affine_rotate( xform, 180. * theta / PI );
2040  plP_affine_yskew( affineL, -180. * phi / PI );
2041  plP_affine_multiply( xform, affineL, xform );
2042  cosphi = cos( phi );
2043  if ( fabs( cosphi ) > 1.e-300 )
2044  plP_affine_scale( affineL, 1. / stride, 1. / cosphi );
2045  else
2046  plP_affine_scale( affineL, 1. / stride, 1.e300 );
2047  plP_affine_multiply( xform, affineL, xform );
2048 
2049  plP_text( 0, just, xform, (PLINT) xpc, (PLINT) ypc, (PLINT) xrefpc, (PLINT) yrefpc, text );
2050 }
2051 
2052 //--------------------------------------------------------------------------
2053 // void plsfont()
2054 //
2055 // Set the family, style and weight of the current font.
2056 // This is a user-friendly front-end to plsfci.
2057 // Note: A negative value signifies that this element should not be changed.
2058 //--------------------------------------------------------------------------
2059 void
2060 c_plsfont( PLINT family, PLINT style, PLINT weight )
2061 {
2062  PLUNICODE fci;
2063 
2064  plgfci( &fci );
2065 
2066  if ( family >= 0 )
2067  {
2068  // Bounds checking assumes symbol is last font
2069  if ( family > PL_FCI_SYMBOL )
2070  plwarn( "plsfont: Value for family is out of range" );
2071  else
2072  plP_hex2fci( (unsigned char) family, PL_FCI_FAMILY, &fci );
2073  }
2074 
2075  if ( style >= 0 )
2076  {
2077  // Bounds checking assumes oblique is last style
2078  if ( style > PL_FCI_OBLIQUE )
2079  plwarn( "plsfont: Value for style is out of range" );
2080  else
2081  plP_hex2fci( (unsigned char) style, PL_FCI_STYLE, &fci );
2082  }
2083 
2084  if ( weight >= 0 )
2085  {
2086  // Bounds checking assumes bold is last weight
2087  if ( weight > PL_FCI_BOLD )
2088  plwarn( "plsfont: Value for weight is out of range" );
2089  else
2090  plP_hex2fci( (unsigned char) weight, PL_FCI_WEIGHT, &fci );
2091  }
2092 
2093  plsfci( fci );
2094 }
2095 
2096 //--------------------------------------------------------------------------
2097 // void plgfont()
2098 //
2099 // Get the family, style and weight of the current font.
2100 // This is a user-friendly front-end to plgfci.
2101 // Note: A NULL pointer signifies that this value should not be returned.
2102 //--------------------------------------------------------------------------
2103 void
2104 c_plgfont( PLINT *p_family, PLINT *p_style, PLINT *p_weight )
2105 {
2106  PLUNICODE fci;
2107  unsigned char val;
2108 
2109  plgfci( &fci );
2110 
2111  if ( p_family )
2112  {
2113  plP_fci2hex( fci, &val, PL_FCI_FAMILY );
2114  *p_family = (PLINT) val;
2115  }
2116 
2117  if ( p_style )
2118  {
2119  plP_fci2hex( fci, &val, PL_FCI_STYLE );
2120  *p_style = (PLINT) val;
2121  }
2122 
2123  if ( p_weight )
2124  {
2125  plP_fci2hex( fci, &val, PL_FCI_WEIGHT );
2126  *p_weight = (PLINT) val;
2127  }
2128 }
2129 
2130 
2131 #undef PLSYM_H
2132 #endif