blob: 9d5a80f980f27e9d807ffdecdc1b01971d73430c [file] [log] [blame]
/* 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;
}