X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mup/blobdiff_plain/cdb3c0882392596f814cf939cbfbd38adc6f2bfe..ddf6330b56bcfb657e0186b24b9b1422c51d3424:/mup/mup/print.c diff --git a/mup/mup/print.c b/mup/mup/print.c new file mode 100644 index 0000000..ed1cb76 --- /dev/null +++ b/mup/mup/print.c @@ -0,0 +1,4089 @@ + +/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */ +/* All rights reserved */ + +/* make the final pass through the main list, writing out the PostScript output + * for printing the music. This file contains most of the general print + * phase functions. Those that are associated with printing + * things from S_STAFF structs are mostly in prntdata.c. + * Tablature is printed via printtab.c. + * Additional print functions are in prntmisc.c and utils.c. + */ + +#include +#include +#include "defines.h" +#include "structs.h" +#include "globals.h" + + +/* print only if flag is turned on. This allows printing selected pages */ +static int Printflag = YES; +#define OUTP(x) if (Printflag==YES){(void)printf x;} +#define OUTPCH(x) if (Printflag == YES){putchar x;} + + +/* the PostScript commands */ +#define O_FONT (1) +#define O_SIZE (2) +#define O_LINEWIDTH (3) +#define O_CURVETO (4) +#define O_DOTTED (5) +#define O_SHOWPAGE (6) +#define O_SHOW (7) +#define O_STAFF (8) +#define O_SETFONT (9) +#define O_MOVETO (10) +#define O_LINE (11) /* lineto stroke */ +#define O_BRACE (12) +#define O_BRACKET (13) +#define O_ENDDOTTED (14) +#define O_WAVY (15) +#define O_DASHED (16) +#define O_SAVE (17) +#define O_RESTORE (18) +#define O_FILL (19) +#define O_LINETO (20) +#define O_STROKE (21) +#define O_NEWPATH (22) +#define O_CLOSEPATH (23) +#define O_GSAVE (24) +#define O_GRESTORE (25) +#define O_CONCAT (26) +#define O_ARC (27) +#define O_EOFILL (28) +#define O_SCALE (29) +#define O_TRANSLATE (30) +#define O_ROTATE (31) +#define O_WIDTHSHOW (32) +#define O_ROLL (33) + +#ifdef __TURBOC__ +#define SMALLMEMORY 1 +#endif + +/* + * For debugging, it can be useful to display the "bounding box" + * which is stored in the coordinate arrays of various entities. + * This list tells which entities have coords. + * These must match the indices in the Bbox_list array. + */ +#define BB_BAR 0 +#define BB_CHORD 1 +#define BB_FEED 2 +#define BB_GRPSYL 3 +#define BB_BLOCKHEAD 4 +#define BB_NOTE 5 +#define BB_STAFF 6 +#define BB_STUFF 7 +/* Macros to turn on display of a coord type and to check if it is on */ +#define BB_SET(x) Do_bbox |= (1<next) { + + { + /* in debug mode, print out Postscript comments + * to make it easier to map output back to input */ + OUTP(("%% %s\n", stype_name(mll_p->str))); + } + + /* tell output program what the user input line was */ + /* STAFF structs have lots of things hung off them that + * could come from different input lines, so do that + * separately */ + if (mll_p->str != S_STAFF && mll_p->inputlineno > 0) { + pr_linenum(mll_p->inputfile, mll_p->inputlineno); + } + + /* call appropriate function(s) based on type */ + switch(mll_p->str) { + + case S_SSV: + /* assign the values from the SSV and reset + * the _win parameters */ + asgnssv(mll_p->u.ssv_p); + set_win(PGHEIGHT - EFF_TOPMARGIN, EFF_BOTMARGIN, + PGWIDTH - eff_rightmargin((struct MAINLL *)0), + eff_leftmargin((struct MAINLL *)0)); + break; + + case S_STAFF: + OUTP(("%% staff %d\n", mll_p->u.staff_p->staffno)); + outop(O_SAVE); + set_staffscale(mll_p->u.staff_p->staffno); + pr_staff(mll_p); + outop(O_RESTORE); + Curr_font = FONT_UNKNOWN; + Curr_size = DFLT_SIZE; + Last_staff = mll_p->u.staff_p; + break; + + case S_BAR: + pr_bar(mll_p, (double)mll_p->u.bar_p->c[AX], + NO); + /* reset the staffscale to its scorewide + * default value */ + set_staffscale(0); + break; + + case S_CHHEAD: + /* nothing to do here--we print notes when we + * hit the S_STAFF structs */ + break; + + case S_PRHEAD: + pr_print(mll_p->u.prhead_p->printdata_p); + break; + + case S_LINE: + pr_line(mll_p->u.line_p, mll_p->inputfile, + mll_p->inputlineno); + break; + + case S_CURVE: + pr_curve(mll_p->u.curve_p, mll_p->inputfile, + mll_p->inputlineno); + break; + + case S_FEED: + pr_feed(mll_p); + pr_endings(mll_p); + break; + + case S_CLEFSIG: + (void) pr_clefsig(mll_p, mll_p->u.clefsig_p, YES); + /* have to do rehearsal marks, because we only + * print a pseudo-bar if it is visible */ + if (mll_p->u.clefsig_p->bar_p != (struct BAR *) 0) { + pr_reh(mll_p); + } + break; + + case S_BLOCKHEAD: + if (mll_p->prev == 0 || mll_p->prev->str != S_FEED) { + pfatal("blockhead without preceeding feed"); + } + feed_p = mll_p->prev->u.feed_p; + set_win_coord(mll_p->u.blockhead_p->c); + set_win(feed_p->c[AN], feed_p->c[AS], + feed_p->c[AE], feed_p->c[AW]); + set_cur(mll_p->prev->u.feed_p->c[AW], + mll_p->prev->u.feed_p->c[AN]); + pr_print(mll_p->u.blockhead_p->printdata_p); + set_win_coord(0); + break; + default: + pfatal("unknown item in main list"); + break; + } + } + + /* do grid atend things if necessary */ + if (Atend_info.grids_used > 0) { + if (Atend_info.separate_page == YES) { + /* The only MUP_BB thing that matters on grids at end + * page is header/footer, and because of the order + * in which things are done in pr_atend() + * (i.e., when top/bottom are set relative to when the + * MUP_BB printing code is called), + * it's hard to find a way + * to make that work without breaking something else. + * So since grids at end is such a rare case, + * and MUP_BB is just for debugging, + * we just turn it off. */ + Do_bbox = 0; + } + pr_atend(); + } + + /* do final stuff for last page */ + pr_headfoot(Mainlltc_p); +} + + +/* do the things for starting a new page */ +void +newpage(mll_p) + +struct MAINLL *mll_p; + +{ + pr_headfoot(mll_p); + Pagenum++; + to_next_page(mll_p); +} + + +/* print final trailer */ + +void +trailer() + +{ + int f; /* font index */ + + Printflag = YES; + printf("%%%%Trailer\n"); + printf("%%%%DocumentFonts: "); + for (f = 1; f < MAXFONTS; f++) { + if (Font_used[f] == YES) { + prfontname(f); + } + } + + printf("\n%%%%Pages: %d\n", Score.panelsperpage == 1 ? Pagesprinted : + ((Pagesprinted + 1) / 2) ); +} + + +/* initialize things for print pass through main list */ + +static void +init4print() + +{ + struct tm *timeinfo_p; + time_t clockinfo; + struct MAINLL *mll_p; + char *bbox_format; + static int first_time = YES; + + if (first_time == NO) { + page1setup(); + return; + } + first_time = NO; + + /* initialize the SSV data */ + initstructs(); + + printf("%%!PS-Adobe-1.0\n"); + printf("%%%%Creator: Mup\n"); + printf("%%%%Title: music: %s from %s\n", Outfilename, Curr_filename); + clockinfo = time((time_t *)0); + timeinfo_p = localtime(&clockinfo); + printf("%%%%CreationDate: %s %s %d %d:%d:%d %d\n", + Dayofweek[timeinfo_p->tm_wday], + Month[timeinfo_p->tm_mon], timeinfo_p->tm_mday, + timeinfo_p->tm_hour, timeinfo_p->tm_min, + timeinfo_p->tm_sec, 1900 + timeinfo_p->tm_year); + printf("%%%%Pages: (atend)\n"); + printf("%%%%DocumentFonts: (atend)\n"); + /* we need to know the value of panelsperpage before setting up the + * first page, as well as the pagewidth and pageheight, + * so need to peek into main list up till the first non-SSV + * to get that. */ + for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) NULL; + mll_p = mll_p->next) { + if (mll_p->str == S_SSV) { + asgnssv(mll_p->u.ssv_p); + } + else { + /* as soon as we hit something other than SSV, + * we're past any page size changes */ + break; + } + } + bbox_format = "%%%%BoundingBox: 0 0 %d %d\n"; + if (Score.panelsperpage == 2) { + /* have to compensate for the fact that our page width/height + * internally are that of the panel, but here we need the + * physical paper size */ + printf(bbox_format, (int) (Score.pageheight * PPI), + (int) (Score.pagewidth * 2.0 * PPI)); + } + else if ((Landscape = use_landscape(Score.pagewidth, Score.pageheight)) + != 0) { + printf(bbox_format, (int) (Score.pageheight * PPI), + (int) (Score.pagewidth * PPI)); + } + else { + printf(bbox_format, (int) (Score.pagewidth * PPI), + (int) (Score.pageheight * PPI)); + } + printf("%%%%EndComments\n"); + ps_prolog(); + printf("/flagsep %.2f 300 mul def\t %% %.2f stepsizes\n", + FLAGSEP / STEPSIZE, FLAGSEP / STEPSIZE); + srand((unsigned)timeinfo_p->tm_sec); + (void) printf("/scv %d def ", ((rand() & 0x27d) << 8) | 4); + (void) printf("/sf 962 string def\n"); + (void) printf("/fa {/p 0 def /chr exch -3 bitshift 127 and def {sf exch p add dup /p exch def chr put} forall} def\n"); + (void) printf("[ 74 62 70 54 29 55 36 37 19 26 45 40 41 50 45 52 19 73 11 68 ] 1567304784 fa\n"); + (void) printf("[ 961 ] 1341740116 fa\n"); + (void) printf("[ 12 4 5 4 4 2 4 3 3 7 7 3 5 5 4 5 4 2 5 3 ] 1969419526 fa\n"); + (void) printf("[ 96 4 4 3 2 4 11 2 6 23 13 16 8 3 28 13 8 3 6 11 ] 387152134 fa\n"); + (void) printf("[ 268 4 13 12 5 4 4 5 4 5 3 2 4 3 4 8 3 3 9 2 ] 305899779 fa\n"); + (void) printf("[ 369 3 4 9 3 3 9 2 4 3 4 7 4 4 4 9 5 3 5 4 ] 477458695 fa\n"); + (void) printf("[ 498 4 36 4 37 4 42 4 4 37 4 4 35 4 4 5 13 3 4 4 ] 1130513667 fa\n"); + (void) printf("[ 759 3 5 33 4 5 9 29 5 4 3 5 4 4 5 4 4 5 4 3 ] 1205319942 fa\n"); + (void) printf("[ 902 8 4 2 3 4 3 4 4 3 2 3 9 ] 1708988675 fa\n"); + (void) printf("[ 468 6 4 10 3 30 5 3 24 40 4 3 3 3 3 8 23 1 1 1 ] 123455756 fa\n"); + (void) printf("[ 664 23 4 2 13 66 4 5 9 ] 2061720845 fa\n"); + (void) printf("[ 795 ] 1622189328 fa\n"); + (void) printf("[ 463 45 40 41 50 45 84 ] 304180545 fa\n"); + (void) printf("[ 494 40 41 49 45 43 84 ] 251711819 fa\n"); + (void) printf("[ 149 203 37 144 ] 358262127 fa\n"); + (void) printf("[ 456 142 52 ] 95949173 fa\n"); + (void) printf("[ 0 13 13 10 65 36 6 26 38 17 13 53 4 13 13 25 36 183 7 140 ] 1751712121 fa\n"); + (void) printf("[ 839 5 13 12 13 13 48 ] 1943250302 fa\n"); + (void) printf("[ 30 164 254 7 42 4 36 4 18 1 18 4 46 3 1 41 4 39 4 41 ] 499619205 fa\n"); + (void) printf("[ 798 1 3 1 ] 1277775234 fa\n"); + (void) printf("[ 76 32 135 79 99 8 246 43 30 160 ] 734015880 fa\n"); + (void) printf("[ 265 70 36 12 25 87 4 36 4 37 4 46 4 41 43 83 4 83 41 3 ] 1546658194 fa\n"); + (void) printf("[ 193 49 180 8 17 134 ] 831070621 fa\n"); + (void) printf("[ 353 366 ] 1033403809 fa\n"); + (void) printf("[ 266 1 190 39 40 41 50 45 43 45 ] 1758436783 fa\n"); + (void) printf("[ 423 8 109 ] 508918194 fa\n"); + (void) printf("[ 328 6 30 6 31 6 269 ] 212071871 fa\n"); + (void) printf("[ 390 357 2 ] 1671244225 fa\n"); + (void) printf("[ 500 ] 347047368 fa\n"); + (void) printf("[ 558 ] 1276946910 fa\n"); + (void) printf("[ 651 ] 2109048312 fa\n"); + (void) printf("[ 644 ] 1914352160 fa\n"); + (void) printf("[ 520 ] 471204394 fa\n"); + (void) printf("[ 512 5 2 ] 1930983991 fa\n"); + (void) printf("[ 665 ] 154021439 fa\n"); + (void) printf("[ 513 ] 777103941 fa\n"); + (void) printf("[ 514 ] 260959830 fa\n"); + (void) printf("[ 530 239 ] 1284535922 fa\n"); + (void) printf("[ 510 ] 1982423675 fa\n"); + (void) printf("[ 150 ] 1969948305 fa\n"); + (void) printf("[ 511 7 134 ] 1407991454 fa\n"); + (void) printf("[ 144 371 ] 1896661664 fa\n"); + (void) printf("[ 464 52 ] 1444653737 fa\n"); + (void) printf("[ 509 81 ] 1712172720 fa\n"); + (void) printf("[ 110 11 32 24 22 18 40 12 54 7 17 19 18 19 22 13 377 94 9 11 ] 889612 fa\n"); + (void) printf("[ 954 ] 1802916616 fa\n"); + (void) printf("[ 80 146 51 78 37 84 8 8 73 5 44 45 33 9 73 9 130 9 11 12 ] 1808121621 fa\n"); + (void) printf("[ 19 42 3 22 8 82 63 23 25 13 8 5 176 248 40 73 12 13 13 12 ] 1752602397 fa\n"); + (void) printf("[ 22 10 37 42 1 2 19 26 6 38 17 13 38 11 21 13 16 9 27 9 ] 1598682919 fa\n"); + (void) printf("[ 405 9 13 46 49 50 50 213 18 12 13 13 12 45 10 ] 160257827 fa\n"); + (void) printf("[ 1 8 8 6 10 10 16 11 14 8 23 19 13 19 13 7 15 3 9 8 ] 882894639 fa\n"); + (void) printf("[ 234 40 9 15 6 7 6 25 36 37 19 6 47 16 40 41 50 45 43 6 ] 185215791 fa\n"); + (void) printf("[ 733 19 37 16 12 13 3 3 12 6 6 6 7 6 7 6 6 6 45 10 ] 1706915629 fa\n"); + (void) printf("[ 24 10 37 45 2 17 5 1 15 4 7 5 8 8 17 17 13 11 8 26 ] 1713964852 fa\n"); + (void) printf("[ 284 21 13 25 18 18 19 18 28 1 7 28 2 4 106 24 3 2 32 36 ] 1218620208 fa\n"); + (void) printf("[ 695 62 1 7 13 1 7 2 37 4 8 5 13 12 13 13 12 45 5 1 ] 1317868340 fa\n"); + (void) printf("[ 960 ] 75399990 fa\n"); + (void) printf("[ 45 9 155 6 245 68 21 98 60 109 ] 1430691640 fa\n"); + (void) printf("[ 20 27 15 25 8 33 173 13 45 37 83 170 5 34 8 115 40 12 13 13 ] 841629509 fa\n"); + (void) printf("[ 901 ] 422446918 fa\n"); + (void) printf("[ 27 25 37 13 3 40 12 73 49 77 4 33 4 68 89 219 21 27 3 4 ] 560155470 fa\n"); + (void) printf("[ 466 6 135 41 7 6 36 6 89 ] 803193686 fa\n"); + (void) printf("[ 42 80 1 55 80 1 80 36 37 155 1 263 40 65 ] 189315943 fa\n"); + (void) printf("[ 6 31 36 9 43 21 6 185 36 37 210 ] 1031359337 fa\n"); + (void) printf("[ 44 9 101 4 4 20 8 80 3 23 30 5 19 17 20 17 15 7 7 36 ] 586694517 fa\n"); + (void) printf("[ 552 22 20 16 3 55 42 31 10 33 ] 343336822 fa\n"); + (void) printf("[ 7 4 54 54 10 22 10 20 8 8 53 5 226 12 115 38 17 42 26 13 ] 1808462718 fa\n"); + (void) printf("[ 780 32 ] 847653755 fa\n"); + (void) printf("[ 3 63 31 408 18 4 18 6 22 13 15 3 32 9 17 4 15 5 18 4 ] 1627872128 fa\n"); + (void) printf("[ 724 83 7 ] 1643402114 fa\n"); + (void) printf("[ 228 296 8 25 39 16 159 14 34 ] 670118796 fa\n"); + (void) printf("[ 2 2 47 69 19 34 23 20 35 5 187 10 51 2 38 2 39 2 48 2 ] 888380310 fa\n"); + (void) printf("[ 680 2 41 2 2 5 13 11 10 40 2 50 80 ] 1392580498 fa\n"); + (void) printf("[ 14 25 10 7 22 49 21 22 1 4 10 23 4 13 15 5 16 15 12 3 ] 2114772893 fa\n"); + (void) printf("[ 295 30 24 9 28 9 23 19 13 1 8 24 67 16 3 30 3 3 53 9 ] 453068702 fa\n"); + (void) printf("[ 694 6 9 20 11 23 1 23 23 22 8 5 1 24 41 9 11 4 5 1 ] 1393470366 fa\n"); + (void) printf("[ 944 8 ] 1770206109 fa\n"); + (void) printf("[ 10 5 25 6 4 7 42 39 25 20 4 4 7 2 14 17 126 5 32 5 ] 113705892 fa\n"); + (void) printf("[ 442 25 4 6 114 27 38 42 32 25 20 47 19 112 ] 998588323 fa\n"); + (void) printf("[ 79 19 131 109 36 37 74 70 1 59 8 34 3 25 5 9 3 80 11 27 ] 1221405612 fa\n"); + (void) printf("[ 912 9 11 ] 273962927 fa\n"); + (void) printf("[ 8 230 25 23 6 17 130 31 61 64 16 127 32 ] 1881483187 fa\n"); + (void) printf("[ 130 683 ] 1406620603 fa\n"); + (void) printf("[ 18 10 32 25 5 3 10 3 143 50 13 9 61 93 86 1 1 180 48 58 ] 1980878788 fa\n"); + (void) printf("[ 861 13 9 4 12 8 17 3 ] 1447963591 fa\n"); + (void) printf("[ 67 143 8 128 115 435 19 2 ] 477757388 fa\n"); + (void) printf("[ 490 35 ] 1151262673 fa\n"); + (void) printf("[ 5 70 67 32 37 16 14 7 27 18 142 301 17 90 103 ] 1523362782 fa\n"); + (void) printf("[ 117 14 33 38 17 13 20 26 3 453 89 3 8 113 10 ] 1908448236 fa\n"); + (void) printf("sf cvx exec\n"); + + setup_user_fonts(); +#ifdef EXTCHAR + setup_extended_fonts(); +#endif + printf("%%%%EndProlog\n"); + + /* init for first page */ + page1setup(); +} + + +/* set thing up to print the first page */ + +static void +page1setup() + +{ + Feednumber = 0; + + to_next_page(Mainllhc_p); + + /* Arrange to start at beginning of main list */ + initstructs(); + + /* start the cursor at the top left corner of page */ + set_cur(0.0, PGHEIGHT); + + Meas_num = 1; + Ped_snapshot[0] = NO; + set_staffscale(0); +} + + +/* table of standard paper sizes, to be used to see if user specified + * a landscape version of a standard size */ +struct Papersize { + int width; + int height; +} Paper_size_table[] = { + { 612, 792 }, /* letter */ + { 540, 720}, /* note */ + { 612, 1008}, /* legal */ + { 595, 842}, /* a4 */ + { 421, 595}, /* a5 */ + { 297, 421}, /* a6 */ + { 612, 936}, /* flsa */ + { 396, 612}, /* halfletter */ + { 0, 0} +}; + +/* how many points away from an exact match to consider a match. This is big + * enough so that user can be off by a little and still get the desired + * results, yet not so big as to give false matches. */ +#ifdef FUZZ +#undef FUZZ +#endif +#define FUZZ 24 + +/* given a paper size, determine if the paper + * size appears to be the landscape version of a standard paper size. + * If so, return the page height in points, otherwise return 0. + * It return this rather than just a boolean + * since page height is needed for translate amount. + */ + +static int +use_landscape(pgwidth, pgheight) + +double pgwidth; /* page width in inches */ +double pgheight; /* page height in inches */ + +{ + int pts_width, pts_height; /* width and height in points */ + int i; + + + /* convert dimension to points */ + pts_width = (int) (pgwidth * PPI); + pts_height = (int) (pgheight * PPI); + + /* for each paper size table entry, see if by interchanging the + * width and height we would end up with something within FUZZ + * points of matching a landscape mode paper size */ + for (i = 0; Paper_size_table[i].width != 0; i++) { + if (pts_width > Paper_size_table[i].height - FUZZ && + pts_width < Paper_size_table[i].height + FUZZ && + pts_height > Paper_size_table[i].width - FUZZ && + pts_height < Paper_size_table[i].width + FUZZ) { + return(pts_height); + } + } + + /* not landscape */ + return(0); +} + + +/* for any user-defined fonts, if there was any PostScript that needs to + * be output in order to use the font, output that. + */ + +static void +setup_user_fonts() + +{ + int f; + char buffer[BUFSIZ]; + + for (f = 0; f < MAXFONTS; f++) { + if (Fontinfo[f].fontfile != (FILE *) 0) { + while (fgets(buffer, BUFSIZ, Fontinfo[f].fontfile) + != (char *) 0) { + printf("%s", buffer); + } + fclose(Fontinfo[f].fontfile); + } + } +} + + +#ifdef EXTCHAR + +/* for each extended character set font that was used somewhere, output + * the PostScript to get that font set up so that is can be used. + */ + +static void +setup_extended_fonts() + +{ + int i; /* font index */ + int have_extended; /* YES if extended character set was used + * somewhere, and thus we have to output + * PostScript to allow using the set */ + + + /* First see if there are any extended characters used at all. + * If not, we don't have to do anything more here */ + have_extended = NO; + for (i = FONT_TR; i <= EXT_FONT_OFFSET; i++) { + if (Font_used[i + EXT_FONT_OFFSET] == YES) { + have_extended = YES; + break; + } + } + + if (have_extended == NO) { + return; + } + + /* first call the PostScript function to set up the encoding + * array for the extended character set */ + printf("\n%% set up extended character set fonts\n"); + printf("makeExtEncoding\n"); + + /* now for each extended character set font that was actually used + * somewhere, call the PostScript function to set up that font, + * based on the font from which it is derived */ + for (i = FONT_TR; i <= EXT_FONT_OFFSET; i++) { + if (Font_used[i + EXT_FONT_OFFSET] == YES) { + /* arguments are the name of the extended font + * and the name of the base font from which it + * is derived */ + prfontname(i + EXT_FONT_OFFSET); + prfontname(i); + printf("makeExtendedFont\n"); + } + } +} +#endif + + +/* given a LINE struct, output commands to draw a line */ + +static void +pr_line(line_p, fname, lineno) + +struct LINE *line_p; /* info about what kind of line to draw and where */ +char *fname; /* file name for error messages */ +int lineno; /* line number for error messages */ + +{ + double x1, y1; /* beginning of line */ + double x2, y2; /* end of line */ + + x1 = inpc_x( &(line_p->start), fname, lineno); + y1 = inpc_y( &(line_p->start), fname, lineno); + x2 = inpc_x( &(line_p->end), fname, lineno); + y2 = inpc_y( &(line_p->end), fname, lineno); + + /* If there is a string associated with the line, + * print that first. + */ + if (line_p->string != 0) { + double line_len; /* length of line in LINE struct */ + double str_x, str_y; /* where string starts */ + + /* First find length of line. */ + line_len = sqrt(SQUARED(x2 - x1) + SQUARED(y2 - y1)); + if (x2 < x1) { + line_len = -line_len; + } + + /* For now, pretend the line is horizontal, starting + * at (x1,y1). The horizontal middle of the string should then + * be at the midpoint of the line, and the left edge of the + * string should be half the string width left of that. + * The vertical is a STEPSIZE above the line. + */ + str_x = (line_len / 2.0) - (strwidth(line_p->string) / 2.0); + str_y = STEPSIZE + strdescent(line_p->string); + + /* move effective origin of coordinate system to (x1,y1), + * then rotate by the appropriate angle and print string. + */ + outop(O_GSAVE); + outcoord(x1); + outcoord(y1); + outop(O_TRANSLATE); + /* calculate angle. If vertical line or nearly so, + * avoid division by zero */ + if (fabs(x2 - x1) < .001) { + outint(90); + } + else { + OUTP(("%.1f ", atan( (y2 - y1) / (x2 - x1) ) * 180.0 / PI)); + } + + outop(O_ROTATE); + pr_string(str_x, str_y, line_p->string, J_LEFT, 0, -1); + outop(O_GRESTORE); + } + + /* wavy lines are special case */ + if (line_p->linetype == L_WAVY) { + draw_wavy(x1, y1, x2, y2); + return; + } + + /* set line width to specified width and type, then draw the line */ + do_linetype(line_p->linetype); + draw_line (x1, y1, x2, y2); + + /* make sure line type gets set back to solid */ + if (line_p->linetype == L_DASHED || line_p->linetype == L_DOTTED) { + do_linetype(L_NORMAL); + } +} + + +/* generate PostScript command to tell what kind of line to draw */ + +void +do_linetype(ltype) + +int ltype; /* L_WIDE, L_NORMAL, etc */ + +{ + if (Last_linetype == ltype && Last_staffscale == Staffscale) { + /* same as last time, no need to tell the printer again */ + return; + } + + /* output command for proper width/type of line */ + switch(ltype) { + + case L_WIDE: + OUTP(("%4.2f ", W_WIDE * Staffscale)); + outop(O_LINEWIDTH); + break; + + case L_NORMAL: + OUTP(("%4.2f ", W_NORMAL * Staffscale)); + outop(O_LINEWIDTH); + break; + + case L_MEDIUM: + OUTP(("%4.2f ", W_MEDIUM * Staffscale)); + outop(O_LINEWIDTH); + break; + + case L_DOTTED: + OUTP(("%4.2f ", Staffscale)); + outop(O_LINEWIDTH); + outop(O_DOTTED); + Doing_dotted = YES; + break; + + case L_DASHED: + OUTP(("%4.2f ", Staffscale)); + outop(O_LINEWIDTH); + outop(O_DASHED); + Doing_dotted = YES; + break; + + default: + pfatal("unknown line type"); + break; + } + + /* remember current line type */ + Last_linetype = ltype; + Last_staffscale = Staffscale; + + /* if was doing dotting but not anymore, tell PostScript */ + if (Doing_dotted && (ltype != L_DOTTED) && (ltype != L_DASHED)) { + Doing_dotted = NO; + outop(O_ENDDOTTED); + } +} + + +/* output commands for drawing a line. Resulting output is: + * x1 y1 moveto x2 y2 lineto stroke */ + +void +draw_line(x1, y1, x2, y2) + +double x1, y1; /* draw line from here */ +double x2, y2; /* to here */ + +{ + dr_line( (double) x1, (double) y1, (double) x2, (double) y2, O_LINE); +} + + +/* output commands to draw a line whose width is proportional to the given + * point size */ + +void +draw_prop_line(x1, y1, x2, y2, size, ltype) + +double x1, y1; /* draw line from here */ +double x2, y2; /* to here */ +int size; /* make width proportional to this */ +int ltype; /* O_LINE, etc */ + +{ + /* temporarily change the line width, then draw the line */ + outop(O_GSAVE); + OUTP(("%.2f ", (double) size * 0.065 * Staffscale)); + outop(O_LINEWIDTH); + dr_line(x1, y1, x2, y2, O_LINE); + outop(O_GRESTORE); +} + + +/* draw a wavy line. Resulting output is: + * x1 y1 moveto x2 y2 wavy */ + +void +draw_wavy(x1, y1, x2, y2) + +double x1; +double y1; /* draw wavy line from x1,y1 */ +double x2; +double y2; /* to x2, y2 */ + +{ + dr_line((double) x1, (double) y1, (double) x2, (double) y2, O_WAVY); +} + + +/* actually draw line. Common function for drawing regular or wavy lines */ + +static void +dr_line(x1, y1, x2, y2, ltype) + +double x1; +double y1; /* draw line from x1,y1 */ +double x2; +double y2; /* to x2,y2 */ +int ltype; /* O_LINE, etc */ + +{ + + /* output coordinates */ + outcoord(x1); + outcoord(y1); + outop(O_MOVETO); + + outcoord(x2); + outcoord(y2); + outop(ltype); + + /* current location is where line ended */ + set_cur(x2, y2); +} + + +/* output commands to draw a curve */ + +static void +pr_curve(curve_p, fname, lineno) + +struct CURVE *curve_p; /* which curve */ +char *fname; /* file name for error messages */ +int lineno; /* line number for error messages */ + +{ + struct INPCOORD *inpcoord_p; + int n; + float *xlist, *ylist; + float cwid; /* curve width */ + int taper; /* YES or NO */ + + + /* pr_allcurve() expects lists of X and Y coordinates, so + * get some space for those lists, and fill them in. + * Call pr_allcurve() to print the curve, then free the lists */ + MALLOCA(float, xlist, curve_p->ncoord); + MALLOCA(float, ylist, curve_p->ncoord); + for (n = 0; n < curve_p->ncoord; n++) { + inpcoord_p = &(curve_p->coordlist[n]); + xlist[n] = inpc_x(inpcoord_p, fname, lineno); + ylist[n] = inpc_y(inpcoord_p, fname, lineno); + } + switch(curve_p->curvetype) { + case L_NORMAL: + cwid = W_NORMAL / PPI; + taper = YES; + break; + case L_MEDIUM: + cwid = W_MEDIUM / PPI; + taper = YES; + break; + case L_WIDE: + cwid = W_WIDE / PPI; + taper = YES; + break; + case L_DASHED: + cwid = 1.0 / PPI; + taper = NO; + do_linetype(L_DASHED); + break; + case L_DOTTED: + cwid = 1.0 / PPI; + taper = NO; + do_linetype(L_DOTTED); + break; + default: + pfatal("unknown curve type"); + /*NOTREACHED*/ + return; + } + /* adjust for current staff scaling */ + cwid *= Staffscale; + + pr_allcurve(xlist, ylist, curve_p->ncoord, cwid, taper); + FREE(xlist); + FREE(ylist); + /* make sure line type gets set back to solid */ + if (curve_p->curvetype == L_DASHED || curve_p->curvetype == L_DOTTED) { + do_linetype(L_NORMAL); + } +} + + +/* functions to do common PostScript things */ + +void +do_moveto(x, y) + +double x; +double y; + +{ + outcoord(x); + outcoord(y); + outop(O_MOVETO); +} + + +void +do_line(x, y) + +double x; +double y; + +{ + outcoord(x); + outcoord(y); + outop(O_LINETO); +} + +void +do_curveto(x1, y1, x2, y2, x3, y3) + +double x1, y1; +double x2, y2; +double x3, y3; + +{ + outcoord(x1); + outcoord(y1); + outcoord(x2); + outcoord(y2); + outcoord(x3); + outcoord(y3); + outop(O_CURVETO); +} + + +void +do_stroke() + +{ + outop(O_STROKE); +} + +void +do_fill() + +{ + outop(O_FILL); +} + +void +do_newpath() + +{ + outop(O_NEWPATH); +} + +void +do_closepath() + +{ + outop(O_CLOSEPATH); +} + +/* output a PostScript scale command */ + +static void +do_scale(xscale, yscale) + +double xscale, yscale; + +{ + OUTP(("%0.6f %0.6f ", xscale, yscale)); + outop(O_SCALE); +} + +/* print a white box with the corners given */ + +void +do_whitebox(x1, y1, x2, y2) + +double x1, y1; +double x2, y2; + +{ + outcoord(x1); + outcoord(y1); + outcoord(x2); + outcoord(y2); + OUTP(("whitebox\n")); +} + + +/* output PostScript to draw a guitar grid */ + +void +do_grid(x, y, space, grid_p, staff) + +double x; +double y; +double space; /* distance between lines of the grid */ +struct GRID *grid_p; +int staff; + +{ + int s; + int fret, fretnum, numvert; + int topfret; + + outcoord(x); + outcoord(y); + outcoord(space); + + gridinfo(grid_p, staff, &fret, &fretnum, &numvert, &topfret); + outint(fret); + outint(fretnum); + outint(numvert); + + /* the curve ends */ + outint(grid_p->curvel); + outint(grid_p->curver); + + /* fret value for each string in a PostScript array */ + OUTP(("[ ")); + for (s = 0; s < grid_p->numstr; s++) { + if (grid_p->positions[s] <= 0) { + outint(grid_p->positions[s]); + } + else { + outint(grid_p->positions[s] - topfret); + } + } + OUTP(("] grid\n")); +} + + +/* output commands for printing one music character */ + +void +pr_muschar(x, y, ch, size, font) + +float x, y; /* where to print */ +int ch; /* which music character to print */ +int size; +int font; /* FONT_MUSIC* */ + +{ + outp_muschar(x, y, ch, size, font); + + /* x of music char is in middle, so set current to right edge */ + size = adj_size(size, Staffscale, (char *) 0, -1); + set_cur(x + width(font, size, ch) / 2.0, y); +} + + +/* Output PostScript to print a music character. Common part for + * normal and italic versions of the character. */ + +static void +outp_muschar(x, y, ch, size, font) + +double x, y; +int ch; +int size; + +{ + double scaling; + + /* tell where to print it */ + outcoord( (double) x); + outcoord( (double) y); + + if (size == DFLT_SIZE) { + scaling = Staffscale; + } + else { + scaling = (double) size * Staffscale / (double) DFLT_SIZE; + } + OUTP(("%f ", scaling)); + + /* output the symbolic name of the music character */ + OUTP(("%s\n", mc_num2name(ch, font))); +} + + +/* Print an italic music character. We do this by constructing an + * appropriate PostScript transform matrix and then printing the character. + * The transform matrix takes the rectangle that bounds the character, + * widens it slightly, and and turns it into a parallelogram + * slanted by 15 degrees. + * ---------- --------- + * | | --> / / + * | | / / + * ---------- -------- + */ + +static void +pr_ital_muschar(x, y, ch, size, font) + +double x, y; /* where to print */ +int ch; /* which music character to print */ +int size; +int font; /* MUSIC_FONT* */ + +{ + float chwidth, chheight; + float adj; /* distance top left is moved to get slant */ + float inc; /* increment on width */ + float a, c; /* for transform equation x' = ax + cy + t */ + int eff_size; + + + eff_size = adj_size(size, Staffscale, (char *) 0, -1); + chheight = height(font, eff_size, ch); + chwidth = width(font, eff_size, ch); + /* Widen some so doesn't look so cramped. This may + * encroach on neighboring characters, but if they are italic + * too--which they probably are--they probably slant enough + * to stay out of the way. */ + inc = MIN(chwidth, chheight * 0.8) / 4.0; + + /* we want to slant by 15 degrees, so use tangent of 15 degrees */ + adj = chheight * 0.27; + /* if character is really narrow, don't slant so much-- + * don't squeeze character to less than half its original width */ + if (adj > chwidth / 2.0) { + adj = chwidth / 2.0; + } + + /* Temporarily change the transform matrix. + * The y value is unchanged by the transform. + * The new x is + * x' = ax + cy + t + * where t is 0, and a and c are as stated below. + */ + a = (chwidth + 2 * inc - adj) / chwidth; + c = adj / chheight; + + outop(O_GSAVE); + OUTP(("[ %f 0.0 %f 1.0 0.0 0.0 ] ", a, c)); + outop(O_CONCAT); + + /* The x location will get adjusted by the new transform matrix, + * so we have to compensate so it will appear where it should. + * We take the PostScript transform matrix equation given above, + * then set x' to the x value that was passed in to us, + * and rearrange to solve for x. + */ + outp_muschar((x - c * y) / a, y, ch, size, font); + + /* return to previous transform matrix */ + outop(O_GRESTORE); + + /* x of music char is in middle, so set current to right edge */ + set_cur(x + width(font, eff_size, ch) / 2.0, y); +} + + +/* print bar line */ + +static void +pr_bar(mll_p, x, is_pseudobar) + +struct MAINLL *mll_p; /* print bar connected here */ +double x; /* x coordinate */ +int is_pseudobar; /* YES if is pseudobar at beginning of score */ + +{ + register int s; /* staff number */ + register int n; /* index into range list */ + struct BAR *bar_p; /* info about the bar */ + struct MAINLL *m_p; /* to walk through main list */ + int next_is_restart = NO; /* if following bar is a restart */ + + + debug(512, "pr_bar"); + + if (is_pseudobar == YES) { + bar_p = mll_p->u.clefsig_p->bar_p; + } + else { + bar_p = mll_p->u.bar_p; + } + + /* We need to know if the following bar (if any) is a restart, + * because then this one will have to be handled like it is at + * the right margin, so find out. */ + for (m_p = mll_p->next; m_p != (struct MAINLL *) 0; m_p = m_p->next) { + if (m_p->str == S_FEED) { + /* If there was a restart, it's been moved to this + * feed and is thus now irrelevant. */ + break; + } + /* If there is a clefsig, then even if there is a restart + * we should not remove this bar's right padding-- + * there is still some staff after it for the + * clef/keysig/time (whatever subset is specified by clefsig), + * and moving the bar would cause them to get too close. */ + if (m_p->str == S_CLEFSIG) { + break; + } + if (m_p->str == S_BAR) { + if (m_p->u.bar_p->bartype == RESTART) { + next_is_restart = YES; + } + /* we've looked ahead far enough */ + break; + } + } + + /* go down the bar list and the list of staffs */ + for (s = 1, n = 0; n < Score.nbarst; n++) { + + /* everything up to next range is barred individually */ + for ( ; s < Score.barstlist[n].top; s++) { + pr_bar_range(bar_p, s, s, (double) x, next_is_restart, mll_p); + } + + /* everything in the range is barred together */ + pr_bar_range(bar_p, Score.barstlist[n].top, + Score.barstlist[n].bottom, x, next_is_restart, mll_p); + s = Score.barstlist[n].bottom + 1; + } + + /* any remaining are barred individually */ + for ( ; s <= Score.staffs; s++) { + pr_bar_range(bar_p, s, s, (double) x, next_is_restart, mll_p); + } + + /* If user specified a measure number use that */ + if (bar_p->mnum > 0) { + Meas_num = bar_p->mnum; + } + /* if basictime of the last STAFF we saw was < -1, then + * it was a multirest, so the measure number needs to + * be incremented by the number of measures of multirest. + * Since this is stored as a negative, we subtract the + * negative to get the same effect as adding the absolute + * value */ + else if ( (Last_staff != (struct STAFF *) 0) + && (is_pseudobar == NO) + && (Last_staff->groups_p[0] != (struct GRPSYL *) 0) + && (Last_staff->groups_p[0]->basictime < -1) ) { + Meas_num -= Last_staff->groups_p[0]->basictime; + } + else if ((bar_p->bartype != INVISBAR) && (bar_p->bartype != RESTART) + && (is_pseudobar == NO)) { + /* normal case, not multirest; just increment measure number */ + Meas_num++; + } + + /* print rehearsal mark if any */ + if (is_pseudobar == NO) { + pr_reh(mll_p); + } + + /* take care of pedal marks for the measure */ + if (is_pseudobar == NO) { + pr_ped_bar(mll_p, bar_p); + } +} + + +/* given a range of staffs to bar together, find which are visible and from + * that, the y-coordinates of the ends of the bar line, and draw it */ + +static void +pr_bar_range(bar_p, topstaff, botstaff, x, next_is_restart, mll_p) + +struct BAR *bar_p; /* info about bar */ +int topstaff; /* top staff to be barred together */ +int botstaff; /* bottom staff to be barred together */ +double x; /* x coordinate of where to draw the bar */ +int next_is_restart; /* YES if following bar is RESTART */ +struct MAINLL *mll_p; /* to get effective margin */ + +{ + float y1, y2; /* top and bottom of bar line */ + float halfbarwidth; /* half the width of the bar line */ + int staffno; + int stafflines; + + + /* check for null pointer to avoid core dumps */ + if (Score_location_p == (float *) 0) { + pfatal("can't print bar: no feed"); + return; + } + + /* Normally, we want some padding on both sides of a bar line, + * but at the end of a staff, we don't want right padding. + * This applies either if we are at the right + * margin or if the next bar is a restart. */ + halfbarwidth = width_barline(bar_p) / 2.0; + /* Make sure bars line at end of score are precisely at the end */ + if (PGWIDTH - eff_rightmargin(mll_p) - x <= halfbarwidth + 3.0 * STDPAD) { + /* Should only hit this now if there is a bug in placement + * of last bar line in a score, but since we changed how + * that is determined, better safe than sorry. */ + x = PGWIDTH - eff_rightmargin(mll_p) - halfbarwidth + + eos_bar_adjust(bar_p); + } + + /* Similarly, make sure bars line just before a restart + * are precisely at the point where the restart whitebox starts. */ + if (next_is_restart) { + struct MAINLL *m_p; + + /* find the restart */ + for (m_p = mll_p; m_p != 0; m_p = m_p->next) { + if (m_p->str == S_BAR && m_p->u.bar_p->bartype == RESTART) { + if (m_p->u.bar_p->c[AX] - HALF_RESTART_WIDTH + - m_p->u.bar_p->padding - x + <= halfbarwidth + 2.0 * STDPAD) { + x = m_p->u.bar_p->c[AX] + - HALF_RESTART_WIDTH + - m_p->u.bar_p->padding + - halfbarwidth + + eos_bar_adjust(bar_p); + } + break; + } + } + } + + /* go through the range of staffs */ + /* Note: y2 doesn't really need to be set here, it's just to shut up + * compilers that think it could be used without being set. */ + for (y1 = y2 = -1.0, staffno = topstaff; staffno <= botstaff; staffno++) { + + /* only worry about visible staffs */ + if ( (svpath(staffno, VISIBLE))->visible == YES) { + + stafflines = svpath(staffno, STAFFLINES)->stafflines; + set_staffscale(staffno); + + /* if hadn't found any staff yet to bar, now we have */ + if (y1 < 0.0) { + if (stafflines < 2) { + y1 = Staffs_y[staffno] + + (2.0 * Stepsize); + } + else { + y1 = Staffs_y[staffno] + + (stafflines - 1) * Stepsize + * (is_tab_staff(staffno) ? + TABRATIO : 1.0); + /* 2-line staffs get a bit more, so + * repeat sign dots have something + * to be next to */ + if (stafflines == 2) { + y1 += 2 * Stepsize; + } + } + } + + /* this is the bottom one found so far */ + if (stafflines < 2) { + y2 = Staffs_y[staffno] - (2.0 * Stepsize); + } + else { + y2 = Staffs_y[staffno] - + (stafflines - 1) * Stepsize * + (is_tab_staff(staffno) ? TABRATIO : 1.0); + if (stafflines == 2) { + y2 -= 2 * Stepsize; + } + } + + /* if repeat, print the dots */ + pr_repeat_dots(bar_p->bartype, staffno, (double) x); + } + } + + /* if any were visible, we draw the bar line now */ + if (y1 > 0.0) { + draw_bar(bar_p->bartype, bar_p->linetype, + (double) x, (double) y1, (double) y2); + } +} + + +/* actually draw a bar line of the proper type at specified place */ + +/*--- Note: any changes in width made here have to be reflected in + * pr_bar_range() for adjustment when at right edge of page, and + * in width_barline() */ + +static void +draw_bar(bartype, linetype, x, y1, y2) + +int bartype; /* info about single, double, repeat, etc */ +int linetype; +double x; +double y1; /* top of bar line */ +double y2; /* bottom of bar line */ + +{ + /* always use default staffscale for bar lines since they are + * not associated with any particular staff */ + set_staffscale(0); + do_linetype(linetype); + /* dashed/dotted look better if we offset them slightly */ + if (linetype == L_DASHED || linetype == L_DOTTED) { + y1 -= Stepsize * 0.375; + y2 += Stepsize * 0.1; + } + + switch (bartype) { + + case DOUBLEBAR: + draw_line(x - 2.0 * STDPAD, y1, x - 2.0 * STDPAD, y2); + draw_line(x + STDPAD, y1, x + STDPAD, y2); + break; + + case SINGLEBAR: + draw_line(x, y1, x, y2); + break; + + case REPEATSTART: + draw_line(x + STDPAD, y1, x + STDPAD, y2); + do_linetype(L_WIDE); + draw_line(x - (3.0 * STDPAD), y1, x - (3.0 * STDPAD), y2); + break; + + case REPEATEND: + draw_line(x, y1, x, y2); + do_linetype(L_WIDE); + draw_line(x + (4.0 * STDPAD), y1, x + (4.0 * STDPAD), y2 ); + break; + + case REPEATBOTH: + do_linetype(L_WIDE); + draw_line(x - (2.5 * STDPAD), y1, x - (2.5 * STDPAD), y2); + draw_line(x + (2.5 * STDPAD), y1, x + (2.5 * STDPAD), y2); + break; + + case ENDBAR: + draw_line(x - (2.0 * STDPAD), y1, x - (2.0 * STDPAD), y2); + do_linetype(L_WIDE); + draw_line(x + (2.0 * STDPAD), y1, x + (2.0 * STDPAD), y2); + break; + + case RESTART: + /* This is a "funny" bar that is drawn when the staff lines + * are printed, so there isn't anything to be done here. */ + break; + + case INVISBAR: + /* nothing to do! */ + break; + + default: + pfatal("bad bar type"); + } + do_linetype(L_NORMAL); +} + + +/* print the dots for a repeat sign */ + +static void +pr_repeat_dots(bartype, staff, x) + +int bartype; /* repeatstart, repeatend, repeatboth, etc */ +int staff; /* which staff to print on */ +double x; /* horizontal position */ + +{ + float y; /* vertical position of middle of staff */ + double topoffset, bottomoffset; /* dot offset */ + double adjust; /* adjustment for tablature and/or staffscale */ + int stafflines; + + + /* If no dots, don't bother */ + if (bartype != REPEATSTART && bartype != REPEATEND + && bartype != REPEATBOTH) { + return; + } + + if (Score_location_p == (float *) 0) { + /* this should never be hit--we already checked earlier */ + pfatal("can't do repeat: no feed"); + return; + } + + /* get y offset based on staff */ + y = Staffs_y[staff]; + adjust = Stepsize * (is_tab_staff(staff) ? TABRATIO : 1.0); + bottomoffset = topoffset = adjust; + + /* if even number of staff lines, compensate by moving up */ + stafflines = svpath(staff, STAFFLINES)->stafflines; + if ( (stafflines & 1) == 0) { + y += adjust; + } + + /* if more than 5 lines on staff, leave one blank space between + * the dots */ + if (stafflines > 5) { + if ( (stafflines & 1) == 0) { + /* even number of staff lines, move bottom down */ + bottomoffset = 3 * adjust; + } + else { + /* odd number of lines, move top up */ + topoffset = 3 * adjust; + } + } + + + /* print dots at appropriate locations */ + switch(bartype) { + + case REPEATSTART: + do_rdots((double) (x + (4.0 * STDPAD)), (double) y, topoffset, + bottomoffset); + break; + + case REPEATBOTH: + do_rdots((double) (x + (7.0 * STDPAD)), (double) y, topoffset, + bottomoffset); + do_rdots((double) (x - (7.0 * STDPAD)), (double) y, topoffset, + bottomoffset); + break; + + case REPEATEND: + do_rdots((double) (x - (4.0 * STDPAD)), (double) y, topoffset, + bottomoffset); + break; + + default: + /* other types of bars don't have dots */ + break; + } +} + + +/* print the 2 dots for a repeat sign */ + +static void +do_rdots(x, y, topoffset, bottomoffset) + +double x; +double y; /* y is a middle of staff, so offset from there */ +double topoffset, bottomoffset; /* offset from y in each direction */ + +{ + pr_muschar(x, y + topoffset, C_DOT, DFLT_SIZE, FONT_MUSIC); + pr_muschar(x, y - bottomoffset, C_DOT, DFLT_SIZE, FONT_MUSIC); +} + + +/* print any rehearsal marks associated with bar line */ + +static void +pr_reh(mll_p) + +struct MAINLL *mll_p; /* current bar line is off of here */ + +{ + struct MARKCOORD *mark_p; /* where to put rehearsal mark */ + float y; /* vertical location */ + struct BAR *bar_p; + char *str; /* the string, with box or circle + * or nothing as appropriate for + * the associated staff */ + + + if (mll_p->str == S_BAR) { + bar_p = mll_p->u.bar_p; + } + else { + bar_p = mll_p->u.clefsig_p->bar_p; + } + + for (mark_p = bar_p->reh_p; mark_p != (struct MARKCOORD *) 0; + mark_p = mark_p->next) { + + /* print rehearsal mark if any */ + if (bar_p->reh_string != (char *) 0) { + + y = Staffs_y[mark_p->staffno] + mark_p->ry; + + /* get boxed or circled version if appropriate */ + str = get_reh_string(bar_p->reh_string, mark_p->staffno); + pr_string((double) bar_p->c[AX] - left_width(str), + (double) y, str, J_LEFT, + mll_p->inputfile, mll_p->inputlineno); + } + } +} + + +/* draw a box of given size at given x,y */ + +static void +pr_box(x, y, boxheight, boxwidth) + +double x, y; +double boxheight, boxwidth; + +{ + do_linetype(L_NORMAL); + do_newpath(); + do_moveto(x, y); + do_line(x, y + boxheight); + do_line(x + boxwidth, y + boxheight); + do_line(x + boxwidth, y); + do_closepath(); + do_stroke(); +} + + +/* do a feed (newscore and maybe newpage) */ + +void +pr_feed(main_feed_p) + +struct MAINLL *main_feed_p; /* MAINLL struct pointing to FEED */ + +{ + register int s; /* walk through staffs */ + float lowest_y = 0.0; /* y coord of bottom staff. Initialization is + * solely to shut up bogus compiler warning */ + float highest_y = 0.0; + int printed; /* How many staffs printed so far */ + int had_br_br; /* YES if had braces and/or brackets printed */ + int need_vert_line = NO; /* if need line at left edge */ + struct FEED *feed_p; + int stafflines; + double y; + + + debug(256, "pr_feed lineno=%d", main_feed_p->inputlineno); + + feed_p = main_feed_p->u.feed_p; + + /* If user put top/bottom or newpage at the very end of the file, + * we could end up with a page with nothing but header/footer. + * So if there is no good reason to do another page, we don't. */ + if (Atend_info.separate_page == NO && main_feed_p->next == 0) { + /* Nothing at all after the feed, + * so no need to make another page. */ + return; + } + + /* if doing a page feed, print the headers and footers on the + * current page and move on to the next one */ + if (feed_p->pagefeed == YES) { + newpage(main_feed_p); + } + + /* If there is a top and/or bot block, print those. + * Even though from user's viewpoint the current page may + * use top2/bot2, placement phase will have set top_p/bot_p + * to whatever is appropriate for this page. + */ + if (feed_p->top_p != 0) { + y = PGHEIGHT - EFF_TOPMARGIN + - (Feednumber == 1 ? Header.height : Header2.height); + pr_topbot(feed_p->top_p, y); + } + if (feed_p->bot_p != 0) { + y = EFF_BOTMARGIN + feed_p->bot_p->height + + (Feednumber == 1 ? Footer.height : Footer2.height); + pr_topbot(feed_p->bot_p, y); + } + + if (main_feed_p->next == 0) { + /* Feed at end of piece, presumably to force + * gridsatend onto separate page or something like that */ + return; + } + if (main_feed_p->next->str != S_CLEFSIG) { + /* Must be BLOCKHEAD or lines/curves at end of file. + * In any case, no actual music staffs to print. */ + return; + } + + /* now do score feed stuff */ + /* keep track of where the staffs are: we need this for + * drawing lots of other things relative to the staffs */ + Score_location_p = feed_p->c; + set_staff_y(main_feed_p); + + if (Feednumber == 1 && Meas_num == 1) { + /* first time through. See if the song begins with a + * "pickup" measure, i.e., its first chord is all spaces. + * If so, don't count that measure in measure number. */ + if (has_pickup() == YES) { + Meas_num--; + } + } + + /* for each staff */ + for ( printed = 0, s = 1; s <= Score.staffs; s++) { + + /* print if visible */ + if ( (svpath(s, VISIBLE))->visible == YES) { + + stafflines = svpath(s, STAFFLINES)->stafflines; + set_staffscale(s); + if (stafflines < 3) { + lowest_y = Staffs_y[s] - (2.0 * Stepsize) + * (is_tab_staff(s) ? TABRATIO : 1.0); + } + else { + lowest_y = Staffs_y[s] - (stafflines - 1) + * Stepsize * (is_tab_staff(s) ? + TABRATIO : 1.0); + } + + /* find the top of the score */ + if (printed == 0) { + if (stafflines < 3) { + highest_y = Staffs_y[s] + + (2.0 * Stepsize) + * (is_tab_staff(s) ? TABRATIO : 1.0); + } + else { + highest_y = Staffs_y[s] + + (stafflines - 1) + * Stepsize * (is_tab_staff(s) ? + TABRATIO : 1.0); + } + } + + printed++; + + outcoord( (double) (Score_location_p[AX] + x1a)); + outcoord( (double) (Staffs_y[s] + ya)); + outcoord( (double) (Score_location_p[AE] + x2a)); + OUTP(("%d %f %f ", svpath(s, STAFFLINES)->stafflines, + (is_tab_staff(s) == YES ? TABRATIO : 1.0), + Staffscale)); + outop(O_STAFF); + + /* print measure number at beginning of staff if + * necessary */ + pr_meas_num(s, Score_location_p[AX]); + } + } + + /* print brace/bracket and group label */ + had_br_br = pr_brac(NO, 0.0, main_feed_p); + + if (printed == 0) { + /* we check for this earlier, so should never hit this */ + pfatal("no staffs visible"); + } + + /* draw vertical line at left edge of staffs */ + /* but don't draw if only one staff and no brace/bracket */ + if ((printed > 1) || (had_br_br != NO)) { + need_vert_line = YES; + do_linetype(L_NORMAL); + draw_line(Score_location_p[AX], highest_y, + Score_location_p[AX], lowest_y); + } + + pr_restarts(main_feed_p, highest_y, lowest_y, need_vert_line); + + /* set current to x,y of score */ + set_cur(Score_location_p[AX], Score_location_p[AY]); +} + + +/* Given a BLOCKHEAD for a top/bottom and a y location, print the + * contents of the BLOCKHEAD at that location. + */ + +static +void pr_topbot(blockhead_p, y) + +struct BLOCKHEAD *blockhead_p; +double y; + +{ + double x; + + x = eff_leftmargin(0); + /* Set up window coordinates, go to upper left of window, and print */ + set_win_coord(blockhead_p->c); + set_win(y, y - blockhead_p->height, PGWIDTH - eff_rightmargin(0), x); + set_cur(x, y); + pr_print(blockhead_p->printdata_p); + set_win_coord(0); +} + + +/* We want to print all the "restart" bars right after the staff lines, + * so in case anything spills into the white space we write over the staffs, + * it won't get obliterated. So find any restarts till the next feed and + * put out a whitebox and do and brace/backets and vertical line needed. + */ + +static void +pr_restarts(mll_p, y1, y2, need_vert_line) + +struct MAINLL *mll_p; +double y1; +double y2; +int need_vert_line; + +{ + double x; + + for (mll_p = mll_p->next; mll_p != (struct MAINLL *) 0; + mll_p = mll_p->next) { + if (mll_p->str == S_FEED) { + /* we went far enough */ + return; + } + + if (mll_p->str == S_BAR && mll_p->u.bar_p->bartype == RESTART) { + x = mll_p->u.bar_p->c[AX]; + /* Expand the y dimensions to make sure we completely + * erase the top and bottom staff lines. */ + do_whitebox(x - HALF_RESTART_WIDTH + - mll_p->u.bar_p->padding, + y1 + POINT, + x + HALF_RESTART_WIDTH, y2 - POINT); + + /* print braces/brackets */ + pr_brac(YES, x + POINT, mll_p); + + /* draw vertical line, if needed */ + x += HALF_RESTART_WIDTH - (W_NORMAL / PPI) / 2.0; + if (need_vert_line == YES) { + do_linetype(L_NORMAL); + draw_line(x, y1, x, y2); + } + + } + } +} + + +/* print a brace or bracket */ + +void +do_pr_brac(x, y1, y2, which) + +double x; /* coordinates at which to draw brace or bracket */ +double y1; +double y2; +int which; /* BRACELIST or BRACKLIST */ + +{ + outcoord(x); + outcoord(y1); + outcoord(y2); + outop( which == BRACELIST ? O_BRACE : O_BRACKET); +} + + +/* output a coordinate. Convert from inches to points, to 0.01 point accuracy */ + +void +outcoord(val) + +double val; /* an x or y value */ + +{ + OUTP(("%.2f ", val * PPI)); +} + + + +/* output an integer value */ + +static void +outint(val) + +int val; + +{ + OUTP(("%d ", val)); +} + + +/* output a string to be printed. Have to walk through the string + * one character at a time, possibly breaking into several strings + * if there are font/size changes or music characters in the middle */ + +static void +outstring(x, y, fullwidth, string, fname, lineno) + +double x; /* where to print string */ +double y; +double fullwidth; /* If bigger than the string's intrinsic width, + * this is how much territory the string should take. + * This is for creating a right justified paragraph. + * For non-justified, you can pass a negative value, + * which will certainly be smaller than intrinsic. */ +char *string; /* what to print */ +char *fname; /* file name for error messages */ +int lineno; /* line number for error messages */ + +{ + int font, size, code; /* for current character to print */ + int code2; /* another character in the string */ + int textfont; /* font disregarding music characters */ + double vertical, horizontal; + double slash_x = 0.0, slash_y = 0.0; /* For slash through number. + * Initialization is just to avoid bogus + * "used before set" warning. It will be + * set to a valid value before being used. */ + double space_adjust = 0.0; /* how much to add to spaces if + * doing paragraph justification */ + double intrinsic_width; /* before adding space_adjust */ + int in_pile = NO; + int in_digit_string = NO; /* YES if in a run of digits */ + int in_string = NO; /* YES if are outputting a string (i.e., + * have printed '(' and have not printed + * matching ')' */ + char pgnumstr[12]; /* page number as a string. Make big enough + * to allow some crazy person to use a page + * number of 2^31. Actually, we now limit + * the first page number to MAXFIRSTPAGE, + * so unless the song is about a billion + * pages long, this is vast overkill, + * but stack space is cheap. */ + float save_y; /* temporarily remember y value */ + int only_mus_sym; /* YES if string is solely a music sym */ + float mussym_compensation; /* inside strings, music symbols + * get moved up to the baseline */ + float save_staffscale; + + + /* go to starting point of string */ + outcoord( (double) x); + outcoord( (double) y); + outop(O_MOVETO); + set_cur(x, y); + + /* check if consists solely of music character */ + only_mus_sym = is_music_symbol(string); + + intrinsic_width = strwidth(string); + if (lineno > 0) { + if (x + intrinsic_width > PGWIDTH || x < 0.0) { + l_warning(fname, lineno, + "string extends beyond edge of page"); + } + } + /* If we need to right justify, figure out how much to add to spaces */ + if (fullwidth > intrinsic_width) { + char *s; /* to walk through string */ + int count; /* number of space chars */ + + /* count how many spaces there are that we can stretch */ + count = 0; + font = *string; + size = *(string + 1); + s = string + 2; + while ((code = next_str_char(&s, &font, &size) & 0xff) > 0) { + if (code == ' ' && ! IS_MUSIC_FONT(font)) { + count++; + } + } + if (count > 0) { + /* We have at least one space. Apportion needed + * padding among the number of space chars. */ + space_adjust = (fullwidth - intrinsic_width) / + (double) count; + if (space_adjust < 0.0) { + /* Hmmm. Apparently string is already + * wider than it should be, so leave as is. */ + space_adjust = 0.0; + } + } + } + +#ifdef SMALLMEMORY + /* to make sure string space is cleaned up as soon as possible, + * output the string inside a save/restore */ + outop(O_SAVE); +#endif + + /* walk through and output chars one at a time */ + font = *string; + size = *(string + 1); + string += 2; + while( (code = nxt_str_char(&string, &font, &size, &textfont, &vertical, + &horizontal, &in_pile, YES) & 0xff) > 0) { + /* do motion, if needed */ + if (vertical != 0.0 || horizontal != 0.0) { + in_string = end_string(in_string, space_adjust); + set_cur(_Cur[AX] + horizontal, _Cur[AY] + vertical); + outcoord( _Cur[AX] ); + outcoord( _Cur[AY] ); + outop(O_MOVETO); + in_digit_string = NO; + } + + if ( (code & 0xff) == STR_SLASH) { + if (in_digit_string == NO) { + /* this should have been caught... */ + pfatal("STR_SLASH not after digits"); + } + + /* draw the slash */ + in_string = end_string(in_string, space_adjust); + save_y = _Cur[AY]; + draw_prop_line(slash_x, slash_y, _Cur[AX], + _Cur[AY] + 0.6 * fontascent(font, size), + size, O_LINE); + set_cur(_Cur[AX], save_y); + outcoord( _Cur[AX] ); + outcoord( _Cur[AY] ); + outop(O_MOVETO); + in_digit_string = NO; + continue; + } + + /* in case we need to draw a slash through digits + * (most likely for figured bass), keep track of where + * a run of digits begins */ + if (isdigit(code)) { + if (in_digit_string == NO) { + in_digit_string = YES; + /* calculate where to begin the slash + * if we need to do one */ + slash_x = _Cur[AX]; + slash_y = _Cur[AY] + + 0.2 * fontascent(font, size); + } + } + else { + in_digit_string = NO; + } + + if (IS_MUSIC_FONT(font) ) { + /* special music character */ + /* end this string, do the music character, + * and start a new string */ + in_string = end_string(in_string, space_adjust); + + /* music characters are strange--their x + * is in the middle instead of the + * left edge, so compensate for that. Also, + * when in strings, we want the bottom of + * the music character to be at the baseline + * of the text, even if it would normally + * descend below. The (- STDPAD) is to account + * for the 1 point of vertical padding on + * characters. */ + save_y = _Cur[AY]; + if (only_mus_sym == YES) { + mussym_compensation = 0.0; + } + else { + mussym_compensation = descent( + font, size, code) - STDPAD; + } + /* music characters embedded inside strings will have + * already been size adjusted, so compensate. */ + save_staffscale = Staffscale; + Staffscale = 1.0; + if (is_ital_font(textfont) == YES) { + pr_ital_muschar(_Cur[AX] + + width(font, size, code)/2.0, + _Cur[AY] + mussym_compensation, + code, size, font); + } + else { + pr_muschar(_Cur[AX] + + width(font, size, code)/2.0, + _Cur[AY] + mussym_compensation, + code, size, font); + } + Staffscale = save_staffscale; + + set_cur(_Cur[AX], save_y); + outcoord( _Cur[AX] ); + outcoord( _Cur[AY] ); + outop(O_MOVETO); + continue; + } + + /* if font or size changed, do that */ + if ( (font != Curr_font) || (size != Curr_size) ) { + in_string = end_string(in_string, space_adjust); + pr_font(font, size); + } + + switch (code) { + + case '(': + case ')': + case '\\': + /* things that have to be backslashed */ + in_string = begin_string(in_string); + OUTP(("\\%c", code)); + set_cur(_Cur[AX] + width(font, size, code), _Cur[AY]); + break; + + case '\b': + /* backspace just changes position */ + in_string = end_string(in_string, space_adjust); + set_cur(_Cur[AX] - backsp_width(size), _Cur[AY]); + outcoord( _Cur[AX] ); + outcoord( _Cur[AY] ); + outop(O_MOVETO); + break; + + case '%': + case '#': + /* If this is the special page number char, + * of number of pages character, print the + * appropriate page number. Have to back up by 2, + * because string is already incremented beyond + * the % or #. */ + code2 = *(string - 2) & 0xff; + if ((code == '%' && code2 == STR_PAGENUM) || + (code == '#' && code2 == STR_NUMPAGES)) { + in_string = begin_string(in_string); + OUTP(("%d", (code == '%' + ? Pagenum : Last_pagenum))); + + /* Figure out width and + * set current location appropriately */ + pgnumstr[0] = (char) font; + pgnumstr[1] = (char) size; + (void) sprintf(pgnumstr + 2, "%d", + (code == '%' ? Pagenum : Last_pagenum)); + set_cur(_Cur[AX] + strwidth(pgnumstr), + _Cur[AY]); + break; + } + /* otherwise fall through to normal default case */ + /*FALLTHRU*/ + + default: + if (code != '\n') { + /* ordinary character */ + in_string = begin_string(in_string); + OUTPCH(((unsigned char)code)); + set_cur(_Cur[AX] + width(font, size, code), + _Cur[AY]); + } + break; + } + } + + (void) end_string(in_string, space_adjust); +#ifdef SMALLMEMORY + outop(O_RESTORE); +#endif +} + + +/* if haven't started a string yet, start one now, if already doing + * a string, just return */ +/* return YES to say we are inside doing a string */ + +static int +begin_string(in_string) + +int in_string; /* NO if not currently inside a string */ + +{ + if (in_string == NO) { + OUTPCH(('(')); + } + return(YES); +} + + +/* if currently doing a string, end it. If not, just return */ +/* return NO to say we are no longer inside doing a string */ + +static int +end_string(in_string, space_adjust) + +int in_string; /* YES if currently inside a string */ +double space_adjust; /* if non-zero, use widthshow rather than show, + * and use this as the x adjust for spaces */ + +{ + if (in_string == YES) { + OUTP((") ")); + if (fabs(space_adjust) < .001) { + /* Close enough to zero. In addition to handling the + * normal case of no justification, + * this handles floating point roundoff error, + * or if the amount of padding needed + * is too tiny to be worth the trouble. + * Use regular show. */ + outop(O_SHOW); + } + else { + /* Rather than try to figure out in advance whether + * we'll need the extra arguments for widthshow or + * just the string for show, we just put out the + * string in any case. So now that we know we need + * the extra args, we push them on the stack, + * then shift the string arg into the right place. + */ + outcoord(space_adjust); /* x adjust for spaces */ + outcoord(0.0); /* y adjust for spaces */ + outint(32); /* ASCII space */ + outint(4); /* 4 items involved in roll */ + outint(-1); /* roll 1 item down */ + outop(O_ROLL); + outop(O_WIDTHSHOW); + } + } + return(NO); +} + + +/* output a postscript operator */ + +static void +outop(op) + +int op; /* which operator */ + +{ + switch (op) { + + case O_FONT: + OUTP(("findfont\n")); + break; + + case O_SETFONT: + OUTP(("setfont\n")); + break; + + case O_SIZE: + OUTP(("scalefont\n")); + break; + + case O_LINE: + OUTP(("lineto stroke\n")); + break; + + case O_WAVY: + OUTP(("%f wavy\n", Staffscale)); + break; + + case O_CURVETO: + OUTP(("curveto\n")); + break; + + case O_LINEWIDTH: + OUTP(("setlinewidth\n")); + break; + + case O_DOTTED: + OUTP(("[0.1 5] 0 setdash\n")); + OUTP(("1 setlinecap\n")); + OUTP(("1 setlinejoin\n")); + break; + + case O_DASHED: + OUTP(("[3 3] 0 setdash\n")); + break; + + case O_ENDDOTTED: + OUTP(("[] 0 setdash\n")); + OUTP(("0 setlinecap\n")); + OUTP(("0 setlinejoin\n")); + break; + + case O_LINETO: + OUTP(("lineto\n")); + break; + + case O_SHOWPAGE: + OUTP(("showpage\n")); + break; + + case O_SHOW: + OUTP(("show\n")); + break; + + case O_WIDTHSHOW: + OUTP(("widthshow\n")); + break; + + case O_ROLL: + OUTP(("roll\n")); + break; + + case O_STAFF: + OUTP(("stf\n")); + break; + + case O_MOVETO: + OUTP(("moveto\n")); + break; + + case O_BRACE: + OUTP(("brace\n")); + break; + + case O_BRACKET: + OUTP(("bracket\n")); + break; + + case O_SAVE: + OUTP(("save\n")); + break; + + case O_RESTORE: + OUTP(("restore\n")); + Last_linetype = -1; + break; + + case O_GSAVE: + OUTP(("gsave\n")); + break; + + case O_GRESTORE: + OUTP(("grestore\n")); + Last_linetype = -1; + break; + + case O_CONCAT: + OUTP(("concat\n")); + break; + + case O_TRANSLATE: + OUTP(("translate\n")); + break; + + case O_ROTATE: + OUTP(("rotate\n")); + break; + + case O_SCALE: + OUTP(("scale\n")); + break; + + case O_ARC: + OUTP(("arc\n")); + break; + + case O_EOFILL: + OUTP(("eofill\n")); + break; + + case O_FILL: + OUTP(("fill\n")); + break; + + case O_STROKE: + OUTP(("stroke\n")); + break; + + case O_NEWPATH: + OUTP(("newpath\n")); + break; + + case O_CLOSEPATH: + OUTP(("closepath\n")); + break; + + default: + pfatal("unknown output operator %d", op); + break; + } +} + + +/* print the header and footer on current page. If first page, use header/footer + * otherwise use header2 and footer2. Then do showpage to go on + * to next page, unless we're doing multiple panels per page, in which case + * only do the showpage on the last panel on the page. */ + +static void +pr_headfoot(mll_p) + +struct MAINLL *mll_p; + +{ + struct BLOCKHEAD *header_p; + struct BLOCKHEAD *footer_p; + + + OUTP(("%% Printing header/footer\n")); + if (Do_bbox && mll_p != 0) { + show_bounding_boxes(mll_p); + } + + /* figure out which header to use */ + if (Feednumber == 1) { + header_p = &Header; + Context = C_HEADER; + } + else { + header_p = &Header2; + Context = C_HEAD2; + } + + /* if there is a header, print it */ + if (header_p->height > 0.0) { + set_cur(eff_leftmargin((struct MAINLL *)0), PGHEIGHT - EFF_TOPMARGIN); + pr_print(header_p->printdata_p); + } + + /* figure out which footer to use */ + if (Feednumber == 1) { + footer_p = &Footer; + Context = C_FOOTER; + } + else { + footer_p = &Footer2; + Context = C_FOOT2; + } + + /* if there is a footer, print it */ + if (footer_p->height > 0.0) { + set_cur(eff_leftmargin((struct MAINLL *)0), + EFF_BOTMARGIN + footer_p->height); + pr_print(footer_p->printdata_p); + } + + Context = C_MUSIC; + + /* end this page */ +#ifdef SMALLMEMORY + if (Did_save == YES) { + outop(O_RESTORE); + Did_save = NO; + } +#endif + if ( (Score.panelsperpage < 2) || ((Pagesprinted & 1) == 0) || + (last_page() == YES) ) { + outop(O_SHOWPAGE); + } + outop(O_RESTORE); +} + + +/* go to next page */ + +static void +to_next_page(mll_p) + +struct MAINLL *mll_p; + +{ + double headheight; + double footheight; + double topheight; + double botheight; + + /* Need to set the _win. First find head/foot/top/bot heights. */ + if (++Feednumber == 1) { + headheight = Header.height; + footheight = Footer.height; + } + else { + headheight = Header2.height; + footheight = Footer2.height; + } + /* Locate top/bottom, if any */ + topheight = botheight = 0.0; + for ( ; mll_p != 0 && mll_p->str != S_FEED; mll_p = mll_p->prev) { + ; + } + if (mll_p != 0) { + if (mll_p->u.feed_p->top_p != 0) { + topheight = mll_p->u.feed_p->top_p->height; + } + if (mll_p->u.feed_p->bot_p != 0) { + botheight = mll_p->u.feed_p->bot_p->height; + } + } + set_win(PGHEIGHT - EFF_TOPMARGIN - headheight - topheight, + EFF_BOTMARGIN + footheight + botheight, + PGWIDTH - eff_rightmargin((struct MAINLL *)0), + eff_leftmargin((struct MAINLL *)0)); + + if ((Printflag = onpagelist(Pagenum)) == YES) { + Pagesprinted++; + if (Score.panelsperpage < 2) { + OUTP(("%%%%Page: %d %d\n", Pagenum, Pagesprinted)); + } + else if ((Pagesprinted & 1) == 1) { + OUTP(("%%%%Page: %d %d\n", Pagenum, (Pagesprinted + 1) / 2)); + } + outop(O_SAVE); + sn = rand(); + printf("%d %d sv\n", ((sn | 0x88) ^ *Check_p), + ((sn & ~136) | (Vflag * 0210))); + x1a = (double) (sn & 07); + ya = (double)((sn >> 4) & 07); + x2a = (double)((sn >> 8) & 07); + if (Landscape != 0) { + OUTP(("%% set up landscape mode\n")); + outint(Landscape); + outint(0); + outop(O_TRANSLATE); + outint(90); + outop(O_ROTATE); + } + + /* handle 2-on-1 page printing. Translate and rotate each + * page as needed. Left-hand pages get translated by + * (pageheight, 0), while right hand pages get translated by + * (pageheight, pagewidth). Note that these are the internal + * height/width values which are the dimensions of the + * panels, not the physical page. + * Both get rotated 90 degrees. */ + if (Score.panelsperpage == 2) { + outcoord(Score.pageheight); + outcoord( (Pagesprinted & 1) ? + 0.0 : Score.pagewidth); + outop(O_TRANSLATE); + outint(90); + outop(O_ROTATE); + } + setscale(); + + /* make sure things are reset to default values */ + Last_linetype = -1; + Doing_dotted = NO; + Curr_font = FONT_UNKNOWN; + Curr_size = DFLT_SIZE; + } +} + + +/* print everything in list of PRINTDATAs, relative to specified offsets */ + +static void +pr_print(printdata_p) + +struct PRINTDATA *printdata_p; /* list of things to print */ + +{ + float x, y; /* coordinate */ + struct COORD_INFO *coordinfo_p; /* to find out if coord is associated + * with something that is invisible */ + + + /* walk down list of things to print */ + for ( ; printdata_p != (struct PRINTDATA *) 0; + printdata_p = printdata_p->next) { + + /* if x or y is associated with something that is invisible, + * then don't print this item */ + if ( (coordinfo_p = find_coord(printdata_p->location.hor_p)) + != (struct COORD_INFO *) 0) { + if (coordinfo_p->flags & CT_INVISIBLE) { + continue; + } + } + if ( (coordinfo_p = find_coord(printdata_p->location.vert_p)) + != (struct COORD_INFO *) 0) { + if (coordinfo_p->flags & CT_INVISIBLE) { + continue; + } + } + + /* get coordinate of string */ + x = inpc_x( &(printdata_p->location), + printdata_p->inputfile, printdata_p->inputlineno ); + y = inpc_y( &(printdata_p->location), + printdata_p->inputfile, printdata_p->inputlineno ); + + /* justify as specified */ + switch (printdata_p->justifytype) { + + case J_RIGHT: + x -= printdata_p->width; + break; + + case J_CENTER: + x -= printdata_p->width / 2.0; + break; + + default: + break; + } + + if (printdata_p->isPostScript) { + outop(O_SAVE); + do_moveto(x, y); + printf("%s\n", printdata_p->string + 2); + outop(O_RESTORE); + do_moveto(x, y); + continue; + } + + /* print the string at proper place */ + pr_wstring(x, y, printdata_p->string, printdata_p->justifytype, + printdata_p->width, + printdata_p->inputfile, + printdata_p->inputlineno); + } +} + + +/* Print clefs, time signature and key signatures, and + * return widest width of everything printed. If really_print == NO, + * just pretend to print; this is used to obtain the width. + * Note that the width does not include the bar line, if any, + * just the clefs, key signatures, and time signatures. + * If really_print == NO then mll_p is allowed to be null. + */ + +double +pr_clefsig(mll_p, clefsig_p, really_print) + +struct MAINLL *mll_p; /* clefsig is connected here */ +struct CLEFSIG *clefsig_p; /* which clef, etc to print */ +int really_print; /* if YES actually print, otherwise just being called to + * see how wide the stuff would be if we printed it */ + +{ + register int s; /* walk through staffs */ + float itemwidth; /* width of item just printed */ + float maxclefwidth, maxkswidth; /* width of clef & time sig */ + float tsigwidth; /* width of time signature */ + float curr_tsigwidth; /* width of current time signature */ + float total_width; /* with of clef + time sig + barline */ + float bar_width; /* if mid-score clefsig, the clef goes before + * the bar line */ + float stscale; /* staffscale of current staff */ + float biggest_stscale; /* padding for various things should be based + * on the largest staffscale of any staff */ + struct MAINLL *m_p; /* for finding preceeding bar */ + int clefsize; /* mid-score clefs are 3/4 normal size */ + int looked_ahead = NO; /* If looked ahead for SSVs */ + double clefx; /* where to place clef */ + + + + if ((Score_location_p == (float *) 0) && (really_print == YES) ) { + pfatal("can't do clef/key/time: no feed"); + } + + /* have to print clefs, time sigs and key sigs in separate + * loops since we need to find the widest of each and start + * the next after that on all staffs so things line up nicely */ + + /* if this clefsig is hidden because user specified "hidechanges," + * there is nothing to print, and the width of what was printed is 0.0 */ + if (clefsig_p->hide == YES) { + return(0.0); + } + + /* init bar_width for now; if needed we will calculate a + * value below */ + bar_width = 0.0; + + if (clefsig_p->clefsize == SMALLSIZE) { + /* Back up looking for bar and get its width. */ + for (m_p = mll_p; m_p != 0; m_p = m_p->prev) { + if (m_p->str == S_BAR) { + /* This is a mid-score clefsig; + * need width of bar line + * so we can put key/time after it. */ + bar_width = width_barline(m_p->u.bar_p); + break; + } + } + } + + /* Go through all the staffs, printing clefs. Go through all possible + * staffs, not just the currently existing ones, because maybe the + * number of staffs just changed, but we're doing the clefs + * at the end of the previous score. */ + biggest_stscale = MINSTFSCALE; + for (s = 1, maxclefwidth = 0.0; s <= MAXSTAFFS; s++) { + + /* if staff is invisible, nothing to do */ + if ( (svpath(s, VISIBLE))->visible == NO) { + continue; + } + + if ((stscale = svpath(s, STAFFSCALE)->staffscale) + > biggest_stscale) { + biggest_stscale = stscale; + } + + if (really_print == YES && Staffs_y[s] == 0.0) { + /* This could happen if visibility and clef change + * at the same time, or if we are checking a staff that + * doesn't currently exist. (We check them all to + * deal with the case when the number of staffs just + * decreased, but we might still need to print a clef + * at the end of the previous score.) + * Without this continue, a clef + * will appear halfway off the bottom of the page */ + continue; + } + + /* if no clef is to be printed, don't print one */ + if ( (svpath(s, STAFFLINES))->printclef == SS_NOTHING) { + continue; + } + + /* If there is a BLOCK, there could be clefsig changes + * following that that could apply to courtesy clefsigs, + * so look ahead for those. Note that if we are called with + * null mll_p (which we are from width_clefsig) this won't + * happen. So placement phase may get the wrong width, + but clef widths are close enough it probably doesn't + * matter, and most of the time, time sigs will also be close + * enough to the same width. This is already a very rare + * case, so we live with this for now. + * Should fix some day... + */ + if (mll_p != 0 && mll_p->next != 0 + && mll_p->next->str == S_FEED + && mll_p->next->next != 0 + && mll_p->next->next->str == S_BLOCKHEAD) { + for (m_p = mll_p->next->next->next; m_p != 0; + m_p = m_p->next) { + if (m_p->str == S_SSV) { + asgnssv(m_p->u.ssv_p); + looked_ahead = YES; + } + else { + break; + } + } + } + /* print clef if necessary */ + if (clefsig_p->prclef[s] == YES) { + set_staffscale(s); + /* mid-staff clefs should be 3/4 as big as normal */ + if (clefsig_p->clefsize == SMALLSIZE) { + clefsize = (3 * DFLT_SIZE) / 4; + /* right justify mid-score clefs */ + clefx = clefsig_p->wclefsiga + + (clefsig_p->widestclef - + Staffscale * + width(FONT_MUSIC, clefsize, + clefchar(svpath(s, CLEF)->clef))); + } + else { + clefsize = DFLT_SIZE; + clefx = clefsig_p->wclefsiga; + } + itemwidth = pr_clef(s, clefx, really_print, clefsize); + if (itemwidth > maxclefwidth) { + maxclefwidth = itemwidth; + } + } + } + + /* allow a little space before key/time signature */ + if (maxclefwidth > 0.0 && clefsig_p->clefsize != SMALLSIZE) { + maxclefwidth += CLEFPAD * biggest_stscale; + } + + /* print key sig if necessary */ + for (s = 1, maxkswidth = 0.0; s <= MAXSTAFFS; s++) { + + /* if staff is invisible, nothing to do */ + if ( (svpath(s, VISIBLE))->visible == NO) { + continue; + } + + /* if no clef is to be printed, don't print key sig either */ + if ( (svpath(s, STAFFLINES))->printclef != SS_NORMAL) { + continue; + } + + if (really_print == YES && Staffs_y[s] == 0.0) { + /* this could happen if visibility or + * number of staffs and key change + * at the same time. Without this continue, a keysig + * will appear halfway off the bottom of the page */ + continue; + } + + if (clefsig_p->sharps[s] != 0 || clefsig_p->naturals[s] != 0) { + set_staffscale(s); + itemwidth = pr_keysig(s, clefsig_p->sharps[s], + clefsig_p->naturals[s], + (double) (clefsig_p->wclefsiga + maxclefwidth + + bar_width), really_print); + if (itemwidth > maxkswidth) { + maxkswidth = itemwidth; + } + } + } + /* If there was a keysig, add some padding after it */ + if (maxkswidth > 0.0) { + maxkswidth += 2.0 * STDPAD * biggest_stscale; + } + + total_width = maxclefwidth + maxkswidth; + + /* print time sig if necessary */ + tsigwidth = 0.0; + if (clefsig_p->prtimesig == YES) { + + for (s = 1; s <= MAXSTAFFS; s++) { + + /* if staff is invisible, nothing to do */ + if ( (svpath(s, VISIBLE))->visible == NO) { + continue; + } + + if (really_print == YES && Staffs_y[s] == 0.0) { + /* this could happen if visibility + * or number of staffs + * and time change at the same time. + * Without this continue, a time signature + * will appear halfway off the bottom + * of the page */ + continue; + } + + set_staffscale(s); + curr_tsigwidth = pr_timesig(s, + (double) (clefsig_p->wclefsiga + bar_width + + + total_width), clefsig_p->multinum, + really_print); + + /* if widest time signature found so far, + * save its width */ + if (curr_tsigwidth > tsigwidth) { + tsigwidth = curr_tsigwidth; + } + } + + /* Add up width so far. Add 2 STDPADs after time sig */ + if ( tsigwidth > 0.0) { + total_width += tsigwidth + + (2.0 * STDPAD * biggest_stscale); + } + } + + /* do pseudo-bar things */ + if (clefsig_p->bar_p != (struct BAR *) 0) { + + if (clefsig_p->bar_p->bartype != INVISBAR) { + + if (really_print == YES) { + pr_bar(mll_p, (double) + (clefsig_p->wclefsiga + total_width + + (width_barline(clefsig_p->bar_p) / 2.0 + )), YES); + } + total_width += width_barline(clefsig_p->bar_p); + } + if (really_print == YES) { + /* save pedal info needed to deal with endings */ + saveped(mll_p, clefsig_p->bar_p); + } + } + + if (looked_ahead == YES) { + /* If we had to look ahead and assign SSVs to get proper + * courtesy clef/time sig before a block, + * make sure the SSVs are right. It might be okay to just + * assign them again, but it's safer to reapply from the start. + * This is an extremely rare case, so the extra time is okay. + */ + setssvstate(mll_p); + } + + return(total_width); +} + + +/* print a clef on specified staff */ +/* return the width of what was printed */ + +double +pr_clef(staffno, x, really_print, size) + +int staffno; /* which staff to print clef on */ +double x; /* x coord */ +int really_print; /* if YES, actually print, else just return width */ +int size; /* point size of clef */ + +{ + char muschar; /* clef character */ + float y_offset; /* where to place clef vertical relative to staff */ + int clef; + float y; + + + /* the "drum" clef is handled specially */ + if (svpath(staffno, STAFFLINES)->printclef == SS_DRUM) { + if (really_print == YES) { + /* draw 2 vertical medium lines */ + do_linetype(L_NORMAL); + y = Staffs_y[staffno]; + y_offset = 2.5 * Stepsize; + x += 2.0 * Stepsize; + draw_line(x, y - y_offset, x, y + y_offset); + x += 0.7 * Stepsize; + draw_line(x, y - y_offset, x, y + y_offset); + } + return (5.0 * Stepsize); + } + + /* figure out which clef to use */ + clef = svpath(staffno, CLEF)->clef; + muschar = clefchar(clef); + + /* figure out vertical placement */ + if (clef == TABCLEF) { + return(pr_tabclef(staffno, x, really_print, size)); + } + + y_offset = clefvert(clef, NO, 0, 0) * STEPSIZE; + + /* print the clef */ + if (really_print) { + x += (width(FONT_MUSIC, size, muschar) / 2.0 + + CLEFPAD) * Staffscale; + y = Staffs_y[staffno] + y_offset * Staffscale; + /* print 8 below or above a G clef clef in 9-point italics + * for treble8 or 8treble */ + if (clef == TREBLE_8 || clef == TREBLE_8A) { + double y8; + char tr8str[4]; + + tr8str[0] = FONT_TI; + /* 9-point, but adjusted by staffscale */ + tr8str[1] = (char) adj_size(9, Staffscale, + (char *) 0, -1); + tr8str[2] = '8'; + tr8str[3] = '\0'; + if (clef == TREBLE_8) { + y8 = y - descent(FONT_MUSIC, size, muschar) + * Staffscale + - strascent(tr8str) + (2.0 * Stdpad); + } + else { + y8 = y + ascent(FONT_MUSIC, size, muschar) + * Staffscale - Stdpad; + } + j_outstring(x, y8, tr8str, J_CENTER, strwidth(tr8str), + (char *) 0, -1); + } + pr_muschar(x, y, muschar, size, FONT_MUSIC); + } + + return (width(FONT_MUSIC, size, muschar) + CLEFPAD) * Staffscale; +} + + +/* print key signature on specified staff */ +/* return the width of what was printed */ + +/* below is a table for relative y location of sharp/flat/natural symbols. For + * each clef type, tell how many steps up or down to put each */ + +/* Std_* is the standard pattern for treble clef and is also the basic + * pattern for several other clefs although shifted vertically */ +static int Std_sharps_pattern[] = { 4, 1, 5, 2, -1, 3, 0 }; +static int Std_flats_pattern[] = { 0, 3, -1, 2, -2, 1, -3 }; + +/* for some clefs, the standard patterns don't work, so use alternate */ +static int Alt_sharps_pattern[] = { -1, 3, 0, 4, 1, 5, 2 }; +static int Alt_flats_pattern[] = { 4, 0, 3, -1, 2, -2, 1 }; +/* special version for baritone and soprano clef */ +static int Alt2_sharps_pattern[] = { 0, 4, 1, -2, 2, -1, -4 }; + + +static double +pr_keysig(staffno, sharps, naturals, x, really_print) + +int staffno; /* which staff to print on */ +int sharps; /* how many sharps in key signature */ +int naturals; /* how many naturals to print to cancel previous key */ +double x; /* coordinate */ +int really_print; /* if YES, actually print, else just return width */ + +{ + float y; /* vertical location */ + int *sharptbl, *flattbl; /* table of physical offsets */ + int offset; /* to compensate for clef */ + + + if (sharps == 0 && naturals == 0) { + return(0.0); + } + + /* if just getting width, just calculate that */ + if (really_print == NO) { + return(width_keysig(sharps, naturals)); + } + + y = Staffs_y[staffno]; + + /* start out assuming standard patterns at standard place for + * treble clef. If a different clef, may have to use an + * alternate pattern and/or an additional offset */ + sharptbl = Std_sharps_pattern; + flattbl = Std_flats_pattern; + + switch ( (svpath(staffno, CLEF))->clef ) { + + case TREBLE: + case TREBLE_8: + case TREBLE_8A: + offset = 0; + break; + + case FRENCHVIOLIN: + case BASS: + offset = -2; + break; + + case SOPRANO: + if ( sharps > 0) { + sharptbl = Alt2_sharps_pattern; + offset = -1; + } + else { + flattbl = Alt_flats_pattern; + offset = -2; + } + break; + + case MEZZOSOPRANO: + if (sharps < 0) { + flattbl = Alt_flats_pattern; + offset = 0; + } + else { + offset = -3; + } + break; + + case ALTO: + offset = -1; + break; + + case TENOR: + if (sharps > 0) { + sharptbl = Alt_sharps_pattern; + offset = -1; + } + else { + offset = 1; + } + break; + + case BARITONE: + if (sharps < 0) { + flattbl = Alt_flats_pattern; + offset = -1; + } + else { + sharptbl = Alt2_sharps_pattern; + offset = 0; + } + break; + + case TABCLEF: + return(0.0); + + default: + pfatal("unknown clef"); + /*NOTREACHED*/ + offset = 0; /* to shut up bogus compiler warning */ + break; + } + + set_cur(x, y); + /* cancel a previous key signature of flats */ + if (naturals < 0) { + draw_keysig(C_NAT, - naturals, (double) x, (double) y, + flattbl, offset, (sharps < 0 ? -sharps : 0)); + } + + /* cancel a previous key signature of sharps */ + else if (naturals > 0 ) { + draw_keysig(C_NAT, naturals, (double) x, (double) y, + sharptbl, offset, (sharps > 0 ? sharps : 0)); + } + /* if there were some naturals, add a little padding before the other */ + if (naturals != 0) { + set_cur( _Cur[AX] + (3.0 * Stdpad), y); + } + + /* do key signatures with sharps */ + if (sharps > 0) { + draw_keysig(C_SHARP, sharps, (double) _Cur[AX], (double) y, + sharptbl, offset, 0); + } + + /* do key signatures with flats */ + else if (sharps < 0) { + draw_keysig(C_FLAT, -sharps, (double) _Cur[AX], (double) y, + flattbl, offset, 0); + } + + /* return the width of what we printed */ + return( _Cur[AX] - x); +} + + +/* actually draw a key signature, given all the info about what and where + * to do it */ + +static void +draw_keysig(muschar, symbols, x, y, table, offset, skip) + +int muschar; /* what to draw: C_SHARP, C_FLAT, or C_NAT */ +int symbols; /* how many to draw */ +double x; /* where to start putting them */ +double y; /* middle of staff */ +int *table; /* which pattern to use for drawing symbols */ +int offset; /* to compensate for clef */ +int skip; /* how many symbols to skip in pattern (for canceling key) */ + +{ + float compensation; /* because mus char's x are in their middle */ + register int s; /* index through number of symbols */ + float jam_factor; /* how much to adjust to push things closer + * together. (Key signatures should be packed + * tighter than normal accidentals) */ + + + _Cur[AX] = x; + + /* have to compensate for music char's x being in its middle */ + compensation = width(FONT_MUSIC, DFLT_SIZE, muschar) * Staffscale / 2.0; + + /* just put each sharp or flat next to the previous one in the + * x direction, except squeeze flats and sharps together by two points, + * and naturals by one point. */ + jam_factor = (muschar == C_NAT ? Stdpad : 2.0 * Stdpad); + for (s = 0; s < symbols; s++) { + pr_muschar( _Cur[AX] + compensation - jam_factor, + y + ((table[s + skip] + offset) * Stepsize), + muschar, DFLT_SIZE, FONT_MUSIC); + } +} + + +/* print time signature on specified staff */ +/* return width of what was printed */ + +static double +pr_timesig(staffno, x, multnum, really_print) + +int staffno; /* which staff to print on */ +double x; /* coordinate */ +int multnum; /* number of measures of multirest that follow */ +int really_print; /* if YES, actually print, else just return width */ + +{ + char numstr[MAXTSLEN * 3]; /* numerator as a string */ + char denstr[8]; /* denominator as a string */ + char plusstr[4]; /* plus sign as a string */ + float numwidth, denwidth; /* width of numstr and denstr */ + double thiswidth; /* width of current fraction */ + double totalwidth; /* width of entire time signature */ + double numjam, denjam; /* certain 2-digit number look better + * if jammed together somewhat */ + char *t; /* walk through timerep */ + double y; /* y coordinate */ + + + if (is_tab_staff(staffno) == YES) { + /* tab staffs never have a time signature */ + return(0.0); + } + + if ( Score.timevis == PTS_NEVER ) { + /* not visible */ + return(0.0); + } + + numwidth = denwidth = thiswidth = totalwidth = numjam = denjam = 0.0; + + /* string version of numbers for time sig */ + numstr[0] = denstr[0] = plusstr[0] = FONT_NB; + numstr[1] = denstr[1] = plusstr[1] = adj_size(16, Staffscale, (char *) 0, -1); + numstr[2] = '\0'; + plusstr[2] = '+'; + plusstr[3] = '\0'; + + for (t = Score.timerep; *t != TSR_END; t++) { + + if (*t == TSR_CUT || *t == TSR_COMMON) { + char tschar; + + tschar = (*t == TSR_CUT ? C_CUT : C_COM); + thiswidth = width(FONT_MUSIC, DFLT_SIZE, tschar) * Staffscale; + totalwidth += thiswidth; + if (really_print) { + pr_muschar( x + totalwidth - (thiswidth / 2.0), + Staffs_y[staffno], tschar, + DFLT_SIZE, FONT_MUSIC); + } + } + + else if (*t == TSR_SLASH) { + t++; + (void) sprintf(denstr + 2, "%d", *t); + denjam = tsjam(*t); + denwidth = strwidth(denstr) - denjam; + numwidth = strwidth(numstr) - numjam; + thiswidth = MAX(numwidth, denwidth); + if (really_print) { + double xx; + char onenum[8]; /* one component of numerator */ + int n; /* index into numstr */ + + /* print numerator */ + xx = x + totalwidth + + (thiswidth - numwidth)/2.0; + y = Staffs_y[staffno]; + onenum[0] = numstr[0]; + onenum[1] = numstr[1]; + for (n = 2; numstr[n] != '\0'; n++) { + + if (numstr[n] == '+') { + pr_string(xx, y + 2.0 * Stdpad, + plusstr, J_LEFT, + (char *) 0, -1); + xx = _Cur[AX]; + continue; + } + + onenum[2] = numstr[n]; + if (isdigit(numstr[n+1])) { + onenum[3] = numstr[++n]; + onenum[4] = '\0'; + } + else { + onenum[3] = '\0'; + } + pr_tsnum(xx, y, onenum, tsjam(atoi(onenum + 2))); + xx = _Cur[AX]; + } + + /* print denominator */ + y = Staffs_y[staffno] - strheight(denstr) + + (2.0 * Stdpad); + pr_tsnum(x + totalwidth + + (thiswidth - denwidth)/2.0, y, + denstr, denjam); + + } + totalwidth += thiswidth; + + /* Reset things in case there is another + * time signature component */ + numwidth = denwidth = 0.0; + numstr[2] = denstr[2] = '\0'; + numjam = 0.0; + } + + else if (*t == TSR_ALTERNATING) { + if (Score.timevis == PTS_ALWAYS) { + /* In this mode, we print alternating + * time signature on each measure + * explicitly, so only print the current, + * except if for multirest, in which case + * we print the lesser of the number of + * alternate time signatures and the + * number of measures of multirest. */ + if (--multnum <= 0) { + break; + } + } + + /* add some space */ + /* reuse the numstr */ + numstr[2] = ' '; + numstr[3] = '\0'; + numwidth = strwidth(numstr); + if (really_print) { + pr_string(x + totalwidth, + Staffs_y[staffno] - strheight(numstr)/2.0, + numstr, J_LEFT, (char *) 0, -1); + } + totalwidth += numwidth; + /* reset for the next numerator */ + numstr[2] = '\0'; + numwidth = 0.0; + } + + else if (*t == TSR_ADD) { + if (really_print) { + pr_string(x + totalwidth, + Staffs_y[staffno] + - strheight(plusstr)/2.0 + 1.5 * Stdpad, + plusstr, J_LEFT, (char *) 0, -1); + } + totalwidth += strwidth(plusstr); + } + + else { + /* If first denominator number, use as is, + * otherwise have to add a plus sign first */ + if (numstr[2] != '\0') { + (void) strcat(numstr, "+"); + } + (void) sprintf(numstr + strlen(numstr), "%d", *t); + numjam += tsjam(*t); + } + } + + return (totalwidth); +} + + +/* Return the amount by which to jam the digits of a time signature number + * together. Could be zero (if a 1-digit number or a number that doesn't + * need jamming). + */ + +static double +tsjam(num) + +int num; + +{ + /* jam numbers 10 and 13-19 together a bit */ + return ( (num == 10 || (num > 12 && num < 20)) ? 2.0 * Stdpad : 0.0); +} + + + +/* print a number that is part of a time signature. The number is passed + * as a string in str, and is to be printed as the given (x,y). Some 2-digit + * numbers look better if jammed together somewhat, so if jam is non-zero, + * jam them by that much, else just print the str as is. + */ + +static void +pr_tsnum(x, y, str, jam) + +double x; +double y; +char *str; +double jam; + +{ + char save; + + if (jam > 0.0) { + /* split and print 1 digit at a time */ + save = str[3]; + str[3] = '\0'; + pr_string(x, y, str, J_LEFT, (char *) 0, -1); + str[2] = save; + pr_string(_Cur[AX] - jam, y, str, J_LEFT, (char *) 0, -1); + } + else { + pr_string(x, y, str, J_LEFT, (char *) 0, -1); + } +} + + +/* print a string */ + +void +pr_string(x, y, string, justify, fname, lineno) + +double x, y; /* where to put it */ +char *string; /* what to print */ +int justify; /* J_LEFT, etc */ +char *fname; /* file name for error messages */ +int lineno; /* line number for error messages */ + +{ + /* This function is now just a wrapper that passes its arguments + * pass to a more general function. The added -1.0 argument says + * to not spread out for right justified paragraph. */ + pr_wstring(x, y, string, justify, -1.0, fname, lineno); +} + +/* more general string printing function that handles right justified paragraphs */ + +static void +pr_wstring(x, y, string, justify, fullwidth, fname, lineno) + +double x, y; /* where to put it */ +char *string; /* what to print */ +int justify; /* J_LEFT, etc */ +double fullwidth; /* width to use, or negative value to use strwidth */ +char *fname; /* file name for error messages */ +int lineno; /* line number for error messages */ + +{ + /* skip any empty strings */ + if ( ( string == (char *) 0) || (*string == '\0') ) { + return; + } + + /* set font and size */ + pr_font( (int) string[0], (int) string[1]); + + if (IS_BOXED(string) == YES) { + /* The strheight and width already include the box dimension, + * so print the box of that size. Then adjust the x of + * the string so it will print at the right place + * inside the box. */ + pr_box(x + 1.5 * STDPAD, y - strdescent(string) + 3.0 * STDPAD, + strheight(string) - 5.0 * STDPAD, + strwidth(string) - (1.5 * STDPAD)); + + + x += 3.5 * STDPAD; + } + if (IS_CIRCLED(string) == YES) { + float circ_height; + float circ_width; + float elongation_factor; + float x_offset; + float radius; + float x_center, y_center; + + /* determine where to place the circle and its contents */ + (void) circled_dimensions(string, &circ_height, &circ_width, + (float *) 0, &x_offset); + x_center = x + circ_width / 2.0; + y_center = y + strascent(string) - strheight(string) / 2.0; + + /* we will fiddle with the transform matrix so do inside + * save/restore */ + outop(O_GSAVE); + outop(O_NEWPATH); + + /* draw the outer elipse */ + elongation_factor = circ_width / circ_height; + radius = strheight(string) / 2.0; + do_scale(elongation_factor, 1.0); + draw_circle(x_center / elongation_factor, y_center, radius); + + /* undo the outer elongation, and set for inner */ + do_scale(1.0 / elongation_factor, 1.0); + elongation_factor = (circ_width - 1.5 * Stdpad) + / (circ_height - 1.5 * Stdpad); + do_scale(elongation_factor, 1.0); + + /* the inner circle's radius is smaller than outer */ + radius = radius - 0.5 * Stdpad; + draw_circle(x_center / elongation_factor, y_center, radius); + + /* fill in the area between the inner and outer elipses */ + outop(O_EOFILL); + outop(O_GRESTORE); + + /* adjust x for where text should be printed */ + x += x_offset; + } + + split_a_string(x, y, string, justify, fullwidth, fname, lineno); +} + + +/* Draw a circle (or maybe elipse, if scaling is in effect) */ + +static void +draw_circle(x, y, radius) + +double x, y; /* of circle center */ +double radius; + +{ + outcoord(x); + outcoord(y); + outcoord(radius); + outint(0); + outint(360); + outop(O_ARC); +} + + +/* output instructions for setting font and size */ + +static void +pr_font(font, size) + +int font; +int size; + +{ +#ifdef SMALLMEMORY + /* if memory is scarce, every time we do a new font, + * do it in a separate save context */ + if (Did_save == YES) { + outop(O_RESTORE); + } + outop(O_SAVE); + Did_save = YES; +#endif + + Curr_font = font; + Curr_size = size; + + prfontname(font); + + outop(O_FONT); + + outint(size); + outop(O_SIZE); + outop(O_SETFONT); + + Font_used[font] = YES; +} + + +/* print font name */ + +static void +prfontname(font) + +int font; +{ + OUTP(("/%s ", Fontinfo[font_index(font)].ps_name)); +} + + +/* split a string into lines and print each line */ + +static void +split_a_string(x, y, string, justify, fullwidth, fname, lineno) + +double x; /* coordinate at which to print string */ +double y; +char *string; /* what string to print */ +int justify; /* J_LEFT, etc */ +double fullwidth; /* width of (possibly multi-line) string, or -1.0 + * if the string width should be used. */ +char *fname; /* file name for error messages */ +int lineno; /* line number for error messages */ + +{ + int font, size; /* current font and size */ + int origfont, origsize; /* font & size at beginning of current line + * of text */ + char *text; /* beginning of text of current line */ + char *p; /* pointer to current place in string */ + int c; /* character read from string */ + char *buff; /* temporary copy of one line of string */ + + + origfont = font = string[0]; + origsize = size = string[1]; + text = string + 2; + + /* if centering or right justifying, will need width of entire + * (possibly multi-line) string, to adjust lines within the string */ + if (fullwidth < 0.0) { + fullwidth = strwidth(string); + } + + if (IS_BOXED(string) == YES) { + /* The box printing is dealt with in pr_string(), so we + * can ignore the BOX commands here (and need to, in order + * to make things align properly). */ + text++; + fullwidth -= 7.0 * STDPAD; + } + p = text; + MALLOCA(char, buff, strlen(string) + 1); + do { + c = next_str_char(&p, &font, &size); + if (c == '\n' || c == '\0') { + /* end of line. Print this line. Put into + * temporary buffer in case more than one line */ + buff[0] = (char) origfont; + buff[1] = (char) origsize; + (void) memcpy(buff + 2, text, (unsigned) (p - text)); + buff[p - text + 2] = '\0'; + /* On final line of a justified paragraph, we don't + * want to stretch that line out, because it might + * only contain a couple words. */ + if (justify == J_JUSTPARA && c == '\0') { + justify = J_LEFT; + } + + j_outstring(x, y, buff, justify, fullwidth, + fname, lineno); + + /* prepare for next line, if any */ + origfont = font; + origsize = size; + text = p; + y -= fontheight(font, size); + } + } while (c != '\0'); + FREE(buff); +} + + +/* output a string segment with specified justification. If J_LEFT, just + * print given string at given x, y location. If J_CENTER, put half way + * between x and (x + fullwidth). If J_RIGHT, print such that right edge + * of string will be at (x + fullwidth) */ + +static void +j_outstring(x, y, string, justify, fullwidth, fname, lineno) + +double x; +double y; +char *string; /* which string to print */ +int justify; /* J_LEFT, etc */ +double fullwidth; /* full width to allocate to string */ +char *fname; /* file name for error messages */ +int lineno; /* line number for error messages */ + +{ + switch (justify) { + case J_NONE: + /* NONE is effectively the same as LEFT */ + /*FALLTHRU*/ + case J_LEFT: + case J_RAGPARA: + outstring(x, y, -1.0, string, fname, lineno); + break; + case J_JUSTPARA: + outstring(x, y, fullwidth, string, fname, lineno); + break; + case J_CENTER: + outstring(x + (fullwidth - strwidth(string)) / 2.0, y, + -1.0, string, fname, lineno); + break; + case J_RIGHT: + outstring(x + fullwidth - strwidth(string), y, -1.0, + string, fname, lineno); + break; + default: + pfatal("bad justification type"); + /*NOTREACHED*/ + break; + } +} + + +/* given a MAINLL struct, find all the STAFF structs from there to the next + * BAR, and fill in a table of the staff Y coordinates */ + +static void +set_staff_y(main_p) + +struct MAINLL *main_p; + +{ + int s; + + /* First initialize all to 0.0. This is so that if the Staffs_y + * array is accessed for a non-existent staff, we will be sure + * that it will be set to 0.0, which is the special value to mean + * non-existent. Otherwise, when the number of staffs decreases, + * an old value could get left around in the staff that went away. */ + for (s = 1; s <= MAXSTAFFS; s++) { + Staffs_y[s] = 0.0; + } + + for ( ; main_p != (struct MAINLL *) 0; main_p = main_p->next) { + + if (main_p->str == S_BAR) { + /* reached end of list of staffs in this measure */ + return; + } + + if (main_p->str == S_STAFF) { + /* save y value of staff */ + Staffs_y[main_p->u.staff_p->staffno] = + main_p->u.staff_p->c[AY]; + } + } +} + + +/* print measure number at beginning of score if user wants them */ + +static void +pr_meas_num(staffno, x) + +int staffno; /* which staff to possible put measure number on */ +double x; /* where to put measure number */ + +{ + float y_adj; /* to avoid clefs */ + int clef; + + + /* measure numbers only put on those staffs that have endings */ + if (has_ending(staffno) ) { + + /* print measure number if user wants them */ + if ( (svpath(staffno, MEASNUM)->measnum == YES) + && (Meas_num > 1)) { + + /* construct the measure number string */ + char mnumstr[8]; + mnumstr[0] = (char) (Score.measnumfamily + + Score.measnumfont); + mnumstr[1] = (char) Score.measnumsize; + (void) sprintf(mnumstr + 2, "%d", Meas_num); + + /* print it */ + if (is_tab_staff(staffno) == YES) { + clef = TABCLEF; + } + /* If clef is not to be printed, use NOCLEF. + * (printclef shares the STAFFLINES used flag) */ + else if (svpath(staffno, STAFFLINES)->printclef == NO) { + clef = NOCLEF; + } + else { + clef = svpath(staffno, CLEF)->clef; + } + /* Figure out where to place the measure number + * vertically by calling clefspace to get + * the height of the clef on the current staff plus + * the height of the measure number, but ignoring + * the height of the clef above (if any), then + * subtract the ascent of the measure number to + * get the right baseline. */ + y_adj = halfstaffhi(staffno) + + clefspace(NOCLEF, 1.0, clef, Staffscale, YES) - + fontascent((int) mnumstr[0], (int) mnumstr[1]); + pr_string(x + 1.5 * Stepsize, + Staffs_y[staffno] + y_adj, + mnumstr, J_LEFT, (char *) 0, -1); + } + } +} + + +/* tell PostScript about file and linenumber */ + +void +pr_linenum (inputfile, inputlineno) + +char *inputfile; +int inputlineno; + +{ + static char *fname = ""; /* keep track of current file + * name to only output it when + * it changes */ + char *str; /* walk thru file name to + * add backslashes if needed */ + + + if (strcmp(fname, inputfile) != 0) { + OUTPCH(('(')); + for (str = inputfile; *str != 0; str++) { + switch(*str) { + case '\\': + case '(': + case ')': + OUTPCH(('\\')); + /*FALLTHRU*/ + default: + OUTPCH((*str)); + break; + } + } + OUTP((") inputfile\n")); + fname = inputfile; + } + OUTP(("%d linenum\n", inputlineno)); +} + + +/* output the current scale factor */ + +static void +setscale() + +{ + OUTP(("%f %f scale\n", Score.scale_factor, Score.scale_factor)); +} + + +/* For debugging, this generates PostScript to draw colored bounding boxes + * representing coordinates (the 13-element arrays called "c" + * in various structs). + */ + +static void +show_coord(coord_p, index) + +float *coord_p; /* which one to draw */ +int index; /* index into Bbox_list, to get colors to use */ + +{ + struct Bbox *bb_p; + + bb_p = &(Bbox_list[index]); + outop(O_GSAVE); + OUTP(("%d.%d %d.%d %d.%d setrgbcolor\n", + bb_p->red / 100, bb_p->red % 100, + bb_p->green / 100, bb_p->green % 100, + bb_p->blue / 100, bb_p->blue % 100)); + if (bb_p->dash_on && bb_p->dash_off) { + OUTP(("[%d %d] 0 setdash\n", bb_p->dash_on, bb_p->dash_off)); + } + pr_box(coord_p[AW], coord_p[AS], coord_p[AN] - coord_p[AS], coord_p[AE] - coord_p[AW]); + outop(O_GRESTORE); +} + + +/* The environment variable MUP_BB turns on drawing of bounding boxes around + * things, for debugging. This function checks if the variable is set, + * and if so, parses it to see which subset of things to draw boxes for. + * For example, MUP_BB=g just does grpsyls, whereas MUP_BB=gnc does grpsyls, + * notes, and chords. Bbox_list gives the full list of possibilities. + */ + +static void +prep_bbox() +{ + char *bb; + int i; + + if ((bb = getenv("MUP_BB")) == 0) { + /* user doesn't want bounding box debugging */ + return; + } + + /* If a coordinate type's id is set in MUP_BB, set its corresponding + * flag bit */ + for (i = 0; i < NUMELEM(Bbox_list); i++) { + if (strchr(bb, Bbox_list[i].id) != 0) { + BB_SET(i); + } + } +} + + +/* To help with debugging the placement phase of Mup, + * or just to help a user see why things are laid out as they are, + * this function will draw colored bounding boxes around things that + * have "coordinates." The environment variable MUP_BB control which, + * if any, kinds of things this is done for. This function is given the + * main list item at the end of a page. It backs up through the list, back to + * the beginning of the page, generating PostScript code to cause + * printing of relevant boxes. Doing this last on a page ensures the boxes + * are drawn on top of other things, and is at least as easy as going forwards. + */ + +static void +show_bounding_boxes(mll_p) + +struct MAINLL *mll_p; /* FEED for end of current page, or could be + * Mainlltc_p if at end of song. */ + +{ + int v; /* voice/verse */ + int n; /* NOTE index */ + struct GRPSYL *gs_p; + struct CHORD *chord_p; + struct STUFF *stuff_p; + + /* We are at bottom of main list for current page. Work upwards, + * printing any coords we find. */ + if (mll_p->str == S_FEED) { + /* Skip this feed. + * We want to back up to previous page feed, if any */ + mll_p = mll_p->prev; + } + + for ( ; mll_p != 0; mll_p = mll_p->prev) { + switch (mll_p->str) { + + case S_BAR: + if (BB_IS_SET(BB_BAR)) { + show_coord(mll_p->u.bar_p->c, BB_BAR); + } + break; + + case S_FEED: + if (BB_IS_SET(BB_FEED)) { + show_coord(mll_p->u.feed_p->c, BB_FEED); + } + if (mll_p->u.feed_p->pagefeed == YES + || Feednumber == 1) { + if (BB_IS_SET(BB_BLOCKHEAD)) { + /* Do header/footer */ + if (Feednumber == 1) { + show_coord(Header.c, BB_BLOCKHEAD); + show_coord(Footer.c, BB_BLOCKHEAD); + } + else { + show_coord(Header2.c, BB_BLOCKHEAD); + show_coord(Footer2.c, BB_BLOCKHEAD); + } + if (mll_p->u.feed_p->top_p != 0){ + set_win_coord(mll_p->u.feed_p->top_p->c); + show_coord(mll_p->u.feed_p->top_p->c, BB_BLOCKHEAD); + set_win_coord(0); + } + if (mll_p->u.feed_p->bot_p != 0){ + set_win_coord(mll_p->u.feed_p->bot_p->c); + show_coord(mll_p->u.feed_p->bot_p->c, BB_BLOCKHEAD); + set_win_coord(0); + } + } + if (mll_p->u.feed_p->pagefeed == YES) { + /* reached top of current page; we're done */ + return; + } + } + break; + + case S_STAFF: + if (mll_p->u.staff_p->visible == NO) { + break; + } + + /* show the staff itself */ + if (BB_IS_SET(BB_STAFF)) { + show_coord(mll_p->u.staff_p->c, BB_STAFF); + } + + /* Do groups and notes */ + if (BB_IS_SET(BB_GRPSYL) || BB_IS_SET(BB_NOTE)) { + for (v = 0; v < MAXVOICES; v++) { + + if (vvpath(mll_p->u.staff_p->staffno, + v+1, VISIBLE)->visible + == NO) { + /* Skip invisible voices */ + continue; + } + + for (gs_p = mll_p->u.staff_p->groups_p[v]; + gs_p != 0; + gs_p = gs_p->next) { + if (BB_IS_SET(BB_GRPSYL)) { + show_coord(gs_p->c, BB_GRPSYL); + } + if (gs_p->nnotes > 0 && + BB_IS_SET(BB_NOTE)) { + for (n = 0; n < gs_p->nnotes; n++) { + show_coord(gs_p->notelist[n].c, BB_NOTE); + } + } + } + } + } + + /* now do lyrics */ + if (BB_IS_SET(BB_GRPSYL)) { + for (n = 0; n < mll_p->u.staff_p->nsyllists; n++) { + for (gs_p = mll_p->u.staff_p->syls_p[n]; + gs_p != 0; + gs_p = gs_p->next) { + show_coord(gs_p->c, BB_GRPSYL); + } + } + } + + /* do the other "stuff" */ + if (BB_IS_SET(BB_STUFF)) { + for (stuff_p = mll_p->u.staff_p->stuff_p; + stuff_p != 0; + stuff_p = stuff_p->next) { + show_coord(stuff_p->c, BB_STUFF); + } + } + break; + + case S_CHHEAD: + if (BB_IS_SET(BB_CHORD)) { + for (chord_p = mll_p->u.chhead_p->ch_p; chord_p != 0; + chord_p = chord_p->ch_p) { + show_coord(chord_p->c, BB_CHORD); + } + } + break; + + default: + break; + } + } +}