💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › foreign › pmw › src › paginate.c captured on 2021-12-17 at 13:26:06.
View Raw
More Information
-=-=-=-=-=-=-
/*************************************************
- The PMW Music Typesetter - 3rd incarnation *
- ************************************************/
/* Copyright (c) Philip Hazel, 1991 - 2020 */
/* Written by Philip Hazel, starting November 1991 */
/* This file last modified: April 2020 */
/* This file contains code for splitting the music bars into systems and making
up pages from them. */
#include "pmwhdr.h"
#include "pagehdr.h"
enum { page_state_newmovt, page_state_newsystem,
page_state_insystem, page_state_donesystem,
page_state_donemovt };
/*************************************************
- Pagination function *
- ************************************************/
/* This function is called when the music has been successfully read in to
memory.
Arguments: none
Returns: nothing
void
paginate_go(void)
{
int i;
int nextbarwidth;
int xposition;
int adjustkeyposition;
int adjusttimeposition;
int lengthwarn = 0;
int page_layoutptr = 0;
int save_notespacing[8];
int page_layoutstack[20];
int page_layoutstackptr = 0;
int page_state = page_state_newmovt;
BOOL page_done = FALSE;
DEBUG(("paginate_go() start\n"));
/* Font initialization */
font_reset(); /* To set no transformation */
font_xstretch = 0; /* No justification */
/* Set up page and line lengths in magnified units, and compute the total
number of bars to be set. Once we have the line length, we can split and
justify heading and footing lines. */
main_pagelength = (main_truepagelength * 1000)/main_magnification;
main_totalbars = 0;
for (i = 1; i <= main_lastmovement; i++)
{
curmovt = movement[i];
curmovt->linelength = (curmovt->truelinelength * 1000)/main_magnification;
main_totalbars += curmovt->barcount;
page_justifyheading(curmovt->heading);
page_justifyheading(curmovt->footing);
page_justifyheading(curmovt->lastfooting);
}
/* Get store for the three data structures */
page_accepteddata = store_Xget(sizeof(pagedatastr));
page_nextdata = store_Xget(sizeof(pagedatastr));
page_previousdata = store_Xget(sizeof(pagedatastr));
/* Other initialization */
page_barcount = 0;
page_movtnumber = 1;
/* Set up the first page block */
curpage = main_pageanchor = store_Xget(sizeof(pagestr));
curpage->next = NULL;
curpage->number = main_lastpage = main_firstpage;
curpage->spaceleft = main_pagelength;
curpage->overrun = 0;
curpage->sysblocks = NULL;
curpage->footing = NULL;
page_sysprevptr = &(curpage->sysblocks);
page_countsystems = 0;
page_lastsystem = NULL;
page_movtpending = FALSE;
curmovt = movement[page_movtnumber];
page_footing = page_footnotes = NULL;
page_footnotedepth = 0;
/* Set the spreading parameters for the first page */
page_botmargin = curmovt->botmargin;
page_justify = curmovt->justify;
page_topmargin = curmovt->topmargin;
/* Working clefs while measuring. The current clef is needed only for measuring
special keys, which may depend on the clef. We can't use the page_cont versions
because they are not updated until after we know which bars are to be included
in this system. */
page_sysclef = store_Xget(sizeof(uschar) * (MAX_STAVE + 1));
/* Loop that does the job; page_state controls which action is taken. */
while (!page_done) switch(page_state)
{
/****************************************************************************/
/* Deal with the start of a movement. We deal with the heading lines and then
set up for paginating the rest of the movement. */
case page_state_newmovt:
format_movt = curmovt;
if (curmovt->barlinespace == (int)0x80000000)
{
page_barlinewidth = (curmovt->notespacing)[minim]/2 - 5000;
if (page_barlinewidth < 3000) page_barlinewidth = 3000;
curmovt->barlinespace = page_barlinewidth;
}
else page_barlinewidth = curmovt->barlinespace;
memcpy(page_stavemap, curmovt->staves, STAVE_BITVEC_SIZE*sizeof(int));
page_ulaysize = ((curmovt->fontsizes)->fontsize_text)[ff_offset_ulay];
page_olaysize = ((curmovt->fontsizes)->fontsize_text)[ff_offset_olay];
/* Deal with heading texts if we know the movement is to go on this page. If
we are at the top of a page, do the page heading unless it has been turned
off explicitly. Also, set up a footing for this page. */
if (!page_movtpending)
{
if (page_movtnumber > 1 && curmovt->pageheading != NULL &&
(curmovt->movt_opt & movt_nopageheading) == 0 &&
curpage->spaceleft == main_pagelength)
page_dopageheading(curmovt->pageheading);
if (curmovt->heading != NULL) page_dopageheading(curmovt->heading);
if (curmovt->footing != NULL) page_footing = curmovt->footing;
}
/* Create vector of per-bar data structures */
curmovt->posvector =
(barposstr *)store_Xget(curmovt->barcount * sizeof(barposstr)) - 1;
/* Now set up to process the bars. Start by finding the highest possible
stave number. If no staves are present in the movement, the result is -1. */
page_lastwanted = -1;
{
usint x;
for (i = STAVE_BITVEC_SIZE - 1; i >= 0; i--)
{
if ((x = curmovt->staves[i]) != 0)
{
page_lastwanted += i << 5;
break;
}
}
while (x != 0)
{
page_lastwanted++;
if (page_lastwanted >= curmovt->laststave) break;
x = x >> 1;
}
}
/* Cut back the count of staves to those requested */
curmovt->laststave = page_lastwanted;
/* Initialize values in the accepted data structure */
for (i = 0; i < STAVE_BITVEC_SIZE; i++)
page_accepteddata->notsuspend[i] = curmovt->staves[i] & (~curmovt->suspend[i]);
memcpy(page_accepteddata->notespacing, curmovt->notespacing, 8*sizeof(int));
page_accepteddata->stavenames =
store_Xget((page_lastwanted+1)*sizeof(uschar *));
for (i = 1; i <= page_lastwanted; i++)
{
snamestr *sn = ((curmovt->stavetable)[i])->stave_name;
page_accepteddata->stavenames[i] = sn;
}
/* Stavespacing is set only if it has been mentioned. If not, set the
default. */
if (curmovt->stave_spacing == NULL && page_lastwanted >= 0)
{
page_ssnext = store_Xget((page_lastwanted+1)*sizeof(int));
page_ssnext[0] = 0; /* No space for stave 0 */
for (i = 1; i <= page_lastwanted; i++) page_ssnext[i] = 44000;
}
else page_ssnext = curmovt->stave_spacing;
page_sgnext = curmovt->systemgap;
/* Create vectors for {und,ov}erlay level handling */
page_ulevel = store_Xget((page_lastwanted+1)*sizeof(int));
page_ulhere = store_Xget((page_lastwanted+1)*sizeof(int));
page_olevel = store_Xget((page_lastwanted+1)*sizeof(int));
page_olhere = store_Xget((page_lastwanted+1)*sizeof(int));
for (i = 0; i <= page_lastwanted; i++)
page_ulevel[i] = page_olevel[i] = BIGNUMBER;
/* The ensure spacing data is often NULL, but if not, set up separate "here"
and "next" vectors. */
page_ssenext = curmovt->stave_ensure;
page_ssehere = (page_ssenext == NULL)? NULL :
store_Xget((page_lastwanted+1)*sizeof(int));
/* Set up the continuation data vector, in the default state */
page_cont = store_Xget((page_lastwanted+1)*sizeof(contstr));
for (i = 0; i <= page_lastwanted; i++)
{
contstr *p = page_cont + i;
p->slurs = NULL;
p->hairpin = NULL;
p->nbar = NULL;
p->ulay = NULL;
p->tie = NULL;
p->overbeam = NULL;
p->tiex = 0;
p->noteheadstyle = nh_normal;
p->flags = cf_default;
p->clef = clef_treble;
p->time = curmovt->time;
p->key = transpose_key(curmovt->key, curmovt->transpose);
}
/* Final set up */
if (curmovt->startnotime) mac_initstave(page_showtimes, 0); else
{
mac_initstave(page_showtimes, ~0);
mac_clearstave(page_showtimes, 0);
}
page_firstsystem = TRUE;
page_startline = curmovt->startline;
page_state = page_state_newsystem;
/* Set up for fixed layout if required */
if (curmovt->layout == NULL) page_layoutptr = -1; else
{
page_layoutstack[0] = BIGNUMBER;
page_layoutstackptr = 1;
page_layoutptr = 0;
while (curmovt->layout[page_layoutptr++] == lv_repeatcount)
page_layoutstack[page_layoutstackptr++] = curmovt->layout[page_layoutptr++];
}
/* The left and right justification bits can be set immediately, so that they
apply to all systems in the new movement. The top and bottom bits can't be
changed yet, because we don't know if this movement will start on the current
page. */
page_justifyLR = curmovt->justify;
/* Initialize the bar number and deal with the case of no bars of music in
the movement. */
if ((page_barnumber = 1) > curmovt->barcount)
{
page_botmargin = curmovt->botmargin; /* Normally set when we know */
page_justify = curmovt->justify; /* that the first system fits */
page_topmargin = curmovt->topmargin; /* on the page. This code copes */
page_state = page_state_donemovt; /* with a single movement file */
}
break;
/****************************************************************************/
/* Deal with the start of a new system. There will always be at least one bar
left when control gets here. */
case page_state_newsystem:
{
int keyxposition = 0;
int timexposition = 0;
int timewidth = 0;
int Cmajor = TRUE;
page_newpagewanted = FALSE; /* initialize flags */
page_layout_stretchn = page_layout_stretchd = 1; /* first time; no stretch */
if (page_ssenext != NULL)
memcpy(page_ssehere, page_ssenext, (page_lastwanted+1)*sizeof(int));
/* If page_ssenext is NULL, but page_sshere is not, it means that
page_ssehere was set up in the previous system by [ssabove]. */
else if (page_ssehere != NULL)
{
store_free(page_ssehere);
page_ssehere = NULL;
}
/* Get a new system block and initialize */
page_sysblock = store_Xget(sizeof(sysblock));
page_sysblock->next = NULL;
page_sysblock->type = sh_system;
page_sysblock->flags = 0;
page_sysblock->movt = curmovt;
memcpy(page_sysblock->notsuspend, page_accepteddata->notsuspend,
STAVE_BITVEC_SIZE*sizeof(int));
mac_initstave(page_sysblock->showtimes, 0);
page_sysblock->barstart = page_barnumber;
page_sysblock->barend = page_barnumber;
page_sysblock->stavenames = page_accepteddata->stavenames;
page_sysblock->stavespacing = page_ssnext;
page_sysblock->systemgap = page_sgnext;
page_sysblock->cont = misc_copycontstr(page_cont, page_lastwanted, TRUE);
/* Set up working clefs at start of system; these are needed only for use
when measuring the widths of special key signatures, which may depend on
the clef. We can't use the page_cont values because they don't get updated
dynamically ('cause we don't want to go past the bars we accept). */
for (i = 1; i <= page_lastwanted; i++)
page_sysclef[i] = page_sysblock->cont[i].clef;
page_sysblock->ulevel = store_Xget((page_lastwanted+1) * sizeof(int));
page_sysblock->olevel = store_Xget((page_lastwanted+1) * sizeof(int));
for (i = 0; i <= page_lastwanted; i++)
{
contstr *c = page_cont + i;
if (c->overbeam != NULL) /* Clear continued beams for next system */
{
store_free(c->overbeam);
c->overbeam = NULL;
}
page_ulhere[i] = page_olhere[i] = 0;
page_sysblock->ulevel[i] = -page_ulaysize - 1000;
page_sysblock->olevel[i] = 20000;
}
/* Update the current key/time signatures if necessary */
page_setsignatures();
/* Initialize those fields of the current data structure that are reset for
each system */
page_accepteddata->endkey = FALSE;
page_accepteddata->endtime = FALSE;
page_accepteddata->endbar = page_barnumber - 1;
page_accepteddata->count = 0;
/* Save the initial notespacing so that it can be restored for re-spacing
bars when there's a big stretch factor. */
memcpy(save_notespacing, page_accepteddata->notespacing, 8*sizeof(int));
/* If all staves are suspended (can happen during part extraction with the
use of [newline] or with S! bars) unsuspend the lowest numbered stave. If
any staves get resumed in the system, this gets undone again later. */
if (!mac_anystave(page_sysblock->notsuspend)) for (i = 0; i < STAVE_BITVEC_SIZE; i++)
{
if (curmovt->staves[i] != 0)
{
page_sysblock->notsuspend[i] = curmovt->staves[i] & (-curmovt->staves[i]);
break;
}
}
/* Find the starting position of the stave */
page_accepteddata->startxposition = page_startwidth(page_accepteddata,
page_stavemap, page_sysblock->notsuspend);
/* Compute position for key signatures. We make them all line up
vertically. The top bit in the stavelines value indicates no clefs are ever
printed. This is to support the old [percussion] directive (which pre-dates
[noclef]). For the moment, the value generated is relative to
startxposition. */
for (i = 1; i <= page_lastwanted; i++)
{
stavestr *ss = curmovt->stavetable[i];
if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i) &&
(!ss->omitempty || (ss->barindex)[page_barnumber] != NULL))
{
mac_setstavesize(i);
if ((curmovt->stavetable[i])->stavelines < 127)
{
int clef = (page_sysblock->cont[i]).clef;
int xpos =
curmovt->clefwidths[clef] * main_stavemagn + page_startline->clefspace;
if (xpos > keyxposition) keyxposition = xpos;
}
}
}
/* Compute the position for time signatures after the widest key signature
(again relative to startxposition). Again, the top stavelines bit
suppresses key signatures. */
for (i = 1; i <= page_lastwanted; i++)
{
if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i))
{
mac_setstavesize(i);
if ((curmovt->stavetable[i])->stavelines < 127)
{
int key = (page_sysblock->cont[i]).key;
int xpos = keyxposition +
(main_stavemagn * misc_keywidth(key, page_sysblock->cont[i].clef))/1000;
if (xpos > timexposition) timexposition = xpos;
if (key != 2) Cmajor = FALSE;
}
}
}
/* If at least one key signature is not C major, insert extra space
before the key position. */
if (!Cmajor)
{
keyxposition += page_startline->keyspace;
timexposition += page_startline->keyspace;
}
page_sysblock->keyxposition = keyxposition;
/* If all staves are [percussion] staves, no clefs or keys get written.
This can also happen for [noclef] staves with no key signature. In this
case we need to put a little bit of space before the time signature or
first note. */
if (timexposition == 0) timexposition += 2000;
/* We are now at the position for the time signature, or first note if
there is no time signature. */
if (mac_anystave(page_showtimes))
{
timexposition += page_startline->timespace;
for (i = 1; i <= page_lastwanted; i++)
{
if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i))
{
int tw;
mac_setstavesize(i);
tw = (main_stavemagn *
misc_timewidth((page_sysblock->cont[i]).time))/1000;
if (tw > timewidth) timewidth = tw;
}
}
memcpy(page_sysblock->showtimes, page_showtimes, STAVE_BITVEC_SIZE * sizeof(int));
mac_initstave(page_showtimes, 0);
}
page_sysblock->timexposition = timexposition;
/* Set the first note position */
page_sysblock->firstnoteposition =
timexposition + timewidth + PAGE_LEFTBARSPACE + page_startline->notespace;
/* The xposition is an absolute position, used during calculations */
page_accepteddata->xposition =
page_accepteddata->startxposition + page_sysblock->firstnoteposition;
/* Initialize start of line flags and accidental space insertion flag */
page_startlinebar = TRUE; /* becomes FALSE after one acceptance */
page_lastendwide = FALSE; /* at start of line */
page_lastenddouble = FALSE; /* ditto */
/* Enter the mid-system state */
page_state = page_state_insystem;
}
break;
/****************************************************************************/
/* In the middle of a system - measure the next bar and see if it will fit.
It it doesn't, and it starts with a key or time signature and there is not
even enough room for that, we have to back off from accepting the previous
bar. When the system is full, change state again. */
case page_state_insystem:
/* Make copy of current status for MakePosTable to update */
*page_nextdata = *page_accepteddata;
/* Measure the bar -- this also sets various flags such as page_warnkey and
also sets page_xxwidth if there is a key and/or time signature. The
lengthwarn variable is normally zero (or less), but is set to 1 after the end
of a system so that re-measuring the bar, for the next system, doesn't give a
length warning again. If we back up two bars, in order to fit in a key/time
signature, lengthwarn is set to 2. */
nextbarwidth = page_makepostable(lengthwarn-- < 1);
/* Compute position if bar were accepted */
xposition = page_accepteddata->xposition + nextbarwidth;
/* If a stave has been resumed in this bar, or if a stave name has changed,
it may be necessary to change the width of the stave name space (i.e.
startxposition). This may make the bar unacceptable.
We must also check the clef of the resumed stave, if it has a bar to print at
the start of the system, because a wider clef will alter the position of the
key signature.
Likewise the key signature of the resumed stave, because a wider key
signature will alter the position of the time signature. */
adjustkeyposition = 0;
adjusttimeposition = 0;
if (mac_diffstave(page_nextdata->notsuspend, page_accepteddata->notsuspend) ||
page_nextdata->stavenames != page_accepteddata->stavenames)
{
int newkeyxposition = 0;
int newtimexposition = 0;
/* Deal with stave names */
int newstartx = page_startwidth(page_nextdata,
page_stavemap, page_nextdata->notsuspend);
page_nextdata->startxposition = newstartx;
xposition += newstartx - page_accepteddata->startxposition;
/* Deal with change of key signature position */
for (i = 1; i <= page_lastwanted; i++)
{
stavestr *ss = curmovt->stavetable[i];
if (mac_teststave2(page_stavemap, page_nextdata->notsuspend, i) &&
(!ss->omitempty || (ss->barindex)[page_sysblock->barstart] != NULL))
{
mac_setstavesize(i);
if ((curmovt->stavetable[i])->stavelines < 127)
{
int clef = (page_sysblock->cont[i]).clef;
int key = (page_sysblock->cont[i]).key;
int xp =
curmovt->clefwidths[clef] * main_stavemagn + page_startline->clefspace;
if (xp > newkeyxposition) newkeyxposition = xp;
xp = newkeyxposition +
(main_stavemagn * misc_keywidth(key, clef))/1000;
if (xp > newtimexposition) newtimexposition = xp;
}
}
}
if (newkeyxposition > page_sysblock->keyxposition)
{
adjustkeyposition = newkeyxposition - page_sysblock->keyxposition;
xposition += adjustkeyposition;
}
if (newtimexposition > page_sysblock->timexposition + adjustkeyposition)
{
adjusttimeposition = newtimexposition - page_sysblock->timexposition -
adjustkeyposition;
xposition += adjusttimeposition;
}
}
/* Default overrun is "infinity" */
page_sysblock->overrun = 255;
/* If this is not the first bar on the line, see if it will fit. We always
accept one bar - it gets squashed (with a warning). If a fixed layout has
been specified, accept bars until we have one more than required. */
if ((page_layoutptr < 0 && page_accepteddata->count > 0 &&
xposition > curmovt->linelength) ||
(page_layoutptr >= 0 &&
page_accepteddata->count >= curmovt->layout[page_layoutptr]))
{
int overrun = xposition - curmovt->linelength;
lengthwarn = 1; /* Don't warn when we reprocess the bar */
/* See if cautionary signature(s) are needed. */
if (!page_startlinebar && (page_warnkey || page_warntime))
{
xposition = page_accepteddata->xposition + page_xxwidth;
/* If there is not even enough space for the end-of-line signature(s),
back up to the previous bar, unless there isn't one to back up to, in
which case give an overlong line warning. */
if (page_layoutptr < 0 && xposition > curmovt->linelength)
{
if (page_accepteddata->count > 1)
{
pagedatastr *temp = page_previousdata;
page_previousdata = page_accepteddata;
page_accepteddata = temp;
page_barcount -= page_lastbarcountbump;
page_barnumber -= page_lastbarcountbump;
overrun = xposition - curmovt->linelength;
lengthwarn++; /* Two bars not to warn for */
}
else
{
error_moan(ERR56, page_previousdata->endbar);
overrun = 25500;
}
}
/* Room for key/time -- set flag(s) and xposition */
else
{
page_accepteddata->xposition = xposition + page_barlinewidth;
page_accepteddata->endkey = page_warnkey;
page_accepteddata->endtime = page_warntime;
}
}
/* Set up the overrun value as a whole number of points, rounded up */
overrun = (overrun + 999)/1000;
if (overrun > 255) overrun = 255;
page_sysblock->overrun = (uschar)overrun;
/* In all cases we've finished the system */
page_state = page_state_donesystem;
}
/* There is room on the line for this bar, or it is the first bar on the
line, so accept it, first freeing any vectors that previous points to which
are no longer needed. Give a warning for an over- flowing single bar, which
will be squashed to fit. */
else
{
pagedatastr *temp = page_previousdata;
page_previousdata = page_accepteddata;
page_accepteddata = page_nextdata;
page_nextdata = temp;
if (page_accepteddata->count == 0 && xposition > curmovt->linelength)
{
error_moan(ERR55, page_accepteddata->endbar + 1, nextbarwidth,
xposition-nextbarwidth, curmovt->linelength);
}
/* If this bar starts with a key or time change, then bump the previous x
position to account for it, as that will be what happens if we have to back
off. */
if (page_xxwidth > 0 && !page_startlinebar)
{
page_previousdata->xposition += page_xxwidth + page_barlinewidth;
page_previousdata->endkey = page_warnkey;
page_previousdata->endtime = page_warntime;
}
else
{
page_previousdata->endkey = FALSE;
page_previousdata->endtime = FALSE;
}
/* Allow for key & time signature positioning adjustment */
if (adjustkeyposition || adjusttimeposition)
{
page_sysblock->keyxposition += adjustkeyposition;
page_sysblock->timexposition += adjustkeyposition + adjusttimeposition;
page_sysblock->firstnoteposition += adjustkeyposition + adjusttimeposition;
}
/* Update xposition, endbar number, and counts */
page_accepteddata->xposition = xposition + page_barlinewidth;
if (page_manyrest >= 2)
{
page_accepteddata->endbar += page_manyrest;
page_barcount += page_manyrest; /* total count */
page_barnumber += page_manyrest;
page_lastbarcountbump = page_manyrest; /* for backing off */
}
else
{
page_accepteddata->endbar++;
page_barcount++;
page_barnumber++;
page_lastbarcountbump = 1;
}
page_accepteddata->count += 1; /* counts printed bars */
page_startlinebar = FALSE;
if (page_barnumber > curmovt->barcount) page_state = page_state_donesystem;
}
break;
/****************************************************************************/
/* Completed a system - tidy the data structures and see if it fits onto the
current page. The positions at the start of the line can be made absolute now
that startxposition is known. We also perform the stretching operation on the
position tables in the bars at this point. */
case page_state_donesystem:
{
int startxposition = page_accepteddata->startxposition;
int barlinewidth = page_barlinewidth;
int justbits, stretchn, stretchd, sysdepth, sysfootdepth;
page_sysblock->stavenames = page_accepteddata->stavenames;
page_sysblock->barend = page_accepteddata->endbar;
/* If there are no unsuspended staves in the accepted data, then leave the
sysblock alone, as it will have had one stave forced into it. Otherwise,
overwrite with the accepted value, thereby turning off the fudged stave if
there was one. */
if (mac_anystave(page_accepteddata->notsuspend))
for (i = 0; i < STAVE_BITVEC_SIZE; i++)
page_sysblock->notsuspend[i] = page_accepteddata->notsuspend[i];
/* Fix various initial positions */
page_sysblock->startxposition = startxposition;
page_sysblock->joinxposition = startxposition;
page_sysblock->keyxposition += startxposition;
page_sysblock->timexposition += startxposition;
page_sysblock->firstnoteposition += startxposition;
if (page_accepteddata->endkey) page_sysblock->flags |= sysblock_warnkey;
if (page_accepteddata->endtime) page_sysblock->flags |= sysblock_warntime;
/* Advance the continuation data to the end of the system, ready for the
next one. This scan also handles changes of stave and system spacing, and
local justification. Because we do not yet know if this system is going to
fit on the page, any vertical justification changes that it makes are
placed in page_sys_xxx variables. We initialize them to negative numbers to
detect changes.
There is an unfortunate chicken-and-egg situation here. We need to set the
barlinewidth, as it is used when computing beam slopes for beams that cross
barlines. It should really be set to the stretched value, but we can't
compute the stretching factor until we've done the barcont stuff, in order
to know if we have to justify or not. We cheat by setting it to the
unstretched value and hoping that is near enough... */
page_sysblock->barlinewidth = barlinewidth;
page_sys_topmargin = page_sys_botmargin = page_sys_justify = -1;
/* The page_setcont() routine also collects footnotes and system notes. We
initialize the various variables before calling it. */
page_newfootnotes = NULL;
page_newfootnotedepth = 0;
page_setcont();
/* Set up the xposition of the end of the line and the justify bits, and
compute the spreading parameters. We spread if the line is wider than a
proportion of the linewidth as set by the stretchthresh variables, or if
it's too long (when the "spreading" is actually squashing). Note that
left/right justification bits are taken from this system's flags, if there
were any. */
xposition = page_accepteddata->xposition - barlinewidth;
justbits = page_justifyLR & (just_left + just_right);
/* Left + right justification */
if ((justbits == just_left + just_right &&
xposition - page_sysblock->startxposition >
(main_stretchthreshnum*
(curmovt->linelength - page_sysblock->startxposition)) /
main_stretchthreshden) ||
xposition > curmovt->linelength)
{
int save_xxwidth = page_xxwidth;
int xxadjust = page_sysblock->firstnoteposition +
((page_accepteddata->endkey || page_accepteddata->endtime)?
page_xxwidth + barlinewidth : 0);
stretchn = curmovt->linelength - xxadjust;
stretchd = xposition - xxadjust;
page_sysblock->xjustify = 0;
page_sysblock->flags |= sysblock_stretch;
/* If the stretching factor is large enough, throw away the position
tables and re-format all the bars using the known stretching factor. They
should not get any wider. Then compute revised stretching factors. Repeat
if necessary, up to 4 times. Note that we have to keep re-stretching the
barlinewidth.
From release 4.22 we also do this when the stretching is actually
squashing by a large enough amount, which can happen when the layout
directive forces more bars onto a line than would normally fit. This is
necessary when there is underlay, where words might crash when a bar is
squashed. (While testing this, it turns out that the squashing version
also sometimes kicks in after a stretching time round the loop, which
sometimes overdoes things, it seems. It does no harm.)
There's an option to prevent this new logic from happening, for old
files. It currently selects between three states: do nothing, do it only
for over-stretching, do it for both over-stretching and squashing. */
page_layout_stretchn = stretchn;
page_layout_stretchd = stretchn; /* sic - see below - it changes cumulatively */
i = 0;
while (i++ < 4 &&
(
(mac_muldiv(stretchd, 1000, stretchn) > main_stretchrespacethresh &&
opt_stretchrule >= 2) ||
(mac_muldiv(stretchn, 1000, stretchd) > main_stretchrespacethresh &&
opt_stretchrule >= 1)
))
{
int j;
page_layout_stretchd = mac_muldiv(page_layout_stretchd, stretchd, stretchn);
xposition = page_sysblock->firstnoteposition;
page_startlinebar = TRUE; /* becomes FALSE after one acceptance */
page_lastendwide = FALSE; /* at start of line */
page_lastenddouble = FALSE; /* ditto */
barlinewidth = mac_muldiv(barlinewidth, stretchn, stretchd);
memcpy(page_nextdata->notespacing, save_notespacing, 8*sizeof(int));
/* Reset clefs at system start */
for (j = 1; j <= page_lastwanted; j++)
page_sysclef[j] = page_sysblock->cont[j].clef;
for (page_barnumber = page_sysblock->barstart;
page_barnumber <= page_sysblock->barend;
page_barnumber++)
{
barposstr *bp = curmovt->posvector + page_barnumber;
store_free(bp->vector);
xposition += page_makepostable(FALSE) + barlinewidth;
page_startlinebar = FALSE;
if (page_manyrest >= 2) page_barnumber += page_manyrest - 1;
}
xposition -= barlinewidth;
if (page_accepteddata->endkey || page_accepteddata->endtime)
xposition += save_xxwidth + curmovt->barlinespace; /* Unstretched barlinewidth */
stretchd = xposition - xxadjust;
if (main_tracepos == (-1) ||
(page_sysblock->barstart <= main_tracepos &&
page_sysblock->barend >= main_tracepos))
debug_printf("i=%d bars %d-%d old=%d new=%d\n", i,
page_sysblock->barstart, page_sysblock->barend,
mac_muldiv(page_layout_stretchn, 1000, page_layout_stretchd),
mac_muldiv(stretchn, 1000, stretchd));
}
}
/* Deal with right only or no justification */
else
{
stretchn = stretchd = 1;
if ((justbits & just_left) == 0)
{
int xjustify = curmovt->linelength - xposition;
if (justbits == 0) xjustify /= 2;
page_sysblock->xjustify = xjustify;
}
else page_sysblock->xjustify = 0;
}
/* The barline width for the system is the final stretched value. */
page_sysblock->barlinewidth = mac_muldiv(barlinewidth, stretchn, stretchd);
/* Now apply the stretching operation to the bars in the system. Key and
time signatures and left repeats at the start of a bar are not stretched.
Grace notes are kept at the same distance from their successors. */
for (i = page_sysblock->barstart; i <= page_sysblock->barend; i++)
{
barposstr *bp = curmovt->posvector + i;
posstr *p = bp->vector;
int count = bp->count;
/* If this is the first bar of a multi-rest, make a correction to the
value of i to skip the others. */
i += bp->multi - 1;
/* Skip over any clefs, key signatures or time signatures at the start of
the bar. For big stretches, it is in fact not enough to do this, as the
stretched barlinewidth can make their positioning look silly. We move
them to the left in this case.
This is probably less relevant now that we re-lay-out lines to get the
stretching factor down. */
/***** PRO TEM remove fix to retain previous state pending revised
stretching. In this state, clefs are not tested here (they don't normally
occur at line starts). We need a revised stretching algorithm to keep the
first note fixed even after clefs, keys, and times. ****/
if ((p->moff <= posx_timelast && p->moff >= posx_keyfirst)) /*** || p->moff == posx_clef) ***/
{
int n = 9;
while ((count > 0 && p->moff >= posx_keyfirst && p->moff <= posx_timelast) ||
p->moff == posx_clef)
{
p->xoff -= ((page_sysblock->barlinewidth - page_barlinewidth)*n)/10;
if (n > 2) n -= 2;
p++;
count--;
}
}
/* Else skip over any grace notes and accidentals, and also the first
note, which we do not want to move. But if there is nothing in the bar,
don't skip over the first (= last) item. */
else
{
while (count > 0 && p->moff < 0) { p++; count--; }
if (count > 1) { p++; count--; }
}
/* Now stretch the remaining items, dealing specially with grace notes,
which are identified by finding the next full note and checking the
offset. Also deal specially with clefs. */
while (count-- > 0)
{
int old;
posstr *pp = p;
while (count > 0 && (pp+1)->moff - pp->moff <= -posx_max)
{
pp++;
count--;
}
old = pp->xoff;
pp->xoff = mac_muldiv(pp->xoff, stretchn, stretchd);
while (p < pp)
{
int d = -(pp->moff - p->moff);
int rightmost = p->xoff + pp->xoff - old;
int leftmost = mac_muldiv(p->xoff, stretchn, stretchd);
/* Clef positions are stretched just a bit if they are the last thing
in the bar. Otherwise, the position used is halfway between an
unstretched and stretched position. */
if (d == posx_clef)
p->xoff = (count == 0)? rightmost - (rightmost - leftmost)/5 :
(rightmost+leftmost)/2;
/* Grace notes are never stretched at all; other things are stretched
a bit, but not the full amount. */
else if (d >= posx_gracefirst && d <= posx_gracelast) p->xoff = rightmost;
else p->xoff = (rightmost + leftmost)/2;
p++;
}
p++;
}
}
/* If this was the first system of a movement, re-compute the stave name
structure to use the second name. Also, if an indent is set for the
brackets and braces, adjust the position of the joining signs. */
if (page_firstsystem)
{
int j;
page_firstsystem = FALSE;
page_accepteddata->stavenames =
store_Xget((page_lastwanted+1)*sizeof(uschar *));
for (j = 1; j <= page_lastwanted; j++)
{
snamestr *sn = ((curmovt->stavetable)[j])->stave_name;
page_accepteddata->stavenames[j] = (sn != NULL)? sn->next : NULL;
}
if (curmovt->startbracketbar >= page_sysblock->barstart &&
curmovt->startbracketbar < page_sysblock->barend)
{
int blw = 0;
page_sysblock->joinxposition = page_sysblock->firstnoteposition;
for (i = page_sysblock->barstart; i <= curmovt->startbracketbar; i++)
{
barposstr *bp = curmovt->posvector + i;
posstr *p = bp->vector;
page_sysblock->joinxposition += p[bp->count-1].xoff + blw;
blw = page_sysblock->barlinewidth;
}
}
}
/* Check that the stavespacing vector conforms to the ensure values, and if
not, make a new one that does. At the same time, compute the total depth of
the system. If an unsuspended stave has a zero stave spacing, make sure
that the following stave is not suspended. */
sysdepth = 0;
for (i = 1; i <= page_lastwanted; i++)
{
if (mac_teststave2(page_stavemap, page_sysblock->notsuspend, i))
{
int j = i;
int next = i+1;
while (j < page_lastwanted && page_sysblock->stavespacing[j++] == 0)
{
mac_setstave(page_sysblock->notsuspend, j);
}
while (next <= page_lastwanted &&
mac_testNstave2(page_stavemap, page_sysblock->notsuspend, next))
next++;
if (next <= page_lastwanted)
{
if (page_ssehere != NULL &&
page_sysblock->stavespacing[i] < page_ssehere[next])
{
if (page_sysblock->stavespacing == page_ssnext)
page_sysblock->stavespacing = store_copy(page_ssnext);
page_sysblock->stavespacing[i] = page_ssehere[next];
}
sysdepth += page_sysblock->stavespacing[i];
}
page_lastulevel = page_sysblock->ulevel[i];
}
}
page_sysblock->systemdepth = sysdepth;
/* Compute a testing depth consisting of the system depth plus the total
depth of any footnotes, and space below the current system. And space
between the current footnotes and any new ones. */
sysfootdepth = sysdepth + page_footnotedepth + page_newfootnotedepth;
if (page_footnotedepth + page_newfootnotedepth > 0)
{
sysfootdepth += - page_lastulevel;
if (page_footnotedepth > 0 && page_newfootnotedepth > 0)
sysfootdepth += curmovt->footnotesep;
}
/* If this system is deeper than the page depth, we can't handle it. After
the error, it will cause a new page to be started, but it will never be
printed. */
if (sysfootdepth > main_pagelength)
{
int overflow = sysfootdepth - main_pagelength;
error_moan(ERR64, page_sysblock->barstart, page_movtnumber,
overflow, (overflow == 1000)? "" : "s");
}
/* If we have a new movement pending, find the depth of the headings and
see if the headings plus this system will fit on the current page. If the
system depth is zero, we have a single-stave system, in which case we
insist on there being room for another one as well. */
if (page_movtpending)
{
headstr *h = curmovt->heading;
int depth = (h == NULL)? 0 : 17000;
while (h != NULL)
{
depth += h->space;
h = h->next;
}
depth += (sysfootdepth == 0)? page_sysblock->systemgap : sysfootdepth;
/* If no room, terminate the page and start a new one. We must arrange
that footings are printed from the *previous* movement, but take the
option for lastfooting and pageheading from the *current* movement. */
if (curpage->spaceleft < depth)
{
BOOL ph = ((curmovt->movt_opt) & movt_nopageheading) == 0;
BOOL lf = ((curmovt->movt_opt) & movt_uselastfooting) != 0;
curmovt = movement[page_movtnumber - 1];
page_endpage(lf);
curmovt = movement[page_movtnumber];
page_newpage(curmovt->heading, ph? curmovt->pageheading : NULL);
}
/* There is room: output the new heading on this page, and set the
justification parameters from the new movement. (The horizontal ones will
have been set already, but the vertical ones can't be changed until the
page is known.) We also change the bottom margin, but leave the top
margin until the next page. */
else
{
page_justify = curmovt->justify;
page_botmargin = curmovt->botmargin;
if (curmovt->heading != NULL)
{
page_dopageheading(curmovt->heading);
curpage->spaceleft -= 17000;
}
}
/* Set up a new footing, if present. Note that if there isn't one, and we
didn't start a new page, and there is one still set up from the previous
movement already (in page_footing), then it will still get printed at the
bottom of this page. */
if (curmovt->footing != NULL) page_footing = curmovt->footing;
/* Cancel pending flag */
page_movtpending = FALSE;
}
/* If this system does not fit on the page, start a new one. */
if (curpage->spaceleft < sysfootdepth)
{
curpage->overrun = sysfootdepth - curpage->spaceleft;
page_endpage(FALSE);
page_newpage(NULL, curmovt->pageheading);
}
/* Connect the system to the chain and keep count of the number of
vertically spreadable systems on the page. */
*page_sysprevptr = page_sysblock;
page_sysprevptr = &(page_sysblock->next);
page_countsystems++;
/* If there were any footnotes, connect them to the page's footnote list
for inclusion at the end. Save the current spacing value for use if the
page does actually end here. */
if (page_newfootnotes != NULL)
{
if (page_footnotes == NULL) page_footnotes = page_newfootnotes; else
{
page_lastfootnote->next = page_newfootnotes;
page_newfootnotes->spaceabove = curmovt->footnotesep;
page_footnotedepth += curmovt->footnotesep;
}
page_lastfootnote = page_lastnewfootnote;
page_footnotedepth += page_newfootnotedepth;
}
page_footnotespacing = - page_lastulevel;
/* Update the space left on the page; just take off the space for the
music (the system), not the footnotes. They'll be considered again with the
next system. */
curpage->spaceleft -= sysdepth + page_sysblock->systemgap;
page_state = page_state_newsystem;
page_lastsystem = page_sysblock;
/* Update the vertical justification parameters if they changed in this
system. */
if (page_sys_justify != -1) page_justify = page_sys_justify;
if (page_sys_topmargin != -1) page_topmargin = page_sys_topmargin;
if (page_sys_botmargin != -1) page_botmargin = page_sys_botmargin;
/* If we have an explicit layout, deal with advancing the pointer and
checking for a forced new page. */
if (page_layoutptr >= 0)
{
page_layoutptr++;
for (;;)
{
while (curmovt->layout[page_layoutptr] == lv_newpage)
{
page_layoutptr++;
if (page_barnumber <= curmovt->barcount) page_newpagewanted = TRUE;
}
if (curmovt->layout[page_layoutptr] == lv_repeatptr)
{
if ((page_layoutstack[page_layoutstackptr-1] -= 1) > 0)
page_layoutptr = curmovt->layout[page_layoutptr+1];
else
{
page_layoutstackptr--;
page_layoutptr += 2;
}
}
else
{
while (curmovt->layout[page_layoutptr++] == lv_repeatcount)
page_layoutstack[page_layoutstackptr++] = curmovt->layout[page_layoutptr++];
break;
}
}
}
/* If a new page was forced after this system, set it up. This can only
happen via [newpage] if there are more bars; hence it can't also be a
movement end. Via explicit layout, it is also only set if there are more
bars. */
if (page_newpagewanted)
{
page_endpage(FALSE);
page_newpage(NULL, curmovt->pageheading);
}
/* Handle the end of a movement */
if (page_barnumber > curmovt->barcount) page_state = page_state_donemovt;
}
break;
/****************************************************************************/
/* Completed a movement. Tidy the data structures and deal with the end of
the whole piece or with starting a subsequent movement. */
case page_state_donemovt:
misc_freecontstr(page_cont, page_lastwanted);
if (page_ssehere != NULL) store_free(page_ssehere);
store_free(page_ulevel);
store_free(page_ulhere);
store_free(page_olevel);
store_free(page_olhere);
/* End of the last movement */
if (page_movtnumber++ >= main_lastmovement)
{
page_endpage(TRUE);
page_done = TRUE;
}
/* There is another movement to follow. If it contains no staves, we must
deal with the headings here. */
else
{
movtstr *nextmovt = movement[page_movtnumber];
int movt_opt = (nextmovt->movt_opt & ~(movt_nopageheading | movt_uselastfooting));
/* Deal with the case of no staves in the movement; we must decide now
whether it fits on the page or not if nothing is specified. */
if (nextmovt->barcount < 1 && movt_opt == movt_default)
{
headstr *h = nextmovt->heading;
int depth = 0;
while (h != NULL)
{
depth += h->space;
h = h->next;
}
movt_opt = (curpage->spaceleft < depth)? movt_newpage : movt_thispage;
}
/* Handle forced new page; set page_heading NULL to prevent any heading
output, which will be done by the start-of-movt code. */
if (movt_opt == movt_newpage)
{
page_endpage((nextmovt->movt_opt & movt_uselastfooting) != 0);
curmovt = nextmovt;
page_newpage(NULL, NULL);
}
/* If no page option was specified, we can't decide whether to start a new
page until after the next system has been read. We just set a flag for the
work to be done then. For the very special case of "thisline", we remove
and vertical advance from the last system. Another system of the same depth
will then always fit. We must also reduce the count of spreadable systems,
since this one should not get additional space added to it! */
else
{
curmovt = nextmovt;
if (movt_opt == movt_thisline && page_lastsystem != NULL)
{
curpage->spaceleft += page_lastsystem->systemdepth +
page_lastsystem->systemgap;
page_lastsystem->flags |= sysblock_noadvance;
page_countsystems--;
}
if (movt_opt != movt_thispage) page_movtpending = TRUE;
}
/* Change state */
page_state = page_state_newmovt;
}
break;
}
store_free(page_accepteddata);
store_free(page_nextdata);
store_free(page_previousdata);
DEBUG(("paginate_go() end\n"));
}
/* End of paginate.c */