--- /dev/null
+
+/* 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 <time.h>
+#include <string.h>
+#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<<x)
+#define BB_IS_SET(x) (Do_bbox & (1<<x))
+
+/* This struct holds information about how to display a coord bounding box.
+ * We use the environment variable MUP_BB to turn on this displaying.
+ * For now, we hard-code what color/dashing to use. MUP_BB could optionally
+ * contain color info some day, but that seems like overkill flexibility.
+ */
+static struct Bbox {
+ char id; /* character in $MUP_BB that says to draw these */
+ char red; /* per cent of this color to use when drawing */
+ char green;
+ char blue;
+ char dash_on; /* for setdash */
+ char dash_off;
+} Bbox_list[] = {
+ { 'b', 100, 50, 0, 5, 2 }, /* BAR */
+ { 'c', 0, 80, 0, 5, 2 }, /* CHORD */
+ { 'f', 50, 50, 0, 0, 0 }, /* FEED */
+ { 'g', 100, 0, 0, 0, 0 }, /* GRPSYL */
+ { 'h', 0, 80, 50, 0, 0 }, /* headings, etc */
+ { 'n', 0, 0, 100, 0, 0 }, /* NOTE */
+ { 's', 0, 50, 80, 5, 3 }, /* STAFF */
+ { 'u', 100, 0, 100, 5, 2 } /* STUFF */
+};
+static short Do_bbox = 0;
+
+#ifdef SMALLMEMORY
+/* if memory is scarce, we do each font in a separate save/restore context.
+ * Need flag to keep track of whether we are in one of those */
+static int Did_save = NO;
+#endif
+
+/* for header, to indicate when output file was generated */
+static char *Dayofweek[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+static char *Month[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+static int Pagesprinted = 0; /* number of pages actually printed */
+static int Feednumber; /* how many pagefeeds we've handled */
+
+
+static struct STAFF *Last_staff; /* point to last STAFF we saw, to
+ * save having to back up to it
+ * later to check if it was a
+ * multirest. */
+
+static int Last_linetype = -1; /* keep track of last type. If same,
+ * this time, no reason to output */
+static float Last_staffscale; /* Change in staffscale also changes
+ * line width, so have to remember it */
+static short Doing_dotted = NO; /* keep track of whether last line
+ * was dotted. If it was, but current
+ * one isn't, need to tell PostScript */
+static double x1a, x2a, ya; /* x and y adjustments */
+static int sn;
+extern int *Check_p;
+static int Landscape; /* how much to translate for landscape
+ * mode, or 0 if not in landscape */
+
+/* static functions */
+static void init4print P((void));
+static void page1setup P((void));
+static int use_landscape P((double pgwidth, double pgheight));
+static void setup_user_fonts P((void));
+#ifdef EXTCHAR
+static void setup_extended_fonts P((void));
+#endif
+static void pr_line P((struct LINE *line_p, char *fname, int lineno));
+static void dr_line P((double x1, double y1, double x2, double y2, int ltype));
+static void pr_curve P((struct CURVE *curve_p, char *fname, int lineno));
+static void outp_muschar P((double x, double y, int ch, int size, int font));
+static void pr_ital_muschar P((double x, double y, int ch, int size, int font));
+static void pr_bar P((struct MAINLL *mll_p, double x, int is_pseudobar));
+static void pr_bar_range P((struct BAR *bar_p, int topstaff,
+ int botstaff, double x, int next_is_restart,
+ struct MAINLL *mll_p));
+static void draw_bar P((int bartype, int linetype, double x, double y1,
+ double y2));
+static void pr_repeat_dots P((int bartype, int staff, double x));
+static void do_rdots P((double x, double y, double topoffset, double bottomosffset));
+static void pr_reh P((struct MAINLL *mll_p));
+static void pr_box P((double x, double y, double boxheight, double boxwidth));
+static void pr_topbot P((struct BLOCKHEAD *blockhead_p, double y));
+static void pr_restarts P((struct MAINLL *mll_p, double y1, double y2,
+ int need_vert_line));
+static void outint P((int val));
+static void pr_wstring P((double x, double y, char *string, int justify,
+ double fullwidth, char * fname, int lineno));
+static void outstring P((double x, double y, double fullwidth, char *string,
+ char * fname, int lineno));
+static int begin_string P((int in_string));
+static int end_string P((int in_string, double space_adjust));
+static void outop P((int op));
+static void pr_headfoot P((struct MAINLL *mll_p));
+static void to_next_page P((struct MAINLL *mll_p));
+static void pr_print P((struct PRINTDATA *printdata_p));
+static double pr_keysig P((int staffno, int sharps, int naturals, double x,
+ int really_print));
+static void draw_keysig P((int muschar, int symbols, double x,
+ double y, int *table, int offset, int skip));
+static double pr_timesig P((int staffno, double x, int multnum,
+ int really_print));
+static double tsjam P((int num));
+static void pr_tsnum P((double x, double y, char *str, double jam));
+static void draw_circle P((double x, double y, double radius));
+static void do_scale P((double xscale, double yscale));
+static void pr_font P((int font, int size));
+static void prfontname P((int font));
+static void split_a_string P((double x, double y, char *string, int justify,
+ double fullwidth, char *fname, int lineno));
+static void j_outstring P((double x, double y, char *string, int justify,
+ double fullwidth, char *fname, int lineno));
+static void set_staff_y P((struct MAINLL *main_p));
+static void pr_meas_num P((int staffno, double x));
+static void setscale P((void));
+static void show_coord P((float *coord_p, int index));
+static void prep_bbox P((void));
+static void show_bounding_boxes P((struct MAINLL *mll_p));
+\f
+
+/* main function of print phase. Walk through main list,
+ * printing things as we go */
+
+void
+print_music()
+
+{
+ struct MAINLL *mll_p; /* to walk through list */
+ struct FEED *feed_p;
+
+
+ debug(256, "print_music");
+ prep_bbox();
+
+ /* initialize for printing */
+ init4print();
+
+ /* walk down the list, printing as we go */
+ for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) NULL;
+ mll_p = mll_p->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);
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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) );
+}
+\f
+
+/* 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();
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+ }
+ }
+}
+\f
+
+#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
+\f
+
+/* 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);
+ }
+}
+\f
+
+/* 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);
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+ }
+}
+\f
+
+/* 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"));
+}
+\f
+
+/* 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"));
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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)));
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+ }
+}
+\f
+
+/* 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);
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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;
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+ }
+ }
+}
+\f
+
+/* 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();
+}
+\f
+
+/* 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]);
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+ }
+
+ }
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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));
+}
+\f
+
+
+/* output an integer value */
+
+static void
+outint(val)
+
+int val;
+
+{
+ OUTP(("%d ", val));
+}
+\f
+
+/* 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
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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;
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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;
+ }
+}
+\f
+
+/* 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);
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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;
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+}
+\f
+
+
+/* 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);
+ }
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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;
+}
+\f
+
+/* print font name */
+
+static void
+prfontname(font)
+
+int font;
+{
+ OUTP(("/%s ", Fontinfo[font_index(font)].ps_name));
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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;
+ }
+}
+\f
+
+/* 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];
+ }
+ }
+}
+\f
+
+/* 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);
+ }
+ }
+}
+\f
+
+/* 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));
+}
+\f
+
+/* output the current scale factor */
+
+static void
+setscale()
+
+{
+ OUTP(("%f %f scale\n", Score.scale_factor, Score.scale_factor));
+}
+\f
+
+/* 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);
+}
+\f
+
+/* 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);
+ }
+ }
+}
+\f
+
+/* 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;
+ }
+ }
+}