| /* PAUL MURRELL |
| This is from the GNU plotutils libplot-2.3 distribution |
| Several modifications have been made to use the R graphics engine |
| for output. |
| All references to HAVE_PROTOS removed |
| All references to "plotter" replaced with references to "GEDevDesc" |
| */ |
| |
| /* This file contains the internal method _falabel_hershey(), which plots a |
| label using Hershey fonts. Each character in a Hershey font is a |
| sequence of pen motions, so this function simply calls fmoverel() and |
| fcontrel() to `stroke' each character in the argument string. |
| |
| The width of the string in user units is returned. The internal method |
| _flabelwidth_hershey() is similar, but does not actually plot the label. |
| */ |
| |
| /* PAUL MURRELL |
| sys-defines.h not used |
| */ |
| /* #include "sys-defines.h" */ |
| |
| /* PAUL MURRELL |
| extern.h renamed g_extern.h |
| */ |
| #include "g_extern.h" |
| #include "g_control.h" |
| #include "g_her_metr.h" |
| |
| /* Shearing factor for oblique fonts, new_x = x + SHEAR * y */ |
| |
| #define SHEAR (2.0/7.0) |
| |
| /* Relative size of subscripts/superscripts (i.e. `indexical' size) */ |
| |
| #define SCRIPTSIZE (0.6) |
| |
| /* Positioning of subscripts/superscripts */ |
| |
| #define SUBSCRIPT_DX 0.0 |
| #define SUBSCRIPT_DY (-0.25) |
| #define SUPERSCRIPT_DX 0.0 |
| #define SUPERSCRIPT_DY 0.4 |
| |
| /* Positioning of accents (in Hershey units). UP_SHIFT is amount by which |
| accents are raised when placed over upper-case letters. RIGHT_SHIFT is |
| applied as well, if the upper-case letter is italic. */ |
| |
| #define ACCENT_UP_SHIFT 7.0 |
| #define ACCENT_RIGHT_SHIFT 2.0 |
| |
| /* Relative size of small Japanese Kana */ |
| #define SMALL_KANA_SIZE 0.725 |
| |
| /* Hershey glyph arrays */ |
| |
| #define OCCIDENTAL 0 |
| #define ORIENTAL 1 |
| |
| /* Location of first Kana in the occidental glyph arrays. (Kana, unlike |
| Kanji, are placed in the occidental array, at the very end.) */ |
| #define BEGINNING_OF_KANA 4195 |
| |
| /* PAUL MURRELL |
| Structure to contain vfont current position |
| */ |
| typedef struct { |
| double currX; |
| double currY; |
| double angle; |
| } vfontContext; |
| |
| /* forward references */ |
| static bool _composite_char (unsigned char *composite, |
| unsigned char *character, |
| unsigned char *accent); |
| static void _draw_stroke (vfontContext *vc, const pGEcontext gc, |
| pGEDevDesc dd, |
| bool pendown, double deltax, double deltay); |
| static double _label_width_hershey (const pGEcontext gc, pGEDevDesc dd, |
| const unsigned short *label); |
| static void _draw_hershey_string (vfontContext *vc, const pGEcontext gc, |
| pGEDevDesc dd, |
| const unsigned short *string); |
| |
| /* _draw_hershey_stroke() draws a stroke, taking into account the |
| transformation from Hershey units to user units, and also the current |
| transformation matrix (as set by the user). _draw_stroke is similar, |
| but takes arguments in user units. */ |
| /* PAUL MURRELL |
| _draw_stroke now takes arguments in INCHES; it needs to work in |
| an absolute coordinate system because it does the rotation |
| */ |
| |
| static |
| void _draw_hershey_stroke (vfontContext *vc, const pGEcontext gc, pGEDevDesc dd, |
| bool pendown, double deltax, double deltay) |
| { |
| _draw_stroke(vc, gc, dd, pendown, |
| fromDeviceWidth(HERSHEY_X_UNITS_TO_USER_UNITS (deltax), |
| GE_INCHES, dd), |
| fromDeviceHeight(HERSHEY_Y_UNITS_TO_USER_UNITS (deltay), |
| GE_INCHES, dd)); |
| } |
| |
| static void moverel(double dx, double dy, vfontContext *vc) { |
| vc->currX += dx; |
| vc->currY += dy; |
| } |
| |
| static void linerel(double dx, double dy, |
| vfontContext *vc, const pGEcontext gc, |
| pGEDevDesc dd) { |
| GELine(toDeviceX(vc->currX, GE_INCHES, dd), |
| toDeviceY(vc->currY, GE_INCHES, dd), |
| toDeviceX(vc->currX+dx, GE_INCHES, dd), |
| toDeviceY(vc->currY+dy, GE_INCHES, dd), |
| gc, dd); |
| vc->currX += dx; |
| vc->currY += dy; |
| } |
| |
| /* e.g. for some Windows headers */ |
| #ifndef M_PI |
| #define M_PI 3.141592653589793238462643383279502884197169399375 |
| #endif |
| |
| static void _draw_stroke (vfontContext *vc, const pGEcontext gc, pGEDevDesc dd, |
| bool pendown, double deltax, double deltay) |
| { |
| double dx, dy; |
| double theta; |
| |
| theta = M_PI * vc->angle / 180.0; |
| |
| dx = cos(theta) * deltax - sin(theta) * deltay; |
| dy = sin(theta) * deltax + cos(theta) * deltay; |
| |
| if (pendown) |
| linerel(dx, dy, vc, gc, dd); |
| else |
| moverel(dx, dy, vc); |
| } |
| |
| /* PAUL MURRELL |
| Renamed from _g_flabelwidth_hershey to GVStrWidth |
| ... and added unit argument |
| ... and rearranged order of arguments |
| */ |
| |
| /* this is the version of the flabelwidth() method that is specific to the |
| case when the current Plotter font is a Hershey font; called in |
| g_flabelwidth () . |
| |
| Argument 'enc' is ignored: these are in their own encoding. |
| */ |
| attribute_hidden |
| double R_GE_VStrWidth (const char *s, cetype_t enc, |
| const pGEcontext gc, pGEDevDesc dd) |
| { |
| double label_width; |
| unsigned short *codestring; |
| |
| /* PAUL MURRELL |
| _controlify using R_alloc instead of xmalloc so need to do |
| vmaxget() ... vmaxset() instead of free() |
| */ |
| |
| const void *vmax = vmaxget(); |
| |
| /* convert string to a codestring, including annotations */ |
| codestring = _controlify (dd, (const unsigned char *) s, |
| gc->fontfamily[7] - 1, gc->fontface); |
| |
| label_width = _label_width_hershey (gc, dd, codestring); |
| |
| vmaxset(vmax); |
| /* free (codestring); */ |
| |
| return label_width; |
| } |
| |
| /* PAUL MURRELL |
| Added _label_height_hershey and GVStrHeight function |
| */ |
| |
| static double _label_height_hershey (const pGEcontext gc, |
| pGEDevDesc dd, |
| const unsigned short *label) |
| { |
| return( HERSHEY_Y_UNITS_TO_USER_UNITS(HERSHEY_LARGE_CAPHEIGHT) ); |
| } |
| |
| attribute_hidden |
| double R_GE_VStrHeight (const char *s, cetype_t enc, |
| const pGEcontext gc, pGEDevDesc dd) |
| { |
| double label_height; |
| unsigned short *codestring; |
| |
| const void *vmax = vmaxget(); |
| |
| /* convert string to a codestring, including annotations */ |
| codestring = _controlify (dd, (const unsigned char *) s, |
| gc->fontfamily[7] - 1, gc->fontface); |
| |
| label_height = _label_height_hershey (gc, dd, codestring); |
| |
| vmaxset(vmax); |
| |
| return label_height; |
| } |
| |
| /* PAUL MURRELL |
| This guy is the entry point to the GNU code |
| Renamed from _g_falabel_hershey to GVText |
| Reordered arguments |
| Added x, y, rotation, and font specification arguments |
| Re-typed x/y_justify from int to double |
| Re-typed s from "unsigned char *" to char * |
| Changed return value from double to void |
| */ |
| |
| /* this is the version of the falabel() method that is specific |
| to the case when the current Plotter font is a Hershey font */ |
| attribute_hidden |
| void R_GE_VText (double x, double y, const char *s, cetype_t enc, |
| double x_justify, double y_justify, double rotation, |
| const pGEcontext gc, pGEDevDesc dd) |
| { |
| unsigned short *codestring; |
| double label_width, label_height; |
| double x_offset, y_offset; |
| /* double x_displacement; */ |
| |
| /* PAUL MURRELL |
| _controlify using R_alloc instead of xmalloc so need to do |
| vmaxget() ... vmaxset() instead of free() |
| */ |
| const void *vmax = vmaxget(); |
| |
| /* PAUL MURRELL |
| initialise the local currX and currY |
| work in INCHES because that is what moverel and linerel work in |
| */ |
| vfontContext vc; |
| vc.currX = fromDeviceX(x, GE_INCHES, dd); |
| vc.currY = fromDeviceY(y, GE_INCHES, dd); |
| vc.angle = rotation; |
| |
| /* PAUL MURRELL |
| * Override gc settings for lty and lwd |
| */ |
| gc->lty = LTY_SOLID; |
| gc->lwd = HERSHEY_LINE_WIDTH_TO_LWD (HERSHEY_STROKE_WIDTH); |
| gc->lend = GE_ROUND_CAP; |
| gc->ljoin = GE_ROUND_JOIN; |
| |
| /* convert string to a codestring, including annotations */ |
| codestring = _controlify (dd, (const unsigned char *)s, |
| gc->fontfamily[7] - 1, gc->fontface); |
| |
| |
| /* PAUL MURRELL |
| Justification changed from char (e.g., 'l', 'c', 'r') to |
| double (e.g., 0, .5, 1) |
| Also added handling of NaN x_justify and y_justify |
| ... and replaced HERSHEY_Y_UNITS_TO_USER_UNITS(HERSHEY_HEIGHT) |
| with HERSHEY_Y_UNITS_TO_USER_UNITS(HERSHEY_LARGE_CAPHEIGHT) |
| ... and replaced ((double)HERSHEY_ASCENT / (double)HERSHEY_HEIGHT) |
| with 1.0 |
| */ |
| |
| /* dimensions of the string, in user units */ |
| label_width = _label_width_hershey (gc, dd, codestring); |
| label_height = _label_height_hershey(gc, dd, codestring); |
| |
| if (!R_FINITE(x_justify)) |
| x_justify = 0.5; |
| if (!R_FINITE(y_justify)) |
| y_justify = 0.5; |
| |
| x_offset = 0 - x_justify; |
| /* x_displacement = 1 - 2 * x_justify; */ |
| |
| y_offset = 0 - y_justify * 1.0; |
| |
| /* save relevant drawing attributes, and restore them later */ |
| |
| { |
| /* |
| char *old_line_mode, *old_cap_mode, *old_join_mode; |
| int old_fill_type; |
| double oldposx, oldposy; |
| bool old_dash_array_in_effect; |
| |
| old_line_mode = (char *)_plot_xmalloc (strlen (_plotter->drawstate->line_mode) + 1); |
| old_cap_mode = (char *)_plot_xmalloc (strlen (_plotter->drawstate->cap_mode) + 1); |
| old_join_mode = (char *)_plot_xmalloc (strlen (_plotter->drawstate->join_mode) + 1); |
| oldposx = (_plotter->drawstate->pos).x; |
| oldposy = (_plotter->drawstate->pos).y; |
| |
| strcpy (old_line_mode, _plotter->drawstate->line_mode); |
| strcpy (old_cap_mode, _plotter->drawstate->cap_mode); |
| strcpy (old_join_mode, _plotter->drawstate->join_mode); |
| old_fill_type = _plotter->drawstate->fill_type; |
| old_dash_array_in_effect = _plotter->drawstate->dash_array_in_effect; |
| */ |
| /* Our choices for rendering: solid lines, rounded capitals and joins, |
| a line width equal to slightly more than 1 Hershey unit, and |
| transparent filling. */ |
| /* |
| _plotter->linemod (R___(_plotter) "solid"); |
| _plotter->capmod (R___(_plotter) "round"); |
| _plotter->joinmod (R___(_plotter) "round"); |
| _plotter->filltype (R___(_plotter) 0); |
| */ |
| |
| /* move to take horizontal and vertical justification into account; |
| arguments here are in user units */ |
| /* PAUL MURRELL |
| arguments now in INCHES because _draw_stroke does rotation |
| so it needs to use absolute coordinates |
| */ |
| _draw_stroke (&vc, gc, dd, |
| false, |
| fromDeviceWidth(x_offset * label_width, GE_INCHES, dd), |
| fromDeviceHeight(y_offset * label_height, GE_INCHES, dd)); |
| /* call stroker on the sequence of strokes obtained from each char (the |
| stroker may manipulate the line width) */ |
| /* _draw_hershey_stroke (dd, true, 0, HERSHEY_EM); */ |
| _draw_hershey_string (&vc, gc, dd, codestring); |
| |
| /* Restore original values of relevant drawing attributes, free |
| storage. endpath() will be invoked in here automatically, flushing |
| the created polyline object comprising the stroked text. */ |
| /* |
| _plotter->linemod (R___(_plotter) old_line_mode); |
| _plotter->capmod (R___(_plotter) old_cap_mode); |
| _plotter->joinmod (R___(_plotter) old_join_mode); |
| _plotter->filltype (R___(_plotter) old_fill_type); |
| _plotter->drawstate->dash_array_in_effect = old_dash_array_in_effect; |
| |
| free (old_line_mode); |
| free (old_cap_mode); |
| free (old_join_mode); |
| */ |
| /* return to original position */ |
| /* _plotter->fmove (R___(_plotter) oldposx, oldposy); */ |
| } |
| |
| /* amount by which to shift after printing label (user units) */ |
| /* |
| postdx = x_displacement * label_width; |
| theta = M_PI * rotation / 180.0; |
| dx = cos (theta) * postdx |
| - sin (theta) * 0; |
| dy = sin (theta) * postdx |
| + cos (theta) * 0; |
| |
| moverel(dx, dy); |
| */ |
| |
| vmaxset(vmax); |
| /* free (codestring); */ |
| |
| /* PAUL MURRELL |
| No return value |
| */ |
| /* return label_width; user units */ |
| } |
| |
| /* In addition to scaling the character sizes and the `width', we perform |
| the following (dx, dy): |
| |
| enter subscript (dx, dy) = (-1/9, -1/2) * width |
| exit subscript (dx, dy) = (+1/6, +1/2) * width |
| |
| enter superscript (dx, dy) = (-1/9, +1/2) * width |
| exit superscript (dx, dy) = (+1/6, -1/2) * width |
| |
| For clarity here, `width' refers to the width _before_ it is |
| multiplied by a factor 2/3. |
| |
| [N.B. In Bob Beach's original UGS character stroke generator, |
| the +1/6's here were +2/9 instead. Better?] */ |
| |
| /* _label_width_hershey() computes the width (total delta x) of a |
| controlified character string to be rendered in a vector font, in user |
| units */ |
| static double _label_width_hershey (const pGEcontext gc, pGEDevDesc dd, |
| const unsigned short *label) |
| { |
| const unsigned short *ptr = label; |
| unsigned short c; |
| double charsize = 1.0; /* relative char size, 1.0 means full size */ |
| double saved_charsize = 1.0; |
| double width = 0.0; /* label width */ |
| double saved_width = 0.0; |
| |
| /* loop through unsigned shorts in label */ |
| while ((c = (*ptr)) != (unsigned short)'\0') |
| { |
| int glyphnum; /* glyph in Hershey array */ |
| const unsigned char *glyph; |
| |
| if (c & RAW_HERSHEY_GLYPH) |
| /* glyph was spec'd via an escape, not as a char in a font */ |
| { |
| glyphnum = c & GLYPH_SPEC; |
| glyph = (const unsigned char *)(_occidental_hershey_glyphs[glyphnum]); |
| |
| if (*glyph != '\0') /* nonempty glyph */ |
| /* 1st two chars are bounds */ |
| width += charsize * ((int)glyph[1] - (int)glyph[0]); |
| } |
| else if (c & RAW_ORIENTAL_HERSHEY_GLYPH) |
| /* glyph was spec'd via an escape, not as a char in a font */ |
| { |
| glyphnum = c & GLYPH_SPEC; |
| glyph = (const unsigned char *)_oriental_hershey_glyphs[glyphnum]; |
| |
| if (*glyph != '\0') /* nonempty glyph */ |
| /* 1st two chars are bounds */ |
| width += charsize * ((int)glyph[1] - (int)glyph[0]); |
| } |
| else if (c & CONTROL_CODE) /* parse control code */ |
| { |
| switch (c & ~CONTROL_CODE) |
| { |
| case C_BEGIN_SUBSCRIPT: |
| case C_BEGIN_SUPERSCRIPT : |
| charsize *= SCRIPTSIZE; |
| break; |
| |
| case C_END_SUBSCRIPT: |
| case C_END_SUPERSCRIPT: |
| charsize /= SCRIPTSIZE; |
| break; |
| |
| case C_PUSH_LOCATION: |
| saved_width = width; |
| saved_charsize = charsize; |
| break; |
| |
| case C_POP_LOCATION: |
| width = saved_width; |
| charsize = saved_charsize; |
| break; |
| |
| case C_RIGHT_ONE_EM: |
| width += charsize * HERSHEY_EM; |
| break; |
| |
| case C_RIGHT_HALF_EM: |
| width += charsize * HERSHEY_EM / 2.0; |
| break; |
| |
| case C_RIGHT_QUARTER_EM: |
| width += charsize * HERSHEY_EM / 4.0; |
| break; |
| |
| case C_RIGHT_SIXTH_EM: |
| width += charsize * HERSHEY_EM / 6.0; |
| break; |
| |
| case C_RIGHT_EIGHTH_EM: |
| width += charsize * HERSHEY_EM / 8.0; |
| break; |
| |
| case C_RIGHT_TWELFTH_EM: |
| width += charsize * HERSHEY_EM / 12.0; |
| break; |
| |
| case C_LEFT_ONE_EM: |
| width -= charsize * HERSHEY_EM; |
| break; |
| |
| case C_LEFT_HALF_EM: |
| width -= charsize * HERSHEY_EM / 2.0; |
| break; |
| |
| case C_LEFT_QUARTER_EM: |
| width -= charsize * HERSHEY_EM / 4.0; |
| break; |
| |
| case C_LEFT_SIXTH_EM: |
| width -= charsize * HERSHEY_EM / 6.0; |
| break; |
| |
| case C_LEFT_EIGHTH_EM: |
| width -= charsize * HERSHEY_EM / 8.0; |
| break; |
| |
| case C_LEFT_TWELFTH_EM: |
| width -= charsize * HERSHEY_EM / 12.0; |
| break; |
| |
| /* unrecognized control code */ |
| default: |
| break; |
| } |
| } |
| else /* yow, an actual character */ |
| { |
| int raw_fontnum; |
| |
| /* compute index of font, in table in g_fontdb.c */ |
| raw_fontnum = (c >> FONT_SHIFT) & ONE_BYTE; |
| |
| c &= ~FONT_SPEC; /* extract character proper */ |
| glyphnum = (_hershey_font_info[raw_fontnum].chars)[c]; |
| |
| /* could be a pseudo glyph number, e.g. an indication that |
| character is composite */ |
| if (glyphnum == ACC0 || glyphnum == ACC1 || glyphnum == ACC2) |
| { |
| unsigned char composite, character = '\0' /* -Wall */, accent; |
| |
| /* if so, use 1st element of composite character */ |
| composite = (unsigned char)c; |
| if (_composite_char (&composite, &character, &accent)) |
| glyphnum = (_hershey_font_info[raw_fontnum].chars)[character]; |
| else |
| glyphnum = UNDE; /* hope this won't happen */ |
| } |
| |
| /* could also be a glyph number displaced by KS, to indicate |
| that this is a small kana */ |
| if (glyphnum & KS) |
| glyphnum -= KS; |
| |
| glyph = (const unsigned char *)(_occidental_hershey_glyphs[glyphnum]); |
| if (*glyph != '\0') /* nonempty glyph */ |
| /* 1st two chars are bounds */ |
| width += charsize * ((int)glyph[1] - (int)glyph[0]); |
| } |
| |
| ptr++; /* bump pointer in string */ |
| } |
| |
| return HERSHEY_X_UNITS_TO_USER_UNITS (width); |
| } |
| |
| /* _draw_hershey_penup_stroke() draws a penup stroke, along a vector |
| specified in Hershey units. Size scaling and obliquing (true/false) are |
| specified. This is used for repositioning during rendering of |
| composite (accented) characters. */ |
| static |
| void _draw_hershey_penup_stroke(vfontContext *vc, const pGEcontext gc, |
| pGEDevDesc dd, |
| double dx, double dy, |
| double charsize, bool oblique) |
| { |
| double shear; |
| |
| shear = oblique ? (SHEAR) : 0.0; |
| _draw_hershey_stroke (vc, gc, dd, |
| false, /* pen up */ |
| charsize * (dx + shear * dy), |
| charsize * dy); |
| } |
| |
| /* _draw_hershey_glyph() invokes move() and cont() to draw a raw Hershey |
| glyph, specified by index in the occidental or oriental glyph arrays. |
| Size scaling and obliquing (true/false) are specified. */ |
| static |
| void _draw_hershey_glyph (vfontContext *vc, const pGEcontext gc, pGEDevDesc dd, |
| int glyphnum, |
| double charsize, int type, bool oblique) |
| { |
| double xcurr, ycurr; |
| double xfinal, yfinal; |
| bool pendown = false; |
| const unsigned char *glyph; |
| double dx, dy; |
| double shear; |
| |
| shear = oblique ? (SHEAR) : 0.0; |
| switch (type) |
| { |
| case OCCIDENTAL: |
| default: |
| glyph = (const unsigned char *)(_occidental_hershey_glyphs[glyphnum]); |
| break; |
| case ORIENTAL: |
| glyph = (const unsigned char *)(_oriental_hershey_glyphs[glyphnum]); |
| break; |
| } |
| |
| if (*glyph != '\0') /* nonempty glyph */ |
| { |
| xcurr = charsize * (double)glyph[0]; |
| xfinal = charsize * (double)glyph[1]; |
| ycurr = yfinal = 0.0; |
| glyph += 2; |
| while (*glyph) |
| { |
| int xnewint; |
| |
| xnewint = (int)glyph[0]; |
| |
| if (xnewint == (int)' ') |
| pendown = false; |
| else |
| { |
| double xnew, ynew; |
| |
| xnew = (double)charsize * xnewint; |
| ynew = (double)charsize |
| * ((int)'R' |
| - ((int)glyph[1] + (double)HERSHEY_BASELINE)); |
| dx = xnew - xcurr; |
| dy = ynew - ycurr; |
| _draw_hershey_stroke (vc, gc, dd, |
| pendown, dx + shear * dy, dy); |
| xcurr = xnew, ycurr = ynew; |
| pendown = true; |
| } |
| |
| glyph +=2; /* on to next pair */ |
| } |
| |
| /* final penup stroke, to end where we should */ |
| dx = xfinal - xcurr; |
| dy = yfinal - ycurr; |
| _draw_hershey_stroke (vc, gc, dd, false, dx + shear * dy, dy); |
| } |
| } |
| |
| /* _draw_hershey_string() strokes a string beginning at present location, |
| which is taken to be on the string's baseline. Besides invoking move() |
| and cont(), it invokes linewidth(). */ |
| static |
| void _draw_hershey_string (vfontContext *vc, const pGEcontext gc, pGEDevDesc dd, |
| const unsigned short *string) |
| { |
| unsigned short c; |
| const unsigned short *ptr = string; |
| double charsize = 1.0; |
| int line_width_type = 0; /* 0,1,2 = unset,occidental,oriental */ |
| |
| while ((c = (*ptr++)) != '\0') |
| { |
| /* Check for the four possibilities: (1) a Hershey glyph specified by |
| glyph number, (2) an oriental Hershey glyph specified by glyph |
| number, (3) a control code, and (4) an ordinary font character, |
| which will be mapped to a Hershey glyph by one of the tables in |
| g_fontdb.c. */ |
| |
| if (c & RAW_HERSHEY_GLYPH) |
| { |
| if (line_width_type != 1) |
| { |
| gc->lwd = HERSHEY_LINE_WIDTH_TO_LWD (HERSHEY_STROKE_WIDTH); |
| line_width_type = 1; |
| } |
| _draw_hershey_glyph (vc, gc, dd, |
| c & GLYPH_SPEC, charsize, OCCIDENTAL, false); |
| } |
| |
| else if (c & RAW_ORIENTAL_HERSHEY_GLYPH) |
| { |
| if (line_width_type != 2) |
| { |
| gc->lwd = HERSHEY_LINE_WIDTH_TO_LWD (HERSHEY_STROKE_WIDTH); |
| line_width_type = 2; |
| } |
| _draw_hershey_glyph (vc, gc, dd, |
| c & GLYPH_SPEC, charsize, ORIENTAL, false); |
| } |
| |
| else if (c & CONTROL_CODE) |
| switch (c & ~CONTROL_CODE) /* parse control codes */ |
| { |
| case C_BEGIN_SUPERSCRIPT : |
| _draw_hershey_stroke (vc, gc, dd, |
| false, |
| SUPERSCRIPT_DX * charsize * HERSHEY_EM, |
| SUPERSCRIPT_DY * charsize * HERSHEY_EM); |
| charsize *= SCRIPTSIZE; |
| break; |
| |
| case C_END_SUPERSCRIPT: |
| charsize /= SCRIPTSIZE; |
| _draw_hershey_stroke (vc, gc, dd, |
| false, |
| - SUPERSCRIPT_DX * charsize * HERSHEY_EM, |
| - SUPERSCRIPT_DY * charsize * HERSHEY_EM); |
| break; |
| |
| case C_BEGIN_SUBSCRIPT: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, |
| SUBSCRIPT_DX * charsize * HERSHEY_EM, |
| SUBSCRIPT_DY * charsize * HERSHEY_EM); |
| charsize *= SCRIPTSIZE; |
| break; |
| |
| case C_END_SUBSCRIPT: |
| charsize /= SCRIPTSIZE; |
| _draw_hershey_stroke (vc, gc, dd, |
| false, |
| - SUBSCRIPT_DX * charsize * HERSHEY_EM, |
| - SUBSCRIPT_DY * charsize * HERSHEY_EM); |
| break; |
| |
| case C_PUSH_LOCATION: |
| /* saved_charsize = charsize; |
| saved_position_x = _plotter->drawstate->pos.x; |
| saved_position_y = _plotter->drawstate->pos.y; */ |
| break; |
| |
| case C_POP_LOCATION: |
| /* charsize = saved_charsize; |
| _plotter->fmove (R___(_plotter) |
| saved_position_x, saved_position_y); */ |
| break; |
| |
| case C_RIGHT_ONE_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, charsize * HERSHEY_EM, 0.0); |
| break; |
| |
| case C_RIGHT_HALF_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, charsize * HERSHEY_EM / 2.0, 0.0); |
| break; |
| |
| case C_RIGHT_QUARTER_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, charsize * HERSHEY_EM / 4.0, 0.0); |
| break; |
| |
| case C_RIGHT_SIXTH_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, charsize * HERSHEY_EM / 6.0, 0.0); |
| break; |
| |
| case C_RIGHT_EIGHTH_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, charsize * HERSHEY_EM / 8.0, 0.0); |
| break; |
| |
| case C_LEFT_ONE_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, - charsize * HERSHEY_EM, 0.0); |
| break; |
| |
| case C_LEFT_HALF_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, - charsize * HERSHEY_EM / 2.0, 0.0); |
| break; |
| |
| case C_LEFT_QUARTER_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, - charsize * HERSHEY_EM / 4.0, 0.0); |
| break; |
| |
| case C_LEFT_SIXTH_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, - charsize * HERSHEY_EM / 6.0, 0.0); |
| break; |
| |
| case C_LEFT_EIGHTH_EM: |
| _draw_hershey_stroke (vc, gc, dd, |
| false, - charsize * HERSHEY_EM / 8.0, 0.0); |
| break; |
| |
| /* unrecognized control code, punt */ |
| default: |
| break; |
| } |
| |
| else |
| /* yow, an actual font character! Several possibilities: could be |
| a composite (accented) character, could be a small Kana, or |
| could be a garden-variety character. */ |
| { |
| int raw_fontnum; |
| int glyphnum; /* glyph in Hershey array */ |
| int char_glyphnum, accent_glyphnum; /* for composite chars */ |
| int char_width, accent_width; /* for composite chars */ |
| const unsigned char *char_glyph, *accent_glyph; |
| unsigned char composite, character = '\0', accent = '\0' /* -Wall */; |
| bool oblique, small_kana = false; |
| |
| /* compute index of font, in font table in g_fontdb.c */ |
| raw_fontnum = (c >> FONT_SHIFT) & ONE_BYTE; |
| /* shear font? (for HersheySans-Oblique, etc.) */ |
| oblique = _hershey_font_info[raw_fontnum].obliquing; |
| |
| c &= ~FONT_SPEC; /* extract character proper */ |
| glyphnum = (_hershey_font_info[raw_fontnum].chars)[c]; |
| |
| if (glyphnum & KS) /* a small kana? */ |
| { |
| glyphnum -= KS; |
| small_kana = true; |
| } |
| |
| switch (glyphnum) |
| { |
| /* special case: this is a composite (accented) character; |
| search font table in g_fontdb.c for it */ |
| case ACC0: |
| case ACC1: |
| case ACC2: |
| composite = (unsigned char)c; |
| if (_composite_char (&composite, &character, &accent)) |
| { |
| char_glyphnum = |
| (_hershey_font_info[raw_fontnum].chars)[character]; |
| accent_glyphnum = |
| (_hershey_font_info[raw_fontnum].chars)[accent]; |
| } |
| else |
| { /* hope this won't happen */ |
| char_glyphnum = UNDE; |
| accent_glyphnum = 0; |
| } |
| char_glyph = |
| (const unsigned char *)_occidental_hershey_glyphs[char_glyphnum]; |
| accent_glyph = |
| (const unsigned char *)_occidental_hershey_glyphs[accent_glyphnum]; |
| |
| if (*char_glyph != '\0') /* nonempty glyph */ |
| /* 1st two chars are bounds, in Hershey units */ |
| char_width = (int)char_glyph[1] - (int)char_glyph[0]; |
| else |
| char_width = 0; |
| |
| if (*accent_glyph != '\0') /* nonempty glyph */ |
| /* 1st two chars are bounds, in Hershey units */ |
| accent_width = (int)accent_glyph[1] - (int)accent_glyph[0]; |
| else |
| accent_width = 0; |
| |
| /* draw the character */ |
| if (line_width_type != 1) |
| { |
| gc->lwd = HERSHEY_LINE_WIDTH_TO_LWD (HERSHEY_STROKE_WIDTH); |
| line_width_type = 1; |
| } |
| _draw_hershey_glyph (vc, gc, dd, |
| char_glyphnum, charsize, |
| OCCIDENTAL, oblique); |
| /* back up to draw accent */ |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| -0.5 * (double)char_width |
| -0.5 * (double)accent_width, |
| 0.0, charsize, oblique); |
| |
| /* repositioning for uppercase and uppercase italic */ |
| if (glyphnum == ACC1) |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| 0.0, |
| (double)(ACCENT_UP_SHIFT), |
| charsize, oblique); |
| else if (glyphnum == ACC2) |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| (double)(ACCENT_RIGHT_SHIFT), |
| (double)(ACCENT_UP_SHIFT), |
| charsize, oblique); |
| |
| /* draw the accent */ |
| _draw_hershey_glyph (vc, gc, dd, |
| accent_glyphnum, charsize, |
| OCCIDENTAL, oblique); |
| |
| /* undo special repositioning if any */ |
| if (glyphnum == ACC1) |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| 0.0, |
| -(double)(ACCENT_UP_SHIFT), |
| charsize, oblique); |
| else if (glyphnum == ACC2) |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| -(double)(ACCENT_RIGHT_SHIFT), |
| -(double)(ACCENT_UP_SHIFT), |
| charsize, oblique); |
| |
| /* move forward, to end composite char where we should */ |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| 0.5 * (double)char_width |
| -0.5 * (double)accent_width, |
| 0.0, charsize, oblique); |
| break; |
| |
| /* not a composite (accented) character; just an ordinary |
| glyph from occidental+Kana array (could be a Kana, in |
| particular, could be a small Kana) */ |
| default: |
| if (small_kana) |
| { |
| int kana_width; |
| const unsigned char *kana_glyph; |
| double shift = 0.5 * (1.0 - (SMALL_KANA_SIZE)); |
| |
| kana_glyph = |
| (const unsigned char *)_occidental_hershey_glyphs[glyphnum]; |
| kana_width = (int)kana_glyph[1] - (int)kana_glyph[0]; |
| |
| /* draw small Kana, preceded and followed by a penup |
| stroke in order to traverse the full width of an |
| ordinary Kana */ |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| shift * (double)kana_width, |
| 0.0, charsize, oblique); |
| if (line_width_type != 2) |
| { |
| gc->lwd = HERSHEY_LINE_WIDTH_TO_LWD (HERSHEY_STROKE_WIDTH); |
| line_width_type = 2; |
| } |
| _draw_hershey_glyph (vc, gc, dd, |
| glyphnum, |
| (SMALL_KANA_SIZE) * charsize, |
| OCCIDENTAL, oblique); |
| _draw_hershey_penup_stroke (vc, gc, dd, |
| shift * (double)kana_width, |
| 0.0, charsize, oblique); |
| } |
| else |
| /* whew! just an ordinary glyph from the occidental array |
| (could be a Kana however, since they're confusingly |
| placed in that array, at the end) */ |
| { |
| if (glyphnum >= BEGINNING_OF_KANA) |
| { |
| if (line_width_type != 2) |
| { |
| gc->lwd = HERSHEY_LINE_WIDTH_TO_LWD (HERSHEY_ORIENTAL_STROKE_WIDTH); |
| line_width_type = 2; |
| } |
| } |
| else |
| if (line_width_type != 1) |
| { |
| gc->lwd = HERSHEY_LINE_WIDTH_TO_LWD (HERSHEY_STROKE_WIDTH); |
| line_width_type = 1; |
| } |
| _draw_hershey_glyph (vc, gc, dd, |
| glyphnum, charsize, |
| OCCIDENTAL, oblique); |
| } |
| break; |
| } /* end of case statement that switches based on glyphnum */ |
| |
| } /* end of font character case */ |
| |
| } /* end of loop through unsigned shorts in the codestring */ |
| |
| return; |
| } |
| |
| /* retrieve the two elements of a composite character from the table in |
| g_fontdb.c */ |
| static bool _composite_char (unsigned char *composite, |
| unsigned char *character, |
| unsigned char *accent) |
| { |
| const struct plHersheyAccentedCharInfoStruct *compchar = _hershey_accented_char_info; |
| bool found = false; |
| unsigned char given = *composite; |
| |
| while (compchar->composite) |
| { |
| if (compchar->composite == given) |
| { |
| found = true; |
| /* return char and accent via pointers */ |
| *character = compchar->character; |
| *accent = compchar->accent; |
| } |
| compchar++; |
| } |
| |
| return found; |
| } |
| |