chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / print.c
CommitLineData
69695f33
MW
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 */
21static 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 */
88static 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};
105static 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 */
110static int Did_save = NO;
111#endif
112
113/* for header, to indicate when output file was generated */
114static char *Dayofweek[] = {
115 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
116static char *Month[] = {
117 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
118 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
119
120static int Pagesprinted = 0; /* number of pages actually printed */
121static int Feednumber; /* how many pagefeeds we've handled */
122
123
124static 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
129static int Last_linetype = -1; /* keep track of last type. If same,
130 * this time, no reason to output */
131static float Last_staffscale; /* Change in staffscale also changes
132 * line width, so have to remember it */
133static 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 */
136static double x1a, x2a, ya; /* x and y adjustments */
137static int sn;
138extern int *Check_p;
139static int Landscape; /* how much to translate for landscape
140 * mode, or 0 if not in landscape */
141
142/* static functions */
143static void init4print P((void));
144static void page1setup P((void));
145static int use_landscape P((double pgwidth, double pgheight));
146static void setup_user_fonts P((void));
147#ifdef EXTCHAR
148static void setup_extended_fonts P((void));
149#endif
150static void pr_line P((struct LINE *line_p, char *fname, int lineno));
151static void dr_line P((double x1, double y1, double x2, double y2, int ltype));
152static void pr_curve P((struct CURVE *curve_p, char *fname, int lineno));
153static void outp_muschar P((double x, double y, int ch, int size, int font));
154static void pr_ital_muschar P((double x, double y, int ch, int size, int font));
155static void pr_bar P((struct MAINLL *mll_p, double x, int is_pseudobar));
156static 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));
159static void draw_bar P((int bartype, int linetype, double x, double y1,
160 double y2));
161static void pr_repeat_dots P((int bartype, int staff, double x));
162static void do_rdots P((double x, double y, double topoffset, double bottomosffset));
163static void pr_reh P((struct MAINLL *mll_p));
164static void pr_box P((double x, double y, double boxheight, double boxwidth));
165static void pr_topbot P((struct BLOCKHEAD *blockhead_p, double y));
166static void pr_restarts P((struct MAINLL *mll_p, double y1, double y2,
167 int need_vert_line));
168static void outint P((int val));
169static void pr_wstring P((double x, double y, char *string, int justify,
170 double fullwidth, char * fname, int lineno));
171static void outstring P((double x, double y, double fullwidth, char *string,
172 char * fname, int lineno));
173static int begin_string P((int in_string));
174static int end_string P((int in_string, double space_adjust));
175static void outop P((int op));
176static void pr_headfoot P((struct MAINLL *mll_p));
177static void to_next_page P((struct MAINLL *mll_p));
178static void pr_print P((struct PRINTDATA *printdata_p));
179static double pr_keysig P((int staffno, int sharps, int naturals, double x,
180 int really_print));
181static void draw_keysig P((int muschar, int symbols, double x,
182 double y, int *table, int offset, int skip));
183static double pr_timesig P((int staffno, double x, int multnum,
184 int really_print));
185static double tsjam P((int num));
186static void pr_tsnum P((double x, double y, char *str, double jam));
187static void draw_circle P((double x, double y, double radius));
188static void do_scale P((double xscale, double yscale));
189static void pr_font P((int font, int size));
190static void prfontname P((int font));
191static void split_a_string P((double x, double y, char *string, int justify,
192 double fullwidth, char *fname, int lineno));
193static void j_outstring P((double x, double y, char *string, int justify,
194 double fullwidth, char *fname, int lineno));
195static void set_staff_y P((struct MAINLL *main_p));
196static void pr_meas_num P((int staffno, double x));
197static void setscale P((void));
198static void show_coord P((float *coord_p, int index));
199static void prep_bbox P((void));
200static 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
206void
207print_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 */
345void
346newpage(mll_p)
347
348struct 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
359void
360trailer()
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
381static void
382init4print()
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
556static void
557page1setup()
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 */
578struct 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
608static int
609use_landscape(pgwidth, pgheight)
610
611double pgwidth; /* page width in inches */
612double 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
644static void
645setup_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
669static void
670setup_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
717static void
718pr_line(line_p, fname, lineno)
719
720struct LINE *line_p; /* info about what kind of line to draw and where */
721char *fname; /* file name for error messages */
722int 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
795void
796do_linetype(ltype)
797
798int 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
858void
859draw_line(x1, y1, x2, y2)
860
861double x1, y1; /* draw line from here */
862double 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
872void
873draw_prop_line(x1, y1, x2, y2, size, ltype)
874
875double x1, y1; /* draw line from here */
876double x2, y2; /* to here */
877int size; /* make width proportional to this */
878int 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
893void
894draw_wavy(x1, y1, x2, y2)
895
896double x1;
897double y1; /* draw wavy line from x1,y1 */
898double x2;
899double 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
908static void
909dr_line(x1, y1, x2, y2, ltype)
910
911double x1;
912double y1; /* draw line from x1,y1 */
913double x2;
914double y2; /* to x2,y2 */
915int 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
935static void
936pr_curve(curve_p, fname, lineno)
937
938struct CURVE *curve_p; /* which curve */
939char *fname; /* file name for error messages */
940int 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
1003void
1004do_moveto(x, y)
1005
1006double x;
1007double y;
1008
1009{
1010 outcoord(x);
1011 outcoord(y);
1012 outop(O_MOVETO);
1013}
1014
1015
1016void
1017do_line(x, y)
1018
1019double x;
1020double y;
1021
1022{
1023 outcoord(x);
1024 outcoord(y);
1025 outop(O_LINETO);
1026}
1027
1028void
1029do_curveto(x1, y1, x2, y2, x3, y3)
1030
1031double x1, y1;
1032double x2, y2;
1033double 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
1046void
1047do_stroke()
1048
1049{
1050 outop(O_STROKE);
1051}
1052
1053void
1054do_fill()
1055
1056{
1057 outop(O_FILL);
1058}
1059
1060void
1061do_newpath()
1062
1063{
1064 outop(O_NEWPATH);
1065}
1066
1067void
1068do_closepath()
1069
1070{
1071 outop(O_CLOSEPATH);
1072}
1073
1074/* output a PostScript scale command */
1075
1076static void
1077do_scale(xscale, yscale)
1078
1079double 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
1088void
1089do_whitebox(x1, y1, x2, y2)
1090
1091double x1, y1;
1092double 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
1105void
1106do_grid(x, y, space, grid_p, staff)
1107
1108double x;
1109double y;
1110double space; /* distance between lines of the grid */
1111struct GRID *grid_p;
1112int 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
1148void
1149pr_muschar(x, y, ch, size, font)
1150
1151float x, y; /* where to print */
1152int ch; /* which music character to print */
1153int size;
1154int 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
1168static void
1169outp_muschar(x, y, ch, size, font)
1170
1171double x, y;
1172int ch;
1173int 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
1206static void
1207pr_ital_muschar(x, y, ch, size, font)
1208
1209double x, y; /* where to print */
1210int ch; /* which music character to print */
1211int size;
1212int 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
1270static void
1271pr_bar(mll_p, x, is_pseudobar)
1272
1273struct MAINLL *mll_p; /* print bar connected here */
1274double x; /* x coordinate */
1275int 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
1376static void
1377pr_bar_range(bar_p, topstaff, botstaff, x, next_is_restart, mll_p)
1378
1379struct BAR *bar_p; /* info about bar */
1380int topstaff; /* top staff to be barred together */
1381int botstaff; /* bottom staff to be barred together */
1382double x; /* x coordinate of where to draw the bar */
1383int next_is_restart; /* YES if following bar is RESTART */
1384struct 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
1498static void
1499draw_bar(bartype, linetype, x, y1, y2)
1500
1501int bartype; /* info about single, double, repeat, etc */
1502int linetype;
1503double x;
1504double y1; /* top of bar line */
1505double 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
1571static void
1572pr_repeat_dots(bartype, staff, x)
1573
1574int bartype; /* repeatstart, repeatend, repeatboth, etc */
1575int staff; /* which staff to print on */
1576double 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
1651static void
1652do_rdots(x, y, topoffset, bottomoffset)
1653
1654double x;
1655double y; /* y is a middle of staff, so offset from there */
1656double 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
1666static void
1667pr_reh(mll_p)
1668
1669struct 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
1707static void
1708pr_box(x, y, boxheight, boxwidth)
1709
1710double x, y;
1711double 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
1727void
1728pr_feed(main_feed_p)
1729
1730struct 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
1883static
1884void pr_topbot(blockhead_p, y)
1885
1886struct BLOCKHEAD *blockhead_p;
1887double 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
1908static void
1909pr_restarts(mll_p, y1, y2, need_vert_line)
1910
1911struct MAINLL *mll_p;
1912double y1;
1913double y2;
1914int 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
1952void
1953do_pr_brac(x, y1, y2, which)
1954
1955double x; /* coordinates at which to draw brace or bracket */
1956double y1;
1957double y2;
1958int 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
1970void
1971outcoord(val)
1972
1973double val; /* an x or y value */
1974
1975{
1976 OUTP(("%.2f ", val * PPI));
1977}
1978\f
1979
1980
1981/* output an integer value */
1982
1983static void
1984outint(val)
1985
1986int 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
1997static void
1998outstring(x, y, fullwidth, string, fname, lineno)
1999
2000double x; /* where to print string */
2001double y;
2002double 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. */
2007char *string; /* what to print */
2008char *fname; /* file name for error messages */
2009int 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
2269static int
2270begin_string(in_string)
2271
2272int 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
2285static int
2286end_string(in_string, space_adjust)
2287
2288int in_string; /* YES if currently inside a string */
2289double 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
2327static void
2328outop(op)
2329
2330int 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
2485static void
2486pr_headfoot(mll_p)
2487
2488struct 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
2552static void
2553to_next_page(mll_p)
2554
2555struct 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
2642static void
2643pr_print(printdata_p)
2644
2645struct 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
2719double
2720pr_clefsig(mll_p, clefsig_p, really_print)
2721
2722struct MAINLL *mll_p; /* clefsig is connected here */
2723struct CLEFSIG *clefsig_p; /* which clef, etc to print */
2724int 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
2982double
2983pr_clef(staffno, x, really_print, size)
2984
2985int staffno; /* which staff to print clef on */
2986double x; /* x coord */
2987int really_print; /* if YES, actually print, else just return width */
2988int 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 */
3067static int Std_sharps_pattern[] = { 4, 1, 5, 2, -1, 3, 0 };
3068static 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 */
3071static int Alt_sharps_pattern[] = { -1, 3, 0, 4, 1, 5, 2 };
3072static int Alt_flats_pattern[] = { 4, 0, 3, -1, 2, -2, 1 };
3073/* special version for baritone and soprano clef */
3074static int Alt2_sharps_pattern[] = { 0, 4, 1, -2, 2, -1, -4 };
3075
3076
3077static double
3078pr_keysig(staffno, sharps, naturals, x, really_print)
3079
3080int staffno; /* which staff to print on */
3081int sharps; /* how many sharps in key signature */
3082int naturals; /* how many naturals to print to cancel previous key */
3083double x; /* coordinate */
3084int 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
3215static void
3216draw_keysig(muschar, symbols, x, y, table, offset, skip)
3217
3218int muschar; /* what to draw: C_SHARP, C_FLAT, or C_NAT */
3219int symbols; /* how many to draw */
3220double x; /* where to start putting them */
3221double y; /* middle of staff */
3222int *table; /* which pattern to use for drawing symbols */
3223int offset; /* to compensate for clef */
3224int 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
3254static double
3255pr_timesig(staffno, x, multnum, really_print)
3256
3257int staffno; /* which staff to print on */
3258double x; /* coordinate */
3259int multnum; /* number of measures of multirest that follow */
3260int 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
3426static double
3427tsjam(num)
3428
3429int 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
3444static void
3445pr_tsnum(x, y, str, jam)
3446
3447double x;
3448double y;
3449char *str;
3450double 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
3471void
3472pr_string(x, y, string, justify, fname, lineno)
3473
3474double x, y; /* where to put it */
3475char *string; /* what to print */
3476int justify; /* J_LEFT, etc */
3477char *fname; /* file name for error messages */
3478int 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
3489static void
3490pr_wstring(x, y, string, justify, fullwidth, fname, lineno)
3491
3492double x, y; /* where to put it */
3493char *string; /* what to print */
3494int justify; /* J_LEFT, etc */
3495double fullwidth; /* width to use, or negative value to use strwidth */
3496char *fname; /* file name for error messages */
3497int 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
3569static void
3570draw_circle(x, y, radius)
3571
3572double x, y; /* of circle center */
3573double 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
3587static void
3588pr_font(font, size)
3589
3590int font;
3591int 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
3621static void
3622prfontname(font)
3623
3624int 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
3632static void
3633split_a_string(x, y, string, justify, fullwidth, fname, lineno)
3634
3635double x; /* coordinate at which to print string */
3636double y;
3637char *string; /* what string to print */
3638int justify; /* J_LEFT, etc */
3639double fullwidth; /* width of (possibly multi-line) string, or -1.0
3640 * if the string width should be used. */
3641char *fname; /* file name for error messages */
3642int 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
3708static void
3709j_outstring(x, y, string, justify, fullwidth, fname, lineno)
3710
3711double x;
3712double y;
3713char *string; /* which string to print */
3714int justify; /* J_LEFT, etc */
3715double fullwidth; /* full width to allocate to string */
3716char *fname; /* file name for error messages */
3717int 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
3750static void
3751set_staff_y(main_p)
3752
3753struct 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
3785static void
3786pr_meas_num(staffno, x)
3787
3788int staffno; /* which staff to possible put measure number on */
3789double 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
3842void
3843pr_linenum (inputfile, inputlineno)
3844
3845char *inputfile;
3846int 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
3879static void
3880setscale()
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
3892static void
3893show_coord(coord_p, index)
3894
3895float *coord_p; /* which one to draw */
3896int 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
3922static void
3923prep_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
3954static void
3955show_bounding_boxes(mll_p)
3956
3957struct 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}