1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3 
4    The GNU plotutils package is free software.  You may redistribute it
5    and/or modify it under the terms of the GNU General Public License as
6    published by the Free Software foundation; either version 2, or (at your
7    option) any later version.
8 
9    The GNU plotutils package is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with the GNU plotutils package; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17    Boston, MA 02110-1301, USA. */
18 
19 /* This file contains the internal method _pl_g_alabel_hershey(), which
20    plots a label using Hershey fonts.  Each character in a Hershey font is
21    a sequence of pen motions, so this function calls _API_fmoverel() and
22    _API_fcontrel() to `stroke' each character in the argument string.
23 
24    The width of the string in user units is returned.  The internal method
25    _pl_g_flabelwidth_hershey() is similar, but does not actually plot the
26    label.  */
27 
28 #include "sys-defines.h"
29 #include "extern.h"
30 #include "g_control.h"
31 #include "g_her_metr.h"
32 
33 /* Shearing factor for oblique fonts, new_x = x + SHEAR * y  */
34 
35 #define SHEAR (2.0/7.0)
36 
37 /* Relative size of subscripts/superscripts (i.e. `indexical' size) */
38 
39 #define SCRIPTSIZE (0.6)
40 
41 /* Positioning of subscripts/superscripts */
42 
43 #define SUBSCRIPT_DX 0.0
44 #define SUBSCRIPT_DY (-0.25)
45 #define SUPERSCRIPT_DX 0.0
46 #define SUPERSCRIPT_DY 0.4
47 
48 /* Positioning of accents (in Hershey units).  UP_SHIFT is amount by which
49    accents are raised when placed over upper-case letters.  RIGHT_SHIFT is
50    applied as well, if the upper-case letter is italic. */
51 
52 #define ACCENT_UP_SHIFT 7.0
53 #define ACCENT_RIGHT_SHIFT 2.0
54 
55 /* Relative size of small Japanese Kana */
56 #define SMALL_KANA_SIZE 0.725
57 
58 /* Hershey glyph arrays */
59 
60 #define OCCIDENTAL 0
61 #define ORIENTAL 1
62 
63 /* Location of first Kana in the occidental glyph arrays.  (Kana, unlike
64    Kanji, are placed in the occidental array, at the very end.) */
65 #define BEGINNING_OF_KANA 4195
66 
67 /* forward references */
68 static bool composite_char (unsigned char *composite, unsigned char *character, unsigned char *accent);
69 static double label_width_hershey (const unsigned short *label);
70 
71 /* An version of the alabel() method that is specific to the case when the
72    current Plotter font is a Hershey font.  It handles escape sequences for
73    subscripts, superscripts, shifts among Hershey fonts, etc.  */
74 double
_pl_g_alabel_hershey(R___ (Plotter * _plotter)const unsigned char * s,int x_justify,int y_justify)75 _pl_g_alabel_hershey (R___(Plotter *_plotter) const unsigned char *s, int x_justify, int y_justify)
76 {
77   unsigned short *codestring;
78   char x_justify_c, y_justify_c;
79   double label_width, label_height;
80   double x_offset, y_offset;
81   double x_displacement;
82   double postdx, dx, dy;
83   double theta;
84 
85   /* convert string to a codestring, including annotations */
86   codestring = _pl_g_controlify (R___(_plotter) s);
87 
88   /* dimensions of the string in user units */
89   label_width = HERSHEY_UNITS_TO_USER_UNITS(label_width_hershey (codestring));
90   label_height = HERSHEY_UNITS_TO_USER_UNITS(HERSHEY_HEIGHT);
91 
92   x_justify_c = (char)x_justify;
93   y_justify_c = (char)y_justify;
94 
95   switch (x_justify_c)
96     {
97     case 'l': /* left justified */
98     default:
99       x_offset = 0.0;
100       x_displacement = 1.0;
101       break;
102 
103     case 'c': /* centered */
104       x_offset = -0.5;
105       x_displacement = 0.0;
106       break;
107 
108     case 'r': /* right justified */
109       x_offset = -1.0;
110       x_displacement = -1.0;
111       break;
112     }
113 
114   switch (y_justify_c)
115     {
116     case 'b':			/* current point is at bottom */
117       y_offset = (double)HERSHEY_DESCENT / (double)HERSHEY_HEIGHT;
118       break;
119 
120     case 'x':			/* current point is on baseline */
121     default:
122       y_offset = 0.0;
123       break;
124 
125     case 'c': 			/* current point midway between bottom, top */
126       y_offset = 0.5 * ((double)HERSHEY_DESCENT - (double)HERSHEY_ASCENT)
127 		   / (double)HERSHEY_HEIGHT;
128       break;
129 
130     case 'C':			/* current point is on cap line */
131       y_offset = - (double)HERSHEY_CAPHEIGHT / (double)HERSHEY_HEIGHT;
132       break;
133 
134     case 't':			/* current point is at top */
135       y_offset = - (double)HERSHEY_ASCENT / (double)HERSHEY_HEIGHT;
136       break;
137     }
138 
139   /* save relevant drawing attributes, and restore them later */
140   {
141     char *old_line_mode, *old_cap_mode, *old_join_mode;
142     int old_fill_type;
143     double oldposx, oldposy;
144     bool old_dash_array_in_effect;
145 
146     old_line_mode = (char *)_pl_xmalloc (strlen (_plotter->drawstate->line_mode) + 1);
147     old_cap_mode = (char *)_pl_xmalloc (strlen (_plotter->drawstate->cap_mode) + 1);
148     old_join_mode = (char *)_pl_xmalloc (strlen (_plotter->drawstate->join_mode) + 1);
149     oldposx = _plotter->drawstate->pos.x;
150     oldposy = _plotter->drawstate->pos.y;
151 
152     strcpy (old_line_mode, _plotter->drawstate->line_mode);
153     strcpy (old_cap_mode, _plotter->drawstate->cap_mode);
154     strcpy (old_join_mode, _plotter->drawstate->join_mode);
155     old_fill_type = _plotter->drawstate->fill_type;
156     old_dash_array_in_effect = _plotter->drawstate->dash_array_in_effect;
157 
158     /* Our choices for rendering: solid lines, rounded capitals and joins,
159        a line width equal to slightly more than 1 Hershey unit, and no
160        filling.
161 
162        We don't set the pen type: we allow it to be 0, which will mean no
163        stroking at all. */
164     _API_linemod (R___(_plotter) "solid");
165     _API_capmod (R___(_plotter) "round");
166     _API_joinmod (R___(_plotter) "round");
167     _API_filltype (R___(_plotter) 0);
168 
169     /* move to take horizontal and vertical justification into account */
170     {
171       double theta, deltax, deltay, dx_just, dy_just;
172 
173       theta = M_PI * _plotter->drawstate->text_rotation / 180.0;
174 
175       deltax = x_offset * label_width;
176       deltay = y_offset * label_height;
177       dx_just = cos(theta) * deltax - sin(theta) * deltay;
178       dy_just = sin(theta) * deltax + cos(theta) * deltay;
179 
180       _API_fmoverel (R___(_plotter) dx_just, dy_just);
181     }
182 
183     /* call stroker on the sequence of strokes obtained from each char (the
184        stroker may manipulate the line width) */
185     _pl_g_draw_hershey_string (R___(_plotter) codestring);
186 
187     /* Restore original values of relevant drawing attributes, free
188        storage.  endpath() will be invoked in here automatically, flushing
189        the created polyline object comprising the stroked text. */
190     _API_linemod (R___(_plotter) old_line_mode);
191     _API_capmod (R___(_plotter) old_cap_mode);
192     _API_joinmod (R___(_plotter) old_join_mode);
193     _API_filltype (R___(_plotter) old_fill_type);
194     _plotter->drawstate->dash_array_in_effect = old_dash_array_in_effect;
195 
196     free (old_line_mode);
197     free (old_cap_mode);
198     free (old_join_mode);
199 
200     /* return to original position */
201     _API_fmove (R___(_plotter) oldposx, oldposy);
202   }
203 
204   /* amount by which to shift after printing label (user units) */
205   postdx = x_displacement * label_width;
206   theta = M_PI * _plotter->drawstate->text_rotation / 180.0;
207   dx = cos (theta) * postdx;
208   dy = sin (theta) * postdx;
209 
210   _API_fmoverel (R___(_plotter) dx, dy);
211 
212   free (codestring);
213 
214   return label_width;		/* user units */
215 }
216 
217 /* A version of the flabelwidth() method that is specific to the case when
218    the current Plotter font is a Hershey font. */
219 double
_pl_g_flabelwidth_hershey(R___ (Plotter * _plotter)const unsigned char * s)220 _pl_g_flabelwidth_hershey (R___(Plotter *_plotter) const unsigned char *s)
221 {
222   double label_width;
223   unsigned short *codestring;
224 
225   /* convert string to a codestring, including annotations */
226   codestring = _pl_g_controlify (R___(_plotter) s);
227 
228   label_width = HERSHEY_UNITS_TO_USER_UNITS(label_width_hershey (codestring));
229   free (codestring);
230 
231   return label_width;
232 }
233 
234 /* _pl_g_draw_hershey_stroke() draws a stroke, taking into account the
235    transformation from Hershey units to user units, and also the angle in
236    user space at which the label should be plotted. */
237 
238 void
_pl_g_draw_hershey_stroke(R___ (Plotter * _plotter)bool pendown,double deltax,double deltay)239 _pl_g_draw_hershey_stroke (R___(Plotter *_plotter) bool pendown, double deltax, double deltay)
240 {
241   double theta = M_PI * _plotter->drawstate->text_rotation / 180.0;
242   double dx, dy;
243 
244   deltax = HERSHEY_UNITS_TO_USER_UNITS (deltax);
245   deltay = HERSHEY_UNITS_TO_USER_UNITS (deltay);
246 
247   dx = cos(theta) * deltax - sin(theta) * deltay;
248   dy = sin(theta) * deltax + cos(theta) * deltay;
249 
250   if (pendown)
251     _API_fcontrel (R___(_plotter) dx, dy);
252   else
253     _API_fmoverel (R___(_plotter) dx, dy);
254 }
255 
256 /* label_width_hershey() computes the width (total delta x) of a
257    controlified character string to be rendered in a vector font, in
258    Hershey units.  Parsing must take into account the many control
259    sequences which perform shifts, and initiate/terminate
260    subscripts/superscripts. */
261 
262 /* In addition to scaling the character sizes and the `width', we perform
263    the following (dx, dy):
264 
265    enter subscript	(dx, dy) = (-1/9, -1/2) * width
266    exit subscript	(dx, dy) = (+1/6, +1/2) * width
267 
268    enter superscript	(dx, dy) = (-1/9, +1/2) * width
269    exit superscript	(dx, dy) = (+1/6, -1/2) * width
270 
271    For clarity here, `width' refers to the width _before_ it is
272    multiplied by a factor 2/3.
273 
274    [N.B. In Bob Beach's original UGS character stroke generator,
275    the +1/6's here were +2/9 instead.  Better?] */
276 
277 static double
label_width_hershey(const unsigned short * label)278 label_width_hershey (const unsigned short *label)
279 {
280   const unsigned short *ptr = label;
281   unsigned short c;
282   double charsize = 1.0;	/* relative char size, 1.0 means full size */
283   double saved_charsize = 1.0;
284   double width = 0.0;		/* label width */
285   double saved_width = 0.0;
286 
287   /* loop through unsigned shorts in label */
288   while ((c = (*ptr)) != (unsigned short)'\0')
289     {
290       int glyphnum;		/* glyph in Hershey array */
291       const unsigned char *glyph;
292 
293       if (c & RAW_HERSHEY_GLYPH)
294 	/* glyph was spec'd via an escape, not as a char in a font */
295 	{
296 	  glyphnum = c & GLYPH_SPEC;
297 	  glyph = (const unsigned char *)(_pl_g_occidental_hershey_glyphs[glyphnum]);
298 
299 	  if (*glyph != '\0')	/* nonempty glyph */
300 	    /* 1st two chars are bounds */
301 	    width += charsize * ((int)glyph[1] - (int)glyph[0]);
302 	}
303       else if (c & RAW_ORIENTAL_HERSHEY_GLYPH)
304 	/* glyph was spec'd via an escape, not as a char in a font */
305 	{
306 	  glyphnum = c & GLYPH_SPEC;
307 	  glyph = (const unsigned char *)_pl_g_oriental_hershey_glyphs[glyphnum];
308 
309 	  if (*glyph != '\0')	/* nonempty glyph */
310 	    /* 1st two chars are bounds */
311 	    width += charsize * ((int)glyph[1] - (int)glyph[0]);
312 	}
313       else if (c & CONTROL_CODE)	/* parse control code */
314 	{
315 	  switch (c & ~CONTROL_CODE)
316 	    {
317 	    case C_BEGIN_SUBSCRIPT:
318 	    case C_BEGIN_SUPERSCRIPT :
319 	      charsize *= SCRIPTSIZE;
320 	      break;
321 
322 	    case C_END_SUBSCRIPT:
323 	    case C_END_SUPERSCRIPT:
324 	      charsize /= SCRIPTSIZE;
325 	      break;
326 
327 	    case C_PUSH_LOCATION:
328 	      saved_width = width;
329 	      saved_charsize = charsize;
330 	      break;
331 
332 	    case C_POP_LOCATION:
333 	      width = saved_width;
334 	      charsize = saved_charsize;
335 	      break;
336 
337 	    case C_RIGHT_ONE_EM:
338 	      width += charsize * HERSHEY_EM;
339 	      break;
340 
341 	    case C_RIGHT_HALF_EM:
342 	      width += charsize * HERSHEY_EM / 2.0;
343 	      break;
344 
345 	    case C_RIGHT_QUARTER_EM:
346 	      width += charsize * HERSHEY_EM / 4.0;
347 	      break;
348 
349 	    case C_RIGHT_SIXTH_EM:
350 	      width += charsize * HERSHEY_EM / 6.0;
351 	      break;
352 
353 	    case C_RIGHT_EIGHTH_EM:
354 	      width += charsize * HERSHEY_EM / 8.0;
355 	      break;
356 
357 	    case C_RIGHT_TWELFTH_EM:
358 	      width += charsize * HERSHEY_EM / 12.0;
359 	      break;
360 
361 	    case C_LEFT_ONE_EM:
362 	      width -= charsize * HERSHEY_EM;
363 	      break;
364 
365 	    case C_LEFT_HALF_EM:
366 	      width -= charsize * HERSHEY_EM / 2.0;
367 	      break;
368 
369 	    case C_LEFT_QUARTER_EM:
370 	      width -= charsize * HERSHEY_EM / 4.0;
371 	      break;
372 
373 	    case C_LEFT_SIXTH_EM:
374 	      width -= charsize * HERSHEY_EM / 6.0;
375 	      break;
376 
377 	    case C_LEFT_EIGHTH_EM:
378 	      width -= charsize * HERSHEY_EM / 8.0;
379 	      break;
380 
381 	    case C_LEFT_TWELFTH_EM:
382 	      width -= charsize * HERSHEY_EM / 12.0;
383 	      break;
384 
385 	      /* unrecognized control code */
386 	    default:
387 	      break;
388 	    }
389 	}
390       else			/* yow, an actual character */
391 	{
392 	  int raw_fontnum;
393 
394 	  /* compute index of font, in table in g_fontdb.c */
395 	  raw_fontnum = (c >> FONT_SHIFT) & ONE_BYTE;
396 
397 	  c &= ~FONT_SPEC;	/* extract character proper */
398 	  glyphnum = (_pl_g_hershey_font_info[raw_fontnum].chars)[c];
399 
400 	  /* could be a pseudo glyph number, e.g. an indication that
401 	     character is composite */
402 	  if (glyphnum == ACC0 || glyphnum == ACC1 || glyphnum == ACC2)
403 	    {
404 	      unsigned char composite, character, accent;
405 
406 	      /* if so, use 1st element of composite character */
407 	      composite = (unsigned char)c;
408 	      if (composite_char (&composite, &character, &accent))
409 		glyphnum = (_pl_g_hershey_font_info[raw_fontnum].chars)[character];
410 	      else
411 		glyphnum = UNDE; /* hope this won't happen */
412 	    }
413 
414 	  /* could also be a glyph number displaced by KS, to indicate
415 	     that this is a small kana */
416 	  if (glyphnum & KS)
417 	    glyphnum -= KS;
418 
419 	  glyph = (const unsigned char *)(_pl_g_occidental_hershey_glyphs[glyphnum]);
420 	  if (*glyph != '\0')	/* nonempty glyph */
421 	    /* 1st two chars are bounds */
422 	    width += charsize * ((int)glyph[1] - (int)glyph[0]);
423 	}
424 
425       ptr++;			/* bump pointer in string */
426     }
427 
428   return width;
429 }
430 
431 /* _pl_g_draw_hershey_penup_stroke() draws a penup stroke, along a vector
432    specified in Hershey units.  Size scaling and obliquing (true/false) are
433    specified.  This is used for repositioning during rendering of composite
434    (accented) characters. */
435 void
_pl_g_draw_hershey_penup_stroke(R___ (Plotter * _plotter)double dx,double dy,double charsize,bool oblique)436 _pl_g_draw_hershey_penup_stroke(R___(Plotter *_plotter) double dx, double dy, double charsize, bool oblique)
437 {
438   double shear;
439 
440   shear = oblique ? (SHEAR) : 0.0;
441   _pl_g_draw_hershey_stroke (R___(_plotter)
442 			     false, /* pen up */
443 			     charsize * (dx + shear * dy),
444 			     charsize * dy);
445 }
446 
447 /* _pl_g_draw_hershey_glyph() invokes move() and cont() to draw a raw
448    Hershey glyph, specified by index in the occidental or oriental glyph
449    arrays.  Size scaling and obliquing (true/false) are specified. */
450 void
_pl_g_draw_hershey_glyph(R___ (Plotter * _plotter)int glyphnum,double charsize,int type,bool oblique)451 _pl_g_draw_hershey_glyph (R___(Plotter *_plotter) int glyphnum, double charsize, int type, bool oblique)
452 {
453   double xcurr, ycurr;
454   double xfinal, yfinal;
455   bool pendown = false;
456   const unsigned char *glyph;
457   double dx, dy;
458   double shear;
459 
460   shear = oblique ? (SHEAR) : 0.0;
461   switch (type)
462     {
463     case OCCIDENTAL:
464     default:
465       glyph = (const unsigned char *)(_pl_g_occidental_hershey_glyphs[glyphnum]);
466       break;
467     case ORIENTAL:
468       glyph = (const unsigned char *)(_pl_g_oriental_hershey_glyphs[glyphnum]);
469       break;
470     }
471 
472   if (*glyph != '\0')	/* nonempty glyph */
473     {
474       xcurr = charsize * (double)glyph[0];
475       xfinal = charsize * (double)glyph[1];
476       ycurr = yfinal = 0.0;
477       glyph += 2;
478       while (*glyph)
479 	{
480 	  int xnewint;
481 
482 	  xnewint = (int)glyph[0];
483 
484 	  if (xnewint == (int)' ')
485 	    pendown = false;
486 	  else
487 	    {
488 	      double xnew, ynew;
489 
490 	      xnew = (double)charsize * xnewint;
491 	      ynew = (double)charsize
492 		* ((int)'R'
493 		   - ((int)glyph[1] + (double)HERSHEY_BASELINE));
494 	      dx = xnew - xcurr;
495 	      dy = ynew - ycurr;
496 	      _pl_g_draw_hershey_stroke (R___(_plotter)
497 					 pendown, dx + shear * dy, dy);
498 	      xcurr = xnew, ycurr = ynew;
499 	      pendown = true;
500 	    }
501 
502 	  glyph +=2;	/* on to next pair */
503 	}
504 
505       /* final penup stroke, to end where we should */
506       dx = xfinal - xcurr;
507       dy = yfinal - ycurr;
508       _pl_g_draw_hershey_stroke (R___(_plotter) false, dx + shear * dy, dy);
509     }
510 }
511 
512 /* _pl_g_draw_hershey_string() strokes a string beginning at present
513    location, which is taken to be on the string's baseline.  Besides
514    invoking move() and cont(), it invokes linewidth(). */
515 void
_pl_g_draw_hershey_string(R___ (Plotter * _plotter)const unsigned short * string)516 _pl_g_draw_hershey_string (R___(Plotter *_plotter) const unsigned short *string)
517 {
518   unsigned short c;
519   const unsigned short *ptr = string;
520   double charsize = 1.0;
521   double saved_charsize = 1.0;
522   double saved_position_x = _plotter->drawstate->pos.x;
523   double saved_position_y = _plotter->drawstate->pos.y;
524   double old_line_width;
525   int line_width_type = 0;	/* 0,1,2 = unset,occidental,oriental */
526 
527   /* save line width (will restore at end) */
528   old_line_width = _plotter->drawstate->line_width;
529 
530   while ((c = (*ptr++)) != '\0')
531     {
532       /* Check for the four possibilities: (1) a Hershey glyph specified by
533 	 glyph number, (2) an oriental Hershey glyph specified by glyph
534 	 number, (3) a control code, and (4) an ordinary font character,
535 	 which will be mapped to a Hershey glyph by one of the tables in
536 	 g_fontdb.c. */
537 
538       if (c & RAW_HERSHEY_GLYPH)
539 	{
540 	  if (line_width_type != 1)
541 	    {
542 	      _API_flinewidth (R___(_plotter)
543 				    HERSHEY_UNITS_TO_USER_UNITS (HERSHEY_STROKE_WIDTH));
544 	      line_width_type = 1;
545 	    }
546 	  _pl_g_draw_hershey_glyph (R___(_plotter)
547 				    c & GLYPH_SPEC, charsize, OCCIDENTAL, false);
548 	}
549 
550       else if (c & RAW_ORIENTAL_HERSHEY_GLYPH)
551 	{
552 	  if (line_width_type != 2)
553 	    {
554 	      _API_flinewidth (R___(_plotter)
555 				    HERSHEY_UNITS_TO_USER_UNITS (HERSHEY_ORIENTAL_STROKE_WIDTH));
556 	      line_width_type = 2;
557 	    }
558 	  _pl_g_draw_hershey_glyph (R___(_plotter)
559 				    c & GLYPH_SPEC, charsize, ORIENTAL, false);
560 	}
561 
562       else if (c & CONTROL_CODE)
563 	switch (c & ~CONTROL_CODE) /* parse control codes */
564 	  {
565 	  case C_BEGIN_SUPERSCRIPT :
566 	    _pl_g_draw_hershey_stroke (R___(_plotter)
567 				       false,
568 				       SUPERSCRIPT_DX * charsize * HERSHEY_EM,
569 				       SUPERSCRIPT_DY * charsize * HERSHEY_EM);
570 	    charsize *= SCRIPTSIZE;
571 	    break;
572 
573 	  case C_END_SUPERSCRIPT:
574 	    charsize /= SCRIPTSIZE;
575 	    _pl_g_draw_hershey_stroke (R___(_plotter)
576 				       false,
577 				       - SUPERSCRIPT_DX * charsize * HERSHEY_EM,
578 				       - SUPERSCRIPT_DY * charsize * HERSHEY_EM);
579 	    break;
580 
581 	  case C_BEGIN_SUBSCRIPT:
582 	    _pl_g_draw_hershey_stroke (R___(_plotter)
583 				       false,
584 				       SUBSCRIPT_DX * charsize * HERSHEY_EM,
585 				       SUBSCRIPT_DY * charsize * HERSHEY_EM);
586 	    charsize *= SCRIPTSIZE;
587 	    break;
588 
589 	  case C_END_SUBSCRIPT:
590 	    charsize /= SCRIPTSIZE;
591 	    _pl_g_draw_hershey_stroke (R___(_plotter)
592 				       false,
593 				       - SUBSCRIPT_DX * charsize * HERSHEY_EM,
594 				       - SUBSCRIPT_DY * charsize * HERSHEY_EM);
595 	    break;
596 
597 	  case C_PUSH_LOCATION:
598 	    saved_charsize = charsize;
599 	    saved_position_x = _plotter->drawstate->pos.x;
600 	    saved_position_y = _plotter->drawstate->pos.y;
601 	    break;
602 
603 	  case C_POP_LOCATION:
604 	    charsize = saved_charsize;
605 	    _API_fmove (R___(_plotter)
606 			     saved_position_x, saved_position_y);
607 	    break;
608 
609 	  case C_RIGHT_ONE_EM:
610 	    _pl_g_draw_hershey_stroke (R___(_plotter)
611 				       false, charsize * HERSHEY_EM, 0.0);
612 	    break;
613 
614 	  case C_RIGHT_HALF_EM:
615 	    _pl_g_draw_hershey_stroke (R___(_plotter)
616 				       false, charsize * HERSHEY_EM / 2.0, 0.0);
617 	    break;
618 
619 	  case C_RIGHT_QUARTER_EM:
620 	    _pl_g_draw_hershey_stroke (R___(_plotter)
621 				       false, charsize * HERSHEY_EM / 4.0, 0.0);
622 	    break;
623 
624 	  case C_RIGHT_SIXTH_EM:
625 	    _pl_g_draw_hershey_stroke (R___(_plotter)
626 				       false, charsize * HERSHEY_EM / 6.0, 0.0);
627 	    break;
628 
629 	  case C_RIGHT_EIGHTH_EM:
630 	    _pl_g_draw_hershey_stroke (R___(_plotter)
631 				       false, charsize * HERSHEY_EM / 8.0, 0.0);
632 	    break;
633 
634 	  case C_RIGHT_TWELFTH_EM:
635 	    _pl_g_draw_hershey_stroke (R___(_plotter)
636 				       false, charsize * HERSHEY_EM / 12.0, 0.0);
637 	    break;
638 
639 	  case C_LEFT_ONE_EM:
640 	    _pl_g_draw_hershey_stroke (R___(_plotter)
641 				       false, - charsize * HERSHEY_EM, 0.0);
642 	    break;
643 
644 	  case C_LEFT_HALF_EM:
645 	    _pl_g_draw_hershey_stroke (R___(_plotter)
646 				       false, - charsize * HERSHEY_EM / 2.0, 0.0);
647 	    break;
648 
649 	  case C_LEFT_QUARTER_EM:
650 	    _pl_g_draw_hershey_stroke (R___(_plotter)
651 				       false, - charsize * HERSHEY_EM / 4.0, 0.0);
652 	    break;
653 
654 	  case C_LEFT_SIXTH_EM:
655 	    _pl_g_draw_hershey_stroke (R___(_plotter)
656 				       false, - charsize * HERSHEY_EM / 6.0, 0.0);
657 	    break;
658 
659 	  case C_LEFT_EIGHTH_EM:
660 	    _pl_g_draw_hershey_stroke (R___(_plotter)
661 				       false, - charsize * HERSHEY_EM / 8.0, 0.0);
662 	    break;
663 
664 	  case C_LEFT_TWELFTH_EM:
665 	    _pl_g_draw_hershey_stroke (R___(_plotter)
666 				       false, - charsize * HERSHEY_EM / 12.0, 0.0);
667 	    break;
668 
669 	    /* unrecognized control code, punt */
670 	  default:
671 	    break;
672 	  }
673 
674       else
675 	/* yow, an actual font character!  Several possibilities: could be
676 	   a composite (accented) character, could be a small Kana, or
677 	   could be a garden-variety character. */
678 	{
679 	  int raw_fontnum;
680 	  int glyphnum;		/* glyph in Hershey array */
681 	  int char_glyphnum, accent_glyphnum; /* for composite chars */
682 	  int char_width, accent_width; /* for composite chars */
683 	  const unsigned char *char_glyph, *accent_glyph;
684 	  unsigned char composite, character, accent;
685 	  bool oblique, small_kana = false;
686 
687 	  /* compute index of font, in font table in g_fontdb.c */
688 	  raw_fontnum = (c >> FONT_SHIFT) & ONE_BYTE;
689 	  /* shear font?  (for HersheySans-Oblique, etc.) */
690 	  oblique = _pl_g_hershey_font_info[raw_fontnum].obliquing;
691 
692 	  c &= ~FONT_SPEC;	/* extract character proper */
693 	  glyphnum = (_pl_g_hershey_font_info[raw_fontnum].chars)[c];
694 
695 	  if (glyphnum & KS) /* a small kana? */
696 	    {
697 	      glyphnum -= KS;
698 	      small_kana = true;
699 	    }
700 
701 	  switch (glyphnum)
702 	    {
703 	      /* special case: this is a composite (accented) character;
704 		 search font table in g_fontdb.c for it */
705 	    case ACC0:
706 	    case ACC1:
707 	    case ACC2:
708 	      composite = (unsigned char)c;
709 	      if (composite_char (&composite, &character, &accent))
710 		{
711 		  char_glyphnum =
712 		    (_pl_g_hershey_font_info[raw_fontnum].chars)[character];
713 		  accent_glyphnum =
714 		    (_pl_g_hershey_font_info[raw_fontnum].chars)[accent];
715 		}
716 	      else
717 		{		/* hope this won't happen */
718 		  char_glyphnum = UNDE;
719 		  accent_glyphnum = 0;
720 		}
721 	      char_glyph =
722 		(const unsigned char *)_pl_g_occidental_hershey_glyphs[char_glyphnum];
723 	      accent_glyph =
724 		(const unsigned char *)_pl_g_occidental_hershey_glyphs[accent_glyphnum];
725 
726 	      if (*char_glyph != '\0') /* nonempty glyph */
727 		/* 1st two chars are bounds, in Hershey units */
728 		char_width = (int)char_glyph[1] - (int)char_glyph[0];
729 	      else
730 		char_width = 0;
731 
732 	      if (*accent_glyph != '\0') /* nonempty glyph */
733 		/* 1st two chars are bounds, in Hershey units */
734 		accent_width = (int)accent_glyph[1] - (int)accent_glyph[0];
735 	      else
736 		accent_width = 0;
737 
738 	      /* draw the character */
739 	      if (line_width_type != 1)
740 		{
741 		  _API_flinewidth (R___(_plotter)
742 					HERSHEY_UNITS_TO_USER_UNITS (HERSHEY_STROKE_WIDTH));
743 		  line_width_type = 1;
744 		}
745 	      _pl_g_draw_hershey_glyph (R___(_plotter)
746 					char_glyphnum, charsize,
747 					OCCIDENTAL, oblique);
748 	      /* back up to draw accent */
749 	      _pl_g_draw_hershey_penup_stroke (R___(_plotter)
750 					       -0.5 * (double)char_width
751 					       -0.5 * (double)accent_width,
752 					       0.0, charsize, oblique);
753 
754 	      /* repositioning for uppercase and uppercase italic */
755 	      if (glyphnum == ACC1)
756 		_pl_g_draw_hershey_penup_stroke (R___(_plotter)
757 						 0.0,
758 						 (double)(ACCENT_UP_SHIFT),
759 						 charsize, oblique);
760 	      else if (glyphnum == ACC2)
761 		_pl_g_draw_hershey_penup_stroke (R___(_plotter)
762 						 (double)(ACCENT_RIGHT_SHIFT),
763 						 (double)(ACCENT_UP_SHIFT),
764 						 charsize, oblique);
765 
766 	      /* draw the accent */
767 	      _pl_g_draw_hershey_glyph (R___(_plotter)
768 					accent_glyphnum, charsize,
769 					OCCIDENTAL, oblique);
770 
771 	      /* undo special repositioning if any */
772 	      if (glyphnum == ACC1)
773 		_pl_g_draw_hershey_penup_stroke (R___(_plotter)
774 						 0.0,
775 						 -(double)(ACCENT_UP_SHIFT),
776 						 charsize, oblique);
777 	      else if (glyphnum == ACC2)
778 		_pl_g_draw_hershey_penup_stroke (R___(_plotter)
779 						 -(double)(ACCENT_RIGHT_SHIFT),
780 						 -(double)(ACCENT_UP_SHIFT),
781 						 charsize, oblique);
782 
783 	      /* move forward, to end composite char where we should */
784 	      _pl_g_draw_hershey_penup_stroke (R___(_plotter)
785 					       0.5 * (double)char_width
786 					       -0.5 * (double)accent_width,
787 					       0.0, charsize, oblique);
788 	      break;
789 
790 	      /* not a composite (accented) character; just an ordinary
791 		 glyph from occidental+Kana array (could be a Kana, in
792 		 particular, could be a small Kana) */
793 	    default:
794 	      if (small_kana)
795 		{
796 		  int kana_width;
797 		  const unsigned char *kana_glyph;
798 		  double shift = 0.5 * (1.0 - (SMALL_KANA_SIZE));
799 
800 		  kana_glyph =
801 		    (const unsigned char *)_pl_g_occidental_hershey_glyphs[glyphnum];
802 		  kana_width = (int)kana_glyph[1] - (int)kana_glyph[0];
803 
804 		  /* draw small Kana, preceded and followed by a penup
805 		     stroke in order to traverse the full width of an
806 		     ordinary Kana */
807 		  _pl_g_draw_hershey_penup_stroke (R___(_plotter)
808 						   shift * (double)kana_width,
809 						   0.0, charsize, oblique);
810 		  if (line_width_type != 2)
811 		    {
812 		      _API_flinewidth (R___(_plotter)
813 					    HERSHEY_UNITS_TO_USER_UNITS (HERSHEY_ORIENTAL_STROKE_WIDTH));
814 		      line_width_type = 2;
815 		    }
816 		  _pl_g_draw_hershey_glyph (R___(_plotter)
817 					    glyphnum,
818 					    (SMALL_KANA_SIZE) * charsize,
819 					    OCCIDENTAL, oblique);
820 		  _pl_g_draw_hershey_penup_stroke (R___(_plotter)
821 						   shift * (double)kana_width,
822 						   0.0, charsize, oblique);
823 		}
824 	      else
825 		/* whew! just an ordinary glyph from the occidental array
826 		   (could be a Kana however, since they're confusingly
827 		   placed in that array, at the end) */
828 		{
829 		  if (glyphnum >= BEGINNING_OF_KANA)
830 		    {
831 		      if (line_width_type != 2)
832 			{
833 			  _API_flinewidth (R___(_plotter)
834 						HERSHEY_UNITS_TO_USER_UNITS (HERSHEY_ORIENTAL_STROKE_WIDTH));
835 			  line_width_type = 2;
836 			}
837 		    }
838 		  else
839 		      if (line_width_type != 1)
840 			{
841 			  _API_flinewidth (R___(_plotter)
842 						HERSHEY_UNITS_TO_USER_UNITS (HERSHEY_STROKE_WIDTH));
843 			  line_width_type = 1;
844 			}
845 		_pl_g_draw_hershey_glyph (R___(_plotter)
846 					  glyphnum, charsize,
847 					  OCCIDENTAL, oblique);
848 		}
849 	      break;
850 	    } /* end of case statement that switches based on glyphnum */
851 
852 	} /* end of font character case */
853 
854     } /* end of loop through unsigned shorts in the codestring */
855 
856   if (line_width_type != 0)
857     /* must restore old line width */
858     _API_flinewidth (R___(_plotter) old_line_width);
859 
860   return;
861 }
862 
863 /* retrieve the two elements of a composite character from the table in
864    g_fontdb.c */
865 static bool
composite_char(unsigned char * composite,unsigned char * character,unsigned char * accent)866 composite_char (unsigned char *composite, unsigned char *character, unsigned char *accent)
867 {
868   const struct plHersheyAccentedCharInfoStruct *compchar = _pl_g_hershey_accented_char_info;
869   bool found = false;
870   unsigned char given = *composite;
871 
872   while (compchar->composite)
873     {
874       if (compchar->composite == given)
875 	{
876 	  found = true;
877 	  /* return char and accent via pointers */
878 	  *character = compchar->character;
879 	  *accent = compchar->accent;
880 	}
881       compchar++;
882     }
883 
884   return found;
885 }
886