/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* Copyright (c) Philip Hazel, 1991 - 2019 */ /* Written by Philip Hazel, starting November 1991 */ /* This file last modified: November 2019 */ /* This file contains subroutines for the paginating code. */ #include "pmwhdr.h" #include "pagehdr.h" /************************************************* * Update times/keys for start of line * *************************************************/ /* This function is called at the start of a system, to see if the first bar contains clef, key, or time items that should be transferred into the basic setting for the bar. Arguments: none Returns: nothing */ void page_setsignatures(void) { int stave; for (stave = 0; stave <= page_lastwanted; stave++) { if (mac_teststave(curmovt->staves, stave)) { bstr *p = ((curmovt->stavetable)[stave])->barindex[page_barnumber]; if (p != NULL) { BOOL hadclef = FALSE; BOOL hadkey = FALSE; BOOL hadtime = FALSE; int type = p->type; while (type != b_End) { switch(type) { case b_Jump: p = (bstr *)(((b_Jumpstr *)p)->next); break; case b_clef: if (!hadclef) { b_clefstr *c = (b_clefstr *)p; (page_sysblock->cont[stave]).clef = c->trueclef; c->suppress = hadclef = TRUE; } break; case b_key: /* if (!hadkey) */ if (!hadkey || page_sysblock->cont[stave].key == 2 || page_sysblock->cont[stave].key == 21) { b_keystr *k = (b_keystr *)p; if (k->key <= 63 || !k->warn) { (page_sysblock->cont[stave]).key = k->key; hadkey = TRUE; } k->suppress = TRUE; } break; case b_time: if (!hadtime) { b_timestr *t = (b_timestr *)p; (page_sysblock->cont[stave]).time = t->time; t->suppress = hadtime = TRUE; mac_setstave(page_showtimes, stave); } break; case b_note: case b_lrepeat: goto NEXTSTAVE; } p = (bstr *)((uschar *)p + length_table[type]); type = p->type; } } } NEXTSTAVE: continue; } } /************************************************* * Find width of name string * *************************************************/ /* This function is called by page_startwidth() below. Multiple lines in a name string are indicated by '|'. We return the width of the widest. Arguments: s the string font the font size the font size Returns: a width */ static int page_namestringwidth(uschar *s, int font, int size) { int yield = 0; uschar ss[256]; while (*s) { int w; int i = 0; while (*s != 0 && *s != '|') ss[i++] = *s++; ss[i] = 0; w = string_width(ss, font, size); if (w > yield) yield = w; if (*s == '|') s++; } return yield; } /************************************************* * Compute width of stave names * *************************************************/ /* This function returns the width needed for names to be printed at the start of staves on a page. Two stave bitmaps are supplied: the name is counted only if the stave is selected in both bitmaps. Typically, the first map gives the staves that are active in a system, and the second is those that are currently suspended. Arguments: pagedata the page data block map the first bitmap map2 the second bitmap Returns: the maximum width needed */ int page_startwidth(pagedatastr *pagedata, usint *map, usint *map2) { int x = 0; int *f = (curmovt->fontsizes)->fontsize_text; int **m = (curmovt->fontsizes)->fontmatrix_text; if (pagedata->stavenames != NULL) { int i; snamestr **stavenames = pagedata->stavenames; for (i = 1; i <= page_lastwanted; i++) { if (mac_teststave2(map, map2, i)) { snamestr *s; for (s = stavenames[i]; s != NULL; s = s->extra) { int w = 0; if ((s->flags & snf_vertical) != 0) w = 6000; else if (s->text != NULL) { int *matrix = m[s->offset]; if (matrix != NULL) memcpy(font_transform, matrix, 4*sizeof(int)); w = page_namestringwidth(s->text, font_rm, f[s->offset]); font_reset(); if (w > 0) w += 6000; /* final space if non-null */ } if (w > x) x = w; } } } } /* If there are stave names, add extra if the system is braced or has a thin bracket, to leave space between them and the names. */ if (x != 0) { if (curmovt->bracelist != NULL) x += 6500; else if (curmovt->thinbracketlist != NULL) x += 4000; } return x; } /************************************************* * Deal with a page's heading * *************************************************/ /* This function sets up a headblock and adds it to the chain of headings and systems for the page. It also subtracts the space needed for the heading lines from the space left on the page. Remember to include an additional stave depth + 1pt after any headings at the top of a page. This function is not called if there are no headings. Argument: the heading structure Returns: nothing */ void page_dopageheading(headstr *page_heading) { int used = 0; headblock *h = store_Xget(sizeof(headblock)); h->type = sh_heading; h->pageheading = (page_heading != curmovt->heading); /* Not a movt heading */ h->next = NULL; h->movt = curmovt; h->headings = page_heading; *page_sysprevptr = (sysblock *)h; page_sysprevptr = (sysblock **)(&(h->next)); while (page_heading != NULL) { used += page_heading->space; page_heading = page_heading->next; } if (curpage->spaceleft == main_pagelength) curpage->spaceleft -= 17000; curpage->spaceleft -= used; } /************************************************* * Justify a heading or footing * *************************************************/ /* This function scans a heading text and splits up the "left hand" part into two or more heading lines. For all but the last line the stretch field is set to cause the text to be printed with right justification. (Actually the last line is justified too, if it is near enough to the end.) Argument: a heading structure Returns: nothing */ void page_justifyheading(headstr *h) { while (h != NULL) { if (h->size > 0) /* Text heading (as opposed to drawing or postscript) */ { uschar *lastsplit = NULL; uschar *p = h->text; int spacecount = 0; int splitfont = 0; int lastwidth = 0; if (h->matrix != NULL) memcpy(font_transform, h->matrix, 4*sizeof(int)); for (;;) { if (*p == '|') break; if (*p == ' ' || *p == 0) { int width; int c = *p; *p = 0; width = string_width(h->text, h->font, h->size); *p = c; if (width > curmovt->linelength) { headstr *new; if (lastsplit == NULL) break; p = lastsplit; *p++ = 0; new = store_Xget(sizeof(headstr)); memcpy(new, h, sizeof(headstr)); new->text = p; new->font = splitfont; new->spaceabove = 0; new->stretch = 0; if (spacecount > 1) h->stretch = (curmovt->linelength - lastwidth)/(spacecount - 1); h->next = new; h = new; spacecount = 0; lastsplit = NULL; } else if (*p == 0) { if (curmovt->linelength - width < 5000 && spacecount > 0) h->stretch = (curmovt->linelength - width)/spacecount; break; } else { spacecount++; lastsplit = p++; lastwidth = width; splitfont = string_font; /* Set by string_width */ while (*p == ' ') { p++; spacecount++; } } } else p++; } font_reset(); /* To set no transformation */ } h = h->next; /* Loop through all headings */ } } /************************************************* * Set up a new page * *************************************************/ /* This is not called for the very first page; curpage is always set to the current page. Arguments: heading a movement heading, or NULL if not a movement start page_heading a page heading, or NULL if no page heading Returns: nothing; curpage is updated */ void page_newpage(headstr *heading, headstr *page_heading) { pagestr *newpage = store_Xget(sizeof(pagestr)); curpage->next = newpage; newpage->number = main_lastpage = curpage->number + main_pageinc; curpage = newpage; curpage->next = NULL; curpage->spaceleft = main_pagelength; curpage->overrun = 0; curpage->sysblocks = NULL; curpage->footing = NULL; page_sysprevptr = &(curpage->sysblocks); page_countsystems = 0; page_lastsystem = NULL; page_footnotes = NULL; page_footnotedepth = 0; page_justify = curmovt->justify; page_topmargin = curmovt->topmargin; page_botmargin = curmovt->botmargin; if (page_heading != NULL) page_dopageheading(page_heading); if (heading != NULL) page_dopageheading(heading); } /************************************************* * Finish off a page * *************************************************/ /* Correct the final spaceleft value (for information use), then justify the page if necessary, and set up the footnotes & footing. Argument: TRUE for the final page; causes it to use lastfooting Returns: nothing */ void page_endpage(BOOL final) { headstr *footing; int justbits = page_justify & (just_top + just_bottom); int spaceleft = (curpage->spaceleft += ((page_lastsystem == NULL)? 0 : page_lastsystem->systemgap)); /* Set up any footnotes, making sure the systemgap field on the last system is the last accepted spacing below stave value, plus 10 points. Adjust the spaceleft to allow for footnotes when justifying. */ if (page_footnotes != NULL) { headblock *h = store_Xget(sizeof(headblock)); h->type = sh_heading; h->pageheading = FALSE; h->next = NULL; h->movt = curmovt; h->headings = page_footnotes; *page_sysprevptr = (sysblock *)h; page_lastsystem->systemgap = page_footnotespacing + 10000; spaceleft -= page_footnotedepth + page_footnotespacing; } if (spaceleft < 0) spaceleft = 0; /* can happen on deep systems */ /* Deal with vertical justification data. If there are no spreading points top+bottom is the same as bottom. There is a limit to the distance between systems. */ if (justbits == just_top + just_bottom) { int topmargin = page_topmargin; int botmargin = page_botmargin; int margin = topmargin + botmargin; if (margin > spaceleft) { topmargin = mac_muldiv(topmargin, spaceleft, margin); botmargin = mac_muldiv(botmargin, spaceleft, margin); } spaceleft -= botmargin + topmargin; curpage->topspace = topmargin; if (spaceleft <= main_pagelength/2 && page_countsystems > 1) { int count = page_countsystems - 1; int insert = spaceleft/count; sysblock *s = curpage->sysblocks; if (insert > main_maxvertjustify) insert = main_maxvertjustify; while (s != NULL) /* defensive programming */ { if (s->type != sh_heading && (s->flags & sysblock_noadvance) == 0) { s->systemgap += insert; if (--count <= 0) break; } s = s->next; } } } /* Top-only justification */ else if (justbits == just_top) curpage->topspace = (page_topmargin > spaceleft)? spaceleft : page_topmargin; /* Bottom-only justification */ else if (justbits == just_bottom) { curpage->topspace = spaceleft - page_botmargin; if (curpage->topspace < 0) curpage->topspace = 0; } /* No justification => centred vertically */ else curpage->topspace = spaceleft/2; /* Set up the page footing */ if (page_footing != NULL) /* Start of movement page footing */ { footing = page_footing; page_footing = NULL; } else { footing = (final && curmovt->lastfooting != NULL)? curmovt->lastfooting : curmovt->pagefooting; } if (footing != NULL) { headblock *f = store_Xget(sizeof(headblock)); f->next = NULL; f->movt = curmovt; f->headings = footing; curpage->footing = f; } } /* End of pagesubs.c */