chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / print.c
1
2 /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
3 /* All rights reserved */
4
5 /* make the final pass through the main list, writing out the PostScript output
6  * for printing the music. This file contains most of the general print
7  * phase functions.  Those that are associated with printing
8  * things from S_STAFF structs are mostly in prntdata.c.
9  * Tablature is printed via printtab.c.
10  * Additional print functions are in prntmisc.c and utils.c.
11  */
12
13 #include <time.h>
14 #include <string.h>
15 #include "defines.h"
16 #include "structs.h"
17 #include "globals.h"
18
19
20 /* print only if flag is turned on. This allows printing selected pages */
21 static int Printflag = YES;
22 #define OUTP(x) if (Printflag==YES){(void)printf x;}
23 #define OUTPCH(x) if (Printflag == YES){putchar x;}
24
25
26 /* the PostScript commands */
27 #define O_FONT          (1)
28 #define O_SIZE          (2)
29 #define O_LINEWIDTH     (3)
30 #define O_CURVETO       (4)
31 #define O_DOTTED        (5)
32 #define O_SHOWPAGE      (6)
33 #define O_SHOW          (7)
34 #define O_STAFF         (8)
35 #define O_SETFONT       (9)
36 #define O_MOVETO        (10)
37 #define O_LINE          (11)            /* lineto stroke */
38 #define O_BRACE         (12)
39 #define O_BRACKET       (13)
40 #define O_ENDDOTTED     (14)
41 #define O_WAVY          (15)
42 #define O_DASHED        (16)
43 #define O_SAVE          (17)
44 #define O_RESTORE       (18)
45 #define O_FILL          (19)
46 #define O_LINETO        (20)
47 #define O_STROKE        (21)
48 #define O_NEWPATH       (22)
49 #define O_CLOSEPATH     (23)
50 #define O_GSAVE         (24)
51 #define O_GRESTORE      (25)
52 #define O_CONCAT        (26)
53 #define O_ARC           (27)
54 #define O_EOFILL        (28)
55 #define O_SCALE         (29)
56 #define O_TRANSLATE     (30)
57 #define O_ROTATE        (31)
58 #define O_WIDTHSHOW     (32)
59 #define O_ROLL          (33)
60
61 #ifdef __TURBOC__
62 #define SMALLMEMORY 1
63 #endif
64
65 /*
66  * For debugging, it can be useful to display the "bounding box"
67  * which is stored in the coordinate arrays of various entities.
68  * This list tells which entities have coords.
69  * These must match the indices in the Bbox_list array.
70  */
71 #define BB_BAR          0
72 #define BB_CHORD        1
73 #define BB_FEED         2
74 #define BB_GRPSYL       3
75 #define BB_BLOCKHEAD    4
76 #define BB_NOTE         5
77 #define BB_STAFF        6
78 #define BB_STUFF        7
79 /* Macros to turn on display of a coord type and to check if it is on */
80 #define BB_SET(x)       Do_bbox |= (1<<x)
81 #define BB_IS_SET(x)    (Do_bbox & (1<<x))
82
83 /* This struct holds information about how to display a coord bounding box.
84  * We use the environment variable MUP_BB to turn on this displaying.
85  * For now, we hard-code what color/dashing to use. MUP_BB could optionally
86  * contain color info some day, but that seems like overkill flexibility.
87  */
88 static struct Bbox {
89         char    id;     /* character in $MUP_BB that says to draw these */
90         char    red;    /* per cent of this color to use when drawing */
91         char    green;  
92         char    blue;
93         char    dash_on;        /* for setdash */
94         char    dash_off;
95 } Bbox_list[] = {
96         { 'b',  100,    50,     0,      5,      2 },    /* BAR */
97         { 'c',  0,      80,     0,      5,      2 },    /* CHORD */
98         { 'f',  50,     50,     0,      0,      0 },    /* FEED */
99         { 'g',  100,    0,      0,      0,      0 },    /* GRPSYL */
100         { 'h',  0,      80,     50,     0,      0 },    /* headings, etc */
101         { 'n',  0,      0,      100,    0,      0 },    /* NOTE */
102         { 's',  0,      50,     80,     5,      3 },    /* STAFF */
103         { 'u',  100,    0,      100,    5,      2 }     /* STUFF */
104 };
105 static short Do_bbox = 0;
106
107 #ifdef SMALLMEMORY
108 /* if memory is scarce, we do each font in a separate save/restore context.
109  * Need flag to keep track of whether we are in one of those */
110 static int Did_save = NO;
111 #endif
112
113 /* for header, to indicate when output file was generated */
114 static char *Dayofweek[] = {
115         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
116 static char *Month[] = {
117         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
118         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
119
120 static int Pagesprinted = 0;            /* number of pages actually printed */
121 static int Feednumber;                  /* how many pagefeeds we've handled */
122
123
124 static struct STAFF *Last_staff;        /* point to last STAFF we saw, to
125                                          * save having to back up to it
126                                          * later to check if it was a
127                                          * multirest. */
128
129 static int Last_linetype = -1;          /* keep track of last type. If same,
130                                          * this time, no reason to output */
131 static float Last_staffscale;           /* Change in staffscale also changes
132                                          * line width, so have to remember it */
133 static short Doing_dotted = NO;         /* keep track of whether last line
134                                          * was dotted. If it was, but current
135                                          * one isn't, need to tell PostScript */
136 static double x1a, x2a, ya;             /* x and y adjustments */
137 static int sn;
138 extern int *Check_p;
139 static int Landscape;                   /* how much to translate for landscape
140                                          * mode, or 0 if not in landscape */
141
142 /* static functions */
143 static void init4print P((void));
144 static void page1setup P((void));
145 static int use_landscape P((double pgwidth, double pgheight));
146 static void setup_user_fonts P((void));
147 #ifdef EXTCHAR
148 static void setup_extended_fonts P((void));
149 #endif
150 static void pr_line P((struct LINE *line_p, char *fname, int lineno));
151 static void dr_line P((double x1, double y1, double x2, double y2, int ltype));
152 static void pr_curve P((struct CURVE *curve_p, char *fname, int lineno));
153 static void outp_muschar P((double x, double y, int ch, int size, int font));
154 static void pr_ital_muschar P((double x, double y, int ch, int size, int font));
155 static void pr_bar P((struct MAINLL *mll_p, double x, int is_pseudobar));
156 static void pr_bar_range P((struct BAR *bar_p, int topstaff,
157                 int botstaff, double x, int next_is_restart,
158                 struct MAINLL *mll_p));
159 static void draw_bar P((int bartype, int linetype, double x, double y1,
160                 double y2));
161 static void pr_repeat_dots P((int bartype, int staff, double x));
162 static void do_rdots P((double x, double y, double topoffset, double bottomosffset));
163 static void pr_reh P((struct MAINLL *mll_p));
164 static void pr_box P((double x, double y, double boxheight, double boxwidth));
165 static void pr_topbot P((struct BLOCKHEAD *blockhead_p, double y));
166 static void pr_restarts P((struct MAINLL *mll_p, double y1, double y2,
167                 int need_vert_line));
168 static void outint P((int val));
169 static void pr_wstring P((double x, double y, char *string, int justify,
170                 double fullwidth, char * fname, int lineno));
171 static void outstring P((double x, double y, double fullwidth, char *string,
172                 char * fname, int lineno));
173 static int begin_string P((int in_string));
174 static int end_string P((int in_string, double space_adjust));
175 static void outop P((int op));
176 static void pr_headfoot P((struct MAINLL *mll_p));
177 static void to_next_page P((struct MAINLL *mll_p));
178 static void pr_print P((struct PRINTDATA *printdata_p));
179 static double pr_keysig P((int staffno, int sharps, int naturals, double x,
180                 int really_print));
181 static void draw_keysig P((int muschar, int symbols, double x,
182                 double y, int *table, int offset, int skip));
183 static double pr_timesig P((int staffno, double x, int multnum,
184                 int really_print));
185 static double tsjam P((int num));
186 static void pr_tsnum P((double x, double y, char *str, double jam));
187 static void draw_circle P((double x, double y, double radius));
188 static void do_scale P((double xscale, double yscale));
189 static void pr_font P((int font, int size));
190 static void prfontname P((int font));
191 static void split_a_string P((double x, double y, char *string, int justify,
192                 double fullwidth, char *fname, int lineno));
193 static void j_outstring P((double x, double y, char *string, int justify,
194                 double fullwidth, char *fname, int lineno));
195 static void set_staff_y P((struct MAINLL *main_p));
196 static void pr_meas_num P((int staffno, double x));
197 static void setscale P((void));
198 static void show_coord P((float *coord_p, int index));
199 static void prep_bbox P((void));
200 static void show_bounding_boxes P((struct MAINLL *mll_p));
201 \f
202
203 /* main function of print phase. Walk through main list,
204  * printing things as we go */
205
206 void
207 print_music()
208
209 {
210         struct MAINLL *mll_p;   /* to walk through list */
211         struct FEED *feed_p;
212
213
214         debug(256, "print_music");
215         prep_bbox();
216
217         /* initialize for printing */
218         init4print();
219
220         /* walk down the list, printing as we go */
221         for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) NULL;
222                                                 mll_p = mll_p->next) {
223
224                 {
225                         /* in debug mode, print out Postscript comments
226                          * to make it easier to map output back to input */
227                         OUTP(("%%  %s\n", stype_name(mll_p->str)));
228                 }
229
230                 /* tell output program what the user input line was */
231                 /* STAFF structs have lots of things hung off them that
232                  * could come from different input lines, so do that
233                  * separately */
234                 if (mll_p->str != S_STAFF && mll_p->inputlineno > 0) {
235                         pr_linenum(mll_p->inputfile, mll_p->inputlineno);
236                 }
237
238                 /* call appropriate function(s) based on type */
239                 switch(mll_p->str) {
240
241                 case S_SSV:
242                         /* assign the values from the SSV and reset
243                          * the _win parameters */
244                         asgnssv(mll_p->u.ssv_p);
245                         set_win(PGHEIGHT - EFF_TOPMARGIN, EFF_BOTMARGIN,
246                                         PGWIDTH - eff_rightmargin((struct MAINLL *)0),
247                                         eff_leftmargin((struct MAINLL *)0));
248                         break;
249
250                 case S_STAFF:
251                         OUTP(("%% staff %d\n", mll_p->u.staff_p->staffno));
252                         outop(O_SAVE);
253                         set_staffscale(mll_p->u.staff_p->staffno);
254                         pr_staff(mll_p);
255                         outop(O_RESTORE);
256                         Curr_font = FONT_UNKNOWN;
257                         Curr_size = DFLT_SIZE;
258                         Last_staff = mll_p->u.staff_p;
259                         break;
260
261                 case S_BAR:
262                         pr_bar(mll_p, (double)mll_p->u.bar_p->c[AX],
263                                                                 NO);
264                         /* reset the staffscale to its scorewide
265                          * default value */
266                         set_staffscale(0);
267                         break;
268
269                 case S_CHHEAD:
270                         /* nothing to do here--we print notes when we
271                          * hit the S_STAFF structs */
272                         break;
273
274                 case S_PRHEAD:
275                         pr_print(mll_p->u.prhead_p->printdata_p);
276                         break;
277
278                 case S_LINE:
279                         pr_line(mll_p->u.line_p, mll_p->inputfile,
280                                                         mll_p->inputlineno);
281                         break;
282
283                 case S_CURVE:
284                         pr_curve(mll_p->u.curve_p, mll_p->inputfile,
285                                                         mll_p->inputlineno);
286                         break;
287
288                 case S_FEED:
289                         pr_feed(mll_p);
290                         pr_endings(mll_p);
291                         break;
292
293                 case S_CLEFSIG:
294                         (void) pr_clefsig(mll_p, mll_p->u.clefsig_p, YES);
295                         /* have to do rehearsal marks, because we only
296                          * print a pseudo-bar if it is visible */
297                         if (mll_p->u.clefsig_p->bar_p != (struct BAR *) 0) {
298                                 pr_reh(mll_p);
299                         }
300                         break;
301
302                 case S_BLOCKHEAD:
303                         if (mll_p->prev == 0 || mll_p->prev->str != S_FEED) {
304                                 pfatal("blockhead without preceeding feed");
305                         }
306                         feed_p = mll_p->prev->u.feed_p;
307                         set_win_coord(mll_p->u.blockhead_p->c);
308                         set_win(feed_p->c[AN], feed_p->c[AS],
309                                         feed_p->c[AE], feed_p->c[AW]);
310                         set_cur(mll_p->prev->u.feed_p->c[AW],
311                                         mll_p->prev->u.feed_p->c[AN]);
312                         pr_print(mll_p->u.blockhead_p->printdata_p);
313                         set_win_coord(0);
314                         break;
315                 default:
316                         pfatal("unknown item in main list");
317                         break;
318                 }
319         }
320
321         /* do grid atend things if necessary */
322         if (Atend_info.grids_used > 0) {
323                 if (Atend_info.separate_page == YES) {
324                         /* The only MUP_BB thing that matters on grids at end
325                          * page is header/footer, and because of the order
326                          * in which things are done in pr_atend()
327                          * (i.e., when top/bottom are set relative to when the
328                          * MUP_BB printing code is called),
329                          * it's hard to find a way
330                          * to make that work without breaking something else.
331                          * So since grids at end is such a rare case,
332                          * and MUP_BB is just for debugging,
333                          * we just turn it off. */
334                         Do_bbox = 0;
335                 }
336                 pr_atend();
337         }
338
339         /* do final stuff for last page */
340         pr_headfoot(Mainlltc_p);
341 }
342 \f
343
344 /* do the things for starting a new page */
345 void
346 newpage(mll_p)
347
348 struct MAINLL *mll_p;
349
350 {
351         pr_headfoot(mll_p);
352         Pagenum++;
353         to_next_page(mll_p);
354 }
355 \f
356
357 /* print final trailer */
358
359 void
360 trailer()
361
362 {
363         int f;                  /* font index */
364
365         Printflag = YES;
366         printf("%%%%Trailer\n");
367         printf("%%%%DocumentFonts: ");
368         for (f = 1; f < MAXFONTS; f++) {
369                 if (Font_used[f] == YES) {
370                         prfontname(f);
371                 }
372         }
373         
374         printf("\n%%%%Pages: %d\n", Score.panelsperpage == 1 ? Pagesprinted :
375                                 ((Pagesprinted + 1) / 2) );
376 }
377 \f
378
379 /* initialize things for print pass through main list */
380
381 static void
382 init4print()
383
384 {
385         struct tm *timeinfo_p;
386         time_t clockinfo;
387         struct MAINLL *mll_p;
388         char *bbox_format;
389         static int first_time = YES;
390
391         if (first_time == NO) {
392                 page1setup();
393                 return;
394         }
395         first_time = NO;
396
397         /* initialize the SSV data */
398         initstructs();
399
400         printf("%%!PS-Adobe-1.0\n");
401         printf("%%%%Creator: Mup\n");
402         printf("%%%%Title: music: %s from %s\n", Outfilename, Curr_filename);
403         clockinfo = time((time_t *)0);
404         timeinfo_p = localtime(&clockinfo);
405         printf("%%%%CreationDate: %s %s %d %d:%d:%d %d\n",
406                         Dayofweek[timeinfo_p->tm_wday],
407                         Month[timeinfo_p->tm_mon], timeinfo_p->tm_mday,
408                         timeinfo_p->tm_hour, timeinfo_p->tm_min,
409                         timeinfo_p->tm_sec, 1900 + timeinfo_p->tm_year);
410         printf("%%%%Pages: (atend)\n");
411         printf("%%%%DocumentFonts: (atend)\n");
412         /* we need to know the value of panelsperpage before setting up the
413          * first page, as well as the pagewidth and pageheight,
414          * so need to peek into main list up till the first non-SSV
415          * to get that. */
416         for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) NULL;
417                                                 mll_p = mll_p->next) {
418                 if (mll_p->str == S_SSV) {
419                         asgnssv(mll_p->u.ssv_p);
420                 }
421                 else {
422                         /* as soon as we hit something other than SSV,
423                          * we're past any page size changes */
424                         break;
425                 }
426         }
427         bbox_format = "%%%%BoundingBox: 0 0 %d %d\n";
428         if (Score.panelsperpage == 2) {
429                 /* have to compensate for the fact that our page width/height
430                  * internally are that of the panel, but here we need the
431                  * physical paper size */
432                 printf(bbox_format, (int) (Score.pageheight * PPI),
433                                 (int) (Score.pagewidth * 2.0 * PPI));
434         }
435         else if ((Landscape = use_landscape(Score.pagewidth, Score.pageheight))
436                                                                 != 0) {
437                 printf(bbox_format, (int) (Score.pageheight * PPI),
438                                 (int) (Score.pagewidth * PPI));
439         }
440         else {
441                 printf(bbox_format, (int) (Score.pagewidth * PPI),
442                                 (int) (Score.pageheight * PPI));
443         }
444         printf("%%%%EndComments\n");
445         ps_prolog();
446         printf("/flagsep %.2f 300 mul def\t %% %.2f stepsizes\n",
447                                 FLAGSEP / STEPSIZE, FLAGSEP / STEPSIZE);
448         srand((unsigned)timeinfo_p->tm_sec);
449         (void) printf("/scv %d def ", ((rand() & 0x27d) << 8) | 4);
450         (void) printf("/sf 962 string def\n");
451         (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");
452         (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");
453         (void) printf("[ 961 ] 1341740116 fa\n");
454         (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");
455         (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");
456         (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");
457         (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");
458         (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");
459         (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");
460         (void) printf("[ 902 8 4 2 3 4 3 4 4 3 2 3 9 ] 1708988675 fa\n");
461         (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");
462         (void) printf("[ 664 23 4 2 13 66 4 5 9 ] 2061720845 fa\n");
463         (void) printf("[ 795 ] 1622189328 fa\n");
464         (void) printf("[ 463 45 40 41 50 45 84 ] 304180545 fa\n");
465         (void) printf("[ 494 40 41 49 45 43 84 ] 251711819 fa\n");
466         (void) printf("[ 149 203 37 144 ] 358262127 fa\n");
467         (void) printf("[ 456 142 52 ] 95949173 fa\n");
468         (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");
469         (void) printf("[ 839 5 13 12 13 13 48 ] 1943250302 fa\n");
470         (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");
471         (void) printf("[ 798 1 3 1 ] 1277775234 fa\n");
472         (void) printf("[ 76 32 135 79 99 8 246 43 30 160 ] 734015880 fa\n");
473         (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");
474         (void) printf("[ 193 49 180 8 17 134 ] 831070621 fa\n");
475         (void) printf("[ 353 366 ] 1033403809 fa\n");
476         (void) printf("[ 266 1 190 39 40 41 50 45 43 45 ] 1758436783 fa\n");
477         (void) printf("[ 423 8 109 ] 508918194 fa\n");
478         (void) printf("[ 328 6 30 6 31 6 269 ] 212071871 fa\n");
479         (void) printf("[ 390 357 2 ] 1671244225 fa\n");
480         (void) printf("[ 500 ] 347047368 fa\n");
481         (void) printf("[ 558 ] 1276946910 fa\n");
482         (void) printf("[ 651 ] 2109048312 fa\n");
483         (void) printf("[ 644 ] 1914352160 fa\n");
484         (void) printf("[ 520 ] 471204394 fa\n");
485         (void) printf("[ 512 5 2 ] 1930983991 fa\n");
486         (void) printf("[ 665 ] 154021439 fa\n");
487         (void) printf("[ 513 ] 777103941 fa\n");
488         (void) printf("[ 514 ] 260959830 fa\n");
489         (void) printf("[ 530 239 ] 1284535922 fa\n");
490         (void) printf("[ 510 ] 1982423675 fa\n");
491         (void) printf("[ 150 ] 1969948305 fa\n");
492         (void) printf("[ 511 7 134 ] 1407991454 fa\n");
493         (void) printf("[ 144 371 ] 1896661664 fa\n");
494         (void) printf("[ 464 52 ] 1444653737 fa\n");
495         (void) printf("[ 509 81 ] 1712172720 fa\n");
496         (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");
497         (void) printf("[ 954 ] 1802916616 fa\n");
498         (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");
499         (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");
500         (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");
501         (void) printf("[ 405 9 13 46 49 50 50 213 18 12 13 13 12 45 10 ] 160257827 fa\n");
502         (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");
503         (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");
504         (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");
505         (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");
506         (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");
507         (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");
508         (void) printf("[ 960 ] 75399990 fa\n");
509         (void) printf("[ 45 9 155 6 245 68 21 98 60 109 ] 1430691640 fa\n");
510         (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");
511         (void) printf("[ 901 ] 422446918 fa\n");
512         (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");
513         (void) printf("[ 466 6 135 41 7 6 36 6 89 ] 803193686 fa\n");
514         (void) printf("[ 42 80 1 55 80 1 80 36 37 155 1 263 40 65 ] 189315943 fa\n");
515         (void) printf("[ 6 31 36 9 43 21 6 185 36 37 210 ] 1031359337 fa\n");
516         (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");
517         (void) printf("[ 552 22 20 16 3 55 42 31 10 33 ] 343336822 fa\n");
518         (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");
519         (void) printf("[ 780 32 ] 847653755 fa\n");
520         (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");
521         (void) printf("[ 724 83 7 ] 1643402114 fa\n");
522         (void) printf("[ 228 296 8 25 39 16 159 14 34 ] 670118796 fa\n");
523         (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");
524         (void) printf("[ 680 2 41 2 2 5 13 11 10 40 2 50 80 ] 1392580498 fa\n");
525         (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");
526         (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");
527         (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");
528         (void) printf("[ 944 8 ] 1770206109 fa\n");
529         (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");
530         (void) printf("[ 442 25 4 6 114 27 38 42 32 25 20 47 19 112 ] 998588323 fa\n");
531         (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");
532         (void) printf("[ 912 9 11 ] 273962927 fa\n");
533         (void) printf("[ 8 230 25 23 6 17 130 31 61 64 16 127 32 ] 1881483187 fa\n");
534         (void) printf("[ 130 683 ] 1406620603 fa\n");
535         (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");
536         (void) printf("[ 861 13 9 4 12 8 17 3 ] 1447963591 fa\n");
537         (void) printf("[ 67 143 8 128 115 435 19 2 ] 477757388 fa\n");
538         (void) printf("[ 490 35 ] 1151262673 fa\n");
539         (void) printf("[ 5 70 67 32 37 16 14 7 27 18 142 301 17 90 103 ] 1523362782 fa\n");
540         (void) printf("[ 117 14 33 38 17 13 20 26 3 453 89 3 8 113 10 ] 1908448236 fa\n");
541         (void) printf("sf cvx exec\n");
542
543         setup_user_fonts();
544 #ifdef EXTCHAR
545         setup_extended_fonts();
546 #endif
547         printf("%%%%EndProlog\n");
548
549         /* init for first page */
550         page1setup();
551 }
552 \f
553
554 /* set thing up to print the first page */
555
556 static void
557 page1setup()
558
559 {
560         Feednumber = 0;
561
562         to_next_page(Mainllhc_p);
563
564         /* Arrange to start at beginning of main list */
565         initstructs();
566
567         /* start the cursor at the top left corner of page */
568         set_cur(0.0, PGHEIGHT);
569
570         Meas_num = 1;
571         Ped_snapshot[0] = NO;
572         set_staffscale(0);
573 }
574 \f
575
576 /* table of standard paper sizes, to be used to see if user specified
577  * a landscape version of a standard size */
578 struct Papersize {
579         int     width;
580         int     height;
581 } Paper_size_table[] = { 
582         { 612, 792 },   /* letter */
583         { 540, 720},    /* note */
584         { 612, 1008},   /* legal */
585         { 595, 842},    /* a4 */
586         { 421, 595},    /* a5 */
587         { 297, 421},    /* a6 */
588         { 612, 936},    /* flsa */
589         { 396, 612},    /* halfletter */
590         { 0, 0}
591 };
592
593 /* how many points away from an exact match to consider a match. This is big
594  * enough so that user can be off by a little and still get the desired
595  * results, yet not so big as to give false matches. */
596 #ifdef FUZZ
597 #undef FUZZ
598 #endif
599 #define FUZZ 24
600
601 /* given a paper size, determine if the paper
602  * size appears to be the landscape version of a standard paper size.
603  * If so, return the page height in points, otherwise return 0.
604  * It return this rather than just a boolean
605  *  since page height is needed for translate amount.
606  */
607
608 static int
609 use_landscape(pgwidth, pgheight)
610
611 double pgwidth;         /* page width in inches */
612 double pgheight;        /* page height in inches */
613
614 {
615         int pts_width, pts_height;      /* width and height in points */
616         int i;
617
618
619         /* convert dimension to points */
620         pts_width = (int) (pgwidth * PPI);
621         pts_height = (int) (pgheight * PPI);
622
623         /* for each paper size table entry, see if by interchanging the
624          * width and height we would end up with something within FUZZ
625          * points of matching a landscape mode paper size */
626         for (i = 0; Paper_size_table[i].width != 0; i++) {
627                 if (pts_width > Paper_size_table[i].height - FUZZ &&
628                                 pts_width < Paper_size_table[i].height + FUZZ &&
629                                 pts_height > Paper_size_table[i].width - FUZZ &&
630                                 pts_height < Paper_size_table[i].width + FUZZ) {
631                         return(pts_height);
632                 }
633         }
634
635         /* not landscape */
636         return(0);
637 }
638 \f
639
640 /* for any user-defined fonts, if there was any PostScript that needs to
641  * be output in order to use the font, output that.
642  */
643
644 static void
645 setup_user_fonts()
646
647 {
648         int f;
649         char buffer[BUFSIZ];
650
651         for (f = 0; f < MAXFONTS; f++) {
652                 if (Fontinfo[f].fontfile != (FILE *) 0) {
653                         while (fgets(buffer, BUFSIZ, Fontinfo[f].fontfile)
654                                                 != (char *) 0) {
655                                 printf("%s", buffer);
656                         }
657                         fclose(Fontinfo[f].fontfile);
658                 }
659         }
660 }
661 \f
662
663 #ifdef EXTCHAR
664
665 /* for each extended character set font that was used somewhere, output
666  * the PostScript to get that font set up so that is can be used.
667  */
668
669 static void
670 setup_extended_fonts()
671
672 {
673         int i;                  /* font index */
674         int have_extended;      /* YES if extended character set was used
675                                  * somewhere, and thus we have to output
676                                  * PostScript to allow using the set */
677
678
679         /* First see if there are any extended characters used at all.
680          * If not, we don't have to do anything more here */
681         have_extended = NO;
682         for (i = FONT_TR; i <= EXT_FONT_OFFSET; i++) {
683                 if (Font_used[i + EXT_FONT_OFFSET] == YES) {
684                         have_extended = YES;
685                         break;
686                 }
687         }
688
689         if (have_extended == NO) {
690                 return;
691         }
692
693         /* first call the PostScript function to set up the encoding
694          * array for the extended character set */
695         printf("\n%% set up extended character set fonts\n");
696         printf("makeExtEncoding\n");
697
698         /* now for each extended character set font that was actually used
699          * somewhere, call the PostScript function to set up that font,
700          * based on the font from which it is derived */
701         for (i = FONT_TR; i <= EXT_FONT_OFFSET; i++) {
702                 if (Font_used[i + EXT_FONT_OFFSET] == YES) {
703                         /* arguments are the name of the extended font
704                          * and the name of the base font from which it
705                          * is derived */
706                         prfontname(i + EXT_FONT_OFFSET);
707                         prfontname(i);
708                         printf("makeExtendedFont\n");
709                 }
710         }
711 }
712 #endif
713 \f
714
715 /* given a LINE struct, output commands to draw a line */
716
717 static void
718 pr_line(line_p, fname, lineno)
719
720 struct LINE *line_p;    /* info about what kind of line to draw and where */
721 char *fname;            /* file name for error messages */
722 int lineno;             /* line number for error messages */
723
724 {
725         double x1, y1;  /* beginning of line */
726         double x2, y2;  /* end of line */
727
728         x1 = inpc_x( &(line_p->start), fname, lineno);
729         y1 = inpc_y( &(line_p->start), fname, lineno);
730         x2 = inpc_x( &(line_p->end), fname, lineno);
731         y2 = inpc_y( &(line_p->end), fname, lineno);
732
733         /* If there is a string associated with the line,
734          * print that first.
735          */
736         if (line_p->string != 0) {
737                 double line_len;        /* length of line in LINE struct */
738                 double str_x, str_y;    /* where string starts */
739
740                 /* First find length of line. */
741                 line_len = sqrt(SQUARED(x2 - x1) + SQUARED(y2 - y1));
742                 if (x2 < x1) {
743                         line_len = -line_len;
744                 }
745
746                 /* For now, pretend the line is horizontal, starting
747                  * at (x1,y1). The horizontal middle of the string should then
748                  * be at the midpoint of the line, and the left edge of the
749                  * string should be half the string width left of that.
750                  * The vertical is a STEPSIZE above the line.
751                  */
752                 str_x = (line_len / 2.0) - (strwidth(line_p->string) / 2.0);
753                 str_y = STEPSIZE + strdescent(line_p->string);
754
755                 /* move effective origin of coordinate system to (x1,y1),
756                  * then rotate by the appropriate angle and print string.
757                  */
758                 outop(O_GSAVE);
759                 outcoord(x1);
760                 outcoord(y1);
761                 outop(O_TRANSLATE);
762                 /* calculate angle. If vertical line or nearly so,
763                  * avoid division by zero */
764                 if (fabs(x2 - x1) < .001) {
765                         outint(90);
766                 }
767                 else {
768                         OUTP(("%.1f ", atan( (y2 - y1) / (x2 - x1) ) * 180.0 / PI));
769                 }
770                 
771                 outop(O_ROTATE);
772                 pr_string(str_x, str_y, line_p->string, J_LEFT, 0, -1);
773                 outop(O_GRESTORE);
774         }
775
776         /* wavy lines are special case */
777         if (line_p->linetype == L_WAVY) {
778                 draw_wavy(x1, y1, x2, y2);
779                 return;
780         }
781
782         /* set line width to specified width and type, then draw the line */
783         do_linetype(line_p->linetype);
784         draw_line (x1, y1, x2, y2);
785
786         /* make sure line type gets set back to solid */
787         if (line_p->linetype == L_DASHED || line_p->linetype == L_DOTTED) {
788                 do_linetype(L_NORMAL);
789         }
790 }
791 \f
792
793 /* generate PostScript command to tell what kind of line to draw */
794
795 void
796 do_linetype(ltype)
797
798 int ltype;              /* L_WIDE, L_NORMAL, etc  */
799
800 {
801         if (Last_linetype == ltype && Last_staffscale == Staffscale) {
802                 /* same as last time, no need to tell the printer again */
803                 return;
804         }
805
806         /* output command for proper width/type of line */
807         switch(ltype) {
808
809         case L_WIDE:
810                 OUTP(("%4.2f ", W_WIDE * Staffscale));
811                 outop(O_LINEWIDTH);
812                 break;
813
814         case L_NORMAL:
815                 OUTP(("%4.2f ", W_NORMAL * Staffscale));
816                 outop(O_LINEWIDTH);
817                 break;
818
819         case L_MEDIUM:
820                 OUTP(("%4.2f ", W_MEDIUM * Staffscale));
821                 outop(O_LINEWIDTH);
822                 break;
823
824         case L_DOTTED:
825                 OUTP(("%4.2f ", Staffscale));
826                 outop(O_LINEWIDTH);
827                 outop(O_DOTTED);
828                 Doing_dotted = YES;
829                 break;
830
831         case L_DASHED:
832                 OUTP(("%4.2f ", Staffscale));
833                 outop(O_LINEWIDTH);
834                 outop(O_DASHED);
835                 Doing_dotted = YES;
836                 break;
837
838         default:
839                 pfatal("unknown line type");
840                 break;
841         }
842
843         /* remember current line type */
844         Last_linetype = ltype;
845         Last_staffscale = Staffscale;
846
847         /* if was doing dotting but not anymore, tell PostScript */
848         if (Doing_dotted && (ltype != L_DOTTED) && (ltype != L_DASHED)) {
849                 Doing_dotted = NO;
850                 outop(O_ENDDOTTED);
851         }
852 }
853 \f
854
855 /* output commands for drawing a line. Resulting output is:
856  *      x1 y1 moveto x2 y2 lineto stroke */
857
858 void
859 draw_line(x1, y1, x2, y2)
860
861 double x1, y1;  /* draw line from here */
862 double x2, y2;  /* to here */
863
864 {
865         dr_line( (double) x1, (double) y1, (double) x2, (double) y2, O_LINE);
866 }
867
868
869 /* output commands to draw a line whose width is proportional to the given
870  * point size */
871
872 void
873 draw_prop_line(x1, y1, x2, y2, size, ltype)
874
875 double x1, y1;  /* draw line from here */
876 double x2, y2;  /* to here */
877 int size;       /* make width proportional to this */
878 int ltype;      /* O_LINE, etc */
879
880 {
881         /* temporarily change the line width, then draw the line */
882         outop(O_GSAVE);
883         OUTP(("%.2f ", (double) size * 0.065 * Staffscale));
884         outop(O_LINEWIDTH);
885         dr_line(x1, y1, x2, y2, O_LINE);
886         outop(O_GRESTORE);
887 }
888 \f
889
890 /* draw a wavy line. Resulting output is:
891  *      x1 y1 moveto x2 y2 wavy */
892
893 void
894 draw_wavy(x1, y1, x2, y2)
895
896 double x1;
897 double y1;      /* draw wavy line from x1,y1 */
898 double x2;
899 double y2;      /* to x2, y2 */
900
901 {
902         dr_line((double) x1, (double) y1, (double) x2, (double) y2, O_WAVY);
903 }
904 \f
905
906 /* actually draw line. Common function for drawing regular or wavy lines */
907
908 static void
909 dr_line(x1, y1, x2, y2, ltype)
910
911 double x1;
912 double y1;      /* draw line from x1,y1 */
913 double x2;
914 double y2;      /* to x2,y2 */
915 int ltype;      /* O_LINE, etc */
916
917 {
918
919         /* output coordinates */
920         outcoord(x1);
921         outcoord(y1);
922         outop(O_MOVETO);
923
924         outcoord(x2);
925         outcoord(y2);
926         outop(ltype);
927
928         /* current location is where line ended */
929         set_cur(x2, y2);
930 }
931 \f
932
933 /* output commands to draw a curve */
934
935 static void
936 pr_curve(curve_p, fname, lineno)
937
938 struct CURVE *curve_p;  /* which curve */
939 char *fname;            /* file name for error messages */
940 int lineno;             /* line number for error messages */
941
942 {
943         struct INPCOORD *inpcoord_p;
944         int n;
945         float *xlist, *ylist;
946         float cwid;             /* curve width */
947         int taper;              /* YES or NO */
948
949
950         /* pr_allcurve() expects lists of X and Y coordinates, so
951          * get some space for those lists,  and fill them in.
952          * Call pr_allcurve() to print the curve, then free the lists */
953         MALLOCA(float, xlist, curve_p->ncoord);
954         MALLOCA(float, ylist, curve_p->ncoord);
955         for (n = 0; n < curve_p->ncoord; n++) {
956                 inpcoord_p = &(curve_p->coordlist[n]);
957                 xlist[n] = inpc_x(inpcoord_p, fname, lineno);
958                 ylist[n] = inpc_y(inpcoord_p, fname, lineno);
959         }
960         switch(curve_p->curvetype) {
961         case L_NORMAL:
962                 cwid = W_NORMAL / PPI;
963                 taper = YES;
964                 break;
965         case L_MEDIUM:
966                 cwid = W_MEDIUM / PPI;
967                 taper = YES;
968                 break;
969         case L_WIDE:
970                 cwid = W_WIDE / PPI;
971                 taper = YES;
972                 break;
973         case L_DASHED:
974                 cwid = 1.0 / PPI;
975                 taper = NO;
976                 do_linetype(L_DASHED);
977                 break;
978         case L_DOTTED:
979                 cwid = 1.0 / PPI;
980                 taper = NO;
981                 do_linetype(L_DOTTED);
982                 break;
983         default:
984                 pfatal("unknown curve type");
985                 /*NOTREACHED*/
986                 return;
987         }
988         /* adjust for current staff scaling */
989         cwid *= Staffscale;
990
991         pr_allcurve(xlist, ylist, curve_p->ncoord, cwid, taper);
992         FREE(xlist);
993         FREE(ylist);
994         /* make sure line type gets set back to solid */
995         if (curve_p->curvetype == L_DASHED || curve_p->curvetype == L_DOTTED) {
996                 do_linetype(L_NORMAL);
997         }
998 }
999 \f
1000
1001 /* functions to do common PostScript things */
1002
1003 void
1004 do_moveto(x, y)
1005
1006 double x;
1007 double y;
1008
1009 {
1010         outcoord(x);
1011         outcoord(y);
1012         outop(O_MOVETO);
1013 }
1014
1015
1016 void
1017 do_line(x, y)
1018
1019 double x;
1020 double y;
1021
1022 {
1023         outcoord(x);
1024         outcoord(y);
1025         outop(O_LINETO);
1026 }
1027
1028 void
1029 do_curveto(x1, y1, x2, y2, x3, y3)
1030
1031 double x1, y1;
1032 double x2, y2;
1033 double x3, y3;
1034
1035 {
1036         outcoord(x1);
1037         outcoord(y1);
1038         outcoord(x2);
1039         outcoord(y2);
1040         outcoord(x3);
1041         outcoord(y3);
1042         outop(O_CURVETO);
1043 }
1044
1045
1046 void
1047 do_stroke()
1048
1049 {
1050         outop(O_STROKE);
1051 }
1052
1053 void
1054 do_fill()
1055
1056 {
1057         outop(O_FILL);
1058 }
1059
1060 void
1061 do_newpath()
1062
1063 {
1064         outop(O_NEWPATH);
1065 }
1066
1067 void
1068 do_closepath()
1069
1070 {
1071         outop(O_CLOSEPATH);
1072 }
1073
1074 /* output a PostScript scale command */
1075
1076 static void
1077 do_scale(xscale, yscale)
1078
1079 double xscale, yscale;
1080
1081 {
1082         OUTP(("%0.6f %0.6f ", xscale, yscale));
1083         outop(O_SCALE);
1084 }
1085
1086 /* print a white box with the corners given */
1087
1088 void
1089 do_whitebox(x1, y1, x2, y2)
1090
1091 double x1, y1;
1092 double x2, y2;
1093
1094 {
1095         outcoord(x1);
1096         outcoord(y1);
1097         outcoord(x2);
1098         outcoord(y2);
1099         OUTP(("whitebox\n"));
1100 }
1101 \f
1102
1103 /* output PostScript to draw a guitar grid */
1104
1105 void
1106 do_grid(x, y, space, grid_p, staff)
1107
1108 double x;
1109 double y;
1110 double space;   /* distance between lines of the grid */
1111 struct GRID *grid_p;
1112 int staff;
1113
1114 {
1115         int s;
1116         int fret, fretnum, numvert;
1117         int topfret;
1118
1119         outcoord(x);
1120         outcoord(y);
1121         outcoord(space);
1122
1123         gridinfo(grid_p, staff, &fret, &fretnum, &numvert, &topfret);
1124         outint(fret);
1125         outint(fretnum);
1126         outint(numvert);
1127
1128         /* the curve ends */
1129         outint(grid_p->curvel);
1130         outint(grid_p->curver);
1131
1132         /* fret value for each string in a PostScript array */
1133         OUTP(("[ "));
1134         for (s = 0; s < grid_p->numstr; s++) {
1135                 if (grid_p->positions[s] <= 0) {
1136                         outint(grid_p->positions[s]);
1137                 }
1138                 else {
1139                         outint(grid_p->positions[s] - topfret);
1140                 }
1141         }
1142         OUTP(("] grid\n"));
1143 }
1144 \f
1145
1146 /* output commands for printing one music character */
1147
1148 void
1149 pr_muschar(x, y, ch, size, font)
1150
1151 float x, y;     /* where to print */
1152 int ch;         /* which music character to print */
1153 int size;
1154 int font;       /* FONT_MUSIC*   */
1155
1156 {
1157         outp_muschar(x, y, ch, size, font);
1158
1159         /* x of music char is in middle, so set current to right edge */
1160         size = adj_size(size, Staffscale, (char *) 0, -1);
1161         set_cur(x + width(font, size, ch) / 2.0, y);
1162 }
1163 \f
1164
1165 /* Output PostScript to print a music character. Common part for
1166  * normal and italic versions of the character. */
1167
1168 static void
1169 outp_muschar(x, y, ch, size, font)
1170
1171 double x, y;
1172 int ch;
1173 int size;
1174
1175 {
1176         double scaling;
1177
1178         /* tell where to print it */
1179         outcoord( (double) x);
1180         outcoord( (double) y);
1181
1182         if (size == DFLT_SIZE) {
1183                 scaling = Staffscale;
1184         }
1185         else {
1186                 scaling = (double) size * Staffscale / (double) DFLT_SIZE;
1187         }
1188         OUTP(("%f ", scaling));
1189
1190         /* output the symbolic name of the music character */
1191         OUTP(("%s\n", mc_num2name(ch, font)));
1192 }
1193 \f
1194
1195 /* Print an italic music character. We do this by constructing an
1196  * appropriate PostScript transform matrix and then printing the character.
1197  * The transform matrix takes the rectangle that bounds the character,
1198  * widens it slightly, and and turns it into a parallelogram
1199  * slanted by 15 degrees.
1200  *    ----------          ---------
1201  *    |        |  -->    /       /
1202  *    |        |        /       /
1203  *    ----------        --------
1204  */
1205
1206 static void
1207 pr_ital_muschar(x, y, ch, size, font)
1208
1209 double x, y;    /* where to print */
1210 int ch;         /* which music character to print */
1211 int size;
1212 int font;       /* MUSIC_FONT*  */
1213
1214 {
1215         float chwidth, chheight;
1216         float adj;              /* distance top left is moved to get slant */
1217         float inc;              /* increment on width */
1218         float a, c;             /* for transform equation  x' = ax + cy + t  */
1219         int eff_size;
1220
1221
1222         eff_size = adj_size(size, Staffscale, (char *) 0, -1);
1223         chheight = height(font, eff_size, ch);
1224         chwidth = width(font, eff_size, ch);
1225         /* Widen some so doesn't look so cramped. This may 
1226          * encroach on neighboring characters, but if they are italic
1227          * too--which they probably are--they probably slant enough
1228          * to stay out of the way. */
1229         inc = MIN(chwidth, chheight * 0.8) / 4.0;
1230
1231         /* we want to slant by 15 degrees, so use tangent of 15 degrees */
1232         adj = chheight * 0.27;
1233         /* if character is really narrow, don't slant so much--
1234          * don't squeeze character to less than half its original width */
1235         if (adj > chwidth / 2.0) {
1236                 adj = chwidth / 2.0;
1237         }
1238         
1239         /* Temporarily change the transform matrix.
1240          * The y value is unchanged by the transform.
1241          * The new x is
1242          *      x' = ax + cy + t
1243          * where t is 0, and a and c are as stated below.
1244          */
1245         a = (chwidth + 2 * inc - adj) / chwidth;
1246         c = adj / chheight;
1247
1248         outop(O_GSAVE);
1249         OUTP(("[ %f 0.0 %f 1.0 0.0 0.0 ] ", a, c));
1250         outop(O_CONCAT);
1251
1252         /* The x location will get adjusted by the new transform matrix,
1253          * so we have to compensate so it will appear where it should.
1254          * We take the PostScript transform matrix equation given above,
1255          * then set x' to the x value that was passed in to us,
1256          * and rearrange to solve for x.
1257          */
1258         outp_muschar((x - c * y) / a, y, ch, size, font);
1259
1260         /* return to previous transform matrix */
1261         outop(O_GRESTORE);
1262
1263         /* x of music char is in middle, so set current to right edge */
1264         set_cur(x + width(font, eff_size, ch) / 2.0, y);
1265 }
1266 \f
1267
1268 /* print bar line */
1269
1270 static void
1271 pr_bar(mll_p, x, is_pseudobar)
1272
1273 struct MAINLL *mll_p;   /* print bar connected here */
1274 double x;               /* x coordinate */
1275 int is_pseudobar;       /* YES if is pseudobar at beginning of score */
1276
1277 {
1278         register int s;         /* staff number */
1279         register int n;         /* index into range list */
1280         struct BAR *bar_p;      /* info about the bar */
1281         struct MAINLL *m_p;     /* to walk through main list */
1282         int next_is_restart = NO;       /* if following bar is a restart */
1283
1284
1285         debug(512, "pr_bar");
1286
1287         if (is_pseudobar == YES) {
1288                 bar_p = mll_p->u.clefsig_p->bar_p;
1289         }
1290         else {
1291                 bar_p = mll_p->u.bar_p;
1292         }
1293
1294         /* We need to know if the following bar (if any) is a restart,
1295          * because then this one will have to be handled like it is at
1296          * the right margin, so find out. */
1297         for (m_p = mll_p->next; m_p != (struct MAINLL *) 0; m_p = m_p->next) {
1298                 if (m_p->str == S_FEED) {
1299                         /* If there was a restart, it's been moved to this
1300                          * feed and is thus now irrelevant. */
1301                         break;
1302                 }
1303                 /* If there is a clefsig, then even if there is a restart
1304                  * we should not remove this bar's right padding--
1305                  * there is still some staff after it for the
1306                  * clef/keysig/time (whatever subset is specified by clefsig),
1307                  * and moving the bar would cause them to get too close. */
1308                 if (m_p->str == S_CLEFSIG) {
1309                         break;
1310                 }
1311                 if (m_p->str == S_BAR) {
1312                         if (m_p->u.bar_p->bartype == RESTART) {
1313                                 next_is_restart = YES;
1314                         }
1315                         /* we've looked ahead far enough */
1316                         break;
1317                 }
1318         }
1319
1320         /* go down the bar list and the list of staffs */
1321         for (s = 1, n = 0; n < Score.nbarst; n++) {
1322
1323                 /* everything up to next range is barred individually */
1324                 for (   ; s < Score.barstlist[n].top; s++) {
1325                         pr_bar_range(bar_p, s, s, (double) x, next_is_restart, mll_p);
1326                 }
1327
1328                 /* everything in the range is barred together */
1329                 pr_bar_range(bar_p, Score.barstlist[n].top,
1330                         Score.barstlist[n].bottom, x, next_is_restart, mll_p);
1331                 s = Score.barstlist[n].bottom + 1;
1332         }
1333
1334         /* any remaining are barred individually */
1335         for (   ; s <= Score.staffs; s++) {
1336                 pr_bar_range(bar_p, s, s, (double) x, next_is_restart, mll_p);
1337         }
1338
1339         /* If user specified a measure number use that */
1340         if (bar_p->mnum > 0) {
1341                 Meas_num = bar_p->mnum;
1342         }
1343         /* if basictime of the last STAFF we saw was < -1, then
1344          * it was a multirest, so the measure number needs to
1345          * be incremented by the number of measures of multirest.
1346          * Since this is stored as a negative, we subtract the
1347          * negative to get the same effect as adding the absolute
1348          * value */
1349         else if ( (Last_staff != (struct STAFF *) 0)
1350                         && (is_pseudobar == NO)
1351                         && (Last_staff->groups_p[0] != (struct GRPSYL *) 0)
1352                         && (Last_staff->groups_p[0]->basictime < -1) ) {
1353                 Meas_num -= Last_staff->groups_p[0]->basictime;
1354         }
1355         else if ((bar_p->bartype != INVISBAR) && (bar_p->bartype != RESTART)
1356                                                 && (is_pseudobar == NO)) {
1357                 /* normal case, not multirest; just increment measure number */
1358                 Meas_num++;
1359         }
1360
1361         /* print rehearsal mark if any */
1362         if (is_pseudobar == NO) {
1363                 pr_reh(mll_p);
1364         }
1365
1366         /* take care of pedal marks for the measure */
1367         if (is_pseudobar == NO) {
1368                 pr_ped_bar(mll_p, bar_p);
1369         }
1370 }
1371 \f
1372
1373 /* given a range of staffs to bar together, find which are visible and from
1374  * that, the y-coordinates of the ends of the bar line, and draw it */
1375
1376 static void
1377 pr_bar_range(bar_p, topstaff, botstaff, x, next_is_restart, mll_p)
1378
1379 struct BAR *bar_p;      /* info about bar */
1380 int topstaff;           /* top staff to be barred together */
1381 int botstaff;           /* bottom staff to be barred together */
1382 double x;               /* x coordinate of where to draw the bar */
1383 int next_is_restart;    /* YES if following bar is RESTART */
1384 struct MAINLL *mll_p;   /* to get effective margin */
1385
1386 {
1387         float y1, y2;           /* top and bottom of bar line */
1388         float halfbarwidth;     /* half the width of the bar line */
1389         int staffno;
1390         int stafflines;
1391
1392
1393         /* check for null pointer to avoid core dumps */
1394         if (Score_location_p == (float *) 0) {
1395                 pfatal("can't print bar: no feed");
1396                 return;
1397         }
1398
1399         /* Normally, we want some padding on both sides of a bar line,
1400          * but at the end of a staff, we don't want right padding.
1401          * This applies either if we are at the right
1402          * margin or if the next bar is a restart. */
1403         halfbarwidth = width_barline(bar_p) / 2.0;
1404         /* Make sure bars line at end of score are precisely at the end */
1405         if (PGWIDTH - eff_rightmargin(mll_p) - x <= halfbarwidth + 3.0 * STDPAD) {
1406                 /* Should only hit this now if there is a bug in placement
1407                  * of last bar line in a score, but since we changed how
1408                  * that is determined, better safe than sorry. */
1409                 x = PGWIDTH - eff_rightmargin(mll_p) - halfbarwidth
1410                                                         + eos_bar_adjust(bar_p);
1411         }
1412
1413         /* Similarly, make sure bars line just before a restart
1414          * are precisely at the point where the restart whitebox starts. */
1415         if (next_is_restart) {
1416                 struct MAINLL *m_p;
1417
1418                 /* find the restart */
1419                 for (m_p = mll_p; m_p != 0; m_p = m_p->next) {
1420                         if (m_p->str == S_BAR && m_p->u.bar_p->bartype == RESTART) {
1421                                 if (m_p->u.bar_p->c[AX] - HALF_RESTART_WIDTH
1422                                                 - m_p->u.bar_p->padding - x
1423                                                 <= halfbarwidth + 2.0 * STDPAD) {
1424                                         x = m_p->u.bar_p->c[AX]
1425                                                         - HALF_RESTART_WIDTH
1426                                                         - m_p->u.bar_p->padding
1427                                                         - halfbarwidth
1428                                                         + eos_bar_adjust(bar_p);
1429                                 }
1430                                 break;
1431                         }
1432                 }
1433         }
1434
1435         /* go through the range of staffs */
1436         /* Note: y2 doesn't really need to be set here, it's just to shut up
1437          * compilers that think it could be used without being set. */
1438         for (y1 = y2 = -1.0, staffno = topstaff; staffno <= botstaff; staffno++) {
1439
1440                 /* only worry about visible staffs */
1441                 if ( (svpath(staffno, VISIBLE))->visible == YES) {
1442
1443                         stafflines = svpath(staffno, STAFFLINES)->stafflines;
1444                         set_staffscale(staffno);
1445
1446                         /* if hadn't found any staff yet to bar, now we have */
1447                         if (y1 < 0.0) {
1448                                 if (stafflines < 2) {
1449                                         y1 = Staffs_y[staffno] +
1450                                                         (2.0 * Stepsize);
1451                                 }
1452                                 else {
1453                                         y1 = Staffs_y[staffno] +
1454                                                 (stafflines - 1) * Stepsize
1455                                                 * (is_tab_staff(staffno) ?
1456                                                 TABRATIO : 1.0);
1457                                         /* 2-line staffs get a bit more, so
1458                                          * repeat sign dots have something
1459                                          * to be next to */
1460                                         if (stafflines == 2) {
1461                                                 y1 += 2 * Stepsize;
1462                                         }
1463                                 }
1464                         }
1465
1466                         /* this is the bottom one found so far */
1467                         if (stafflines < 2) {
1468                                 y2 = Staffs_y[staffno] - (2.0 * Stepsize);
1469                         }
1470                         else {
1471                                 y2 = Staffs_y[staffno] -
1472                                         (stafflines - 1) * Stepsize *
1473                                         (is_tab_staff(staffno) ? TABRATIO : 1.0);
1474                                 if (stafflines == 2) {
1475                                         y2 -= 2 * Stepsize;
1476                                 }
1477                         }
1478
1479                         /* if repeat, print the dots */
1480                         pr_repeat_dots(bar_p->bartype, staffno, (double) x);
1481                 }
1482         }
1483
1484         /* if any were visible, we draw the bar line now */
1485         if (y1 > 0.0) {
1486                 draw_bar(bar_p->bartype, bar_p->linetype,
1487                                         (double) x, (double) y1, (double) y2);
1488         }
1489 }
1490 \f
1491
1492 /* actually draw a bar line of the proper type at specified place */
1493
1494 /*--- Note: any changes in width made here have to be reflected in
1495  * pr_bar_range() for adjustment when at right edge of page, and
1496  * in width_barline() */
1497
1498 static void
1499 draw_bar(bartype, linetype, x, y1, y2)
1500
1501 int bartype;    /* info about single, double, repeat, etc */
1502 int linetype;
1503 double x;
1504 double y1;      /* top of bar line */
1505 double y2;      /* bottom of bar line */
1506
1507 {
1508         /* always use default staffscale for bar lines since they are
1509          * not associated with any particular staff */
1510         set_staffscale(0);
1511         do_linetype(linetype);
1512         /* dashed/dotted look better if we offset them slightly */
1513         if (linetype == L_DASHED || linetype == L_DOTTED) {
1514                 y1 -= Stepsize * 0.375;
1515                 y2 += Stepsize * 0.1;
1516         }
1517
1518         switch (bartype) {
1519
1520         case DOUBLEBAR:
1521                 draw_line(x - 2.0 * STDPAD, y1, x - 2.0 * STDPAD, y2);
1522                 draw_line(x + STDPAD, y1, x + STDPAD, y2);
1523                 break;
1524
1525         case SINGLEBAR:
1526                 draw_line(x, y1, x, y2);
1527                 break;
1528
1529         case REPEATSTART:
1530                 draw_line(x + STDPAD, y1, x + STDPAD, y2);
1531                 do_linetype(L_WIDE);
1532                 draw_line(x - (3.0 * STDPAD), y1, x - (3.0 * STDPAD), y2);
1533                 break;
1534
1535         case REPEATEND:
1536                 draw_line(x, y1, x, y2);
1537                 do_linetype(L_WIDE);
1538                 draw_line(x + (4.0 * STDPAD), y1, x + (4.0 * STDPAD), y2 );
1539                 break;
1540
1541         case REPEATBOTH:
1542                 do_linetype(L_WIDE);
1543                 draw_line(x - (2.5 * STDPAD), y1, x - (2.5 * STDPAD), y2);
1544                 draw_line(x + (2.5 * STDPAD), y1, x + (2.5 * STDPAD), y2);
1545                 break;
1546
1547         case ENDBAR:
1548                 draw_line(x - (2.0 * STDPAD), y1, x - (2.0 * STDPAD), y2);
1549                 do_linetype(L_WIDE);
1550                 draw_line(x + (2.0 * STDPAD), y1, x + (2.0 * STDPAD), y2);
1551                 break;
1552
1553         case RESTART:
1554                 /* This is a "funny" bar that is drawn when the staff lines
1555                  * are printed, so there isn't anything to be done here. */
1556                 break;
1557
1558         case INVISBAR:
1559                 /* nothing to do! */
1560                 break;
1561
1562         default:
1563                 pfatal("bad bar type");
1564         }
1565         do_linetype(L_NORMAL);
1566 }
1567 \f
1568
1569 /* print the dots for a repeat sign */
1570
1571 static void
1572 pr_repeat_dots(bartype, staff, x)
1573
1574 int bartype;    /* repeatstart, repeatend, repeatboth, etc */
1575 int staff;      /* which staff to print on */
1576 double x;       /* horizontal position */
1577
1578 {
1579         float y;        /* vertical position of middle of staff */
1580         double topoffset, bottomoffset;         /* dot offset */
1581         double adjust;          /* adjustment for tablature and/or staffscale */
1582         int stafflines;
1583         
1584
1585         /* If no dots, don't bother */
1586         if (bartype != REPEATSTART && bartype != REPEATEND
1587                                                 && bartype != REPEATBOTH) {
1588                 return;
1589         }
1590
1591         if (Score_location_p == (float *) 0) {
1592                 /* this should never be hit--we already checked earlier */
1593                 pfatal("can't do repeat: no feed");
1594                 return;
1595         }
1596
1597         /* get y offset based on staff */
1598         y = Staffs_y[staff];
1599         adjust = Stepsize * (is_tab_staff(staff) ? TABRATIO : 1.0);
1600         bottomoffset = topoffset = adjust;
1601
1602         /* if even number of staff lines, compensate by moving up */
1603         stafflines = svpath(staff, STAFFLINES)->stafflines;
1604         if ( (stafflines & 1) == 0) {
1605                 y += adjust;
1606         }
1607
1608         /* if more than 5 lines on staff, leave one blank space between
1609          * the dots */
1610         if (stafflines > 5) {
1611                 if ( (stafflines & 1) == 0) {
1612                         /* even number of staff lines, move bottom down */
1613                         bottomoffset = 3 * adjust;
1614                 }
1615                 else {
1616                         /* odd number of lines, move top up */
1617                         topoffset = 3 * adjust;
1618                 }
1619         }
1620         
1621
1622         /* print dots at appropriate locations */
1623         switch(bartype) {
1624
1625         case REPEATSTART:
1626                 do_rdots((double) (x + (4.0 * STDPAD)), (double) y, topoffset,
1627                                         bottomoffset);
1628                 break;
1629
1630         case REPEATBOTH:
1631                 do_rdots((double) (x + (7.0 * STDPAD)), (double) y, topoffset,
1632                                         bottomoffset);
1633                 do_rdots((double) (x - (7.0 * STDPAD)), (double) y, topoffset,
1634                                         bottomoffset);
1635                 break;
1636
1637         case REPEATEND:
1638                 do_rdots((double) (x - (4.0 * STDPAD)), (double) y, topoffset,
1639                                         bottomoffset);
1640                 break;
1641
1642         default:
1643                 /* other types of bars don't have dots */
1644                 break;
1645         }
1646 }
1647 \f
1648
1649 /* print the 2 dots for a repeat sign */
1650
1651 static void
1652 do_rdots(x, y, topoffset, bottomoffset)
1653
1654 double x;
1655 double y;       /* y is a middle of staff, so offset from there */
1656 double topoffset, bottomoffset;         /* offset from y in each direction */
1657
1658 {
1659         pr_muschar(x, y + topoffset, C_DOT, DFLT_SIZE, FONT_MUSIC);
1660         pr_muschar(x, y - bottomoffset, C_DOT, DFLT_SIZE, FONT_MUSIC);
1661 }
1662 \f
1663
1664 /* print any rehearsal marks associated with bar line */
1665
1666 static void
1667 pr_reh(mll_p)
1668
1669 struct MAINLL *mll_p;   /* current bar line is off of here */
1670
1671 {
1672         struct MARKCOORD *mark_p;       /* where to put rehearsal mark */
1673         float y;                        /* vertical location */
1674         struct BAR *bar_p;
1675         char *str;                      /* the string, with box or circle
1676                                          * or nothing as appropriate for
1677                                          * the associated staff */
1678
1679
1680         if (mll_p->str == S_BAR) {
1681                 bar_p = mll_p->u.bar_p;
1682         }
1683         else {
1684                 bar_p = mll_p->u.clefsig_p->bar_p;
1685         }
1686
1687         for (mark_p = bar_p->reh_p; mark_p != (struct MARKCOORD *) 0;
1688                                         mark_p = mark_p->next) {
1689
1690                 /* print rehearsal mark if any */
1691                 if (bar_p->reh_string != (char *) 0) {
1692
1693                         y = Staffs_y[mark_p->staffno] + mark_p->ry;
1694
1695                         /* get boxed or circled version if appropriate */
1696                         str = get_reh_string(bar_p->reh_string, mark_p->staffno);
1697                         pr_string((double) bar_p->c[AX] - left_width(str),
1698                                         (double) y, str, J_LEFT,
1699                                         mll_p->inputfile, mll_p->inputlineno);
1700                 }
1701         }
1702 }
1703 \f
1704
1705 /* draw a box of given size at given x,y */
1706
1707 static void
1708 pr_box(x, y, boxheight, boxwidth)
1709
1710 double x, y;
1711 double boxheight, boxwidth;
1712
1713 {
1714         do_linetype(L_NORMAL);
1715         do_newpath();
1716         do_moveto(x, y);
1717         do_line(x, y + boxheight);
1718         do_line(x + boxwidth, y + boxheight);
1719         do_line(x + boxwidth, y);
1720         do_closepath();
1721         do_stroke();
1722 }
1723 \f
1724
1725 /* do a feed (newscore and maybe newpage) */
1726
1727 void
1728 pr_feed(main_feed_p)
1729
1730 struct MAINLL *main_feed_p;     /* MAINLL struct pointing to FEED */
1731
1732 {
1733         register int s;         /* walk through staffs */
1734         float lowest_y = 0.0;   /* y coord of bottom staff. Initialization is
1735                                  * solely to shut up bogus compiler warning */
1736         float highest_y = 0.0;
1737         int printed;            /* How many staffs printed so far */
1738         int had_br_br;          /* YES if had braces and/or brackets printed */
1739         int need_vert_line = NO;        /* if need line at left edge */
1740         struct FEED *feed_p;
1741         int stafflines;
1742         double y;
1743
1744
1745         debug(256, "pr_feed lineno=%d", main_feed_p->inputlineno);
1746
1747         feed_p = main_feed_p->u.feed_p;
1748
1749         /* If user put top/bottom or newpage at the very end of the file,
1750          * we could end up with a page with nothing but header/footer.
1751          * So if there is no good reason to do another page, we don't. */
1752         if (Atend_info.separate_page == NO && main_feed_p->next == 0) {
1753                 /* Nothing at all after the feed,
1754                  * so no need to make another page. */
1755                 return;
1756         }
1757
1758         /* if doing a page feed, print the headers and footers on the
1759          * current page and move on to the next one */
1760         if (feed_p->pagefeed == YES) {
1761                 newpage(main_feed_p);
1762         }
1763
1764         /* If there is a top and/or bot block, print those.
1765          * Even though from user's viewpoint the current page may
1766          * use top2/bot2, placement phase will have set top_p/bot_p
1767          * to whatever is appropriate for this page.
1768          */
1769         if (feed_p->top_p != 0) {
1770                 y = PGHEIGHT - EFF_TOPMARGIN
1771                         - (Feednumber == 1 ? Header.height : Header2.height);
1772                 pr_topbot(feed_p->top_p, y);
1773         }
1774         if (feed_p->bot_p != 0) {
1775                 y = EFF_BOTMARGIN + feed_p->bot_p->height
1776                         + (Feednumber == 1 ? Footer.height : Footer2.height);
1777                 pr_topbot(feed_p->bot_p, y);
1778         }
1779
1780         if (main_feed_p->next == 0) {
1781                 /* Feed at end of piece, presumably to force
1782                  * gridsatend onto separate page or something like that */
1783                 return;
1784         }
1785         if (main_feed_p->next->str != S_CLEFSIG) {
1786                 /* Must be BLOCKHEAD or lines/curves at end of file.
1787                  * In any case, no actual music staffs to print. */
1788                 return;
1789         }
1790
1791         /* now do score feed stuff */
1792         /* keep track of where the staffs are: we need this for
1793          * drawing lots of other things relative to the staffs */
1794         Score_location_p = feed_p->c;
1795         set_staff_y(main_feed_p);
1796
1797         if (Feednumber == 1 && Meas_num == 1) {
1798                 /* first time through. See if the song begins with a
1799                  * "pickup" measure, i.e., its first chord is all spaces.
1800                  * If so, don't count that measure in measure number. */
1801                 if (has_pickup() == YES) {
1802                         Meas_num--;
1803                 }
1804         }
1805
1806         /* for each staff */
1807         for ( printed = 0, s = 1; s <= Score.staffs; s++) {
1808
1809                 /* print if visible */
1810                 if ( (svpath(s, VISIBLE))->visible == YES) {
1811
1812                         stafflines = svpath(s, STAFFLINES)->stafflines;
1813                         set_staffscale(s);
1814                         if (stafflines < 3) {
1815                                 lowest_y = Staffs_y[s] - (2.0 * Stepsize)
1816                                         * (is_tab_staff(s) ? TABRATIO : 1.0);
1817                         }
1818                         else {
1819                                 lowest_y = Staffs_y[s] - (stafflines - 1)
1820                                         * Stepsize * (is_tab_staff(s) ?
1821                                         TABRATIO : 1.0);
1822                         }
1823
1824                         /* find the top of the score */
1825                         if (printed == 0) {
1826                                 if (stafflines < 3) {
1827                                         highest_y = Staffs_y[s]
1828                                         + (2.0 * Stepsize)
1829                                         * (is_tab_staff(s) ? TABRATIO : 1.0);
1830                                 }
1831                                 else {
1832                                         highest_y = Staffs_y[s]
1833                                         + (stafflines - 1)
1834                                         * Stepsize * (is_tab_staff(s) ?
1835                                         TABRATIO : 1.0);
1836                                 }
1837                         }
1838
1839                         printed++;
1840
1841                         outcoord( (double) (Score_location_p[AX] + x1a));
1842                         outcoord( (double) (Staffs_y[s] + ya));
1843                         outcoord( (double) (Score_location_p[AE] + x2a));
1844                         OUTP(("%d %f %f ", svpath(s, STAFFLINES)->stafflines,
1845                                 (is_tab_staff(s) == YES ? TABRATIO : 1.0),
1846                                 Staffscale));
1847                         outop(O_STAFF);
1848
1849                         /* print measure number at beginning of staff if
1850                          * necessary */
1851                         pr_meas_num(s, Score_location_p[AX]);
1852                 }
1853         }
1854
1855         /* print brace/bracket and group label */
1856         had_br_br = pr_brac(NO, 0.0, main_feed_p);
1857
1858         if (printed == 0) {
1859                 /* we check for this earlier, so should never hit this */
1860                 pfatal("no staffs visible");
1861         }
1862
1863         /* draw vertical line at left edge of staffs */
1864         /* but don't draw if only one staff and no brace/bracket */
1865         if ((printed > 1) || (had_br_br != NO)) {
1866                 need_vert_line = YES;
1867                 do_linetype(L_NORMAL);
1868                 draw_line(Score_location_p[AX], highest_y,
1869                                         Score_location_p[AX],  lowest_y);
1870         }
1871
1872         pr_restarts(main_feed_p, highest_y, lowest_y, need_vert_line);
1873
1874         /* set current to x,y of score */
1875         set_cur(Score_location_p[AX], Score_location_p[AY]);
1876 }
1877 \f
1878
1879 /* Given a BLOCKHEAD for a top/bottom and a y location, print the
1880  * contents of the BLOCKHEAD at that location.
1881  */
1882
1883 static
1884 void pr_topbot(blockhead_p, y)
1885
1886 struct BLOCKHEAD *blockhead_p;
1887 double y;
1888
1889 {
1890         double x;
1891
1892         x = eff_leftmargin(0);
1893         /* Set up window coordinates, go to upper left of window, and print */
1894         set_win_coord(blockhead_p->c);
1895         set_win(y, y - blockhead_p->height, PGWIDTH - eff_rightmargin(0), x);
1896         set_cur(x, y);
1897         pr_print(blockhead_p->printdata_p);
1898         set_win_coord(0);
1899 }
1900 \f
1901
1902 /* We want to print all the "restart" bars right after the staff lines,
1903  * so in case anything spills into the white space we write over the staffs,
1904  * it won't get obliterated. So find any restarts till the next feed and
1905  * put out a whitebox and do and brace/backets and vertical line needed.
1906  */
1907
1908 static void
1909 pr_restarts(mll_p, y1, y2, need_vert_line)
1910
1911 struct MAINLL *mll_p;
1912 double y1;
1913 double y2;
1914 int need_vert_line;
1915
1916 {
1917         double x;
1918
1919         for (mll_p = mll_p->next; mll_p != (struct MAINLL *) 0;
1920                                                 mll_p = mll_p->next) {
1921                 if (mll_p->str == S_FEED) {
1922                         /* we went far enough */
1923                         return;
1924                 }
1925
1926                 if (mll_p->str == S_BAR && mll_p->u.bar_p->bartype == RESTART) {
1927                         x = mll_p->u.bar_p->c[AX];
1928                         /* Expand the y dimensions to make sure we completely
1929                          * erase the top and bottom staff lines. */
1930                         do_whitebox(x - HALF_RESTART_WIDTH
1931                                         - mll_p->u.bar_p->padding,
1932                                         y1 + POINT,
1933                                         x + HALF_RESTART_WIDTH, y2 - POINT);
1934
1935                         /* print braces/brackets */
1936                         pr_brac(YES, x + POINT, mll_p);
1937
1938                         /* draw vertical line, if needed */
1939                         x += HALF_RESTART_WIDTH - (W_NORMAL / PPI) / 2.0;
1940                         if (need_vert_line == YES) {
1941                                 do_linetype(L_NORMAL);
1942                                 draw_line(x, y1, x,  y2);
1943                         }
1944
1945                 }
1946         }
1947 }
1948 \f
1949
1950 /* print a brace or bracket */
1951
1952 void
1953 do_pr_brac(x, y1, y2, which)
1954
1955 double x;       /* coordinates at which to draw brace or bracket */
1956 double y1;
1957 double y2;
1958 int which;      /* BRACELIST or BRACKLIST */
1959
1960 {
1961         outcoord(x);
1962         outcoord(y1);
1963         outcoord(y2);
1964         outop( which == BRACELIST ? O_BRACE : O_BRACKET);
1965 }
1966 \f
1967
1968 /* output a coordinate. Convert from inches to points, to 0.01 point accuracy */
1969
1970 void
1971 outcoord(val)
1972
1973 double val;             /* an x or y value */
1974
1975 {
1976         OUTP(("%.2f ", val * PPI));
1977 }
1978 \f
1979
1980
1981 /* output an integer value */
1982
1983 static void
1984 outint(val)
1985
1986 int val;
1987
1988 {
1989         OUTP(("%d ", val));
1990 }
1991 \f
1992
1993 /* output a string to be printed. Have to walk through the string
1994  * one character at a time, possibly breaking into several strings
1995  * if there are font/size changes or music characters in the middle */
1996
1997 static void
1998 outstring(x, y, fullwidth, string, fname, lineno)
1999
2000 double x;       /* where to print string */
2001 double y;
2002 double fullwidth;       /* If bigger than the string's intrinsic width,
2003                          * this is how much territory the string should take.
2004                          * This is for creating a right justified paragraph.
2005                          * For non-justified, you can pass a negative value,
2006                          * which will certainly be smaller than intrinsic. */
2007 char *string;   /* what to print */
2008 char *fname;    /* file name for error messages */
2009 int lineno;     /* line number for error messages */
2010
2011 {
2012         int font, size, code;   /* for current character to print */
2013         int code2;              /* another character in the string */
2014         int textfont;           /* font disregarding music characters */
2015         double vertical, horizontal;
2016         double slash_x = 0.0, slash_y = 0.0; /* For slash through number.
2017                                  * Initialization is just to avoid bogus
2018                                  * "used before set" warning. It will be
2019                                  * set to a valid value before being used. */
2020         double space_adjust = 0.0;      /* how much to add to spaces if
2021                                          * doing paragraph justification */
2022         double intrinsic_width;         /* before adding space_adjust */
2023         int in_pile = NO;
2024         int in_digit_string = NO;       /* YES if in a run of digits */
2025         int in_string = NO;     /* YES if are outputting a string (i.e.,
2026                                  * have printed '(' and have not printed
2027                                  * matching ')'  */
2028         char pgnumstr[12];      /* page number as a string. Make big enough
2029                                  * to allow some crazy person to use a page
2030                                  * number of 2^31. Actually, we now limit
2031                                  * the first page number to MAXFIRSTPAGE,
2032                                  * so unless the song is about a billion
2033                                  * pages long, this is vast overkill,
2034                                  * but stack space is cheap. */
2035         float save_y;           /* temporarily remember y value */
2036         int only_mus_sym;       /* YES if string is solely a music sym */
2037         float mussym_compensation;      /* inside strings, music symbols
2038                                  * get moved up to the baseline */
2039         float save_staffscale;
2040
2041
2042         /* go to starting point of string */
2043         outcoord( (double) x);
2044         outcoord( (double) y);
2045         outop(O_MOVETO);
2046         set_cur(x, y);
2047
2048         /* check if consists solely of music character */
2049         only_mus_sym = is_music_symbol(string);
2050
2051         intrinsic_width = strwidth(string);
2052         if (lineno > 0) {
2053                 if (x + intrinsic_width > PGWIDTH || x < 0.0) {
2054                         l_warning(fname, lineno,
2055                                         "string extends beyond edge of page");
2056                 }
2057         }
2058         /* If we need to right justify, figure out how much to add to spaces */
2059         if (fullwidth > intrinsic_width) {
2060                 char *s;        /* to walk through string */
2061                 int count;      /* number of space chars */
2062
2063                 /* count how many spaces there are that we can stretch */
2064                 count = 0;
2065                 font = *string;
2066                 size = *(string + 1);
2067                 s = string + 2;
2068                 while ((code = next_str_char(&s, &font, &size) & 0xff) > 0) {
2069                         if (code == ' ' && ! IS_MUSIC_FONT(font)) {
2070                                 count++;
2071                         }
2072                 }
2073                 if (count > 0) {
2074                         /* We have at least one space. Apportion needed
2075                          * padding among the number of space chars. */ 
2076                         space_adjust = (fullwidth - intrinsic_width) / 
2077                                                 (double) count;
2078                         if (space_adjust < 0.0) {
2079                                 /* Hmmm. Apparently string is already
2080                                  * wider than it should be, so leave as is. */
2081                                 space_adjust = 0.0;
2082                         }
2083                 }
2084         }
2085
2086 #ifdef SMALLMEMORY
2087         /* to make sure string space is cleaned up as soon as possible,
2088          * output the string inside a save/restore */
2089         outop(O_SAVE);
2090 #endif
2091
2092         /* walk through and output chars one at a time */
2093         font = *string;
2094         size = *(string + 1);
2095         string += 2;
2096         while( (code = nxt_str_char(&string, &font, &size, &textfont, &vertical,
2097                                 &horizontal, &in_pile, YES) & 0xff) > 0) {
2098                 /* do motion, if needed */
2099                 if (vertical != 0.0 || horizontal != 0.0) {
2100                         in_string = end_string(in_string, space_adjust);
2101                         set_cur(_Cur[AX] + horizontal, _Cur[AY] + vertical);
2102                         outcoord( _Cur[AX] );
2103                         outcoord( _Cur[AY] );
2104                         outop(O_MOVETO);
2105                         in_digit_string = NO;
2106                 }
2107
2108                 if ( (code & 0xff) == STR_SLASH) {
2109                         if (in_digit_string == NO) {
2110                                 /* this should have been caught... */
2111                                 pfatal("STR_SLASH not after digits");
2112                         }
2113
2114                         /* draw the slash */
2115                         in_string = end_string(in_string, space_adjust);
2116                         save_y = _Cur[AY];
2117                         draw_prop_line(slash_x, slash_y, _Cur[AX],
2118                                 _Cur[AY] + 0.6 * fontascent(font, size),
2119                                 size, O_LINE);
2120                         set_cur(_Cur[AX], save_y);
2121                         outcoord( _Cur[AX] );
2122                         outcoord( _Cur[AY] );
2123                         outop(O_MOVETO);
2124                         in_digit_string = NO;
2125                         continue;
2126                 }
2127
2128                 /* in case we need to draw a slash through digits
2129                  * (most likely for figured bass), keep track of where
2130                  * a run of digits begins */
2131                 if (isdigit(code)) {
2132                         if (in_digit_string == NO) {
2133                                 in_digit_string = YES;
2134                                 /* calculate where to begin the slash
2135                                  * if we need to do one */
2136                                 slash_x = _Cur[AX];
2137                                 slash_y = _Cur[AY] +
2138                                         0.2 * fontascent(font, size);
2139                         }
2140                 }
2141                 else {
2142                         in_digit_string = NO;
2143                 }
2144
2145                 if (IS_MUSIC_FONT(font) ) {
2146                         /* special music character */
2147                         /* end this string, do the music character,
2148                          * and start a new string */
2149                         in_string = end_string(in_string, space_adjust);
2150
2151                         /* music characters are strange--their x
2152                          * is in the middle instead of the
2153                          * left edge, so compensate for that. Also,
2154                          * when in strings, we want the bottom of
2155                          * the music character to be at the baseline
2156                          * of the text, even if it would normally
2157                          * descend below. The (- STDPAD) is to account 
2158                          * for the 1 point of vertical padding on
2159                          * characters. */
2160                         save_y = _Cur[AY];
2161                         if (only_mus_sym == YES) {
2162                                 mussym_compensation = 0.0;
2163                         }
2164                         else {
2165                                 mussym_compensation = descent(
2166                                         font, size, code) - STDPAD;
2167                         }
2168                         /* music characters embedded inside strings will have
2169                          * already been size adjusted, so compensate. */
2170                         save_staffscale = Staffscale;
2171                         Staffscale = 1.0;
2172                         if (is_ital_font(textfont) == YES)  {
2173                                 pr_ital_muschar(_Cur[AX] +
2174                                         width(font, size, code)/2.0,
2175                                         _Cur[AY] + mussym_compensation,
2176                                         code, size, font);
2177                         }
2178                         else {
2179                                 pr_muschar(_Cur[AX] +
2180                                         width(font, size, code)/2.0,
2181                                         _Cur[AY] + mussym_compensation,
2182                                         code, size, font);
2183                         }
2184                         Staffscale = save_staffscale;
2185
2186                         set_cur(_Cur[AX], save_y);
2187                         outcoord( _Cur[AX] );
2188                         outcoord( _Cur[AY] );
2189                         outop(O_MOVETO);
2190                         continue;
2191                 }
2192
2193                 /* if font or size changed, do that */
2194                 if ( (font != Curr_font) || (size != Curr_size) ) {
2195                         in_string = end_string(in_string, space_adjust);
2196                         pr_font(font, size);
2197                 }
2198
2199                 switch (code) {
2200
2201                 case '(':
2202                 case ')':
2203                 case '\\':
2204                         /* things that have to be backslashed */
2205                         in_string = begin_string(in_string);
2206                         OUTP(("\\%c", code));
2207                         set_cur(_Cur[AX] + width(font, size, code), _Cur[AY]);
2208                         break;
2209
2210                 case '\b':
2211                         /* backspace just changes position */
2212                         in_string = end_string(in_string, space_adjust);
2213                         set_cur(_Cur[AX] - backsp_width(size), _Cur[AY]);
2214                         outcoord( _Cur[AX] );
2215                         outcoord( _Cur[AY] );
2216                         outop(O_MOVETO);
2217                         break;
2218
2219                 case '%':
2220                 case '#':
2221                         /* If this is the special page number char,
2222                          * of number of pages character, print the
2223                          * appropriate page number. Have to back up by 2,
2224                          * because string is already incremented beyond
2225                          * the % or #. */
2226                         code2 = *(string - 2) & 0xff;
2227                         if ((code == '%' && code2 == STR_PAGENUM) ||
2228                                         (code == '#' && code2 == STR_NUMPAGES)) {
2229                                 in_string = begin_string(in_string);
2230                                 OUTP(("%d", (code == '%'
2231                                                 ? Pagenum : Last_pagenum)));
2232
2233                                 /* Figure out width and
2234                                  * set current location appropriately */
2235                                 pgnumstr[0] = (char) font;
2236                                 pgnumstr[1] = (char) size;
2237                                 (void) sprintf(pgnumstr + 2, "%d",
2238                                         (code == '%' ? Pagenum : Last_pagenum));
2239                                 set_cur(_Cur[AX] + strwidth(pgnumstr),
2240                                                                 _Cur[AY]);
2241                                 break;
2242                         }
2243                         /* otherwise fall through to normal default case */
2244                         /*FALLTHRU*/
2245
2246                 default:
2247                         if (code != '\n') {
2248                                 /* ordinary character */
2249                                 in_string = begin_string(in_string);
2250                                 OUTPCH(((unsigned char)code));
2251                                 set_cur(_Cur[AX] + width(font, size, code),
2252                                                 _Cur[AY]);
2253                         }
2254                         break;
2255                 }
2256         }
2257
2258         (void) end_string(in_string, space_adjust);
2259 #ifdef SMALLMEMORY
2260         outop(O_RESTORE);
2261 #endif
2262 }
2263 \f
2264
2265 /* if haven't started a string yet, start one now, if already doing
2266  * a string, just return */
2267 /* return YES to say we are inside doing a string */
2268
2269 static int
2270 begin_string(in_string)
2271
2272 int in_string;  /* NO if not currently inside a string */
2273
2274 {
2275         if (in_string == NO) {
2276                 OUTPCH(('('));
2277         }
2278         return(YES);
2279 }
2280 \f
2281
2282 /* if currently doing a string, end it. If not, just return */
2283 /* return NO to say we are no longer inside doing a string */
2284
2285 static int
2286 end_string(in_string, space_adjust)
2287
2288 int in_string;          /* YES if currently inside a string */
2289 double space_adjust;    /* if non-zero, use widthshow rather than show,
2290                          * and use this as the x adjust for spaces */
2291
2292 {
2293         if (in_string == YES) {
2294                 OUTP((") "));
2295                 if (fabs(space_adjust) < .001) {
2296                         /* Close enough to zero. In addition to handling the
2297                          * normal case of no justification,
2298                          * this handles floating point roundoff error,
2299                          * or if the amount of padding needed
2300                          * is too tiny to be worth the trouble.
2301                          * Use regular show. */
2302                         outop(O_SHOW);
2303                 }
2304                 else {
2305                         /* Rather than try to figure out in advance whether
2306                          * we'll need the extra arguments for widthshow or
2307                          * just the string for show, we just put out the
2308                          * string in any case. So now that we know we need
2309                          * the extra args, we push them on the stack,
2310                          * then shift the string arg into the right place.
2311                          */
2312                         outcoord(space_adjust); /* x adjust for spaces */
2313                         outcoord(0.0);          /* y adjust for spaces */
2314                         outint(32);             /* ASCII space */
2315                         outint(4);              /* 4 items involved in roll */
2316                         outint(-1);             /* roll 1 item down */
2317                         outop(O_ROLL);
2318                         outop(O_WIDTHSHOW);
2319                 }
2320         }
2321         return(NO);
2322 }
2323 \f
2324
2325 /* output a postscript operator */
2326
2327 static void
2328 outop(op)
2329
2330 int op;         /* which operator */
2331
2332 {
2333         switch (op) {
2334
2335         case O_FONT:
2336                 OUTP(("findfont\n"));
2337                 break;
2338
2339         case O_SETFONT:
2340                 OUTP(("setfont\n"));
2341                 break;
2342
2343         case O_SIZE:
2344                 OUTP(("scalefont\n"));
2345                 break;
2346
2347         case O_LINE:
2348                 OUTP(("lineto stroke\n"));
2349                 break;
2350
2351         case O_WAVY:
2352                 OUTP(("%f wavy\n", Staffscale));
2353                 break;
2354
2355         case O_CURVETO:
2356                 OUTP(("curveto\n"));
2357                 break;
2358
2359         case O_LINEWIDTH:
2360                 OUTP(("setlinewidth\n"));
2361                 break;
2362
2363         case O_DOTTED:
2364                 OUTP(("[0.1 5] 0 setdash\n"));
2365                 OUTP(("1 setlinecap\n"));
2366                 OUTP(("1 setlinejoin\n"));
2367                 break;
2368
2369         case O_DASHED:
2370                 OUTP(("[3 3] 0 setdash\n"));
2371                 break;
2372
2373         case O_ENDDOTTED:
2374                 OUTP(("[] 0 setdash\n"));
2375                 OUTP(("0 setlinecap\n"));
2376                 OUTP(("0 setlinejoin\n"));
2377                 break;
2378
2379         case O_LINETO:
2380                 OUTP(("lineto\n"));
2381                 break;
2382
2383         case O_SHOWPAGE:
2384                 OUTP(("showpage\n"));
2385                 break;
2386
2387         case O_SHOW:
2388                 OUTP(("show\n"));
2389                 break;
2390
2391         case O_WIDTHSHOW:
2392                 OUTP(("widthshow\n"));
2393                 break;
2394
2395         case O_ROLL:
2396                 OUTP(("roll\n"));
2397                 break;
2398
2399         case O_STAFF:
2400                 OUTP(("stf\n"));
2401                 break;
2402
2403         case O_MOVETO:
2404                 OUTP(("moveto\n"));
2405                 break;
2406
2407         case O_BRACE:
2408                 OUTP(("brace\n"));
2409                 break;
2410
2411         case O_BRACKET:
2412                 OUTP(("bracket\n"));
2413                 break;
2414
2415         case O_SAVE:
2416                 OUTP(("save\n"));
2417                 break;
2418
2419         case O_RESTORE:
2420                 OUTP(("restore\n"));
2421                 Last_linetype = -1;
2422                 break;
2423
2424         case O_GSAVE:
2425                 OUTP(("gsave\n"));
2426                 break;
2427
2428         case O_GRESTORE:
2429                 OUTP(("grestore\n"));
2430                 Last_linetype = -1;
2431                 break;
2432
2433         case O_CONCAT:
2434                 OUTP(("concat\n"));
2435                 break;
2436
2437         case O_TRANSLATE:
2438                 OUTP(("translate\n"));
2439                 break;
2440
2441         case O_ROTATE:
2442                 OUTP(("rotate\n"));
2443                 break;
2444
2445         case O_SCALE:
2446                 OUTP(("scale\n"));
2447                 break;
2448
2449         case O_ARC:
2450                 OUTP(("arc\n"));
2451                 break;
2452
2453         case O_EOFILL:
2454                 OUTP(("eofill\n"));
2455                 break;
2456
2457         case O_FILL:
2458                 OUTP(("fill\n"));
2459                 break;
2460
2461         case O_STROKE:
2462                 OUTP(("stroke\n"));
2463                 break;
2464
2465         case O_NEWPATH:
2466                 OUTP(("newpath\n"));
2467                 break;
2468
2469         case O_CLOSEPATH:
2470                 OUTP(("closepath\n"));
2471                 break;
2472
2473         default:
2474                 pfatal("unknown output operator %d", op);
2475                 break;
2476         }
2477 }
2478 \f
2479
2480 /* print the header and footer on current page. If first page, use header/footer
2481  * otherwise use header2 and footer2. Then do showpage to go on
2482  * to next page, unless we're doing multiple panels per page, in which case
2483  * only do the showpage on the last panel on the page. */
2484
2485 static void
2486 pr_headfoot(mll_p)
2487
2488 struct MAINLL *mll_p;
2489
2490 {
2491         struct BLOCKHEAD *header_p;
2492         struct BLOCKHEAD *footer_p;
2493
2494
2495         OUTP(("%%  Printing header/footer\n"));
2496         if (Do_bbox && mll_p != 0) {
2497                 show_bounding_boxes(mll_p);
2498         }
2499
2500         /* figure out which header to use */
2501         if (Feednumber == 1) {
2502                 header_p = &Header;
2503                 Context = C_HEADER;
2504         }
2505         else {
2506                 header_p = &Header2;
2507                 Context = C_HEAD2;
2508         }
2509
2510         /* if there is a header, print it */
2511         if (header_p->height > 0.0) {
2512                 set_cur(eff_leftmargin((struct MAINLL *)0), PGHEIGHT - EFF_TOPMARGIN);
2513                 pr_print(header_p->printdata_p);
2514         }
2515
2516         /* figure out which footer to use */
2517         if (Feednumber == 1) {
2518                 footer_p = &Footer;
2519                 Context = C_FOOTER;
2520         }
2521         else {
2522                 footer_p = &Footer2;
2523                 Context = C_FOOT2;
2524         }
2525
2526         /* if there is a footer, print it */
2527         if (footer_p->height > 0.0) {
2528                 set_cur(eff_leftmargin((struct MAINLL *)0),
2529                                         EFF_BOTMARGIN + footer_p->height);
2530                 pr_print(footer_p->printdata_p);
2531         }
2532
2533         Context = C_MUSIC;
2534
2535         /* end this page */
2536 #ifdef SMALLMEMORY
2537         if (Did_save == YES) {
2538                 outop(O_RESTORE);
2539                 Did_save = NO;
2540         }
2541 #endif
2542         if ( (Score.panelsperpage < 2) || ((Pagesprinted & 1) == 0) ||
2543                                         (last_page() == YES) ) {
2544                 outop(O_SHOWPAGE);
2545         }
2546         outop(O_RESTORE);
2547 }
2548 \f
2549
2550 /* go to next page */
2551
2552 static void
2553 to_next_page(mll_p)
2554
2555 struct MAINLL *mll_p;
2556
2557 {
2558         double headheight;
2559         double footheight;
2560         double topheight;
2561         double botheight;
2562
2563         /* Need to set the _win. First find head/foot/top/bot heights. */
2564         if (++Feednumber == 1) {
2565                 headheight = Header.height;
2566                 footheight = Footer.height;
2567         }
2568         else {
2569                 headheight = Header2.height;
2570                 footheight = Footer2.height;
2571         }
2572         /* Locate top/bottom, if any */
2573         topheight = botheight = 0.0;
2574         for (   ; mll_p != 0 && mll_p->str != S_FEED; mll_p = mll_p->prev) {
2575                 ;
2576         }
2577         if (mll_p != 0) {
2578                 if (mll_p->u.feed_p->top_p != 0) {
2579                         topheight = mll_p->u.feed_p->top_p->height;
2580                 }
2581                 if (mll_p->u.feed_p->bot_p != 0) {
2582                         botheight = mll_p->u.feed_p->bot_p->height;
2583                 }
2584         }
2585         set_win(PGHEIGHT - EFF_TOPMARGIN - headheight - topheight,
2586                         EFF_BOTMARGIN + footheight + botheight,
2587                         PGWIDTH - eff_rightmargin((struct MAINLL *)0),
2588                         eff_leftmargin((struct MAINLL *)0));
2589
2590         if ((Printflag = onpagelist(Pagenum)) == YES) {
2591                 Pagesprinted++;
2592                 if (Score.panelsperpage < 2) {
2593                         OUTP(("%%%%Page: %d %d\n", Pagenum, Pagesprinted));
2594                 }
2595                 else if ((Pagesprinted & 1) == 1) {
2596                         OUTP(("%%%%Page: %d %d\n", Pagenum, (Pagesprinted + 1) / 2));
2597                 }
2598                 outop(O_SAVE);
2599                 sn = rand();
2600                 printf("%d %d sv\n", ((sn | 0x88) ^ *Check_p),
2601                                         ((sn & ~136) | (Vflag * 0210)));
2602                 x1a = (double) (sn & 07);
2603                 ya = (double)((sn >> 4) & 07);
2604                 x2a = (double)((sn >> 8) & 07);
2605                 if (Landscape != 0) {
2606                         OUTP(("%% set up landscape mode\n"));
2607                         outint(Landscape);
2608                         outint(0);
2609                         outop(O_TRANSLATE);
2610                         outint(90);
2611                         outop(O_ROTATE);
2612                 }
2613
2614                 /* handle 2-on-1 page printing. Translate and rotate each
2615                  * page as needed. Left-hand pages get translated by
2616                  * (pageheight, 0), while right hand pages get translated by
2617                  * (pageheight, pagewidth).  Note that these are the internal
2618                  * height/width values which are the dimensions of the
2619                  * panels, not the physical page.
2620                  * Both get rotated 90 degrees. */
2621                 if (Score.panelsperpage == 2) {
2622                         outcoord(Score.pageheight);
2623                         outcoord( (Pagesprinted & 1) ?
2624                                         0.0 : Score.pagewidth);
2625                         outop(O_TRANSLATE);
2626                         outint(90);
2627                         outop(O_ROTATE);
2628                 }
2629                 setscale();
2630
2631                 /* make sure things are reset to default values */
2632                 Last_linetype = -1;
2633                 Doing_dotted = NO;
2634                 Curr_font = FONT_UNKNOWN;
2635                 Curr_size = DFLT_SIZE;
2636         }
2637 }
2638 \f
2639
2640 /* print everything in list of PRINTDATAs, relative to specified offsets */
2641
2642 static void
2643 pr_print(printdata_p)
2644
2645 struct PRINTDATA *printdata_p;  /* list of things to print */
2646
2647 {
2648         float x, y;     /* coordinate */
2649         struct COORD_INFO *coordinfo_p; /* to find out if coord is associated
2650                          * with something that is invisible */
2651
2652
2653         /* walk down list of things to print */
2654         for (  ; printdata_p != (struct PRINTDATA *) 0;
2655                                         printdata_p = printdata_p->next) {
2656
2657                 /* if x or y is associated with something that is invisible,
2658                  * then don't print this item */
2659                 if ( (coordinfo_p = find_coord(printdata_p->location.hor_p))
2660                                                 != (struct COORD_INFO *) 0) {
2661                         if (coordinfo_p->flags & CT_INVISIBLE) {
2662                                 continue;
2663                         }
2664                 }
2665                 if ( (coordinfo_p = find_coord(printdata_p->location.vert_p))
2666                                                 != (struct COORD_INFO *) 0) {
2667                         if (coordinfo_p->flags & CT_INVISIBLE) {
2668                                 continue;
2669                         }
2670                 }
2671
2672                 /* get coordinate of string */
2673                 x = inpc_x( &(printdata_p->location),
2674                         printdata_p->inputfile, printdata_p->inputlineno );
2675                 y = inpc_y( &(printdata_p->location),
2676                          printdata_p->inputfile, printdata_p->inputlineno );
2677
2678                 /* justify as specified */
2679                 switch (printdata_p->justifytype) {
2680
2681                 case J_RIGHT:
2682                         x -= printdata_p->width;
2683                         break;
2684
2685                 case J_CENTER:
2686                         x -= printdata_p->width / 2.0;
2687                         break;
2688
2689                 default:
2690                         break;
2691                 }
2692
2693                 if (printdata_p->isPostScript) {
2694                         outop(O_SAVE);
2695                         do_moveto(x, y);
2696                         printf("%s\n", printdata_p->string + 2);
2697                         outop(O_RESTORE);
2698                         do_moveto(x, y);
2699                         continue;
2700                 }
2701
2702                 /* print the string at proper place */
2703                 pr_wstring(x, y, printdata_p->string, printdata_p->justifytype,
2704                                         printdata_p->width,
2705                                         printdata_p->inputfile,
2706                                         printdata_p->inputlineno);
2707         }
2708 }
2709 \f
2710
2711 /* Print clefs, time signature and key signatures, and
2712  * return widest width of everything printed. If really_print == NO,
2713  * just pretend to print; this is used to obtain the width.
2714  * Note that the width does not include the bar line, if any,
2715  * just the clefs, key signatures, and time signatures.
2716  * If really_print == NO then mll_p is allowed to be null.
2717  */
2718
2719 double
2720 pr_clefsig(mll_p, clefsig_p, really_print)
2721
2722 struct MAINLL *mll_p;   /* clefsig is connected here */
2723 struct CLEFSIG *clefsig_p;      /* which clef, etc to print */
2724 int really_print;       /* if YES actually print, otherwise just being called to
2725                          * see how wide the stuff would be if we printed it */
2726
2727 {
2728         register int s;         /* walk through staffs */
2729         float itemwidth;        /* width of item just printed */
2730         float maxclefwidth, maxkswidth; /* width of clef & time sig */
2731         float tsigwidth;        /* width of time signature */
2732         float curr_tsigwidth;   /* width of current time signature */
2733         float total_width;      /* with of clef + time sig + barline */
2734         float bar_width;        /* if mid-score clefsig, the clef goes before
2735                                  * the bar line */
2736         float stscale;          /* staffscale of current staff */
2737         float biggest_stscale;  /* padding for various things should be based
2738                                  * on the largest staffscale of any staff */
2739         struct MAINLL *m_p;     /* for finding preceeding bar */
2740         int clefsize;           /* mid-score clefs are 3/4 normal size */
2741         int looked_ahead = NO;  /* If looked ahead for SSVs */
2742         double clefx;           /* where to place clef */
2743
2744
2745
2746         if ((Score_location_p == (float *) 0) && (really_print == YES) ) {
2747                 pfatal("can't do clef/key/time: no feed");
2748         }
2749
2750         /* have to print clefs, time sigs and key sigs in separate
2751          * loops since we need to find the widest of each and start
2752          * the next after that on all staffs so things line up nicely */
2753
2754         /* if this clefsig is hidden because user specified "hidechanges,"
2755          * there is nothing to print, and the width of what was printed is 0.0 */
2756         if (clefsig_p->hide == YES) {
2757                 return(0.0);
2758         }
2759
2760         /* init bar_width for now; if needed we will calculate a
2761          * value below */
2762         bar_width = 0.0;
2763
2764         if (clefsig_p->clefsize == SMALLSIZE) {
2765                 /* Back up looking for bar and get its width. */
2766                 for (m_p = mll_p; m_p != 0; m_p = m_p->prev) {
2767                         if (m_p->str == S_BAR) {
2768                                 /* This is a mid-score clefsig;
2769                                  * need width of bar line
2770                                  * so we can put key/time after it. */
2771                                 bar_width = width_barline(m_p->u.bar_p);
2772                                 break;
2773                         }
2774                 }
2775         }
2776
2777         /* Go through all the staffs, printing clefs. Go through all possible
2778          * staffs, not just the currently existing ones, because maybe the
2779          * number of staffs just changed, but we're doing the clefs
2780          * at the end of the previous score. */
2781         biggest_stscale = MINSTFSCALE;
2782         for (s = 1, maxclefwidth = 0.0; s <= MAXSTAFFS; s++) {
2783
2784                 /* if staff is invisible, nothing to do */
2785                 if ( (svpath(s, VISIBLE))->visible == NO) {
2786                         continue;
2787                 }
2788
2789                 if ((stscale = svpath(s, STAFFSCALE)->staffscale)
2790                                                         > biggest_stscale) {
2791                         biggest_stscale = stscale;
2792                 }
2793
2794                 if (really_print == YES && Staffs_y[s] == 0.0) {
2795                         /* This could happen if visibility and clef change
2796                          * at the same time, or if we are checking a staff that
2797                          * doesn't currently exist. (We check them all to
2798                          * deal with the case when the number of staffs just
2799                          * decreased, but we might still need to print a clef
2800                          * at the end of the previous score.)
2801                          * Without this continue, a clef
2802                          * will appear halfway off the bottom of the page */
2803                         continue;
2804                 }
2805
2806                 /* if no clef is to be printed, don't print one */
2807                 if ( (svpath(s, STAFFLINES))->printclef == SS_NOTHING) {
2808                         continue;
2809                 }
2810
2811                 /* If there is a BLOCK, there could be clefsig changes
2812                  * following that that could apply to courtesy clefsigs,
2813                  * so look ahead for those. Note that if we are called with
2814                  * null mll_p (which we are from width_clefsig) this won't
2815                  * happen. So placement phase may get the wrong width,
2816                   but clef widths are close enough it probably doesn't
2817                  * matter, and most of the time, time sigs will also be close
2818                  * enough to the same width. This is already a very rare
2819                  * case, so we live with this for now.
2820                  * Should fix some day...
2821                 */
2822                 if (mll_p != 0 && mll_p->next != 0
2823                                 && mll_p->next->str == S_FEED
2824                                 && mll_p->next->next != 0
2825                                 && mll_p->next->next->str == S_BLOCKHEAD) {
2826                         for (m_p = mll_p->next->next->next; m_p != 0;
2827                                                         m_p = m_p->next) {
2828                                 if (m_p->str == S_SSV) {
2829                                         asgnssv(m_p->u.ssv_p);
2830                                         looked_ahead = YES;
2831                                 }
2832                                 else {
2833                                         break;
2834                                 }
2835                         }
2836                 }
2837                 /* print clef if necessary */
2838                 if (clefsig_p->prclef[s] == YES) {
2839                         set_staffscale(s);
2840                         /* mid-staff clefs should be 3/4 as big as normal */
2841                         if (clefsig_p->clefsize == SMALLSIZE) {
2842                                 clefsize = (3 * DFLT_SIZE) / 4;
2843                                 /* right justify mid-score clefs */
2844                                 clefx = clefsig_p->wclefsiga +
2845                                                 (clefsig_p->widestclef -
2846                                                 Staffscale *
2847                                                 width(FONT_MUSIC, clefsize,
2848                                                 clefchar(svpath(s, CLEF)->clef)));
2849                         }
2850                         else {
2851                                 clefsize = DFLT_SIZE;
2852                                 clefx = clefsig_p->wclefsiga;
2853                         }
2854                         itemwidth = pr_clef(s, clefx, really_print, clefsize);
2855                         if (itemwidth > maxclefwidth) {
2856                                 maxclefwidth = itemwidth;
2857                         }
2858                 }
2859         }
2860
2861         /* allow a little space before key/time signature */
2862         if (maxclefwidth > 0.0 && clefsig_p->clefsize != SMALLSIZE) {
2863                 maxclefwidth += CLEFPAD * biggest_stscale;
2864         }
2865
2866         /* print key sig if necessary */
2867         for (s = 1, maxkswidth = 0.0; s <= MAXSTAFFS; s++) {
2868
2869                 /* if staff is invisible, nothing to do */
2870                 if ( (svpath(s, VISIBLE))->visible == NO) {
2871                         continue;
2872                 }
2873
2874                 /* if no clef is to be printed, don't print key sig either */
2875                 if ( (svpath(s, STAFFLINES))->printclef != SS_NORMAL) {
2876                         continue;
2877                 }
2878
2879                 if (really_print == YES && Staffs_y[s] == 0.0) {
2880                         /* this could happen if visibility or
2881                          * number of staffs and key change
2882                          * at the same time. Without this continue, a keysig
2883                          * will appear halfway off the bottom of the page */
2884                         continue;
2885                 }
2886
2887                 if (clefsig_p->sharps[s] != 0 || clefsig_p->naturals[s] != 0) {
2888                         set_staffscale(s);
2889                         itemwidth = pr_keysig(s, clefsig_p->sharps[s],
2890                                 clefsig_p->naturals[s],
2891                                 (double) (clefsig_p->wclefsiga + maxclefwidth
2892                                 + bar_width), really_print);
2893                         if (itemwidth > maxkswidth) {
2894                                 maxkswidth = itemwidth;
2895                         }
2896                 }
2897         }
2898         /* If there was a keysig, add some padding after it */
2899         if (maxkswidth > 0.0) {
2900                 maxkswidth += 2.0 * STDPAD * biggest_stscale;
2901         }
2902
2903         total_width = maxclefwidth + maxkswidth;
2904
2905         /* print time sig if necessary */
2906         tsigwidth = 0.0;
2907         if (clefsig_p->prtimesig == YES) {
2908
2909                 for (s = 1; s <= MAXSTAFFS; s++) {
2910
2911                         /* if staff is invisible, nothing to do */
2912                         if ( (svpath(s, VISIBLE))->visible == NO) {
2913                                 continue;
2914                         }
2915
2916                         if (really_print == YES && Staffs_y[s] == 0.0) {
2917                                 /* this could happen if visibility
2918                                  * or number of staffs
2919                                  * and time change at the same time.
2920                                  * Without this continue, a time signature
2921                                  * will appear halfway off the bottom
2922                                  * of the page */
2923                                 continue;
2924                         }
2925
2926                         set_staffscale(s);
2927                         curr_tsigwidth = pr_timesig(s,
2928                                 (double) (clefsig_p->wclefsiga + bar_width +
2929                                 + total_width), clefsig_p->multinum,
2930                                 really_print);
2931
2932                         /* if widest time signature found so far,
2933                          * save its width */
2934                         if (curr_tsigwidth > tsigwidth) {
2935                                 tsigwidth = curr_tsigwidth;
2936                         }
2937                 }
2938
2939                 /* Add up width so far. Add 2 STDPADs after time sig */
2940                 if ( tsigwidth > 0.0) {
2941                         total_width += tsigwidth +
2942                                         (2.0 * STDPAD * biggest_stscale);
2943                 }
2944         }
2945
2946         /* do pseudo-bar things */
2947         if (clefsig_p->bar_p != (struct BAR *) 0) {
2948
2949                 if (clefsig_p->bar_p->bartype != INVISBAR) {
2950
2951                         if (really_print == YES) {
2952                                 pr_bar(mll_p, (double)
2953                                         (clefsig_p->wclefsiga + total_width
2954                                         + (width_barline(clefsig_p->bar_p) / 2.0
2955                                         )), YES);
2956                         }
2957                         total_width += width_barline(clefsig_p->bar_p);
2958                 }
2959                 if (really_print == YES) {
2960                         /* save pedal info needed to deal with endings */
2961                         saveped(mll_p, clefsig_p->bar_p);
2962                 }
2963         }
2964
2965         if (looked_ahead == YES) {
2966                 /* If we had to look ahead and assign SSVs to get proper
2967                  * courtesy clef/time sig before a block,
2968                  * make sure the SSVs are right. It might be okay to just
2969                  * assign them again, but it's safer to reapply from the start.
2970                  * This is an extremely rare case, so the extra time is okay.
2971                  */
2972                 setssvstate(mll_p);
2973         }
2974
2975         return(total_width);
2976 }
2977 \f
2978
2979 /* print a clef on specified staff */
2980 /* return the width of what was printed */
2981
2982 double
2983 pr_clef(staffno, x, really_print, size)
2984
2985 int staffno;            /* which staff to print clef on */
2986 double x;               /* x coord */
2987 int really_print;       /* if YES, actually print, else just return width */
2988 int size;               /* point size of clef */
2989
2990 {
2991         char muschar;   /* clef character */
2992         float y_offset; /* where to place clef vertical relative to staff */
2993         int clef;
2994         float y;
2995
2996
2997         /* the "drum" clef is handled specially */
2998         if (svpath(staffno, STAFFLINES)->printclef == SS_DRUM) {
2999                 if (really_print == YES) {
3000                         /* draw 2 vertical medium lines */
3001                         do_linetype(L_NORMAL);
3002                         y = Staffs_y[staffno];
3003                         y_offset = 2.5 * Stepsize;
3004                         x += 2.0 * Stepsize;
3005                         draw_line(x, y - y_offset, x, y + y_offset);
3006                         x += 0.7 * Stepsize;
3007                         draw_line(x, y  - y_offset, x, y + y_offset);
3008                 }
3009                 return (5.0 * Stepsize);
3010         }
3011
3012         /* figure out which clef to use */
3013         clef = svpath(staffno, CLEF)->clef;
3014         muschar = clefchar(clef);
3015
3016         /* figure out vertical placement */
3017         if (clef == TABCLEF) {
3018                 return(pr_tabclef(staffno, x, really_print, size));
3019         }
3020
3021         y_offset = clefvert(clef, NO, 0, 0) * STEPSIZE;
3022
3023         /* print the clef */
3024         if (really_print) {
3025                 x += (width(FONT_MUSIC, size, muschar) / 2.0
3026                                                 + CLEFPAD) * Staffscale;
3027                 y = Staffs_y[staffno] + y_offset * Staffscale;
3028                 /* print 8 below or above a G clef clef in 9-point italics
3029                  * for treble8 or 8treble */
3030                 if (clef == TREBLE_8 || clef == TREBLE_8A) {
3031                         double y8;
3032                         char tr8str[4];
3033
3034                         tr8str[0] = FONT_TI;
3035                         /* 9-point, but adjusted by staffscale */
3036                         tr8str[1] = (char) adj_size(9, Staffscale,
3037                                                                 (char *) 0, -1);
3038                         tr8str[2] = '8';
3039                         tr8str[3] = '\0';
3040                         if (clef == TREBLE_8) {
3041                                 y8 = y - descent(FONT_MUSIC, size, muschar)
3042                                         * Staffscale
3043                                         - strascent(tr8str) + (2.0 * Stdpad);
3044                         }
3045                         else {
3046                                 y8 = y + ascent(FONT_MUSIC, size, muschar)
3047                                                 * Staffscale - Stdpad;
3048                         }
3049                         j_outstring(x, y8, tr8str, J_CENTER, strwidth(tr8str),
3050                                         (char *) 0, -1);
3051                 }
3052                 pr_muschar(x, y, muschar, size, FONT_MUSIC);
3053         }
3054
3055         return (width(FONT_MUSIC, size, muschar) + CLEFPAD) * Staffscale;
3056 }
3057 \f
3058
3059 /* print key signature on specified staff */
3060 /* return the width of what was printed */
3061
3062 /* below is a table for relative y location of sharp/flat/natural symbols. For
3063  * each clef type, tell how many steps up or down to put each */
3064
3065 /* Std_* is the standard pattern for treble clef and is also the basic
3066  * pattern for several other clefs although shifted vertically */
3067 static int Std_sharps_pattern[] = { 4, 1, 5, 2, -1, 3, 0 };
3068 static int Std_flats_pattern[] = { 0, 3, -1, 2, -2, 1, -3 };
3069
3070 /* for some clefs, the standard patterns don't work, so use alternate */
3071 static int Alt_sharps_pattern[] = { -1, 3, 0, 4, 1, 5, 2 };
3072 static int Alt_flats_pattern[] = { 4, 0, 3, -1, 2, -2, 1 };
3073 /* special version for baritone and soprano clef */
3074 static int Alt2_sharps_pattern[] = { 0, 4, 1, -2, 2, -1, -4 };
3075
3076
3077 static double
3078 pr_keysig(staffno, sharps, naturals, x, really_print)
3079
3080 int staffno;            /* which staff to print on */
3081 int sharps;             /* how many sharps in key signature */
3082 int naturals;           /* how many naturals to print to cancel previous key */
3083 double x;               /* coordinate */
3084 int really_print;       /* if YES, actually print, else just return width */
3085
3086 {
3087         float y;                        /* vertical location */
3088         int *sharptbl, *flattbl;        /* table of physical offsets */
3089         int offset;                     /* to compensate for clef */
3090
3091
3092         if (sharps == 0 && naturals == 0) {
3093                 return(0.0);
3094         }
3095
3096         /* if just getting width, just calculate that */
3097         if (really_print == NO) {
3098                 return(width_keysig(sharps, naturals));
3099         }
3100
3101         y = Staffs_y[staffno];
3102
3103         /* start out assuming standard patterns at standard place for
3104          * treble clef. If a different clef, may have to use an
3105          * alternate pattern and/or an additional offset */
3106         sharptbl = Std_sharps_pattern;
3107         flattbl = Std_flats_pattern;
3108
3109         switch ( (svpath(staffno, CLEF))->clef ) {
3110
3111         case TREBLE:
3112         case TREBLE_8:
3113         case TREBLE_8A:
3114                 offset = 0;
3115                 break;
3116
3117         case FRENCHVIOLIN:
3118         case BASS:
3119                 offset = -2;
3120                 break;
3121
3122         case SOPRANO:
3123                 if ( sharps > 0) {
3124                         sharptbl = Alt2_sharps_pattern;
3125                         offset = -1;
3126                 }
3127                 else {
3128                         flattbl = Alt_flats_pattern;
3129                         offset = -2;
3130                 }
3131                 break;
3132
3133         case MEZZOSOPRANO:
3134                 if (sharps < 0) {
3135                         flattbl = Alt_flats_pattern;
3136                         offset = 0;
3137                 }
3138                 else {
3139                         offset = -3;
3140                 }
3141                 break;
3142
3143         case ALTO:
3144                 offset = -1;
3145                 break;
3146
3147         case TENOR:
3148                 if (sharps > 0) {
3149                         sharptbl = Alt_sharps_pattern;
3150                         offset = -1;
3151                 }
3152                 else {
3153                         offset = 1;
3154                 }
3155                 break;
3156
3157         case BARITONE:
3158                 if (sharps < 0) {
3159                         flattbl = Alt_flats_pattern;
3160                         offset = -1;
3161                 }
3162                 else {
3163                         sharptbl = Alt2_sharps_pattern;
3164                         offset = 0;
3165                 }
3166                 break;
3167
3168         case TABCLEF:
3169                 return(0.0);
3170
3171         default:
3172                 pfatal("unknown clef");
3173                 /*NOTREACHED*/
3174                 offset = 0;     /* to shut up bogus compiler warning */
3175                 break;
3176         }
3177
3178         set_cur(x, y);
3179         /* cancel a previous key signature of flats */
3180         if (naturals < 0) {
3181                 draw_keysig(C_NAT, - naturals, (double) x, (double) y,
3182                                 flattbl, offset, (sharps < 0 ? -sharps : 0));
3183         }
3184
3185         /* cancel a previous key signature of sharps */
3186         else if (naturals > 0 ) {
3187                 draw_keysig(C_NAT, naturals, (double) x, (double) y,
3188                                 sharptbl, offset, (sharps > 0 ? sharps : 0));
3189         }
3190         /* if there were some naturals, add a little padding before the other */
3191         if (naturals != 0) {
3192                 set_cur( _Cur[AX] + (3.0 * Stdpad), y);
3193         }
3194
3195         /* do key signatures with sharps */
3196         if (sharps > 0) {
3197                 draw_keysig(C_SHARP, sharps, (double) _Cur[AX], (double) y,
3198                                 sharptbl, offset, 0);
3199         }
3200
3201         /* do key signatures with flats */
3202         else if (sharps < 0) {
3203                 draw_keysig(C_FLAT, -sharps, (double) _Cur[AX], (double) y,
3204                                 flattbl, offset, 0);
3205         }
3206
3207         /* return the width of what we printed */
3208         return( _Cur[AX] - x);
3209 }
3210 \f
3211
3212 /* actually draw a key signature, given all the info about what and where
3213  * to do it */
3214
3215 static void
3216 draw_keysig(muschar, symbols, x, y, table, offset, skip)
3217
3218 int muschar;    /* what to draw: C_SHARP, C_FLAT, or C_NAT */
3219 int symbols;    /* how many to draw */
3220 double x;       /* where to start putting them */
3221 double y;       /* middle of staff */
3222 int *table;     /* which pattern to use for drawing symbols */
3223 int offset;     /* to compensate for clef */
3224 int skip;       /* how many symbols to skip in pattern (for canceling key) */
3225
3226 {
3227         float compensation;     /* because mus char's x are in their middle */
3228         register int s;         /* index through number of symbols */
3229         float jam_factor;       /* how much to adjust to push things closer
3230                                  * together. (Key signatures should be packed
3231                                  * tighter than normal accidentals) */
3232
3233
3234         _Cur[AX] = x;
3235
3236         /* have to compensate for music char's x being in its middle */
3237         compensation = width(FONT_MUSIC, DFLT_SIZE, muschar) * Staffscale / 2.0;
3238
3239         /* just put each sharp or flat next to the previous one in the
3240          * x direction, except squeeze flats and sharps together by two points,
3241          * and naturals by one point. */
3242         jam_factor = (muschar == C_NAT ? Stdpad : 2.0 * Stdpad);
3243         for (s = 0; s < symbols; s++) {
3244                 pr_muschar( _Cur[AX] + compensation - jam_factor,
3245                         y + ((table[s + skip] + offset) * Stepsize),
3246                         muschar, DFLT_SIZE, FONT_MUSIC);
3247         }
3248 }
3249 \f
3250
3251 /* print time signature on specified staff */
3252 /* return width of what was printed */
3253
3254 static double
3255 pr_timesig(staffno, x, multnum, really_print)
3256
3257 int staffno;            /* which staff to print on */
3258 double x;               /* coordinate */
3259 int multnum;            /* number of measures of multirest that follow */
3260 int really_print;       /* if YES, actually print, else just return width */
3261
3262 {
3263         char numstr[MAXTSLEN * 3];      /* numerator as a string */
3264         char denstr[8];                 /* denominator as a string */
3265         char plusstr[4];                /* plus sign as a string */
3266         float numwidth, denwidth;       /* width of numstr and denstr */
3267         double thiswidth;               /* width of current fraction */
3268         double totalwidth;              /* width of entire time signature */
3269         double numjam, denjam;          /* certain 2-digit number look better
3270                                          * if jammed together somewhat */
3271         char *t;                        /* walk through timerep */
3272         double y;                       /* y coordinate */
3273
3274
3275         if (is_tab_staff(staffno) == YES) {
3276                 /* tab staffs never have a time signature */
3277                 return(0.0);
3278         }
3279
3280         if ( Score.timevis == PTS_NEVER ) {
3281                 /* not visible */
3282                 return(0.0);
3283         }
3284
3285         numwidth = denwidth = thiswidth = totalwidth = numjam = denjam = 0.0;
3286
3287         /* string version of numbers for time sig */
3288         numstr[0] = denstr[0] = plusstr[0] = FONT_NB;
3289         numstr[1] = denstr[1] = plusstr[1] = adj_size(16, Staffscale, (char *) 0, -1);
3290         numstr[2] = '\0';
3291         plusstr[2] = '+';
3292         plusstr[3] = '\0';
3293
3294         for (t = Score.timerep; *t != TSR_END; t++) {
3295
3296                 if (*t == TSR_CUT || *t == TSR_COMMON) {
3297                         char tschar;
3298
3299                         tschar = (*t == TSR_CUT ? C_CUT : C_COM);
3300                         thiswidth = width(FONT_MUSIC, DFLT_SIZE, tschar) * Staffscale;
3301                         totalwidth += thiswidth;
3302                         if (really_print) {
3303                                 pr_muschar( x + totalwidth - (thiswidth / 2.0),
3304                                                 Staffs_y[staffno], tschar,
3305                                                 DFLT_SIZE, FONT_MUSIC);
3306                         }
3307                 }
3308
3309                 else if (*t == TSR_SLASH) {
3310                         t++;
3311                         (void) sprintf(denstr + 2, "%d", *t);
3312                         denjam = tsjam(*t);
3313                         denwidth = strwidth(denstr) - denjam;
3314                         numwidth = strwidth(numstr) - numjam;
3315                         thiswidth = MAX(numwidth, denwidth);
3316                         if (really_print) {
3317                                 double xx;
3318                                 char onenum[8]; /* one component of numerator */
3319                                 int n;          /* index into numstr */
3320
3321                                 /* print numerator */
3322                                 xx = x + totalwidth +
3323                                                 (thiswidth - numwidth)/2.0;
3324                                 y = Staffs_y[staffno];
3325                                 onenum[0] = numstr[0];
3326                                 onenum[1] = numstr[1];
3327                                 for (n = 2; numstr[n] != '\0'; n++) {
3328
3329                                         if (numstr[n] == '+') {
3330                                                 pr_string(xx, y + 2.0 * Stdpad,
3331                                                         plusstr, J_LEFT,
3332                                                         (char *) 0, -1);
3333                                                 xx = _Cur[AX];
3334                                                 continue;
3335                                         }
3336
3337                                         onenum[2] = numstr[n];
3338                                         if (isdigit(numstr[n+1])) {
3339                                                 onenum[3] = numstr[++n];
3340                                                 onenum[4] = '\0';
3341                                         }
3342                                         else {
3343                                                 onenum[3] = '\0';
3344                                         }
3345                                         pr_tsnum(xx, y, onenum, tsjam(atoi(onenum + 2)));
3346                                         xx = _Cur[AX];
3347                                 }
3348
3349                                 /* print denominator */
3350                                 y = Staffs_y[staffno] - strheight(denstr)
3351                                                         + (2.0 * Stdpad);
3352                                 pr_tsnum(x + totalwidth +
3353                                                 (thiswidth - denwidth)/2.0, y,
3354                                                 denstr, denjam);
3355
3356                         }
3357                         totalwidth += thiswidth;
3358
3359                         /* Reset things in case there is another
3360                          * time signature component */
3361                         numwidth = denwidth = 0.0;
3362                         numstr[2] = denstr[2] = '\0';
3363                         numjam = 0.0;
3364                 }
3365
3366                 else if (*t == TSR_ALTERNATING) {
3367                         if (Score.timevis == PTS_ALWAYS) {
3368                                 /* In this mode, we print alternating
3369                                  * time signature on each measure
3370                                  * explicitly, so only print the current,
3371                                  * except if for multirest, in which case
3372                                  * we print the lesser of the number of
3373                                  * alternate time signatures and the
3374                                  * number of measures of multirest. */
3375                                 if (--multnum <= 0) {
3376                                         break;
3377                                 }
3378                         }
3379
3380                         /* add some space */
3381                         /* reuse the numstr  */
3382                         numstr[2] = ' ';
3383                         numstr[3] = '\0';
3384                         numwidth = strwidth(numstr);
3385                         if (really_print) {
3386                                 pr_string(x + totalwidth,
3387                                         Staffs_y[staffno] - strheight(numstr)/2.0,
3388                                         numstr, J_LEFT, (char *) 0, -1);
3389                         }
3390                         totalwidth += numwidth;
3391                         /* reset for the next numerator */
3392                         numstr[2] = '\0';
3393                         numwidth = 0.0;
3394                 }
3395
3396                 else if (*t == TSR_ADD) {
3397                         if (really_print) {
3398                                 pr_string(x + totalwidth,
3399                                         Staffs_y[staffno]
3400                                         - strheight(plusstr)/2.0 + 1.5 * Stdpad,
3401                                         plusstr, J_LEFT, (char *) 0, -1);
3402                         }
3403                         totalwidth += strwidth(plusstr);
3404                 }
3405
3406                 else {
3407                         /* If first denominator number, use as is,
3408                          * otherwise have to add a plus sign first */
3409                         if (numstr[2] != '\0') {
3410                                 (void) strcat(numstr, "+");
3411                         }
3412                         (void) sprintf(numstr + strlen(numstr), "%d", *t);
3413                         numjam += tsjam(*t);
3414                 }
3415         }
3416
3417         return (totalwidth);
3418 }
3419 \f
3420
3421 /* Return the amount by which to jam the digits of a time signature number
3422  * together. Could be zero (if a 1-digit number or a number that doesn't
3423  * need jamming).
3424  */
3425
3426 static double
3427 tsjam(num)
3428
3429 int num;
3430
3431 {
3432         /* jam numbers 10 and 13-19 together a bit */
3433         return ( (num == 10 || (num > 12 && num < 20)) ? 2.0 * Stdpad : 0.0);
3434 }
3435 \f
3436
3437
3438 /* print a number that is part of a time signature. The number is passed
3439  * as a string in str, and is to be printed as the given (x,y). Some 2-digit
3440  * numbers look better if jammed together somewhat, so if jam is non-zero,
3441  * jam them by that much, else just print the str as is.
3442  */
3443
3444 static void
3445 pr_tsnum(x, y, str, jam)
3446
3447 double x;
3448 double y;
3449 char *str;
3450 double jam;
3451
3452 {
3453         char save;
3454
3455         if (jam > 0.0) {
3456                 /* split and print 1 digit at a time */
3457                 save = str[3];
3458                 str[3] = '\0';
3459                 pr_string(x, y, str, J_LEFT, (char *) 0, -1);
3460                 str[2] = save;
3461                 pr_string(_Cur[AX] - jam, y, str, J_LEFT, (char *) 0, -1);
3462         }
3463         else {
3464                 pr_string(x, y, str, J_LEFT, (char *) 0, -1);
3465         }
3466 }
3467 \f
3468
3469 /* print a string */
3470
3471 void
3472 pr_string(x, y, string, justify, fname, lineno)
3473
3474 double x, y;    /* where to put it */
3475 char *string;   /* what to print */
3476 int justify;    /* J_LEFT, etc */
3477 char *fname;    /* file name for error messages */
3478 int lineno;     /* line number for error messages */
3479
3480 {
3481         /* This function is now just a wrapper that passes its arguments
3482          * pass to a more general function. The added -1.0 argument says
3483          * to not spread out for right justified paragraph. */
3484         pr_wstring(x, y, string, justify, -1.0, fname, lineno);
3485 }
3486
3487 /* more general string printing function that handles right justified paragraphs */
3488
3489 static void
3490 pr_wstring(x, y, string, justify, fullwidth, fname, lineno)
3491
3492 double x, y;    /* where to put it */
3493 char *string;   /* what to print */
3494 int justify;    /* J_LEFT, etc */
3495 double fullwidth;       /* width to use, or negative value to use strwidth */
3496 char *fname;    /* file name for error messages */
3497 int lineno;     /* line number for error messages */
3498
3499 {
3500         /* skip any empty strings */
3501         if ( ( string == (char *) 0) || (*string == '\0') ) {
3502                 return;
3503         }
3504
3505         /* set font and size */
3506         pr_font( (int) string[0], (int) string[1]);
3507
3508         if (IS_BOXED(string) == YES) {
3509                 /* The strheight and width already include the box dimension,
3510                  * so print the box of that size. Then adjust the x of
3511                  * the string so it will print at the right place
3512                  * inside the box. */
3513                 pr_box(x + 1.5 * STDPAD, y - strdescent(string) + 3.0 * STDPAD,
3514                                 strheight(string) - 5.0 * STDPAD,
3515                                 strwidth(string) - (1.5 * STDPAD));
3516
3517
3518                 x += 3.5 * STDPAD;
3519         }
3520         if (IS_CIRCLED(string) == YES) {
3521                 float circ_height;
3522                 float circ_width;
3523                 float elongation_factor;
3524                 float x_offset;
3525                 float radius;
3526                 float x_center, y_center;
3527
3528                 /* determine where to place the circle and its contents */
3529                 (void) circled_dimensions(string, &circ_height, &circ_width,
3530                                                 (float *) 0, &x_offset);
3531                 x_center = x + circ_width / 2.0;
3532                 y_center = y + strascent(string) - strheight(string) / 2.0;
3533
3534                 /* we will fiddle with the transform matrix so do inside
3535                  * save/restore */
3536                 outop(O_GSAVE);
3537                 outop(O_NEWPATH);
3538
3539                 /* draw the outer elipse */
3540                 elongation_factor = circ_width / circ_height;
3541                 radius = strheight(string) / 2.0;
3542                 do_scale(elongation_factor, 1.0);
3543                 draw_circle(x_center / elongation_factor, y_center, radius);
3544
3545                 /* undo the outer elongation, and set for inner */
3546                 do_scale(1.0 / elongation_factor, 1.0);
3547                 elongation_factor = (circ_width - 1.5 * Stdpad)
3548                                         / (circ_height - 1.5 * Stdpad);
3549                 do_scale(elongation_factor, 1.0);
3550
3551                 /* the inner circle's radius is smaller than outer */
3552                 radius = radius - 0.5 * Stdpad;
3553                 draw_circle(x_center / elongation_factor, y_center, radius);
3554
3555                 /* fill in the area between the inner and outer elipses */
3556                 outop(O_EOFILL);
3557                 outop(O_GRESTORE);
3558
3559                 /* adjust x for where text should be printed */
3560                 x += x_offset;
3561         }
3562
3563         split_a_string(x, y, string, justify, fullwidth, fname, lineno);
3564 }
3565 \f
3566
3567 /* Draw a circle (or maybe elipse, if scaling is in effect) */
3568
3569 static void
3570 draw_circle(x, y, radius)
3571
3572 double x, y;    /* of circle center */
3573 double radius;
3574
3575 {
3576         outcoord(x);
3577         outcoord(y);
3578         outcoord(radius);
3579         outint(0);
3580         outint(360);
3581         outop(O_ARC);
3582 }
3583 \f
3584
3585 /* output instructions for setting font and size */
3586
3587 static void
3588 pr_font(font, size)
3589
3590 int font;
3591 int size;
3592
3593 {
3594 #ifdef SMALLMEMORY
3595         /* if memory is scarce, every time we do a new font,
3596          * do it in a separate save context */
3597         if (Did_save == YES) {
3598                 outop(O_RESTORE);
3599         }
3600         outop(O_SAVE);
3601         Did_save = YES;
3602 #endif
3603
3604         Curr_font = font;
3605         Curr_size = size;
3606
3607         prfontname(font);
3608
3609         outop(O_FONT);
3610
3611         outint(size);
3612         outop(O_SIZE);
3613         outop(O_SETFONT);
3614
3615         Font_used[font] = YES;
3616 }
3617 \f
3618
3619 /* print font name */
3620
3621 static void
3622 prfontname(font)
3623
3624 int font;
3625 {
3626         OUTP(("/%s ", Fontinfo[font_index(font)].ps_name));
3627 }
3628 \f
3629
3630 /* split a string into lines and print each line */
3631
3632 static void
3633 split_a_string(x, y, string, justify, fullwidth, fname, lineno)
3634
3635 double x;               /* coordinate at which to print string */
3636 double y;
3637 char *string;           /* what string to print */
3638 int justify;            /* J_LEFT, etc */
3639 double fullwidth;       /* width of (possibly multi-line) string, or -1.0
3640                          * if the string width should be used. */
3641 char *fname;            /* file name for error messages */
3642 int lineno;             /* line number for error messages */
3643
3644 {
3645         int font, size;         /* current font and size */
3646         int origfont, origsize; /* font & size at beginning of current line
3647                                  * of text */
3648         char *text;             /* beginning of text of current line */
3649         char *p;                /* pointer to current place in string */
3650         int c;                  /* character read from string */
3651         char *buff;             /* temporary copy of one line of string */
3652
3653
3654         origfont = font = string[0];
3655         origsize = size = string[1];
3656         text = string + 2;
3657
3658         /* if centering or right justifying, will need width of entire
3659          * (possibly multi-line) string, to adjust lines within the string */
3660         if (fullwidth < 0.0) {
3661                 fullwidth = strwidth(string);
3662         }
3663
3664         if (IS_BOXED(string) == YES) {
3665                 /* The box printing is dealt with in pr_string(), so we
3666                  * can ignore the BOX commands here (and need to, in order
3667                  * to make things align properly). */
3668                 text++;
3669                 fullwidth -= 7.0 * STDPAD;
3670         }
3671         p = text;
3672         MALLOCA(char, buff, strlen(string) + 1);
3673         do {
3674                 c = next_str_char(&p, &font, &size);
3675                 if (c == '\n' || c == '\0') {
3676                         /* end of line. Print this line. Put into
3677                          * temporary buffer in case more than one line */
3678                         buff[0] = (char) origfont;
3679                         buff[1] = (char) origsize;
3680                         (void) memcpy(buff + 2, text, (unsigned) (p - text));
3681                         buff[p - text + 2] = '\0';
3682                         /* On final line of a justified paragraph, we don't
3683                          * want to stretch that line out, because it might
3684                          * only contain a couple words. */
3685                         if (justify == J_JUSTPARA && c == '\0') {
3686                                 justify = J_LEFT;
3687                         }
3688
3689                         j_outstring(x, y, buff, justify, fullwidth,
3690                                                 fname, lineno);
3691
3692                         /* prepare for next line, if any */
3693                         origfont = font;
3694                         origsize = size;
3695                         text = p;
3696                         y -= fontheight(font, size);
3697                 }
3698         } while (c != '\0');
3699         FREE(buff);
3700 }
3701 \f
3702
3703 /* output a string segment with specified justification. If J_LEFT, just
3704  * print given string at given x, y location. If J_CENTER, put half way
3705  * between x and (x + fullwidth). If J_RIGHT, print such that right edge
3706  * of string will be at (x + fullwidth) */
3707
3708 static void
3709 j_outstring(x, y, string, justify, fullwidth, fname, lineno)
3710
3711 double x;
3712 double y;
3713 char *string;           /* which string to print */
3714 int justify;            /* J_LEFT, etc */
3715 double fullwidth;       /* full width to allocate to string */
3716 char *fname;            /* file name for error messages */
3717 int lineno;             /* line number for error messages */
3718
3719 {
3720         switch (justify) {
3721         case J_NONE:
3722                 /* NONE is effectively the same as LEFT */
3723                 /*FALLTHRU*/
3724         case J_LEFT:
3725         case J_RAGPARA:
3726                 outstring(x, y, -1.0, string, fname, lineno);
3727                 break;
3728         case J_JUSTPARA:
3729                 outstring(x, y, fullwidth, string, fname, lineno);
3730                 break;
3731         case J_CENTER:
3732                 outstring(x + (fullwidth - strwidth(string)) / 2.0, y,
3733                                 -1.0, string, fname, lineno);
3734                 break;
3735         case J_RIGHT:
3736                 outstring(x + fullwidth - strwidth(string), y, -1.0,
3737                                 string, fname, lineno);
3738                 break;
3739         default:
3740                 pfatal("bad justification type");
3741                 /*NOTREACHED*/
3742                 break;
3743         }
3744 }
3745 \f
3746
3747 /* given a MAINLL struct, find all the STAFF structs from there to the next
3748  * BAR, and fill in a table of the staff Y coordinates */
3749
3750 static void
3751 set_staff_y(main_p)
3752
3753 struct MAINLL *main_p;
3754
3755 {
3756         int s;
3757
3758         /* First initialize all to 0.0. This is so that if the Staffs_y
3759          * array is accessed for a non-existent staff, we will be sure
3760          * that it will be set to 0.0, which is the special value to mean
3761          * non-existent. Otherwise, when the number of staffs decreases,
3762          * an old value could get left around in the staff that went away. */
3763         for (s = 1; s <= MAXSTAFFS; s++) {
3764                 Staffs_y[s] = 0.0;
3765         }
3766
3767         for (   ; main_p != (struct MAINLL *) 0; main_p = main_p->next) {
3768
3769                 if (main_p->str == S_BAR) {
3770                         /* reached end of list of staffs in this measure */
3771                         return;
3772                 }
3773
3774                 if (main_p->str == S_STAFF) {
3775                         /* save y value of staff */
3776                         Staffs_y[main_p->u.staff_p->staffno] = 
3777                                         main_p->u.staff_p->c[AY];
3778                 }
3779         }
3780 }
3781 \f
3782
3783 /* print measure number at beginning of score if user wants them */
3784
3785 static void
3786 pr_meas_num(staffno, x)
3787
3788 int staffno;    /* which staff to possible put measure number on */
3789 double x;       /* where to put measure number */
3790
3791 {
3792         float y_adj;    /* to avoid clefs */
3793         int clef;
3794
3795
3796         /* measure numbers only put on those staffs that have endings */
3797         if (has_ending(staffno) ) {
3798
3799                 /* print measure number if user wants them */
3800                 if ( (svpath(staffno, MEASNUM)->measnum == YES)
3801                                                 && (Meas_num > 1)) {
3802
3803                         /* construct the measure number string */
3804                         char mnumstr[8];
3805                         mnumstr[0] = (char) (Score.measnumfamily
3806                                                         + Score.measnumfont);
3807                         mnumstr[1] = (char) Score.measnumsize;
3808                         (void) sprintf(mnumstr + 2, "%d", Meas_num);
3809
3810                         /* print it */
3811                         if (is_tab_staff(staffno) == YES) {
3812                                 clef = TABCLEF;
3813                         }
3814                         /* If clef is not to be printed, use NOCLEF.
3815                          * (printclef shares the STAFFLINES used flag) */
3816                         else if (svpath(staffno, STAFFLINES)->printclef == NO) {
3817                                 clef = NOCLEF;
3818                         }
3819                         else {
3820                                 clef = svpath(staffno, CLEF)->clef;
3821                         }
3822                         /* Figure out where to place the measure number
3823                          * vertically by calling clefspace to get
3824                          * the height of the clef on the current staff plus
3825                          * the height of the measure number, but ignoring
3826                          * the height of the clef above (if any), then
3827                          * subtract the ascent of the measure number to
3828                          * get the right baseline. */
3829                         y_adj = halfstaffhi(staffno) +
3830                                 clefspace(NOCLEF, 1.0, clef, Staffscale, YES) -
3831                                 fontascent((int) mnumstr[0], (int) mnumstr[1]);
3832                         pr_string(x + 1.5 * Stepsize,
3833                                         Staffs_y[staffno] + y_adj,
3834                                         mnumstr, J_LEFT, (char *) 0, -1);
3835                 }
3836         }
3837 }
3838 \f
3839
3840 /* tell PostScript about file and linenumber */
3841
3842 void
3843 pr_linenum (inputfile, inputlineno)
3844
3845 char *inputfile;
3846 int inputlineno;
3847
3848 {
3849         static char *fname = "";                /* keep track of current file
3850                                                  * name to only output it when
3851                                                  * it changes */
3852         char *str;                              /* walk thru file name to
3853                                                  * add backslashes if needed */
3854
3855         
3856         if (strcmp(fname, inputfile) != 0) {
3857                 OUTPCH(('('));
3858                 for (str = inputfile; *str != 0; str++) {
3859                         switch(*str) {
3860                         case '\\':
3861                         case '(':
3862                         case ')':
3863                                 OUTPCH(('\\'));
3864                                 /*FALLTHRU*/
3865                         default:
3866                                 OUTPCH((*str));
3867                                 break;
3868                         }
3869                 }
3870                 OUTP((") inputfile\n"));
3871                 fname = inputfile;
3872         }
3873         OUTP(("%d linenum\n", inputlineno));
3874 }
3875 \f
3876
3877 /* output the current scale factor */
3878
3879 static void
3880 setscale()
3881
3882 {
3883         OUTP(("%f %f scale\n", Score.scale_factor, Score.scale_factor));
3884 }
3885 \f
3886
3887 /* For debugging, this generates PostScript to draw colored bounding boxes
3888  * representing coordinates (the 13-element arrays called "c"
3889  * in various structs).
3890  */
3891
3892 static void
3893 show_coord(coord_p, index)
3894
3895 float *coord_p;         /* which one to draw */
3896 int index;              /* index into Bbox_list, to get colors to use */
3897
3898 {
3899         struct Bbox *bb_p;
3900
3901         bb_p = &(Bbox_list[index]);
3902         outop(O_GSAVE);
3903         OUTP(("%d.%d %d.%d %d.%d setrgbcolor\n",
3904                 bb_p->red / 100, bb_p->red % 100,
3905                 bb_p->green / 100, bb_p->green % 100,
3906                 bb_p->blue / 100, bb_p->blue % 100));
3907         if (bb_p->dash_on && bb_p->dash_off) {
3908                 OUTP(("[%d %d] 0 setdash\n", bb_p->dash_on, bb_p->dash_off));
3909         }
3910         pr_box(coord_p[AW], coord_p[AS], coord_p[AN] - coord_p[AS], coord_p[AE] - coord_p[AW]);
3911         outop(O_GRESTORE);
3912 }
3913 \f
3914
3915 /* The environment variable MUP_BB turns on drawing of bounding boxes around
3916  * things, for debugging.  This function checks if the variable is set,
3917  * and if so, parses it to see which subset of things to draw boxes for.
3918  * For example, MUP_BB=g just does grpsyls, whereas MUP_BB=gnc does grpsyls,
3919  * notes, and chords. Bbox_list gives the full list of possibilities.
3920  */
3921
3922 static void
3923 prep_bbox()
3924 {
3925         char *bb;
3926         int i;
3927
3928         if ((bb = getenv("MUP_BB")) == 0) {
3929                 /* user doesn't want bounding box debugging */
3930                 return;
3931         }
3932
3933         /* If a coordinate type's id is set in MUP_BB, set its corresponding
3934          * flag bit */
3935         for (i = 0; i < NUMELEM(Bbox_list); i++) {
3936                 if (strchr(bb, Bbox_list[i].id) != 0) {
3937                         BB_SET(i);
3938                 }
3939         }
3940 }
3941 \f
3942
3943 /* To help with debugging the placement phase of Mup,
3944  * or just to help a user see why things are laid out as they are,
3945  * this function will draw colored bounding boxes around things that
3946  * have "coordinates." The environment variable MUP_BB control which,
3947  * if any, kinds of things this is done for. This function is given the
3948  * main list item at the end of a page. It backs up through the list, back to
3949  * the beginning of the page, generating PostScript code to cause
3950  * printing of relevant boxes. Doing this last on a page ensures the boxes
3951  * are drawn on top of other things, and is at least as easy as going forwards.
3952  */
3953
3954 static void
3955 show_bounding_boxes(mll_p)
3956
3957 struct MAINLL *mll_p;   /* FEED for end of current page, or could be
3958                          * Mainlltc_p if at end of song. */
3959
3960 {
3961         int v;                  /* voice/verse */
3962         int n;                  /* NOTE index */
3963         struct GRPSYL *gs_p;
3964         struct CHORD *chord_p;
3965         struct STUFF *stuff_p;
3966
3967         /* We are at bottom of main list for current page. Work upwards,
3968          * printing any coords we find. */
3969         if (mll_p->str == S_FEED) {
3970                 /* Skip this feed.
3971                  * We want to back up to previous page feed, if any */
3972                 mll_p = mll_p->prev;
3973         }
3974
3975         for (   ; mll_p != 0; mll_p = mll_p->prev) {
3976                 switch (mll_p->str) {
3977
3978                 case S_BAR:
3979                         if (BB_IS_SET(BB_BAR)) {
3980                                 show_coord(mll_p->u.bar_p->c, BB_BAR);
3981                         }
3982                         break;
3983
3984                 case S_FEED:
3985                         if (BB_IS_SET(BB_FEED)) {
3986                                 show_coord(mll_p->u.feed_p->c, BB_FEED);
3987                         }
3988                         if (mll_p->u.feed_p->pagefeed == YES
3989                                                         || Feednumber == 1) {
3990                                 if (BB_IS_SET(BB_BLOCKHEAD)) {
3991                                         /* Do header/footer */
3992                                         if (Feednumber == 1) {
3993                                                 show_coord(Header.c, BB_BLOCKHEAD);
3994                                                 show_coord(Footer.c, BB_BLOCKHEAD);
3995                                         }
3996                                         else {
3997                                                 show_coord(Header2.c, BB_BLOCKHEAD);
3998                                                 show_coord(Footer2.c, BB_BLOCKHEAD);
3999                                         }
4000                                         if (mll_p->u.feed_p->top_p != 0){
4001                                                 set_win_coord(mll_p->u.feed_p->top_p->c);
4002                                                 show_coord(mll_p->u.feed_p->top_p->c, BB_BLOCKHEAD);
4003                                                 set_win_coord(0);
4004                                         }
4005                                         if (mll_p->u.feed_p->bot_p != 0){
4006                                                 set_win_coord(mll_p->u.feed_p->bot_p->c);
4007                                                 show_coord(mll_p->u.feed_p->bot_p->c, BB_BLOCKHEAD);
4008                                                 set_win_coord(0);
4009                                         }
4010                                 }
4011                                 if (mll_p->u.feed_p->pagefeed == YES) {
4012                                         /* reached top of current page; we're done */
4013                                         return;
4014                                 }
4015                         }
4016                         break;
4017
4018                 case S_STAFF:
4019                         if (mll_p->u.staff_p->visible == NO) {
4020                                 break;
4021                         }
4022
4023                         /* show the staff itself */
4024                         if (BB_IS_SET(BB_STAFF)) {
4025                                 show_coord(mll_p->u.staff_p->c, BB_STAFF);
4026                         }
4027
4028                         /* Do groups and notes */
4029                         if (BB_IS_SET(BB_GRPSYL) || BB_IS_SET(BB_NOTE)) {
4030                                 for (v = 0; v < MAXVOICES; v++) {
4031
4032                                         if (vvpath(mll_p->u.staff_p->staffno,
4033                                                         v+1, VISIBLE)->visible
4034                                                         == NO) {
4035                                                 /* Skip invisible voices */
4036                                                 continue;
4037                                         }
4038
4039                                         for (gs_p = mll_p->u.staff_p->groups_p[v];
4040                                                         gs_p != 0;
4041                                                         gs_p = gs_p->next) {
4042                                                 if (BB_IS_SET(BB_GRPSYL)) {
4043                                                         show_coord(gs_p->c, BB_GRPSYL);
4044                                                 }
4045                                                 if (gs_p->nnotes > 0 &&
4046                                                                 BB_IS_SET(BB_NOTE)) {
4047                                                         for (n = 0; n < gs_p->nnotes; n++) {
4048                                                                 show_coord(gs_p->notelist[n].c, BB_NOTE);
4049                                                         }
4050                                                 }
4051                                         }
4052                                 }
4053                         }
4054
4055                         /* now do lyrics */
4056                         if (BB_IS_SET(BB_GRPSYL)) {
4057                                 for (n = 0; n < mll_p->u.staff_p->nsyllists; n++) {
4058                                         for  (gs_p = mll_p->u.staff_p->syls_p[n];
4059                                                         gs_p != 0;
4060                                                         gs_p = gs_p->next) {
4061                                                 show_coord(gs_p->c, BB_GRPSYL);
4062                                         }
4063                                 }
4064                         }
4065
4066                         /* do the other "stuff" */
4067                         if (BB_IS_SET(BB_STUFF)) {
4068                                 for (stuff_p = mll_p->u.staff_p->stuff_p;
4069                                                 stuff_p != 0;
4070                                                 stuff_p = stuff_p->next) {
4071                                         show_coord(stuff_p->c, BB_STUFF);
4072                                 }
4073                         }
4074                         break;
4075
4076                 case S_CHHEAD:
4077                         if (BB_IS_SET(BB_CHORD)) {
4078                                 for (chord_p = mll_p->u.chhead_p->ch_p; chord_p != 0;
4079                                                 chord_p = chord_p->ch_p) {
4080                                         show_coord(chord_p->c, BB_CHORD);
4081                                 }
4082                         }
4083                         break;
4084
4085                 default:
4086                         break;
4087                 }
4088         }
4089 }