/*************************************************
*    The PMW Music Typesetter - 3rd incarnation  *
*************************************************/

/* Copyright (c) Philip Hazel, 1991 - 2020 */

/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: November 2020 */


/* This file contains code for supporting the "draw" directives in PMW. It
contains both the code for reading in the definition of a draw funtion, and the
code for executing it. */


#include "pmwhdr.h"
#include "readhdr.h"
#include "outhdr.h"


#define magic 0x74657874


/* Types of entry in draw items; note that the table of required data on the
stack (stack_rqd) in must be kept in step with this. Also remember to add any
new operators into the check_ptr() function. */

enum {
  dr_accleft, dr_add, dr_and,
  dr_barnumber, dr_bra,
  dr_copy, dr_currentgray, dr_currentlinewidth, dr_currentpoint, dr_curveto,
    dr_cvs,
  dr_def, dr_div, dr_draw, dr_dup,
  dr_end, dr_eq, dr_exch, dr_exit,
  dr_false, dr_fill, dr_fillretain, dr_fontsize,
  dr_gaptype, dr_ge, dr_gt,
  dr_headbottom, dr_headleft, dr_headright, dr_headtop,
  dr_if, dr_ifelse,
  dr_jump,
  dr_ket,
  dr_le, dr_leftbarx, dr_linebottom, dr_linegapx, dr_linegapy,
  dr_linelength, dr_lineto, dr_linetop, dr_loop, dr_lt,
  dr_magnification, dr_moveto, dr_mul,
  dr_ne, dr_neg, dr_not, dr_number,
  dr_or, dr_origin, dr_originx, dr_originy,
  dr_pagelength, dr_pagenumber, dr_pop, dr_pstack,
  dr_rcurveto, dr_repeat, dr_rlineto, dr_rmoveto, dr_roll,
  dr_setgray, dr_setlinewidth, dr_show, dr_stavesize,
  dr_stavespace, dr_stavestart, dr_stembottom, dr_stemtop,
  dr_stringwidth, dr_stroke, dr_sub, dr_systemdepth,
  dr_text, dr_topleft, dr_translate, dr_true,
  dr_varname, dr_varref,
  dr_xor
};

/* This table contains the number and type of entries required to be on the
stack for each operator. Up to 8 values are catered for (though the maximum
used is 6). Each entry uses one nibble, starting from the least significant end
of the word. The values are the dd_* values defined in pmwhdr, namely:

  dd_any      1   any type of data
  dd_number   2   number
  dd_text     3   string
  dd_code     4   code fragment
  dd_varname  5   variable name
*/

static usint stack_rqd[] = {
  0,                 /* accleft */
  0x00000022,        /* add */
  0x00000022,        /* and */
  0,                 /* barnumber */
  0,                 /* bra */
  0x00000001,        /* copy */
  0,                 /* currentgray */
  0,                 /* currentlinewidth */
  0,                 /* currentpoint */
  0x00222222,        /* curveto */
  0x00000023,        /* cvs */
  0x00000051,        /* def */
  0x00000022,        /* div */
  0,                 /* draw */
  0x00000001,        /* dup */
  0,                 /* end */
  0x00000022,        /* eq */
  0x00000011,        /* exch */
  0,                 /* exit */
  0,                 /* false */
  0,                 /* fill */
  0,                 /* fillretain */
  0x00000003,        /* fontsize */
  0,                 /* gaptype */
  0x00000022,        /* ge */
  0x00000022,        /* gt */
  0,                 /* headbottom */
  0,                 /* headleft */
  0,                 /* headright */
  0,                 /* headtop */
  0x00000024,        /* if */
  0x00000244,        /* ifelse */
  0,                 /* jump */
  0,                 /* ket */
  0x00000022,        /* le */
  0,                 /* leftbarx */
  0,                 /* linebottom */
  0,                 /* linegapx */
  0,                 /* linegapy */
  0,                 /* linelength */
  0x00000022,        /* lineto */
  0,                 /* linetop */
  0x00000004,        /* loop */
  0x00000022,        /* lt */
  0,                 /* magnification */
  0x00000022,        /* moveto */
  0x00000022,        /* mul */
  0x00000022,        /* ne */
  0x00000002,        /* neg */
  0x00000002,        /* not */
  0,                 /* number */
  0x00000022,        /* or */
  0,                 /* origin */
  0,                 /* originx */
  0,                 /* originy */
  0,                 /* pagelength */
  0,                 /* pagenumber */
  0x00000001,        /* pop */
  0,                 /* pstack: no checking done */
  0x00222222,        /* rcurveto */
  0x00000024,        /* repeat */
  0x00000022,        /* rlineto */
  0x00000022,        /* rmoveto */
  0x00000022,        /* roll */
  0x00000002,        /* setgray */
  0x00000002,        /* setlinewidth */
  0x00000003,        /* show */
  0,                 /* stavesize */
  0,                 /* stavespace */
  0,                 /* stavestart */
  0,                 /* stembottom */
  0,                 /* stemtop */
  0x00000003,        /* stringwidth */
  0,                 /* stroke */
  0x00000022,        /* sub */
  0,                 /* systemdepth */
  0,                 /* text */
  0,                 /* topleft */
  0x00000022,        /* translate */
  0,                 /* true */
  0,                 /* varname */
  0,                 /* varref */
  0x00000022         /* xor */
};




/*************************************************
*                Variables                       *
*************************************************/

static BOOL currentpoint;
static int gray;
static int level;
static int xp, yp, cp;
static drawitem draw_variables[MAX_DRAW_VARIABLE + 1];
static drawitem draw_stack[DRAW_STACKSIZE];



/*************************************************
*          Table of operators/variables etc      *
*************************************************/

typedef struct {
  const char *name;
  int  value;
} draw_op;

/* This table is used for compiling operators, and also for finding a name to
print in an error message. The names that don't correspond to operator names
are enclosed in <> so they don't affect the first usage. */

static draw_op draw_operators[] = {
  { "<number>",     dr_number },
  { "<text>",       dr_text },
  { "<name>",       dr_varname },
  { "<reference>",  dr_varref },
  { "accleft",      dr_accleft },
  { "add",          dr_add },
  { "and",          dr_and },
  { "barnumber",    dr_barnumber },
  { "copy",         dr_copy },
  { "currentgray",  dr_currentgray },
  { "currentlinewidth", dr_currentlinewidth },
  { "currentpoint", dr_currentpoint },
  { "curveto",      dr_curveto },
  { "cvs",          dr_cvs },
  { "def",          dr_def },
  { "div",          dr_div },
  { "dup",          dr_dup },
  { "eq",           dr_eq },
  { "exch",         dr_exch },
  { "exit",         dr_exit },
  { "false",        dr_false },
  { "fill",         dr_fill },
  { "fillretain",   dr_fillretain },
  { "fontsize",     dr_fontsize },
  { "gaptype",      dr_gaptype },
  { "ge",           dr_ge },
  { "gt",           dr_gt },
  { "headbottom",   dr_headbottom },
  { "headleft",     dr_headleft },
  { "headright",    dr_headright },
  { "headtop",      dr_headtop },
  { "if",           dr_if },
  { "ifelse",       dr_ifelse},
  { "le",           dr_le },
  { "leftbarx",     dr_leftbarx },
  { "linebottom",   dr_linebottom },
  { "linegapx",     dr_linegapx },
  { "linegapy",     dr_linegapy },
  { "linelength",   dr_linelength },
  { "lineto",       dr_lineto },
  { "linetop",      dr_linetop },
  { "loop",         dr_loop },
  { "lt",           dr_lt },
  { "magnification",dr_magnification},
  { "moveto",       dr_moveto },
  { "mul",          dr_mul },
  { "ne",           dr_ne },
  { "neg",          dr_neg },
  { "not",          dr_not },
  { "or",           dr_or },
  { "origin",       dr_origin },
  { "originx",      dr_originx },
  { "originy",      dr_originy },
  { "pagelength",   dr_pagelength },
  { "pagenumber",   dr_pagenumber },
  { "pop",          dr_pop },
  { "pstack",       dr_pstack },
  { "rcurveto",     dr_rcurveto },
  { "repeat",       dr_repeat },
  { "rlineto",      dr_rlineto },
  { "rmoveto",      dr_rmoveto },
  { "roll",         dr_roll },
  { "setgray",      dr_setgray },
  { "setlinewidth", dr_setlinewidth },
  { "show",         dr_show },
  { "stavesize",    dr_stavesize },
  { "stavespace",   dr_stavespace },
  { "stavestart",   dr_stavestart },
  { "stembottom",   dr_stembottom },
  { "stemtop",      dr_stemtop },
  { "stringwidth",  dr_stringwidth },
  { "stroke",       dr_stroke },
  { "sub",          dr_sub },
  { "systemdepth",  dr_systemdepth },
  { "topleft",      dr_topleft },
  { "translate",    dr_translate },
  { "true",         dr_true },
  { "xor",          dr_xor }
};

static int draw_operator_count = sizeof(draw_operators)/sizeof(draw_op);



/*************************************************
*         Print out the stack contents           *
*************************************************/

/* This function is called by "pstack" and is also used after some errors.

Arguments:
  s1         introductory string
  s2         terminating string

Returns:     nothing
*/

static void
do_pstack(const char *s1, const char *s2)
{
int i;
drawtextstr *d;
info_printf("%s", s1);
for (i = 0; i < out_drawstackptr; i++)
  {
  switch (draw_stack[i].dtype)
    {
    case dd_text:
    d = draw_stack[i].d.ptr;
    if (d->ident != magic)
      info_printf("<corrupt-text> ");
    else
      {
      info_printf("\"%s\"", d->text);
      if (d->xdelta != 0) info_printf("/%c%f", (d->xdelta > 0)? 'r':'l',
        d->xdelta);
      if (d->ydelta != 0) info_printf("/%c%f", (d->ydelta > 0)? 'u':'d',
        d->ydelta);
      if (d->size != 0) info_printf("/s%d", d->size + 1);
      if (d->rotate != 0) info_printf("/rot%f", d->rotate);
      if ((d->flags & text_box) != 0) info_printf("/box");
      if ((d->flags & text_centre) != 0) info_printf("/c");
      if ((d->flags & text_endalign) != 0) info_printf("/e");
      if ((d->flags & text_ring) != 0) info_printf("/ring");
      info_printf(" ");
      }
    break;

    case dd_code:
    info_printf("<code-block> ");
    break;

    default:
    info_printf("%f ", draw_stack[i].d.val);
    break;
    }
  }
info_printf("\n%s", s2);
}



/*************************************************
*        Read a draw text string                 *
*************************************************/

/* This is also called when reading arguments for draw calls. It reads the
string and parameters into a store block, and yields its address.

Arguments:  none
Returns:    pointer to a textstr
*/

drawtextstr *
read_draw_text(void)
{
drawtextstr *textptr = store_Xget(sizeof(drawtextstr));
textptr->ident = magic;  /* magic number to identify */
textptr->text = string_check(string_read());

textptr->xdelta = 0;
textptr->ydelta = 0;
textptr->flags = 0;
textptr->size = 0;
textptr->rotate = 0;

while (read_ch == '/')
  {
  int delta, size;
  next_ch();
  switch (read_ch)
    {
    case 'b':
    if (Ustrncmp(read_chptr, "ox", 2) == 0)
      {
      next_ch();
      next_ch();
      next_ch();
      textptr->flags |= text_box;
      }
    else error_moan(ERR10, "/box, /c, /e, /ring, /rot, or /s");
    break;

    case 'c':
    textptr->flags &= ~text_endalign;
    textptr->flags |= text_centre;
    next_ch();
    break;

    case 'd':
    next_ch();
    if (read_expect_integer(&delta, TRUE, TRUE)) textptr->ydelta -= delta;
    break;

    case 'e':
    textptr->flags &= ~text_centre;
    textptr->flags |= text_endalign;
    next_ch();
    break;

    case 'l':
    next_ch();
    if (read_expect_integer(&delta, TRUE, TRUE)) textptr->xdelta -= delta;
    break;

    case 'n':
    next_ch();
    if (read_ch == 'c') textptr->flags &= ~text_centre;
    else if (read_ch == 'e') textptr->flags &= ~text_endalign;
    else error_moan(ERR37, "/nc or /ne");
    next_ch();
    break;

    case 's':
    next_ch();
    if (read_expect_integer(&size, FALSE, FALSE))
      {
      if (--size < 0 || size >= MaxFontSizes)
        { error_moan(ERR39, MaxFontSizes); size = 0; }
      textptr->size = size;
      }
    break;

    case 'r':
    if (Ustrncmp(read_chptr, "ot", 2) == 0)
      {
      int rotate;
      next_ch();
      next_ch();
      next_ch();
      if (read_expect_integer(&rotate, TRUE, TRUE)) textptr->rotate = rotate;
      break;
      }
    else if (Ustrncmp(read_chptr, "ing", 3) == 0)
      {
      next_ch();
      next_ch();
      next_ch();
      next_ch();
      textptr->flags |= text_ring;
      break;
      }
    else
      {
      next_ch();
      if (read_expect_integer(&delta, TRUE, TRUE)) textptr->xdelta += delta;
      }
    break;

    case 'u':
    next_ch();
    if (read_expect_integer(&delta, TRUE, TRUE)) textptr->ydelta += delta;
    break;

    default:
    error_moan(ERR10, "/box, /c, /d, /e, /l, /r, /ring, /rot, /s, or /u");
    break;
    }
  }

return textptr;
}


/*************************************************
*                  Read a Draw function          *
*************************************************/

/* The function sets up a structure representing the function, and adds it to
the tree of draw functions.

Arguments: none
Returns:   nothing
*/

void
read_draw(void)
{
tree_node *drawnode = store_Xget(sizeof(tree_node));
drawitem *ptr;
int left;
int bracount = 0;

next_word();
if (read_word[0] == 0) { error_moan(ERR10, "name"); return; }

drawnode->name = store_copystring(read_word);
drawnode->data = store_Xget(DRAW_BLOCKSIZE);

ptr = (drawitem *)drawnode->data;
left = DRAW_BLOCKSIZE/sizeof(drawitem);

/* Loop to read the contents of the draw function */

for (;;)
  {
  int type = -1;
  int value = 0;
  void *pointer = NULL;

  sigch();

  /* Deal with numerical values; data put into "value" */

  if (isdigit(read_ch) || read_ch == '-' || read_ch == '+')
    {
    type = dr_number;
    (void)read_expect_integer(&value, TRUE, TRUE);
    }

  /* Deal with strings; data put into "pointer" */

  else if (read_ch == '\"')
    {
    type = dr_text;
    pointer = read_draw_text();
    }

  /* Deal with brackets; no data */

  else if (read_ch == '{') { next_ch(); bracount++; type = dr_bra; }
  else if (read_ch == '}')
    {
    next_ch();
    type = dr_ket;
    if (--bracount < 0) error_moan(ERR118, drawnode->name);
    }

  /* Deal with variable names; data put into "value" is the variable index */

  else if (read_ch == '/')
    {
    tree_node *node;
    type = dr_varname;
    next_ch();
    if (!isspace(read_ch)) next_word();
      else read_word[0] = 0;

    if (read_word[0] == 0) error_moan(ERR37, "Variable name"); else
      {
      node = Tree_Search(draw_variable_tree, read_word);
      if (node == NULL)
        {
        if (draw_nextvariable > MAX_DRAW_VARIABLE)
          {
          error_moan(ERR100, MAX_DRAW_VARIABLE + 1);
          draw_nextvariable--;
          }
        value = draw_nextvariable++;
        node = store_Xget(sizeof(tree_node));
        node->name = store_copystring(read_word);
        node->val[0] = value;
        Tree_InsertNode(&draw_variable_tree, node);
        }
      else value = node->val[0];
      }
    }

  /* Else it must be a command word */

  else if (isalpha(read_ch))
    {
    next_word();
    if (Ustrcmp(read_word, "enddraw") == 0)
      {
      ptr->d.val = dr_end;
      break;
      }

    /* Deal with "subroutine" call; value put into "pointer" */

    if (Ustrcmp(read_word, "draw") == 0)
      {
      tree_node *node;
      type = dr_draw;
      next_word();
      node = Tree_Search(draw_tree, read_word);
      if (node == NULL) error_moan(ERR70, read_word); else pointer = node;
      }

    /* Deal with normal operators and variables */

    else
      {
      draw_op *first = draw_operators;
      draw_op *last = first + draw_operator_count;

      /* Search for a standard variable or operator; if found, the type is set
      but there is no data. */

      while (last > first)
        {
        int c;
        draw_op *middle = first + (last - first)/2;
        c = Ustrcmp(middle->name, read_word);
        if (c == 0) { type = middle->value; break; }
        if (c > 0) last = middle; else first = middle + 1;
        }
      }

    /* If haven't matched a standard variable or operator, try for a user
    variable. If found, the variable number is put into "value". */

    if (type < 0)
      {
      tree_node *node = Tree_Search(draw_variable_tree, read_word);
      if (node != NULL)
        {
        type = dr_varref;
        value = node->val[0];
        }
      }

    /* Grumble if unrecognized word */

    if (type < 0) error_moan(ERR69, read_word);
    }

  /* Grumble if unrecognized input; error 10 skips to end of line */

  else
    {
    error_moan(ERR10, "number, string, name, or curly bracket");
    continue;
    }

  /* Extend to new block if necessary */

  if (left < 4)
    {
    (ptr++)->d.val = dr_jump;
    ptr->d.ptr = store_Xget(DRAW_BLOCKSIZE);
    ptr = (drawitem *)(ptr->d.ptr);
    left = DRAW_BLOCKSIZE/sizeof(drawitem);
    }

  /* Add this item to the "program". Numbers, variable names, and variable
  references have a numerical argument; strings and draw subroutine calls have
  an address argument. */

  (ptr++)->d.val = type;
  left--;
  if (type == dr_number || type == dr_varname || type == dr_varref)
    {
    (ptr++)->d.val = value;
    left--;
    }
  else if (type == dr_text || type == dr_draw)
    {
    (ptr++)->d.ptr = pointer;
    left--;
    }
  }

/* Insert into tree; give error if duplicate */

if (!Tree_InsertNode(&draw_tree, drawnode)) error_moan(ERR14, drawnode->name);
}


/*************************************************
*     Generate an error while drawing            *
*************************************************/

/*
Arguments:
  n          the error number
  s          a text string to pass to the error function
  t          the current drawing function node (for the name)

Returns:     nothing
*/

static void
draw_error(int n, const char *s, tree_node *t)
{
uschar buff[80];
if (out_stave < 0)
  Ustrcpy(buff, "in a heading or footing");
else
  format_sprintf(buff, "in bar %b of stave %d%M", out_bar, out_stave);
error_moan(n, s, t->name, buff);
}



/*************************************************
*      Scale for a normal or grace/cue note      *
*************************************************/

/* This function is called for dimensions that are relative to the current
note. For normal notes, it just returns its argument. Otherwise, it scales
appropriately for cue notes and grace notes.

Argument:  the dimension to be scaled
Returns:   the scaled dimension
*/

static int
cuegrace_scale(int value)
{
/* Not grace note */
if (n_length != 0)
  {
  return ((n_flags & nf_cuesize) == 0)? value :
    mac_muldiv(value, (curmovt->fontsizes)->fontsize_cue, 10000);
  }

/* Grace note */
else
  {
  int size = ((n_flags & nf_cuesize) != 0)?
    (curmovt->fontsizes)->fontsize_cuegrace :
    (curmovt->fontsizes)->fontsize_grace;
  return mac_muldiv(value, size, 10000);
  }
}


/*************************************************
*   Set up an overdraw saved block for graphic   *
*************************************************/

/* This function saves the parameters of a graphic so that it can be drawn at
the end of the stave (overdrawn).

Arguments:
  thickness      the line thickness
  x              vector of x coordinates
  y              vector of y coordinates
  c              vector of control verbs (move, draw, etc)

Returns:         nothing
*/

static void
setup_overdraw(int thickness, int *x, int *y, int *c)
{
int *pp;
overdrawstr *last = out_overdraw;
overdrawstr *new = store_Xget(sizeof(overdrawstr) + sizeof(int)*(3*cp));
if (last == NULL) out_overdraw = new; else
  {
  while (last->next != NULL) last = last->next;
  last->next = new;
  }
new->next = NULL;
new->texttype = FALSE;
new->d.g.gray = gray;
new->d.g.linewidth = thickness;
new->d.g.ystave = out_ystave;
new->d.g.count = cp;
pp = &(new->d.g.data[0]);
memcpy(pp, x, cp*sizeof(int));
memcpy(pp+cp, y, cp*sizeof(int));
memcpy(pp+cp+cp, c, cp*sizeof(int));
}


/*************************************************
*       Check validity of program pointer        *
*************************************************/

/* This function checks the validity of the argument of a conditional or
looping command, which is a sequence of draw commands, ending at the current
command pointer.

Arguments:
  p          the pointer to the conditional or looping command
  t          the node of the draw subfunction argument start

Returns:     TRUE if all is well; FALSE otherwise
*/

static BOOL
check_ptr(drawitem *p, tree_node *t)
{
drawitem *pp = (drawitem *)t->data;
while (pp != p && pp->d.val != dr_end)
  {
  switch ((pp++)->d.val)
    {
    case dr_jump:
    pp = (drawitem *)(pp->d.ptr);
    break;

    case dr_draw:        /* These have data in the stream, so skip one */
    case dr_number:
    case dr_text:
    case dr_varname:
    case dr_varref:
    pp++;
    break;

    case dr_accleft:     /* These have no data */
    case dr_add:
    case dr_and:
    case dr_barnumber:
    case dr_bra:
    case dr_copy:
    case dr_currentgray:
    case dr_currentlinewidth:
    case dr_currentpoint:
    case dr_curveto:
    case dr_cvs:
    case dr_def:
    case dr_div:
    case dr_dup:
    case dr_end:
    case dr_eq:
    case dr_exch:
    case dr_exit:
    case dr_false:
    case dr_fill:
    case dr_fillretain:
    case dr_fontsize:
    case dr_gaptype:
    case dr_ge:
    case dr_gt:
    case dr_headbottom:
    case dr_headleft:
    case dr_headright:
    case dr_headtop:
    case dr_if:
    case dr_ifelse:
    case dr_ket:
    case dr_le:
    case dr_leftbarx:
    case dr_linebottom:
    case dr_linegapx:
    case dr_linegapy:
    case dr_linelength:
    case dr_lineto:
    case dr_linetop:
    case dr_loop:
    case dr_lt:
    case dr_magnification:
    case dr_moveto:
    case dr_mul:
    case dr_ne:
    case dr_neg:
    case dr_not:
    case dr_or:
    case dr_origin:
    case dr_originx:
    case dr_originy:
    case dr_pagelength:
    case dr_pagenumber:
    case dr_pop:
    case dr_pstack:
    case dr_rcurveto:
    case dr_repeat:
    case dr_rlineto:
    case dr_rmoveto:
    case dr_roll:
    case dr_setgray:
    case dr_setlinewidth:
    case dr_show:
    case dr_stavesize:
    case dr_stavespace:
    case dr_stavestart:
    case dr_stembottom:
    case dr_stemtop:
    case dr_stringwidth:
    case dr_stroke:
    case dr_sub:
    case dr_systemdepth:
    case dr_topleft:
    case dr_translate:
    case dr_true:
    case dr_xor:
    break;

    default:
      {
      char buff[40];
      sprintf(buff, " (bad value %d) ", pp[-1].d.val);
      draw_error(ERR117, buff, t);
      }
    return FALSE;
    }
  }

if (p == pp) return TRUE;
draw_error(ERR117, " (ended too soon) ", t);
return FALSE;
}


/*************************************************
*   Interpret a draw function - recursive call   *
*************************************************/

/* This recursive function is called from the external interface.

Arguments:
  t           the node for the function
  p           vector of draw commands; if NULL, take from node pointer
  x           vector for storing x path coordinates
  y           vector for storing y path coordinates
  c           vector for storing path drawing commands
  overflag    true if drawing is to overprint the stave

Returns:      TRUE if all is well; FALSE on error
*/

static BOOL
sub_draw(tree_node *t, drawitem *p, int *x, int *y, int *c, BOOL overflag)
{
if (p == NULL) p = (drawitem *)t->data;
if (level > 20) draw_error(ERR84, " ", t);

while (p->d.val != dr_end)
  {
  int errornumber = -1;
  if (out_drawstackptr > DRAW_STACKSIZE - 4)
    errornumber = ERR121;
  else
    {
    int cc = 0;
    usint xx = stack_rqd[p->d.val];
    while (xx != 0)
      {
      cc++;
      if (out_drawstackptr < cc)
        {
        errornumber = ERR71;    /* Insufficient items on stack */
        break;
        }
      if ((xx & 0x0f) != dd_any &&
          (xx & 0x0f) != draw_stack[out_drawstackptr - cc].dtype)
        {
        errornumber = ERR6;     /* Wrong type of item on stack */
        break;
        }
      xx >>= 4;
      }
    }

  if (errornumber >= 0)
    {
    int i;
    uschar *s = US"???";
    for (i = 0; i < draw_operator_count; i++)
      if (draw_operators[i].value == p->d.val)
        { s = US draw_operators[i].name; break; }
    draw_error(errornumber, CS s, t);
    do_pstack("** Draw stack contents when error detected:\n", "\n");
    return FALSE;
    }

  switch ((p++)->d.val)
    {
    case dr_jump:
    p = (drawitem *)(p->d.ptr);
    break;

    case dr_bra:
      {
      int count = 1;
      draw_stack[out_drawstackptr].dtype = dd_code;
      draw_stack[out_drawstackptr++].d.ptr = p;
      while (p->d.val != dr_end && (p->d.val != dr_ket || --count > 0))
        {
        int type = (p++)->d.val;
        if (type == dr_jump) p = (drawitem *)(p->d.ptr);
        else if (type == dr_bra) count++;
        else if (type == dr_draw || type == dr_varref || type == dr_number ||
          type == dr_text || type == dr_varname) p++;
        }
      if (p->d.val != dr_ket) draw_error(ERR116, " ", t); else p++;
      }
    break;

    case dr_ket:
    return TRUE;

    case dr_if:
      {
      drawitem *pp = draw_stack[--out_drawstackptr].d.ptr;
      if (!check_ptr(pp, t)) return FALSE;
      if (draw_stack[--out_drawstackptr].d.val != 0 &&
        !sub_draw(t, pp, x, y, c, overflag)) return FALSE;
      }
    break;

    case dr_ifelse:
      {
      drawitem *pp2 = draw_stack[--out_drawstackptr].d.ptr;
      drawitem *pp1 = draw_stack[--out_drawstackptr].d.ptr;
      if (!check_ptr(pp1, t) || !check_ptr(pp2, t)) return FALSE;
      if (!sub_draw(t, (draw_stack[--out_drawstackptr].d.val != 0)? pp1 : pp2,
           x, y, c, overflag))
        return FALSE;
      }
    break;

    case dr_number:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = (p++)->d.val;
    break;

    case dr_text:
    draw_stack[out_drawstackptr].dtype = dd_text;
    draw_stack[out_drawstackptr++].d.ptr = (p++)->d.ptr;
    break;

    case dr_varname:
    draw_stack[out_drawstackptr].dtype = dd_varname;
    draw_stack[out_drawstackptr++].d.val = (p++)->d.val;
    break;

    case dr_varref:
    draw_stack[out_drawstackptr++] = draw_variables[(p++)->d.val];
    break;

    case dr_accleft:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = n_maxaccleft;
    break;

    case dr_add:
    draw_stack[out_drawstackptr-2].d.val +=
      draw_stack[out_drawstackptr-1].d.val;
    out_drawstackptr--;
    break;

    case dr_and:
      {
      int a1 = draw_stack[--out_drawstackptr].d.val / 1000;
      int a2 = draw_stack[--out_drawstackptr].d.val / 1000;
      draw_stack[out_drawstackptr++].d.val = (a1 & a2)*1000;
      }
    break;

    case dr_barnumber:
      {
      int a = 0, b = 0;
      uschar buff[20];
      format_movt = curmovt;
      format_sprintf(buff, "%b", out_bar);
      sscanf(CS buff, "%d.%d", &a, &b);
      if (b < 10) b *= 100; else if (b < 100) b *= 10;
      draw_stack[out_drawstackptr].dtype = dd_number;
      draw_stack[out_drawstackptr++].d.val = a * 1000 + b;
      }
    break;

    case dr_copy:
      {
      int count = draw_stack[--out_drawstackptr].d.val / 1000;
      if (out_drawstackptr < count)
        {
        draw_error(ERR71, "copy", t);
        break;
        }
      memcpy(draw_stack + out_drawstackptr,
        draw_stack + out_drawstackptr - count, count * sizeof(drawitem));
      out_drawstackptr += count;
      }
    break;

    case dr_currentgray:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = gray;
    break;

    case dr_currentlinewidth:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = draw_thickness;
    break;

    case dr_currentpoint:
    if (cp <= 0) draw_error(ERR72, "currentpoint", t);
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = x[xp-1] - draw_ox;
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = y[yp-1] - draw_oy;
    break;

    case dr_curveto:
      {
      int i;
      if (!currentpoint) draw_error(ERR72, "curveto", t);
      for (i = 6; i >= 2; i -= 2)
        {
        x[xp++] = draw_ox + draw_stack[out_drawstackptr - i].d.val;
        y[yp++] = draw_oy + draw_stack[out_drawstackptr - i + 1].d.val;
        }
      out_drawstackptr -= 6;
      c[cp++] = path_curve;
      }
    break;

    case dr_cvs:
      {
      drawtextstr *d = (drawtextstr *)(draw_stack[--out_drawstackptr].d.ptr);
      int n = draw_stack[--out_drawstackptr].d.val;
      if (d->ident != magic) draw_error(ERR82, "cvs", t);
      format_sprintf(d->text, "%f", n);
      draw_stack[out_drawstackptr].dtype = dd_text;
      draw_stack[out_drawstackptr++].d.ptr = d;
      }
    break;

    case dr_def:
      {
      int n = draw_stack[out_drawstackptr-2].d.val;
      if (n < 0 || n > MAX_DRAW_VARIABLE) draw_error(ERR101, "", t);
        else draw_variables[n] = draw_stack[out_drawstackptr-1];
      }
    out_drawstackptr -= 2;
    break;

    case dr_div:
    if (draw_stack[out_drawstackptr-1].d.val == 0)
      { draw_error(ERR95, "", t); }
    else
     {
     draw_stack[out_drawstackptr-2].d.val =
        mac_muldiv(draw_stack[out_drawstackptr-2].d.val, 1000,
          draw_stack[out_drawstackptr-1].d.val);
     out_drawstackptr--;
     }
    break;

    case dr_draw:
    level++;
    (void)sub_draw((tree_node *)((p++)->d.ptr), NULL, x, y, c, overflag);
    level--;
    break;

    case dr_dup:
    draw_stack[out_drawstackptr] = draw_stack[out_drawstackptr-1];
    out_drawstackptr++;
    break;

    case dr_eq:
    draw_stack[out_drawstackptr-2].d.val =
      (draw_stack[out_drawstackptr-2].d.val ==
        draw_stack[out_drawstackptr-1].d.val)?
          1000 : 0;
    out_drawstackptr--;
    break;

    case dr_exch:
      {
      drawitem temp = draw_stack[out_drawstackptr-1];
      draw_stack[out_drawstackptr-1] = draw_stack[out_drawstackptr-2];
      draw_stack[out_drawstackptr-2] = temp;
      }
    break;

    case dr_exit:
    return FALSE;

    case dr_fill:
    if (!currentpoint) draw_error(ERR72, "fill", t);
    c[cp++] = path_end;
    if (overflag) setup_overdraw(-1, x, y, c); else ps_path(x, y, c, -1);
    cp = xp = yp = 0;
    currentpoint = FALSE;
    break;

    case dr_fillretain:
    if (!currentpoint) draw_error(ERR72, "fillretain", t);
    c[cp++] = path_end;
    if (overflag) setup_overdraw(-1, x, y, c); else ps_path(x, y, c, -1);
    break;

    case dr_fontsize:
      {
      drawtextstr *d = (drawtextstr *)(draw_stack[--out_drawstackptr].d.ptr);
      if (d->ident != magic) draw_error(ERR82, "fontsize", t);
      draw_stack[out_drawstackptr].dtype = dd_number;
      draw_stack[out_drawstackptr++].d.val =
        ((curmovt->fontsizes)->fontsize_text)[d->size];
      }
    break;

    case dr_false:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = 0;
    break;

    case dr_gaptype:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = draw_gap;
    break;

    case dr_ge:
    draw_stack[out_drawstackptr-2].d.val =
      (draw_stack[out_drawstackptr-2].d.val >=
        draw_stack[out_drawstackptr-1].d.val)? 1000 : 0;
    out_drawstackptr--;
    break;

    case dr_gt:
    draw_stack[out_drawstackptr-2].d.val =
      (draw_stack[out_drawstackptr-2].d.val >
        draw_stack[out_drawstackptr-1].d.val)?
          1000 : 0;
    out_drawstackptr--;
    break;

    case dr_headbottom:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val =
      (n_minpitch - 128)*main_stavemagn - cuegrace_scale(2*main_stavemagn);
    break;

    case dr_headleft:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = n_invertleft?
      cuegrace_scale(6*main_stavemagn) : 0;
    break;

    case dr_headright:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val =
      cuegrace_scale((n_invertright? 12 : 6)*main_stavemagn);
    break;

    case dr_headtop:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val =
      (n_maxpitch - 128)*main_stavemagn + cuegrace_scale(2*main_stavemagn);
    break;

    case dr_le:
    draw_stack[out_drawstackptr-2].d.val =
      (draw_stack[out_drawstackptr-2].d.val <=
        draw_stack[out_drawstackptr-1].d.val)?
          1000 : 0;
    out_drawstackptr--;
    break;

    case dr_leftbarx:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr].d.val = out_lastbarlinex - draw_ox;
    if (out_startlinebar) draw_stack[out_drawstackptr].d.val -= 6000;
    out_drawstackptr++;
    break;

    case dr_linebottom:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val =
      (n_minpitch & 2)? 2*main_stavemagn : 0;
    break;

    case dr_linegapx:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = draw_lgx;
    break;

    case dr_linegapy:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = draw_lgy;
    break;

    case dr_linelength:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = curmovt->linelength;
    break;

    case dr_lineto:
    if (!currentpoint) draw_error(ERR72, "lineto", t);
    y[yp++] = draw_oy + draw_stack[--out_drawstackptr].d.val;
    x[xp++] = draw_ox + draw_stack[--out_drawstackptr].d.val;
    c[cp++] = path_line;
    break;

    case dr_linetop:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val =
      (n_maxpitch & 2)? 2*main_stavemagn : 0;
    break;

    case dr_loop:
      {
      int count = 1000;
      drawitem *pp = draw_stack[--out_drawstackptr].d.ptr;
      if (!check_ptr(pp, t)) return FALSE;
      while (count-- > 0 && sub_draw(t, pp, x, y, c, overflag));
      }
    break;

    case dr_lt:
    draw_stack[out_drawstackptr-2].d.val =
      (draw_stack[out_drawstackptr-2].d.val <
        draw_stack[out_drawstackptr-1].d.val)?
          1000 : 0;
    out_drawstackptr--;
    break;

    case dr_magnification:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = main_magnification;
    break;

    case dr_moveto:
    y[yp++] = draw_oy + draw_stack[--out_drawstackptr].d.val;
    x[xp++] = draw_ox + draw_stack[--out_drawstackptr].d.val;
    c[cp++] = path_move;
    currentpoint = TRUE;
    break;

    case dr_mul:
    draw_stack[out_drawstackptr-2].d.val =
      mac_muldiv(draw_stack[out_drawstackptr-1].d.val,
        draw_stack[out_drawstackptr-2].d.val, 1000);
    out_drawstackptr--;
    break;

    case dr_ne:
    draw_stack[out_drawstackptr-2].d.val =
      (draw_stack[out_drawstackptr-2].d.val !=
        draw_stack[out_drawstackptr-1].d.val)?
          1000 : 0;
    out_drawstackptr--;
    break;

    case dr_neg:
    draw_stack[out_drawstackptr-1].d.val =
      -draw_stack[out_drawstackptr-1].d.val;
    break;

    case dr_not:
    draw_stack[out_drawstackptr-1].d.val =
      (~(draw_stack[out_drawstackptr-1].d.val/1000))*1000;
    break;

    case dr_or:
      {
      int a1 = draw_stack[--out_drawstackptr].d.val / 1000;
      int a2 = draw_stack[--out_drawstackptr].d.val / 1000;
      draw_stack[out_drawstackptr++].d.val = (a1 | a2)*1000;
      }
    break;

    case dr_origin:
    case dr_originx:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = draw_ox;
    break;

    case dr_originy:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = draw_oy;
    break;

    case dr_pagelength:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = main_pagelength;
    break;

    case dr_pagenumber:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = curpage->number * 1000;
    break;

    case dr_topleft:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = - draw_ox;
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val =   out_ystave - draw_oy;
    break;

    case dr_pop:
    out_drawstackptr--;
    break;

    case dr_pstack:
    do_pstack("Draw stack contents (pstack command):\n", "");
    break;

    case dr_rcurveto:
      {
      int i;
      if (!currentpoint) draw_error(ERR72, "rcurveto", t);
      for (i = 0; i <= 2; i++)
        {
        x[xp+i] = x[xp-1] + draw_stack[out_drawstackptr + 2*i - 6].d.val;
        y[yp+i] = y[yp-1] + draw_stack[out_drawstackptr + 2*i - 5].d.val;
        }
      xp += 3;
      yp += 3;
      out_drawstackptr -= 6;
      c[cp++] = path_curve;
      }
    break;

    case dr_repeat:
      {
      drawitem *pp = draw_stack[--out_drawstackptr].d.ptr;
      int count = draw_stack[--out_drawstackptr].d.val / 1000;
      if (!check_ptr(pp, t)) return FALSE;
      while (count-- > 0 && sub_draw(t, pp, x, y, c, overflag));
      }
    break;

    case dr_rlineto:
    if (!currentpoint) draw_error(ERR72, "rlineto", t);
    y[yp] = y[yp-1] + draw_stack[--out_drawstackptr].d.val;
    yp++;
    x[xp] = x[xp-1] + draw_stack[--out_drawstackptr].d.val;
    xp++;
    c[cp++] = path_line;
    break;

    case dr_rmoveto:
    if (!currentpoint) draw_error(ERR72, "rmoveto", t);
    y[yp] = y[yp-1] + draw_stack[--out_drawstackptr].d.val;
    yp++;
    x[xp] = x[xp-1] + draw_stack[--out_drawstackptr].d.val;
    xp++;
    c[cp++] = path_move;
    break;

    case dr_roll:
      {
      int i, j;
      int amount = (draw_stack[--out_drawstackptr].d.val)/1000;
      int count = (draw_stack[--out_drawstackptr].d.val)/1000;

      if (out_drawstackptr < count) draw_error(ERR71, "roll", t);

      if (amount > 0) for (i = 0; i < amount; i++)
        {
        drawitem temp = draw_stack[out_drawstackptr - 1];
        for (j = 1; j < count; j++)
          draw_stack[out_drawstackptr-j] = draw_stack[out_drawstackptr-j-1];
        draw_stack[out_drawstackptr - count] = temp;
        }

      else for (i = 0; i < -amount; i++)
        {
        drawitem temp = draw_stack[out_drawstackptr - count];
        for (j = count; j > 1; j--)
          draw_stack[out_drawstackptr-j] = draw_stack[out_drawstackptr-j+1];
        draw_stack[out_drawstackptr - 1] = temp;
        }
      }
    break;

    case dr_setgray:
    gray = draw_stack[--out_drawstackptr].d.val;
    ps_setgray(gray);
    break;

    case dr_setlinewidth:
    draw_thickness = mac_muldiv(draw_stack[--out_drawstackptr].d.val,
      main_stavemagn, 1000);
    break;

    case dr_stringwidth:
    case dr_show:
      {
      int *matrix;
      int xx, yy, width, flags, unscaled_fontsize, fontsize;
      int boxring = 0;

      drawtextstr *d = draw_stack[--out_drawstackptr].d.ptr;
      if (d->ident != magic) draw_error(ERR82, "show", t);
      unscaled_fontsize = ((curmovt->fontsizes)->fontsize_text)[d->size];
      fontsize = mac_muldiv(main_stavemagn, unscaled_fontsize, 1000);
      matrix = ((curmovt->fontsizes)->fontmatrix_text)[d->size];
      if (matrix != NULL) memcpy(font_transform, matrix, 4*sizeof(int));
      if (d->rotate) font_rotate(d->rotate);

      width = string_width(d->text, font_rm, fontsize);

      /* If stringwidth, just return values */

      if (p[-1].d.val == dr_stringwidth)
        {
        draw_stack[out_drawstackptr].dtype = dd_number;
        draw_stack[out_drawstackptr++].d.val = width;
        draw_stack[out_drawstackptr].dtype = dd_number;
        draw_stack[out_drawstackptr++].d.val = font_stringheight;
        }

      /* Else carry on to do the showing */

      else
        {
        if (!currentpoint) draw_error(ERR72, "show", t);
        xx = x[xp-1] + d->xdelta;
        yy = y[yp-1] + d->ydelta;

        flags = d->flags;
        boxring = flags & (text_box | text_ring);

        if ((flags & text_centre) != 0)
          {
          xx -= width/2;
          yy -= font_stringheight/2;
          }
        else if ((flags & text_endalign) != 0)
          {
          xx -= width;
          yy -= font_stringheight;
          }
        else
          {
          y[yp++] = yy + font_stringheight;
          x[xp++] = xx + width;
          c[cp++] = path_move;
          }

        if (overflag)
          {
          overdrawstr *last = out_overdraw;
          overdrawstr *new = store_Xget(sizeof(overdrawstr));
          if (last == NULL) out_overdraw = new; else
            {
            while (last->next != NULL) last = last->next;
            last->next = new;
            }
          new->next = NULL;
          new->texttype = TRUE;
          new->d.t.text = d->text;
          new->d.t.fontsize = fontsize;
          new->d.t.boxring = boxring;
          new->d.t.xx = xx;
          new->d.t.yy = out_ystave - yy;
          memcpy(new->d.t.matrix, font_transform, 4*sizeof(int));
          }
        else
          out_string(d->text, font_rm, fontsize, xx, out_ystave - yy, boxring);
        }
      font_reset();
      }
    break;

    case dr_stavesize:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = main_stavemagn;
    break;

    case dr_stavestart:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = (out_stave < 0)? 0 :
      (out_sysblock->startxposition + out_sysblock->xjustify - draw_ox);
    break;

    case dr_stavespace:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val =
      out_sysblock->stavespacing[out_stave];
    break;

    case dr_stembottom:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr].d.val = (n_minpitch - 130)*1000;
    if ((n_flags & (nf_stem | nf_stemup)) == nf_stem)
      draw_stack[out_drawstackptr].d.val -=
        cuegrace_scale(12000 + n_stemlength);
    draw_stack[out_drawstackptr].d.val =
      mac_muldiv(draw_stack[out_drawstackptr].d.val, main_stavemagn, 1000);
    out_drawstackptr++;
    break;

    case dr_stemtop:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr].d.val = (n_maxpitch - 126)*1000;
    if ((n_flags & (nf_stem | nf_stemup)) == (nf_stem | nf_stemup))
      draw_stack[out_drawstackptr].d.val += cuegrace_scale(12000+ n_stemlength);
    draw_stack[out_drawstackptr].d.val =
      mac_muldiv(draw_stack[out_drawstackptr].d.val, main_stavemagn, 1000);
    out_drawstackptr++;
    break;

    /*** Code is common with dr_show above
    case dr_stringwidth:
    ***/

    case dr_stroke:
    if (!currentpoint) draw_error(ERR72, "stroke", t);
    c[cp++] = path_end;
    if (overflag) setup_overdraw(draw_thickness, x, y, c);
      else ps_path(x, y, c, draw_thickness);
    cp = xp = yp = 0;
    currentpoint = FALSE;
    break;

    case dr_systemdepth:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = out_sysblock->systemdepth;
    break;

    case dr_sub:
    draw_stack[out_drawstackptr-2].d.val -=
      draw_stack[out_drawstackptr-1].d.val;
    out_drawstackptr--;
    break;

    case dr_translate:
    draw_oy += draw_stack[--out_drawstackptr].d.val;
    draw_ox += draw_stack[--out_drawstackptr].d.val;
    break;

    case dr_true:
    draw_stack[out_drawstackptr].dtype = dd_number;
    draw_stack[out_drawstackptr++].d.val = 1000;
    break;

    case dr_xor:
      {
      int a1 = draw_stack[--out_drawstackptr].d.val / 1000;
      int a2 = draw_stack[--out_drawstackptr].d.val / 1000;
      draw_stack[out_drawstackptr++].d.val = (a1 ^ a2)*1000;
      }
    break;
    }
  }

return TRUE;
}


/*************************************************
*    Prime stack and interpret a draw function   *
*************************************************/

/* This is the external interface to the drawing action.

Arguments:
  t           the node of the drawing function
  args        vector of arguments
  overflag    TRUE if drawing is to be saved till after the stave is done

Returns:      nothing
*/

void
out_dodraw(tree_node *t, drawitem *args, BOOL overflag)
{
int x[100];
int y[100];
int c[100];

if (args != NULL)
  {
  int i;
  for (i = 1; i <= args[0].d.val; i++) draw_stack[out_drawstackptr++] = args[i];
  }

xp = yp = cp = level = gray = 0;
currentpoint = FALSE;
ps_setgray(0);
(void)sub_draw(t, NULL, x, y, c, overflag);
ps_setgray(0);
}

/* End of setdraw.c */
