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