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