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

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

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


/* This file contains code for outputting a page */


#include "pmwhdr.h"
#include "pagehdr.h"
#include "outhdr.h"



/*************************************************
*              Output one string                 *
*************************************************/

/* We return the font which is current at the end of the string. If small caps
are set, font_sc (which is larger than any font number) is added to the font.
This is interpreted on input as well. The y values are absolute positions
downwards from the top of the page.

Arguments:
  s           the string
  f           the font in which to start
  pointsize   the point size
  x           x-coordinate for the start
  y           y-coordinate for the start
  boxring     flags: text_box or text-ring for boxed or ringed text, else 0

Returns:      the font which is current at the end
*/

int
out_string(uschar *s, int f, int pointsize, int x, int y, int boxring)
{
int i = 0;
int xstart = x;
int ystart = y;
int orig_pointsize = pointsize;
fontstr *fs;
register int c;
uschar ss[256];

if (f >= font_sc)
  {
  pointsize = (pointsize * curmovt->smallcapsize) / 1000;
  f -= font_sc;
  }
fs = &(font_List[font_table[f]]);

/* Process the characters in the string */

for (;;)
  {
  int nf, r;
  uschar xs[80];

  GETCHARINC(c, s);
  if (c == 0) break;
  
  /* If the string buffer is getting full, output the string so far. This
  leaves plenty of room for escapes (which in practice are only a few bytes
  long). */

  if (i > 250)
    {
    ss[i] = 0;
    ps_string(ss, f, pointsize, &x, &y, TRUE);
    i = 0;
    }

  /* Until we hit the escape character, '\', simply copy chars, except
  for quote and grave, which turn into closing and opening quotes, and for fi,
  which ligatures, for standardly encoded fonts. */

  if (c != '\\')
    {
    uschar utf[8];
    if (fs->stdencoding)
      {
      if (c == '`') c = QUOTE_LEFT;
      else if (c == '\'') c = QUOTE_RIGHT;
      else if (c == 'f' && *s == 'i' && !fs->fixedpitch && fs->hasfi)
        {
        c = CHAR_FI;
        s++;
        }
      }   
    utf[misc_ord2utf8(c, utf)] = 0;
    Ustrcpy(ss + i, utf);
    i += Ustrlen(utf);
    continue;
    }

  /* Interpret the escape. It may return a string to print and/or a font
  change, and a flag to say whether the font change is permanent or not */

  s = string_escape(s, xs, &nf, &r);

  /* If there is a font change, first output the string so far in the old font
  (if there is any string). */

  if (nf >= 0)
    {
    if (i)
      {
      ss[i] = 0;
      ps_string(ss, f, pointsize, &x, &y, TRUE);
      i = 0;
      }

    /* If the new font is temporary, output the escaped string and mark it as
    empty. Otherwise, change the current font. */

    if (r)
      {
      if (xs[0])
        {
        ps_string(xs, nf, pointsize, &x, &y, TRUE);
        xs[0] = 0;
        }
      }

    /* If the new font is "small caps", leave the font alone, but adjust the
    size. Otherwise reset the original size. */

    else
      {
      if (nf == font_sc)
        {
        if (pointsize == orig_pointsize)
          pointsize = (pointsize * curmovt->smallcapsize) / 1000;
        }
      else
        {
        f = nf;
        pointsize = orig_pointsize;
        fs = &(font_List[font_table[f]]);
        }
      }
    }

  /* Join the escape string onto the string so far */

  Ustrcpy(ss+i, xs);
  i += Ustrlen(xs);
  }

/* If there are any pending characters, output them. We only need update the
position if boxing or ringing is going to happen. */

if (i)
  {
  ss[i] = 0;
  ps_string(ss, f, pointsize, &x, &y, boxring != 0);
  }

/* Deal with boxed and/or ringed strings. Messy stuff added to cope with
rotated text, leaving original code alone. */

if (boxring != 0)
  {
  int y0 = out_ystave - ystart - 2*main_stavemagn;
  int y1 = y0 + pointsize + main_stavemagn;

  /* Compute length of string along string */

  if (font_sinr != 0)
    {
    double xx = (double)x - (double)xstart;
    double yy = (double)y - (double)ystart;
    x = xstart + (int)(sqrt(xx*xx + yy*yy));
    }

  x += 2*main_stavemagn - pointsize/20;

  /* Boxed string - note the lines() routine is stave-relative. Draw 5 lines
  for the box to get the corner right at the starting position. */

  if ((boxring & text_box) != 0)
    {
    int xx[6], yy[6];

    xx[0] = xx[3] = xx[4] = xstart - 2000;
    xx[1] = xx[2] = xx[5] = x;

    yy[0] = yy[1] = yy[4] = yy[5] = y0;
    yy[2] = yy[3] = y1;

    /* If text is rotated, rotate about the initial point */

    if (font_sinr != 0)
      {
      int i;
      for (i = 0; i <= 5; i++)
        {
        int xxx = xx[i] - xstart;
        int yyy = yy[i] - out_ystave + ystart;
        int xxxx = mac_muldiv(xxx, font_cosr, 1000) - mac_muldiv(yyy, font_sinr, 1000);
        int yyyy = mac_muldiv(yyy, font_cosr, 1000) + mac_muldiv(xxx, font_sinr, 1000);
        xx[i] = xxxx + xstart;
        yy[i] = yyyy + out_ystave - ystart;
        }
      }

    ps_lines(xx, yy, 6, pointsize/15);
    }

  /* Ringed string - the paths routine is also stave-relative */

  if ((boxring & text_ring) != 0)
    {
    int xx[13], yy[13], cc[6];
    int d = (2*pointsize)/7;
    int w = (2*(x - xstart + 2000))/7;

    cc[0] = path_move;
    cc[1] = cc[2] = cc[3] = cc[4] = path_curve;
    cc[5] = path_end;

    xx[0] = xx[9] = xx[12] = xstart - 2000;
    xx[3] = xx[6] = x;
    xx[1] = xx[8] = xx[0] + w;
    xx[2] = xx[7] = xx[3] - w;
    xx[4] = xx[5] = xx[3] + d;
    xx[10] = xx[11] = xx[0] - d;

    yy[0] = yy[3]  = yy[12] = y1;
    yy[6] = yy[9]  = y0;
    yy[1] = yy[2]  = yy[0] + w;
    yy[4] = yy[11] = yy[0] - d;
    yy[5] = yy[10] = yy[6] + d;
    yy[7] = yy[8]  = yy[6] - w;

    /* If text is rotated, rotate about the initial point */

    if (font_sinr != 0)
      {
      int i;
      for (i = 0; i <= 12; i++)
        {
        int xxx = xx[i] - xstart;
        int yyy = yy[i] - out_ystave + ystart;
        int xxxx = mac_muldiv(xxx, font_cosr, 1000) - mac_muldiv(yyy, font_sinr, 1000);
        int yyyy = mac_muldiv(yyy, font_cosr, 1000) + mac_muldiv(xxx, font_sinr, 1000);
        xx[i] = xxxx + xstart;
        yy[i] = yyyy + out_ystave - ystart;
        }
      }

    ps_path(xx, yy, cc, pointsize/15);
    }
  }

/* Return the current font, with font_sc added if we are in small caps. */

return (pointsize == orig_pointsize)? f : f + font_sc;
}




/*************************************************
*             Output one head/foot line          *
*************************************************/

/* Called from out_heading below.

Argument:   pointer to headstr
Returns:    nothing
*/

static void
out_headfootline(headstr *p)
{
int f = p->font;
int i = 0;
int *matrix = p->matrix;

uschar *s = p->a.text;
uschar left[256];
uschar centre[256];
uschar right[256];

if (matrix != NULL) memcpy(font_transform, matrix, 4*sizeof(int));

while (*s != 0 && *s != '|') left[i++] = *s++;
left[i] = 0;

i = 0;
if (*s == '|') s++;
while (*s != 0 &&  *s != '|') centre[i++] = *s++;
centre[i] = 0;

i = 0;
if (*s == '|') s++;
while (*s != 0 &&  *s != '|') right[i++] = *s++;
right[i] = 0;

if (left[0])
  {
  int w = string_width(left, f, p->size);
  font_xstretch = p->stretch;    /* justification stretching */
  f = out_string(left, f, p->size, 0, out_yposition, 0);
  font_xstretch = 0;
  if (-2000 < out_bbox[0]) out_bbox[0] = -2000;
  if (w + 2000 > out_bbox[2]) out_bbox[2] = w + 2000;
  }

if (centre[0])
  {
  int w = string_width(centre, f, p->size);
  int x = (curmovt->linelength - w)/2;
  f = out_string(centre, f, p->size, x, out_yposition, 0);
  if (x - 2000 < out_bbox[0]) out_bbox[0] = x - 2000;
  if (x + w + 2000 > out_bbox[2]) out_bbox[2] = x + w + 2000;
  }

if (right[0])
  {
  int w = string_width(right, f, p->size);
  f = out_string(right, f, p->size, curmovt->linelength - w, out_yposition, 0);
  if (curmovt->linelength - w - 2000 < out_bbox[0])
    out_bbox[0] = curmovt->linelength - w - 2000;
  if (curmovt->linelength + 4000 > out_bbox[2]) out_bbox[2] = curmovt->linelength + 4000;
  }

font_reset();
}



/*************************************************
*              Output heading texts              *
*************************************************/

/* Called from out_page() below.

Argument:   pointer to chain of heading blocks
Returns:    nothing
*/

static void
out_heading(headblock *h)
{
headstr *p = h->headings;

DEBUG(("out_heading() start\n"));

while (p != NULL)
  {
  /* Deal with normal heading/footing */

  if (p->size > 0)
    {
    int descender = (4 * p->size)/10; 
    out_yposition += p->b.spaceabove;
    if (out_yposition + descender > out_bbox[1]) 
      out_bbox[1] = out_yposition + descender;
    if (out_yposition - p->size < out_bbox[3]) 
      out_bbox[3] = out_yposition - p->size;
    out_headfootline(p);
    out_yposition += p->space;
    }

  /* Deal with a drawing. The drawing code is set up for use on staves; hence
  we must set up out_stave as well as the origin. We set out_stave negative to
  control error messages. */

  else if (p->size < 0)
    {
    draw_ox = draw_oy = 0;
    out_stave = -1;
    out_ystave = out_yposition;
    out_dodraw(p->a.drawing, p->b.args, FALSE);
    out_yposition += p->space;
    }

  /* Deal with PostScript heading/footing */

  else ps_headfoot(p);

  /* Advance to next heading/footing */

  p = p->next;
  }

DEBUG(("out_heading() end\n"));
}



/*************************************************
*              Output a system                   *
*************************************************/

/* Called from out_page() below. The system to be output is set in
out_sysblock.

Arguments:   none
Returns:     nothing
*/

static void
out_system(void)
{
snamestr **names = out_sysblock->stavenames;
zcopystr *zcopy = curmovt->zcopy;
int stave, lastystave;
int leftbarx, rightbarx;

DEBUG(("out_system() start\n"));

out_zcopycount = 0;
out_ystave = out_yposition;
out_overdraw = NULL;

/* Frequently used values */

out_bar = out_sysblock->barstart;
out_laststave = curmovt->laststave;

/* Make a copy of the continuation data - but see later for multiple stave
zero copies. */

out_cont = misc_copycontstr(out_sysblock->cont, out_laststave, FALSE);

/* Output the start-of-line matter on each stave, and at the same time compute
the relative position of each stave. */

for (stave = 1; stave <= out_laststave; stave++)
  {
  DEBUG(("start of line matter for stave %d\n", stave));

  out_depthvector[stave] = out_ystave - out_yposition;

  if (mac_teststave(out_sysblock->notsuspend, stave))
    {
    stavestr *ss = curmovt->stavetable[stave];
    snamestr *sname = (names==NULL)? NULL : names[stave];

    /* Set current magnification */

    mac_setstavesize(stave);

    /* Deal with stave name */

    if (sname != NULL)
      {
      /* Deal with textual stave name */
      if (sname->text != NULL)
        {
        int yoffset;
        int size = ((curmovt->fontsizes)->fontsize_text)[sname->offset];
        int linedepth = size;
        int *matrix = ((curmovt->fontsizes)->fontmatrix_text)[sname->offset];
        uschar *t = sname->text;

        if (matrix != NULL) memcpy(font_transform, matrix, 4*sizeof(int));

        /* Find the middle point of the line(s) of text - for vertically
        centred text there is a fudge to get it in the middle of a brace. */

        if ((sname->flags & snf_vcentre) != 0)
          {
          int gap = out_sysblock->stavespacing[stave];
          int st = stave + 1;
          while (gap == 0 && mac_teststave(out_sysblock->notsuspend, stave))
            gap = out_sysblock->stavespacing[st++];
          yoffset = gap/2 - 8*main_stavemagn - 2000;
          }
        else yoffset = -8*main_stavemagn;

        /* Now adjust the offset according to the number of lines */

        yoffset += (linedepth*4)/10 - ((sname->linecount - 1)*linedepth)/2;

        /* Now print the lines; if rotated, only one line, and we have
        to make further adjustments for the vertically centred case. */

        if ((sname->flags & snf_vertical) != 0)
          {
          if ((sname->flags & snf_vcentre) != 0)
            yoffset += string_width(t, font_rm, size)/2 - 2000;
          font_rotate(90000);
          out_string(t, font_rm, size, out_sysblock->xjustify,
            out_ystave + yoffset, 0);
          }

        /* Horizontal labels */

        else
          {
          int maxw = 0;
          uschar tt[256];

          /* If both centre & right adjust flags are set, we need to find the
          length of the longest line of the text. Tedious, but there's no other
          way of doing it as far as I can see... */

          if ((sname->flags & (snf_hcentre | snf_rightjust)) == (snf_hcentre | snf_rightjust))
            {
            while (*t)
              {
              int w;
              int i = 0;
              while (*t != 0 && *t != '|') tt[i++] = *t++;
              tt[i] = 0;
              w = string_width(tt, font_rm, size);
              if (w > maxw) maxw = w;
              if (*t == '|') t++;
              }
            t = sname->text;   /* Restore for printing */
            }

          while (*t)
            {
            int i = 0;
            int adjust = 0;

            while (*t != 0 && *t != '|') tt[i++] = *t++;
            tt[i] = 0;
            if ((sname->flags & (snf_hcentre | snf_rightjust)) != 0)
              {
              int w = string_width(tt, font_rm, size);
              adjust = out_sysblock->startxposition - 6000 - w;
              if (curmovt->bracelist != NULL) adjust -= 6500;
                else if (curmovt->thinbracketlist != NULL) adjust -= 4000;
              if ((sname->flags & snf_hcentre) != 0)
                {
                if ((sname->flags & snf_rightjust) == 0) adjust /= 2;
                  else adjust -= (maxw - w)/2;
                }
              }
            out_string(tt, font_rm, size, out_sysblock->xjustify + adjust,
              out_ystave + yoffset, 0);
            yoffset += linedepth;
            if (*t == '|') t++;
            }
          }

        font_reset();
        }

      /* Deal with stave name drawing */

      if (sname->drawing != NULL)
        {
        draw_ox = draw_oy = 0;
        out_stave = stave;
        out_dodraw(sname->drawing, sname->args, FALSE);
        }
      }

    /* No clefs, keys, etc. if the bar has no data, or if the stavelines value
    has the top bit set (for the old [percussion] action). */

    if ((ss->barindex)[out_sysblock->barstart] != NULL)
      {
      if (ss->stavelines < 128)
        {
        out_writeclef(out_sysblock->startxposition + out_sysblock->xjustify +
          (curmovt->startline)->clefspace, out_ystave,
            out_cont[stave].clef, 10000, FALSE);

        if ((out_sysblock->cont[stave]).key != 2)
          out_writekey(out_sysblock->keyxposition + out_sysblock->xjustify,
            out_ystave, out_cont[stave].clef,
              out_cont[stave].key);
        }

      if (mac_teststave(out_sysblock->showtimes, stave))
        out_writetime(out_sysblock->timexposition + out_sysblock->xjustify,
          out_ystave, out_cont[stave].time);
      }

    /* Advance down to next stave */

    out_ystave += out_sysblock->stavespacing[stave];
    }
  }

/* Compute the levels for copies of stave 0 that are to be printed. If two or
more share a level (because of suspension), keep only the last (-1 => no
print). Don't print one below the system depth. There will always be at least
one block on the list. */

while (zcopy != NULL)
  {
  if (zcopy->stavenumber <= out_laststave)
    {
    zcopy->level = out_depthvector[zcopy->stavenumber];
    if (zcopy->level > out_sysblock->systemdepth) zcopy->level = -1; else
      {
      zcopystr *zz = curmovt->zcopy;
      out_zcopycount++;
      while (zz != zcopy)
        {
        if (zz->level == zcopy->level) { zz->level = -1; out_zcopycount--; }
        zz = zz->next;
        }
      }
    }
  else zcopy->level = -1;
  zcopy = zcopy->next;
  }

/* If we are outputting more than one copy of stave zero, we must set up
private contstr pointers for each one. */

if (out_zcopycount > 1)
  {
  zcopy = curmovt->zcopy;
  while (zcopy != NULL)
    {
    if (zcopy->level >= 0)
      zcopy->cont = (struct contstr *) misc_copycontstr(out_sysblock->cont, 0, FALSE);
    zcopy = zcopy->next;
    }
  }

/* Output the joining signs required at the left hand side of the system of
staves, unless there is only one stave. */

if (out_sysblock->systemdepth > 0)
  {
  int bracketed[stave_bitvec_size];
  int bar = out_bar;

  DEBUG(("joining signs\n"));

  /* If there is an indent set, do true lefthand joins if required. Then adjust
  the bar number to point to the one where the rest of the joins will appear.
  */

  if (out_sysblock->joinxposition != out_sysblock->startxposition)
    {
    if (curmovt->startjoin)
      {
      out_joinxposition = out_sysblock->startxposition + out_sysblock->xjustify;
      out_dojoinsign(curmovt->joinlist, NULL, join_barline, bar_single, bar, NULL);
      out_dojoinsign(curmovt->joindottedlist, NULL, join_barline, bar_dotted, bar, NULL);
      }
    bar += curmovt->startbracketbar;
    }

  /* Set x position for all remaining signs */

  out_joinxposition = out_sysblock->joinxposition + out_sysblock->xjustify;

  /* Deal with solid and dotted lines */

  out_dojoinsign(curmovt->joinlist, NULL, join_barline, bar_single, bar, NULL);
  out_dojoinsign(curmovt->joindottedlist, NULL, join_barline, bar_dotted, bar, NULL);

  /* Deal with (thick) brackets; bracketed gets set to the bracketed staves */

  out_dojoinsign(curmovt->bracketlist, NULL, join_bracket, 0, bar, bracketed);

  /* Deal with thin brackets */

  out_dojoinsign(curmovt->thinbracketlist, bracketed, join_thinbracket, 0, bar, NULL);

  /* Deal with braces */

  out_dojoinsign(curmovt->bracelist, bracketed, join_brace, 0, bar, NULL);
  }

/* Now go through the bars, outputting all the staves for each in turn. */

out_prevtieflag = 0;
out_startlinebar = TRUE;
out_notex = out_sysblock->startxposition;    /* for slurs ending at bar start */
out_barx = out_sysblock->firstnoteposition +
  out_sysblock->xjustify;                    /* start of bar position */
out_lastbarlinex = out_barx;                 /* for continued nth time marks */
out_lastbarwide = FALSE;

for (;;)
  {
  out_bar = out_setbar();
  out_barx = out_lastbarlinex + out_sysblock->barlinewidth;
  if (out_bar > out_sysblock->barend) break;
  out_startlinebar = FALSE;
  out_notex = out_lastbarlinex;    /* for slurs ending at bar start */
  }

/* Output a key or time change at line end if required, adjusting the position
for a non-stretched barline. */

if ((out_sysblock->flags & sysblock_warn) != 0)
  {
  out_barx += curmovt->barlinespace - out_sysblock->barlinewidth;
  out_warnbar();
  }

/* Free the main cont data structure and any copies that have been set up for
multiple stave zeros. */

misc_freecontstr(out_cont, out_laststave);
if (out_zcopycount > 1)
  {
  zcopy = curmovt->zcopy;
  while (zcopy != NULL)
    {
    if (zcopy->level >= 0) misc_freecontstr((contstr *)zcopy->cont, 0);
    zcopy = zcopy->next;
    }
  }

/* Now we know the final x position, we can output the staves. Nothing is
output for stave 0, as it is always overprinted. Also, nothing is output for a
stave with "omitempty" set, as it deals with its own stave lines. */

out_ystave = out_yposition;
lastystave = -1;

leftbarx = out_sysblock->startxposition + out_sysblock->xjustify;
rightbarx = out_lastbarlinex;

if (rightbarx > leftbarx) for (stave = 1; stave <= out_laststave; stave++)
  {
  DEBUG(("lines for stave %d\n", stave));

  if (mac_teststave(out_sysblock->notsuspend, stave))
    {
    if (out_ystave != lastystave)
      {
      stavestr *ss = curmovt->stavetable[stave];
      if (!ss->omitempty && ss->stavelines > 0)
        {
        mac_setstavesize(stave);
        ps_stave(leftbarx, out_ystave, rightbarx, ss->stavelines & 127);
        lastystave = out_ystave;
        }
      }
    out_ystave += out_sysblock->stavespacing[stave];
    }
  }

/* If any drawing items have been saved up for execution after the stave lines
have been drawn, do them now. */

while (out_overdraw != NULL)
  {
  overdrawstr *this = out_overdraw;
  out_overdraw = this->next;

  if (this->texttype)
    {
    memcpy(font_transform, this->d.t.matrix, 4*sizeof(int));
    out_string(this->d.t.text, font_rm, this->d.t.fontsize, this->d.t.xx,
      this->d.t.yy, this->d.t.boxring);
    font_reset();
    }
  else
    {
    int *v = &(this->d.g.data[0]);
    ps_setgray(this->d.g.gray);
    out_ystave = this->d.g.ystave;
    ps_path(v, v+this->d.g.count, v + 2*this->d.g.count, this->d.g.linewidth);
    store_free(this);
    ps_setgray(0);
    }
  }

DEBUG(("out_system() end\n"));
}




/*************************************************
*   Top-level entry point for outputting a page  *
*************************************************/

/* The first sysblock of the page is set in out_sysblock, and the remainder are
chained on.

Arguments:  none
Return:     nothing
*/

void
out_page(void)
{
BOOL lastwasheading = FALSE;
int topspace = curpage->topspace;

DEBUG(("out_page() start\n"));

/* Initialize bounding box - note in y-downwards coordinates */

out_bbox[0] = BIGNUMBER;
out_bbox[2] = 0;
out_bbox[1] = 0;
out_bbox[3] = BIGNUMBER;

/* Initialize for outputting the music */

ps_setgray(0);
ps_setdash(0, 0, caj_butt);
out_sysblock = curpage->sysblocks;
out_yposition = out_drawstackptr = 0;
font_reset();

/* Output any heading and any systems. Note that we must insert a stave's gap
(plus one) between the last heading line and the first system (to account for
the system depth). Note also that we insert the topspace *after* pageheadings,
but *before* non-page headings. */

while (out_sysblock != NULL)
  {
  movtstr *oldmovt = curmovt;

  /* Call the output device (PostScript) function on change of movement; this
  allows, for example, a change of margin. */

  curmovt = format_movt = out_sysblock->movt;
  if (curmovt != oldmovt) ps_newmovt();

  /* Deal with a heading */

  if (out_sysblock->type == sh_heading)
    {
    out_bar = 0;
    main_stavemagn = 1000;
    if (!((headblock *)out_sysblock)->pageheading)
      {
      out_yposition += topspace;
      topspace = 0;
      }
    out_heading((headblock *)out_sysblock);
    lastwasheading = TRUE;
    }

  /* Deal with a system */

  else
    {
    if (lastwasheading) out_yposition += 17000;
    out_yposition += topspace;
    topspace = 0;

    if (out_yposition - 48000 < out_bbox[3]) out_bbox[3] = out_yposition - 48000;
    if (out_yposition + out_sysblock->systemdepth + 32000 > out_bbox[1])
      out_bbox[1] = out_yposition + out_sysblock->systemdepth + 32000;

    out_system();

    if (out_sysblock->xjustify - 10000 < out_bbox[0])
      out_bbox[0] = out_sysblock->xjustify - 10000;
    if (out_lastbarlinex + 4000 > out_bbox[2]) out_bbox[2] = out_lastbarlinex + 4000;

    if ((out_sysblock->flags & sysblock_noadvance) == 0)
      out_yposition += out_sysblock->systemdepth + out_sysblock->systemgap;
    lastwasheading = FALSE;
    }
  out_sysblock = out_sysblock->next;
  }

/* Deal with any footings */

if (curpage->footing != NULL)
  {
  out_bar = 0;
  main_stavemagn = 1000;
  out_yposition = main_pagelength + 20000000/main_magnification;
  out_heading(curpage->footing);
  }

DEBUG(("out_page() end\n"));
}

/* End of out1.c */
