File: g_alabel.c

package info (click to toggle)
plotutils 2.0-2
  • links: PTS
  • area: main
  • in suites: hamm
  • size: 5,964 kB
  • ctags: 2,522
  • sloc: ansic: 38,416; sh: 1,853; yacc: 856; makefile: 181; lex: 144
file content (781 lines) | stat: -rw-r--r-- 25,065 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
/* This file contains the alabel method, which is a GNU extension to
   libplot.  It draws a label, i.e. a text string, at the current location.
   Horizontal and vertical justification must be specified.

   ALABEL takes three arguments X_JUSTIFY, Y_JUSTIFY, and S, and places the
   label S according to the x and y axis adjustments specified in X_JUSTIFY
   and Y_JUSTIFY.  X_JUSTIFY is equal to 'l', 'c', or 'r', signifying
   left-justified, centered, or right-justified, relative to the current
   position.  Y_JUSTIFY is equal to 'b', 'x', 'c', or 't', signifying that
   the bottom, baseline, center, or top of the label should pass through
   the current position. */

/* This file also contains the labelwidth method, which is a GNU extension
   to libplot.  It returns the width in user units of a label, i.e., a text
   string. */

#include "sys-defines.h"
#include "plot.h"
#include "extern.h"
#include "g_control.h"

#define SCRIPTSIZE 0.6		/* rel. size of subscripts/superscripts */

#define SUBSCRIPT_DX 0.0
#define SUBSCRIPT_DY (-0.2)
#define SUPERSCRIPT_DX 0.0
#define SUPERSCRIPT_DY 0.375

/* font we use for symbol escapes if the current font is a user-specified
   one that doesn't belong to any of our builtin typefaces */
#define SYMBOL_FONT "Symbol"

/* forward references */
static unsigned char *_esc_esc_string __P((const unsigned char *s));
static double _g_render_string_non_hershey __P((const char *s, bool do_render, int x_justify, int y_justify));
static double _g_render_simple_string_non_hershey __P((const unsigned char *s, bool do_render, int h_just));
static bool _simple_string __P((const unsigned short *codestring));

/* The generic versions of the flabelwidth() and falabel() methods.  After
   checking for control characters in the input string (not allowed), we
   invoke either a Hershey-specific or a non-Hershey-specific method. */

int
#ifdef _HAVE_PROTOS
_g_alabel (int x_justify, int y_justify, const char *s)
#else
_g_alabel (x_justify, y_justify, s)
     int x_justify, y_justify;
     const char *s;
#endif
{
  char *t;

  if (!_plotter->open)
    {
      _plotter->error ("alabel: invalid operation");
      return -1;
    }

  if (_plotter->drawstate->PointsInLine > 0)
    _plotter->endpath(); /* flush polyline if any */

  if (s == NULL)
    return 0;			/* avoid core dumps */

  /* copy because we may alter the string */
  t = (char *)_plot_xmalloc (strlen (s) + 1);
  strcpy (t, s);

  /* allow only character set in ISO encoding */
  {
    bool was_clean;
    
    was_clean = _clean_iso_string ((unsigned char *)t);
    if (!was_clean)
      _plotter->warning ("ignoring control character (e.g. CR or LF) in label");
  }
  
  if (_plotter->drawstate->font_type == F_HERSHEY)
    /* call Hershey-specific routine, since controlification acts
       differently (there are more control codes for Hershey strings) */
    _g_falabel_hershey (x_justify, y_justify, (unsigned char *)t);
  else
    /* invoke routine below */
    _g_render_string_non_hershey (t, true, x_justify, y_justify);
  free (t);

  return 0;
}

double
#ifdef _HAVE_PROTOS
_g_flabelwidth (const char *s)
#else
_g_flabelwidth (s)
     const char *s;
#endif
{
  double width = 0.0;
  char *t;

  if (!_plotter->open)
    {
      _plotter->error ("flabelwidth: invalid operation");
      return -1;
    }

  if (s == NULL)
    return 0.0;			/* avoid core dumps */

  /* copy because we may alter the string */
  t = (char *)_plot_xmalloc (strlen (s) + 1);
  strcpy (t, s);

  /* allow only character set in ISO encoding */
  {
    bool was_clean;
    
    was_clean = _clean_iso_string ((unsigned char *)t);
    if (!was_clean)
      _plotter->warning ("ignoring control character (e.g. CR or LF) in label");
  }
  
  if (_plotter->drawstate->font_type == F_HERSHEY)
    /* call Hershey-specific routine, since controlification acts
       differently (there are more control codes for Hershey strings) */
    width = _g_flabelwidth_hershey ((unsigned char *)t);
  else
    /* invoke routine below; final two args are irrelevant */
    width = _g_render_string_non_hershey (t, false, 'c', 'c');
  free (t);

  return width;
}

/* The non-Hershey version of the falabel() and flabelwidth() methods
   (merged).  They are distinguished by the do_render flag being
   true/false; the return values (the width of the string) are the same.
   The final two arguments, specifying justification, are relevant only if
   the do_render flag is `true'.  If do_render is true, the string is
   rendered in accordance with the justification instructions, and the
   graphics cursor position is updated accordingly. */

/* We `controlify' the string, translating escape sequences to annotations.
   Note: for fonts of `OTHER' type, shifts between fonts within a single
   typeface are ignored, since we have no information on what the other
   fonts within the font's typeface are.  The annotations simply indicate
   whether or not a symbol font should be switched to, for the purpose of
   symbol escapes. */

/* As noted this version is invoked only if the current font is
   non-Hershey.  But due to failure of retrieval of X fonts, it is possible
   that the font could switch to a Hershey font during rendering. */

static double
#ifdef _HAVE_PROTOS
_g_render_string_non_hershey (const char *s, bool do_render, int x_justify, int y_justify)
#else
_g_render_string_non_hershey (s, do_render, x_justify, y_justify)
     const char *s;
     bool do_render;		/* whether to draw the string */
     int x_justify, y_justify;
#endif
{
  int h_just = JUST_LEFT;	/* all devices can handle left justification */
  unsigned short *codestring;
  unsigned short *cptr;
  double width = 0.0;
  double pushed_width = 0.0;	/* pushed by user */
  int current_font_index;
  /* initial values of these attributes (will be restored at end) */
  double initial_font_size = _plotter->drawstate->font_size;
  char *initial_font_name;
  /* initial and saved locations */
  double initial_position_x = _plotter->drawstate->pos.x;
  double initial_position_y = _plotter->drawstate->pos.y;
  double pushed_position_x = _plotter->drawstate->pos.x;
  double pushed_position_y = _plotter->drawstate->pos.y;
  /* misc. */
  char x_justify_c, y_justify_c;
  double x_offset, y_offset;
  double x_displacement = 1.0, overall_width = 0.0;
  double ascent, descent;
  double userdx, userdy, theta, sintheta = 0.0, costheta = 1.0;
  
  /* convert string to a codestring, including annotations */
  codestring = _controlify ((const unsigned char *)s);

  if (do_render)		/* perform needed computations; reposition */
    {
      /* compute label width in user units via a recursive call; final two
	 args here are ignored */
      overall_width = _g_render_string_non_hershey (s, false, 'c', 'c');
      
      /* compute initial offsets that must be performed due to
       justification; also displacements that must be performed after
       rendering (see above)*/
      x_justify_c = (char)x_justify;
      y_justify_c = (char)y_justify;  

      switch (x_justify_c)
	{
	case 'l': /* left justified */
	default:
	  h_just = JUST_LEFT;
	  x_offset = 0.0;
	  x_displacement = 1.0;
	  /* range [0,1] */
	  break;
	  
	case 'c': /* centered */
	  h_just = JUST_CENTER;
	  x_offset = -0.5;
	  x_displacement = 0.0;
	  /* range [-0.5,0.5] */
	  break;
	  
	case 'r': /* right justified */
	  h_just = JUST_RIGHT;
	  x_offset = -1.0;
	  x_displacement = -1.0;
	  /* range [-1,0] */
	  break;
	}

      /* need these to compute offset for vertical justification */
      ascent = _plotter->drawstate->font_ascent;
      descent = _plotter->drawstate->font_descent;
      
      switch (y_justify_c)		/* placement of label with respect
					   to y coordinate */
	{
	case 'b':			/* current point is at bottom */
	  y_offset = descent;
	  break;
	  
	case 'x':			/* current point is on baseline */
	default:
	  y_offset = 0.0;
	  break;
	  
	case 'c':			/* current point is midway between bottom, top */
	  y_offset = 0.5 * (descent - ascent);
	  break;
	  
	case 't':			/* current point is at top */
	  y_offset = - ascent;
	  break;
	}

      /* If codestring is a string in a single font, with no control codes,
	 we'll render it using native device justification, rather than
	 positioning a left-justified string by hand.  In other words if
	 right or centered justification was specified when alabel() was
	 called by the user, the string as drawn on the device will have
	 the same justification.  This is particularly important for the
	 Fig driver.  Anything else would exasperate the user, even if the
	 positioning is correct. */

      if (_plotter->have_justification && _simple_string (codestring))
	/* don't perform manual horizontal justification */
	x_offset = 0.0;
      else
	h_just = JUST_LEFT;	/* use x_offset to position by hand */
	  
      /* justification-related offsets we'll carry out */
      userdx = x_offset * overall_width;
      userdy = y_offset;
      
      /* label rotation angle in radians */
      theta = M_PI * _plotter->drawstate->text_rotation / 180.0;
      sintheta = sin (theta);
      costheta = cos (theta);

      /* perform both horizontal and vertical offsets; after this, current
	 point will be on intended baseline of label */
      _plotter->drawstate->pos.x += costheta * userdx - sintheta * userdy;
      _plotter->drawstate->pos.y += sintheta * userdx + costheta * userdy;
    }

  /* save font name (will be restored at end) */
  initial_font_name = _plotter->drawstate->font_name;
  _plotter->drawstate->font_name = 
    (char *)_plot_xmalloc (1 + strlen (initial_font_name));
  strcpy (_plotter->drawstate->font_name, initial_font_name);

  /* initialize current font index (font type presumably is not Hershey) */
  switch (_plotter->drawstate->font_type)
    {
    case F_POSTSCRIPT:
      current_font_index =
	(_ps_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
      break;
    case F_PCL:
      current_font_index =
	(_pcl_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];
      break;
    case F_OTHER:
      current_font_index = 1;	/* `1' just means the font we start out with */
      break;
    default:			/* unsupported font type */
      return 0.0;
    }

  /* now loop through codestring, parsing each code in succession */
  cptr = codestring;
  while (*cptr)			/* end when (unsigned short)0 is seen */
    {
      unsigned short c;
      
      c = *cptr;
      if (c & CONTROL_CODE)	
	{	
	  /* parse control code */
	  switch (c & ~CONTROL_CODE)
	    {
	    case C_BEGIN_SUBSCRIPT:
	      width += SUBSCRIPT_DX * _plotter->drawstate->true_font_size;
	      if (do_render)
		{
		  _plotter->drawstate->pos.x += 
		    (costheta * SUBSCRIPT_DX - sintheta * SUBSCRIPT_DY) 
		      * _plotter->drawstate->true_font_size;
		  _plotter->drawstate->pos.y += 
		    (sintheta * SUBSCRIPT_DX + costheta * SUBSCRIPT_DY) 
		      * _plotter->drawstate->true_font_size;
		}
	      _plotter->drawstate->font_size *= SCRIPTSIZE;
	      _plotter->retrieve_font();
	      break;

	    case C_BEGIN_SUPERSCRIPT :
	      width += SUPERSCRIPT_DX * _plotter->drawstate->true_font_size;
	      if (do_render)
		{
		  _plotter->drawstate->pos.x += 
		    (costheta * SUPERSCRIPT_DX - sintheta * SUPERSCRIPT_DY) 
		      * _plotter->drawstate->true_font_size;
		  _plotter->drawstate->pos.y += 
		    (sintheta * SUPERSCRIPT_DX + costheta * SUPERSCRIPT_DY) 
		      * _plotter->drawstate->true_font_size;
		}
	      _plotter->drawstate->font_size *= SCRIPTSIZE;
	      _plotter->retrieve_font();
	      break;

	    case C_END_SUBSCRIPT:
	      width -= SUBSCRIPT_DX * _plotter->drawstate->true_font_size;
	      _plotter->drawstate->font_size /= SCRIPTSIZE;
	      _plotter->retrieve_font();
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= (costheta * SUBSCRIPT_DX 
						   - sintheta * SUBSCRIPT_DY) * _plotter->drawstate->true_font_size;
		  (_plotter->drawstate->pos).y -= (sintheta * SUBSCRIPT_DX
						   + costheta * SUBSCRIPT_DY) * _plotter->drawstate->true_font_size;
		}
	      break;
	      
	    case C_END_SUPERSCRIPT:
	      width -= SUPERSCRIPT_DX * _plotter->drawstate->true_font_size;
	      _plotter->drawstate->font_size /= SCRIPTSIZE;
	      _plotter->retrieve_font();
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= (costheta * SUPERSCRIPT_DX 
						   - sintheta * SUPERSCRIPT_DY) * _plotter->drawstate->true_font_size;
		  (_plotter->drawstate->pos).y -= (sintheta * SUPERSCRIPT_DX
						   + costheta * SUPERSCRIPT_DY) * _plotter->drawstate->true_font_size;
		}
	      break;
	      
	    case C_PUSH_LOCATION:
	      pushed_position_x = _plotter->drawstate->pos.x;
	      pushed_position_y = _plotter->drawstate->pos.y;
	      pushed_width = width;
	      break;
	      
	    case C_POP_LOCATION:
	      if (do_render)
		{
		  _plotter->drawstate->pos.x = pushed_position_x;
		  _plotter->drawstate->pos.y = pushed_position_y;
		}
	      width = pushed_width;
	      break;
	      
	    case C_RIGHT_ONE_EM:
	      if (do_render)
		{
		  _plotter->drawstate->pos.x += costheta * _plotter->drawstate->true_font_size;
		  _plotter->drawstate->pos.y += sintheta * _plotter->drawstate->true_font_size;
		}
	      width += _plotter->drawstate->true_font_size;
	      break;
	      
	    case C_RIGHT_HALF_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x += costheta * _plotter->drawstate->true_font_size / 2.0;
		  (_plotter->drawstate->pos).y += sintheta * _plotter->drawstate->true_font_size / 2.0;
		}
	      
	      width += _plotter->drawstate->true_font_size / 2.0;
	      break;

	    case C_RIGHT_QUARTER_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x += costheta * _plotter->drawstate->true_font_size / 4.0;
		  (_plotter->drawstate->pos).y += sintheta * _plotter->drawstate->true_font_size / 4.0;
		}
	      
	      width += _plotter->drawstate->true_font_size / 4.0;
	      break;

	    case C_RIGHT_SIXTH_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x += costheta * _plotter->drawstate->true_font_size / 6.0;
		  (_plotter->drawstate->pos).y += sintheta * _plotter->drawstate->true_font_size / 6.0;
		}
	      
	      width += _plotter->drawstate->true_font_size / 6.0;
	      break;

	    case C_RIGHT_EIGHTH_EM:
	      if (do_render)
		{

		  (_plotter->drawstate->pos).x += costheta * _plotter->drawstate->true_font_size / 8.0;
		  (_plotter->drawstate->pos).y += sintheta * _plotter->drawstate->true_font_size / 8.0;
		}
	      
	      width += _plotter->drawstate->true_font_size / 8.0;
	      break;

	      /* kludge: used for \rn macro only */
	    case C_RIGHT_RADICAL_SHIFT:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x += costheta * _plotter->drawstate->true_font_size * PS_RADICAL_WIDTH;
		  (_plotter->drawstate->pos).y += sintheta * _plotter->drawstate->true_font_size * PS_RADICAL_WIDTH;
		}
	      
	      width += _plotter->drawstate->true_font_size * PS_RADICAL_WIDTH;
	      break;

	    case C_LEFT_ONE_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= costheta * _plotter->drawstate->true_font_size;
		  (_plotter->drawstate->pos).y -= sintheta * _plotter->drawstate->true_font_size;
		}
	      
	      width -= _plotter->drawstate->true_font_size;
	      break;
	      
	    case C_LEFT_HALF_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= costheta * _plotter->drawstate->true_font_size / 2.0;
		  (_plotter->drawstate->pos).y -= sintheta * _plotter->drawstate->true_font_size / 2.0;
		}
	      
	      width -= _plotter->drawstate->true_font_size / 2.0;
	      break;

	    case C_LEFT_QUARTER_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= costheta * _plotter->drawstate->true_font_size / 4.0;
		  (_plotter->drawstate->pos).y -= sintheta * _plotter->drawstate->true_font_size / 4.0;
		}
	      
	      width -= _plotter->drawstate->true_font_size / 4.0;
	      break;

	    case C_LEFT_SIXTH_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= costheta * _plotter->drawstate->true_font_size / 6.0;
		  (_plotter->drawstate->pos).y -= sintheta * _plotter->drawstate->true_font_size / 6.0;
		}
	      
	      width -= _plotter->drawstate->true_font_size / 6.0;
	      break;

	    case C_LEFT_EIGHTH_EM:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= costheta * _plotter->drawstate->true_font_size / 8.0;
		  (_plotter->drawstate->pos).y -= sintheta * _plotter->drawstate->true_font_size / 8.0;
		}
	      
	      width -= _plotter->drawstate->true_font_size / 8.0;
	      break;

	      /* kludge: used for \rn macro only */
	    case C_LEFT_RADICAL_SHIFT:
	      if (do_render)
		{
		  (_plotter->drawstate->pos).x -= costheta * _plotter->drawstate->true_font_size * PS_RADICAL_WIDTH;
		  (_plotter->drawstate->pos).y -= sintheta * _plotter->drawstate->true_font_size * PS_RADICAL_WIDTH;
		}
	      width -= _plotter->drawstate->true_font_size * PS_RADICAL_WIDTH;
	      break;

	      /* unrecognized control code */
	    default:
	      break;
	    }

	  cptr++;		/* on to next element of codestring */
	}
      
      else		/* an ordinary character, with font annotation */
	{
	  unsigned char *s, *sptr;
	  int new_font_index = (c >> FONT_SHIFT) & ONE_BYTE;

	  /* perform font switching if necessary */
	  if (new_font_index != current_font_index)
	    {
	      switch (_plotter->drawstate->font_type)
		{
		case F_HERSHEY:
		  free (_plotter->drawstate->font_name);
		  _plotter->drawstate->font_name =
		    (char *)_plot_xmalloc(1 + strlen (_vector_font_info[new_font_index].name));
		  strcpy (_plotter->drawstate->font_name, _vector_font_info[new_font_index].name);
		  break;
		case F_POSTSCRIPT:
		  free (_plotter->drawstate->font_name);
		  _plotter->drawstate->font_name =
		    (char *)_plot_xmalloc(1 + strlen (_ps_font_info[new_font_index].ps_name));
		  strcpy (_plotter->drawstate->font_name, _ps_font_info[new_font_index].ps_name);
		  break;
		case F_PCL:
		  free (_plotter->drawstate->font_name);
		  _plotter->drawstate->font_name =
		    (char *)_plot_xmalloc(1 + strlen (_pcl_font_info[new_font_index].ps_name));
		  strcpy (_plotter->drawstate->font_name, _pcl_font_info[new_font_index].ps_name);
		  break;
		case F_OTHER:
		  free (_plotter->drawstate->font_name);
		  if (new_font_index == 0) /* symbol font */
		    {
		      _plotter->drawstate->font_name =
			(char *)_plot_xmalloc(1 + strlen (SYMBOL_FONT));
		      strcpy (_plotter->drawstate->font_name, SYMBOL_FONT);
		    }
		  else		/* 1, i.e. restore font we started out with */
		    {
		      _plotter->drawstate->font_name =
			(char *)_plot_xmalloc(1 + strlen (initial_font_name));
		      strcpy (_plotter->drawstate->font_name, initial_font_name);
		    }
		  break;
		default:	/* unsupported font type */
		  break;
		}
	      _plotter->retrieve_font();
	      current_font_index = new_font_index;
	    }
	  
	  /* extract substring consisting of characters in the same font */
	  sptr = s 
	    = (unsigned char *)_plot_xmalloc ((unsigned int) (4 * _codestring_len (cptr) + 1));
	  while (*cptr 
		 && (*cptr & CONTROL_CODE) == 0 
		 && ((*cptr >> FONT_SHIFT) & ONE_BYTE) == current_font_index)
	    *sptr++ = (*cptr++) & ONE_BYTE;
	  *sptr = (unsigned char)'\0';

	  /* Compute width of single-font substring in user units, add it.
	     Either render or not, as requested. */
	  width += _g_render_simple_string_non_hershey (s, do_render, h_just);
	  free (s);
	}
    }

  /* free the codestring (no memory leaks please) */
  free (codestring);

  /* restore initial font */
  free (_plotter->drawstate->font_name);
  _plotter->drawstate->font_name = initial_font_name;
  _plotter->drawstate->font_size = initial_font_size;
  _plotter->retrieve_font();
  
  if (do_render)
    {
      /* restore position to what it was before printing label */
      _plotter->drawstate->pos.x = initial_position_x;
      _plotter->drawstate->pos.y = initial_position_y;
      /* shift due to printing of label */
      _plotter->drawstate->pos.x += costheta * x_displacement * overall_width;
      _plotter->drawstate->pos.y += sintheta * x_displacement * overall_width;
    }

  return width;
}

/* Compute the width of a single-font string (escape sequences not
   recognized), and render it, if requested.  The method that is invoked
   may depend on the Plotter object type (i.e. type of display device), as
   well as on the font type.  The PS and PCL methods appear below.

   The rendering only takes place if the do_render flag is set.  If it is
   not, the width is returned only (the h_just argument being ignored). */

static double 
#ifdef _HAVE_PROTOS
_g_render_simple_string_non_hershey (const unsigned char *s, bool do_render, int h_just)
#else
_g_render_simple_string_non_hershey (s, do_render, h_just)
     const unsigned char *s;
     bool do_render;
     int h_just;		/* horiz. justification: JUST_LEFT, etc. */
#endif
{
  switch (_plotter->drawstate->font_type)
    {
    case F_HERSHEY:
      /* Aargh.  The Hershey-specific routines do much more than is needed:
	 they handles escape sequences too, via their own controlification.
	 So we must escape all backslashes before using them. */
      {
	unsigned char *t;
	double width;
	
	t = _esc_esc_string (s);
	width = (do_render ? 
		 _g_falabel_hershey ('l', 'x', t) : 
		 _g_flabelwidth_hershey (t));
	free (t);
      }
      
    case F_POSTSCRIPT:
      return (do_render ? 
	      _plotter->falabel_ps (s, h_just) : 
	      _plotter->flabelwidth_ps (s));
    case F_PCL:
      return (do_render ? 
	      _plotter->falabel_pcl (s, h_just) : 
	      _plotter->flabelwidth_pcl (s));
    case F_OTHER:
      return (do_render ? 
	      _plotter->falabel_other (s, h_just) : 
	      _plotter->flabelwidth_other (s));
    default:			/* unsupported font type */
      return 0.0;
    }
}

/* A generic internal method that computes the width (total delta x) of a
   character string to be rendered in one of the 35 standard PS fonts, in
   user units.  The font used is the currently selected one (assumed to be
   a PS font). */

double
#ifdef _HAVE_PROTOS
_g_flabelwidth_ps (const unsigned char *s)
#else
_g_flabelwidth_ps (s)
     const unsigned char *s;
#endif
{
  int index;
  int width = 0;
  unsigned char current_char;
  int master_font_index;	/* index into master table */

  /* compute font index in master PS font table */
  master_font_index =
    (_ps_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];

  for (index=0; s[index]!='\0'; index++)
    {
      current_char = (unsigned int)s[index];
      width 
	+= ((_ps_font_info[master_font_index]).width)[current_char];
    }

  return _plotter->drawstate->true_font_size * (double)width / 1000.0;
}

/* A generic internal method that computes the width (total delta x) of a
   character string to be rendered in one of the 45 standard PCL fonts, in
   user units.  The font used is the currently selected one (assumed to be
   a PCL font). */

double
#ifdef _HAVE_PROTOS
_g_flabelwidth_pcl (const unsigned char *s)
#else
_g_flabelwidth_pcl (s)
     const unsigned char *s;
#endif
{
  int index;
  int width = 0;
  unsigned char current_char;
  int master_font_index;	/* index into master table */

  /* compute font index in master PCL font table */
  master_font_index =
    (_pcl_typeface_info[_plotter->drawstate->typeface_index].fonts)[_plotter->drawstate->font_index];

  for (index=0; s[index]!='\0'; index++)
    {
      current_char = (unsigned int)s[index];
      width 
	+= ((_pcl_font_info[master_font_index]).width)[current_char];
    }

  return _plotter->drawstate->true_font_size * (double)width / 1000.0;
}


/* test whether a controlified string is simple in the sense that it
   consists of characters in the same font, and no control codes */
static bool
#ifdef _HAVE_PROTOS
_simple_string (const unsigned short *codestring)
#else
_simple_string (codestring)
     const unsigned short *codestring;
#endif
{
  const unsigned short *cptr = codestring;
  unsigned short c, d;
  int font_index;

  if (*codestring == 0)
    return true;
  c = *codestring;
   if (c & CONTROL_CODE)
    return false;
  font_index = (c >> FONT_SHIFT) & ONE_BYTE;
  while ((d = *cptr++) != 0)
    {
      int local_font_index;

      if (d & CONTROL_CODE)
	return false;
      local_font_index = (d >> FONT_SHIFT) & ONE_BYTE;      
      if (local_font_index != font_index)
	return false;
    }
  return true;
}

/* escape all backslashes in a string; the returned string is allocated on
   the heap and can be freed. */
static unsigned char *
#ifdef _HAVE_PROTOS
_esc_esc_string (const unsigned char *s)
#else
_esc_esc_string (s)
     unsigned const char *s;
#endif
{
  const unsigned char *sptr;
  unsigned char *t, *tptr;

  t = (unsigned char *)_plot_xmalloc (2 * strlen ((char *)s) + 1);
  sptr = s;
  tptr = t;
  while (*sptr)
    {
      *tptr++ = *sptr;
      if (*sptr == '\\')
	*tptr++ = *sptr;
      sptr++;
    }
  *tptr = '\0';

  return t;
}