/* * This file is part of abc2ps, Copyright (C) 1996,1997 Michael Methfessel * See file abc2ps.c for details. */ #include #include #include #include #include #include #ifdef _MSVC #include #endif #include "abc2ps.h" #define STYLE "std" #define MAXNTEXT 400 /* for text output */ #define MAXWLEN 21 static int music_only; /* no vocals if 1 */ static int pretty; /* for pretty but sprawling layout */ static int bars_per_line; /* bars for auto linebreaking */ static int continue_lines; /* flag to continue all lines */ static int landscape; /* flag for landscape output */ static float lmargin; /* left margin */ static float swidth; /* staff width */ static float scalefac; /* scale factor for symbol size */ static float alfa_c; /* max compression allowed */ static float staffsep; /* staff separation */ static char outfnam[STRL1]; /* internal file name for open/close */ static char styf[STRL1]; /* layout style file name */ static char styd[STRL1]; /* layout style directory */ static char txt[MAXNTEXT][MAXWLEN]; /* for output of text */ int ntxt; extern char *p; /* global pointers for parsing music line */ extern char *p0; /* beginning of line */ static int PNTop; /* [JSA] */ static int PMirror; /* [JSA] */ int FirstPgNum = 0; /* [JSA] */ int ParamUnits = 0; /* miscellaneous subroutines */ /* ----- write_help ----- */ void write_help() { printf("Usage: jaabc2ps files.. [-e nums-or-pats] [other flags]\n" " - show index of abc files or typeset tunes in Postscript.\n" "where: files input files in abc format\n" " nums tune xref numbers, i.e. 1 3 6-7,20-\n" " pats patterns for title search\n" "Tunes are selected if they match a number or a pattern.\n" "Flags: -o write output for selected tunes\n" " -E produce EPSF output, one tune per file\n" " -O aa set outfile name to aaa\n" " -O = make outfile name from infile/title\n" " -i run in interactive mode\n" " -v nn set verbosity level to nn\n" " -h show this command summary\n" " Selection:\n" " -e following arguments are selectors\n" " -f following arguments are file names\n" " -C search Composer field instead of title\n" " -R search Rhythm field instead of title\n" " -S search Source field instead of title\n" " -A select all tunes (override other selectors)\n" " -V n deselect voice # \"n\" (cumulative)\n" " Formatting:\n" " -H [cm|in|pt] show the format parameters (default is inch)\n" " -p pretty output (looks better, needs more space)\n" " -P select second predefined pretty output style\n" " -s xx set scale factor for symbol size to xx\n" " -w xx set staff width (cm/in/pt)\n" " -m xx set left margin (cm/in/pt)\n" " -d xx set staff separation (cm/in/pt)\n" " -x include xref numbers in output\n" " -n include notes and history in output\n" " -N write page numbers\n" " -1 write one tune per page\n" " -l landscape mode\n" " -L fix line breaks in source files\n" " -g xx set glue mode to shrink|space|stretch|fill\n" " -F foo read format from \"foo.fmt\"\n" " -D bar look for format files in directory \"bar\"\n" " -j n Number the measures every n bars or on the left if 0\n" " -# n Start page numbering at \"n.\"\n" " Tablature:\n" " -Wvk Print whistle tablature for voice \"v\" and a\n" " whistle in the key of \"k\" (ex: \"-W1Bb\" or \"-W3LD\").\n" " May be used for up to four voices. Whistle\n" " tablature appears below the staves.\n" " -Tv[capo fret][:simple|marked]\n" " Print string tablature for voice \"v.\" Instrument\n" " is hromatic or mountain ulcimer. The \"+\" means\n" " that the dulcimer has the 6-1/2 fret. The string pitches\n" " give the tuning for each string (ex. \"^d'\"). Up to\n" " eight strings are permitted. Optionally a fret may\n" " be specified as the capo location. Also optionally\n" " a style may be specified. The default style is simple\n" " meaning that only specific notes as supplied in the\n" " voice will be tabbed. \"Marked\" means that open strings\n" " will be explicitly tabbed whenever an upbow or downbow\n" " is encountered.\n" " Line breaks:\n" " -a xx set max shrinkage to xx (between 0 and 1)\n" " -b break at all line ends (ignore continuations)\n" " -c continue all line ends (append '\\')\n" " -B bb put line break every bb bars\n"); } /* ----- is_xrefstr: check if string ok for xref selection ---- */ int is_xrefstr(char str[]) { char *c; c=str; while (*c != '\0') { if (!isdigit(*c) && *c!='-' && *c!=',' && *c!=' ') return 0; c++; } return 1; } /* ----- make_arglist: splits one string into list or arguments ---- */ int make_arglist(char str[], char *av[]) { char *q; int n; q=str; n=1; /* first real arg is 1, as in argv */ for (;;) { while (*q==' ') q++; if (*q=='\0') break; av[n]=q; n++; while (*q!=' ' && *q!='\0') q++; if (*q=='\0') break; *q='\0'; q++; } return n; } /* ----- init_ops ----- */ void init_ops(int job) { one_per_page = -1; landscape = -1; scalefac = -1.0; lmargin = -1.0; swidth = -1.0; write_history = -1; staffsep = -1; break_continues = -1; continue_lines = -1; include_xrefs = -1; music_only = -1; alfa_c = -1.0; PNTop = PMirror = -1; select_all = 0; do_mode = DO_INDEX; pagenumbers = 0; styf[0] = '\0'; if (job) { strcpy(styd, DEFAULT_FDIR); strcpy(outf, OUTPUTFILE); interactive = 0; pretty = 0; epsf = 0; choose_outname = 0; gmode = G_FILL; vb = VERBOSE0; search_field0 = S_TITLE; } } /* ----- ops_into_fmt ----- */ void ops_into_fmt(struct FORMAT *fmt) { if (landscape >= 0) fmt->landscape=landscape; if (scalefac >= 0) fmt->scale=scalefac; if (lmargin >= 0) fmt->leftmargin=lmargin; if (swidth >= 0) fmt->staffwidth=swidth; if (continue_lines >= 0) fmt->continueall=continue_lines; if (break_continues >= 0) fmt->breakall=break_continues; if (write_history >= 0) fmt->writehistory=write_history; if (bars_per_line >= 0) fmt->barsperstaff=bars_per_line; if (include_xrefs >= 0) fmt->withxrefs=include_xrefs; if (staffsep >= 0) fmt->staffsep=staffsep; if (one_per_page >= 0) fmt->oneperpage=one_per_page; if (music_only >= 0) fmt->musiconly=music_only; if (alfa_c >= 0) fmt->maxshrink=alfa_c; if (PNTop >= 0) fmt->PageNumTop = PNTop; if (PMirror >= 0) fmt->PageMirror = PMirror; } /* ----- parse_args: parse list of arguments, interpret flags ----- */ int parse_args(int ac, char *av[]) { int i,m,k,nsel,sel_arg,j,ok,f_pos,got_value; char c,aaa[201],ext[41]; int w_voice, whistle_pitch; /* [JSA] */ int tab_voice; /* [JSA] */ help_me=0; ninf=0; nsel=0; sel_arg=0; f_pos=-1; sel_str[0][0] = '\0'; s_field[0]=search_field0; for (i = 0; i < MAXVOICE; i++) VoiceMute_tb[i] = FALSE; /* [JSA] */ FixEols = FALSE; for (i = 1; i < ac; i++) { if (av[i][0]=='+') { /* switch off flags with '+' */ m=1; k=strlen(av[i]); while (m= MAXSTRINGTAB) { printf("++++ Too many string tablatures specified.\n"); return 1; } m++; tab_voice = 0; c = av[i][m]; if ((c >= '1') && (c <= '9')) { tab_voice = c - 48; } else { printf("++++ Invalid string tablature voice.\n"); return 1; } m++; c = av[i][m]; if ((c >= '0') && (c <= '9')) { tab_voice = (tab_voice * 10) + (c - 48); m++; c = av[i][m]; } StringTab_tb[nStringTab].voice = tab_voice - 1; StringTab_tb[nStringTab].style = TABSIMPLE; StringTab_tb[nStringTab].nstrings = 0; StringTab_tb[nStringTab].capo = 0; /* Get the tablature fretting. */ if (toupper(c) == 'C') { StringTab_tb[nStringTab].fretting = TABCHROMATIC; } else if (toupper(c) == 'D') { StringTab_tb[nStringTab].fretting = TABDULCIMER; m++; c = av[i][m]; if (c == '+') { StringTab_tb[nStringTab].fretting = TABDULCIMERPLUS; } else { m--; } } else { printf("++++ Invalid string tablature fretting.\n"); return 1; } /* Get the tuning */ do { m++; if (m == k) break; c = av[i][m]; switch (c) { case 'C': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 24; /* There will be one and only one note-value character for a given pitch so */ /* we use that as a trigger to increment the number of strings. */ StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'D': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 26; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'E': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 28; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'F': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 29; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'G': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 31; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'A': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 33; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'B': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 35; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'c': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 36; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'd': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 38; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'e': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 40; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'f': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 41; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'g': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 43; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'a': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 45; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case 'b': StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 47; StringTab_tb[nStringTab].nstrings++; if (StringTab_tb[nStringTab].nstrings > 8) { printf("++++ Too many strings for tablature.\n"); return 1; } break; case '\'': /* this character comes after a note-value so we increment */ /* the previous pitch */ if (!StringTab_tb[nStringTab].nstrings) { printf("++++ Syntax error in string tablature switch.\n"); return 1; } StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings - 1] += 12; break; case 'h': case 'H': /* this character comes before a note-value so we increment*/ /* the present pitch */ StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] += 12; break; case ',': /* this character comes after a note-value so we increment */ /* the previous pitch */ if (!StringTab_tb[nStringTab].nstrings) { printf("++++ Syntax error in string tablature switch.\n"); return 1; } StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings - 1] -= 12; break; case 'l': case 'L': /* this character comes before a note-value so we increment*/ /* the present pitch */ StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings] -= 12; break; case '_': /* this character comes before a note-value so we increment*/ /* the present pitch */ StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings]--; break; case '^': /* this character comes before a note-value so we increment*/ /* the present pitch */ StringTab_tb[nStringTab].pitches[StringTab_tb[nStringTab].nstrings]++; break; case 9: case ' ': break; /* spaces allowed, avoid default handler */ case ':': break; default: if ((c < '0') || (c > '9')) { printf("++++ invalid character for tablature.\n"); return 1; } } } while ((c != ':') && !((c >= '0') && (c <= '9'))); if ((m < k) && (c != ':')) { /* We have a capo number. */ StringTab_tb[nStringTab].capo = c - 48; m++; c = av[i][m]; if ((m < k) && (c >= '0') && (c <= '9')) { StringTab_tb[nStringTab].capo *= 10; StringTab_tb[nStringTab].capo += (c - 48); m++; } } c = av[i][m]; if ((m < (k - 1)) && (c == ':')) { m++; if (!stricmp(&av[i][m], "marked")) { StringTab_tb[nStringTab].style = TABMARKED; } else { StringTab_tb[nStringTab].style = TABSIMPLE; } } nStringTab++; m = k; break; case 'W': /* Whistle tablature */ /* The "W" flag can be used up to 4 times. The first*/ /* time it is encountered the information applies to */ /* the first (lowest on page) tablature line and the */ /* second applies to the second tablature line. */ /* The W flag uses two parameters, the voice and the */ /* native key of the whistle. */ m++; w_voice = 0; whistle_pitch = 0; while (m < k) { c = av[i][m]; if ((c >= '0') && (c <= '9')) { /* the voice selection */ if (w_voice) { /* second digit of voice */ w_voice = (w_voice * 10) + (c - 48); } else { /* first digit of voice */ w_voice = c - 48; } } else { /* part of the whistle key selection */ switch(c) { case 'A': whistle_pitch += 21; break; case 'B': whistle_pitch += 23; break; case 'C': whistle_pitch += 24; break; case 'D': whistle_pitch += 26; break; case 'E': whistle_pitch += 28; break; case 'F': whistle_pitch += 29; break; case 'G': whistle_pitch += 31; break; case 'a': whistle_pitch += 33; break; case 'b': if (whistle_pitch) { /* Assume this is a "flat" character */ whistle_pitch--; } else { /* we haven't specified a note letter yet so this must be "B" */ whistle_pitch += 35; } break; case 'c': whistle_pitch += 36; break; case 'd': whistle_pitch += 38; break; case 'e': whistle_pitch += 40; break; case 'f': whistle_pitch += 41; break; case 'g': whistle_pitch += 43; break; case '\'': case 'h': case 'H': whistle_pitch += 12; break; case ',': case 'l': case 'L': whistle_pitch -= 12; break; case '_': whistle_pitch--; break; case '^': case '#': whistle_pitch++; break; case 9: case ' ': break; /* spaces allowed, avoid default handler */ default: printf("++++ Invalid character <%c> for whistle key.\n", c); return 1; } } m++; } if (nWhistleTab < MAXWHISTLETAB) { WhistleTab_tb[nWhistleTab].voice = w_voice - 1; WhistleTab_tb[nWhistleTab].keypitch = whistle_pitch; nWhistleTab++; } else { printf("++++ Too many whistle tablature selections.\n"); return 1; } break; /* simple flags */ case 'A': select_all=1; break; case 'b': break_continues=1; continue_lines=0; break; case 'C': s_field[nsel]=S_COMPOSER; sel_arg=1; break; case 'c': continue_lines=1; break_continues=0; break; case 'E': epsf=1; break; case 'e': sel_arg=1; break; case 'f': nsel++; strcpy(sel_str[nsel], ""); if (ninf==0) { /* selector before first file */ strcpy(sel_str[nsel],sel_str[nsel-1]); s_field[nsel]=s_field[nsel-1]; } s_field[nsel]=search_field0; sel_arg=0; f_pos=i; break; case 'h': help_me=1; break; case 'i': interactive=1; break; case 'l': landscape=1; break; case 'L': FixEols = TRUE; break; case 'M': music_only=1; break; case 'n': write_history=1; break; case 'P': pretty=2; break; case 'p': pretty=1; break; case 'o': do_mode=DO_OUTPUT; break; case 'N': pagenumbers=1; case 'R': s_field[nsel]=S_RHYTHM; sel_arg=1; break; case 'S': s_field[nsel]=S_SOURCE; sel_arg=1; break; case 'x': include_xrefs=1; break; case '1': one_per_page=1; break; /* flags with parameter.. */ case 'H': case 'a': case 'B': case 'D': case 'd': case 'F': case 'g': case 'j': case 'm': case 'O': case 's': case 'v': case 'V': case 'w': case 'Y': case '#': strcpy(aaa, &av[i][m+1]); if (aaa[0] != '\0' && strchr("glO",c)) { /* no sticky arg */ printf("++++ Incorrect usage of flag -%c\n", c); return 1; } got_value=1; /* check for value */ if (aaa[0] == '\0') { i++; if ((i>=ac) || (av[i][0]=='-')) got_value=0; else strcpy(aaa,av[i]); } if (got_value && strchr("vsYBj#",c)) { /* check num args */ ok=1; for (j=0;j<(int)strlen(aaa);j++) if (!strchr("0123456789.",aaa[j])) ok=0; if (!ok) { printf("++++ Invalid parameter <%s> for flag -%c\n",aaa,c); return 1; } } if (!got_value && (c != 'H')) { /* check value was given */ printf("++++ Missing parameter after flag -%c\n", c); return 1; } switch (c) { case '#': /* set page number */ sscanf (aaa, "%d", &FirstPgNum); FirstPgNum--; break; case 'H': help_me=2; /* Value is optional. */ if (!stricmp(aaa, "cm")) { ParamUnits = 1; } else if (!stricmp(aaa, "pt")) { ParamUnits = 2; } else { ParamUnits = 0; } break; case 'O': /* -O flag */ if (!strcmp(aaa,"=")) { choose_outname=1; } else { getext(aaa, ext); if (strcmp(ext,"ps") && strcmp(ext,"eps") && strcmp(ext,"")) { printf("Wrong extension for output file: %s\n", aaa); return 1; } strcpy(outf, aaa); strext(outf, "ps"); choose_outname=0; } break; case 'a': sscanf (aaa, "%f", &alfa_c); if ((alfa_c>1.05)||(alfa_c<-0.01)) { printf("++++ Bad parameter for flag -a: %s\n", aaa); return 1; } break; case 'B': sscanf(aaa,"%d",&bars_per_line); continue_lines=0; break; case 'j': sscanf(aaa,"%d",&measure_nb); break; case 'v': sscanf(aaa,"%d",&vb); break; case 'V': sscanf(aaa, "%d", &w_voice); if ((w_voice > 0) && (w_voice <= MAXVOICE)) VoiceMute_tb[w_voice-1] = TRUE; break; case 's': sscanf(aaa,"%f",&scalefac); break; case 'd': staffsep = scan_u(aaa); break; case 'w': swidth = scan_u(aaa); break; case 'm': lmargin = scan_u(aaa); break; case 'F': strcpy(styf,aaa); break; case 'D': strcpy(styd,aaa); break; case 'g': if (abbrev(aaa,"shrink", 2)) gmode=G_SHRINK; else if (abbrev(aaa,"stretch",2)) gmode=G_STRETCH; else if (abbrev(aaa,"space", 2)) gmode=G_SPACE; else if (abbrev(aaa,"fill", 2)) gmode=G_FILL; else { printf("++++ Bad parameter for flag -g: %s\n", aaa); return 1; } break; } m = k; /* stop */ break; default: printf("++++ Unknown flag: -%c\n", c); return 1; } m++; } continue; } if (strstr(av[i],".fmt")) /* implict -F */ strcpy(styf, av[i]); else { if (strstr(av[i],".abc") && sel_arg) { /* file if .abc */ nsel++; strcpy(sel_str[nsel], ""); s_field[nsel]=S_TITLE; if (ninf==0) { /* selector before first file */ strcpy(sel_str[nsel],sel_str[nsel-1]); s_field[nsel]=s_field[nsel-1]; } sel_arg=0; } if (is_xrefstr(av[i]) && (!sel_arg)) { /* sel if xref numbers */ if (i-1 != f_pos) sel_arg=1; } if (!sel_arg) { /* this arg is a file name */ if (ninf>=MAXINF) { printf("++++ Too many input files, max is %d\n", MAXINF); return 1; } strcpy(in_file[ninf], av[i]); psel[ninf]=nsel; ninf++; } else { /* this arg is a selector */ strcat(sel_str[nsel], av[i]); strcat(sel_str[nsel], " "); } } } return 0; } /* ----- set_page_format ----- */ int set_page_format() { int i,j; if (pretty==1) set_pretty_format(&cfmt); else if (pretty==2) set_pretty2_format(&cfmt); else set_standard_format(&cfmt); i=read_fmt_file("fonts.fmt", styd, &cfmt); j=0; if (styf[0] != '\0') { strext(styf, "fmt"); j=read_fmt_file(styf, styd, &cfmt); if (j==0) { printf("\n++++ Cannot open file: %s\n", styf); return 0; } strcpy(cfmt.name, styf); } if (i || j) printf("\n"); ops_into_fmt(&cfmt); make_font_list(&cfmt); sfmt=cfmt; dfmt=cfmt; return 1; } /* ----- tex_str: change string to take care of some tex-style codes --- */ /* Puts \ in front of ( and ) in case brackets are not balanced, interprets some TeX-type strings using ISOLatin1 encodings. Returns the length of the string as finally given out on paper. Also returns an estimate of the string width... */ int tex_str(char *c, char s[], float *wid) { int base,add,n; char t[21]; float w; s[0] = '\0'; n=0; w=0; while (*c != '\0') { switch (*c) { case '(': case ')': /* ( ) becomes \( \) */ sprintf(t, "\\%c", *c); strcat(s,t); w+=cwid('('); n++; break; case '\\': /* backslash sequences */ c++; if (*c=='\0') break; add=0; /* accented vowels */ switch (*c) { case '`': add=1; break; case '\'': add=2; break; case '^': add=3; break; case '"': add=4; break; } if (add) { c++; base=0; switch (*c) { case 'a': base=340; if (add==4) add=5; break; case 'e': base=350; break; case 'i': base=354; case 'o': base=362; if (add==4) add=5; break; case 'u': base=371; break; case 'A': base=300; if (add==4) add=5; break; case 'E': base=310; break; case 'I': base=314; break; case 'O': base=322; if (add==4) add=5; break; case 'U': base=331; break; } w+=cwid(*c); if (base) sprintf(t,"\\%d",base+add-1); else sprintf(t,"%c",*c); strcat(s,t); n++; } else if (*c==' ') /* \-space */ { strcat(s," "); w+=cwid(' '); n++; } else if (*c=='O') /* O-slash */ { strcat(s,"\\330"); w+=cwid('O'); n++; } else if (*c=='o') /* o-slash */ { strcat(s,"\\370"); w+=cwid('O'); n++; } else if((*c=='s')&&(*(c+1)=='s')) /* sz */ { c++; strcat(s,"\\337"); w+=cwid('s'); n++; } else if((*c=='a')&&(*(c+1)=='a')) /* a-ring */ { c++; strcat(s,"\\345"); w+=cwid('a'); n++; } else if((*c=='A')&&(*(c+1)=='A')) /* A-ring */ { c++; strcat(s,"\\305"); w+=cwid('A'); n++; } else if((*c=='a')&&(*(c+1)=='e')) /* ae */ { c++; strcat(s,"\\346"); w+=(float)1.5*cwid('a'); n++; } else if((*c=='A')&&(*(c+1)=='E')) /* AE */ { c++; strcat(s,"\\306"); w+=(float)1.5*cwid('A'); n++; } else if (*c=='c') { /* c-cedilla */ c++; w+=cwid(*c); if (*c=='C') { strcat(s,"\\307"); n++; } else if (*c=='c') { strcat(s,"\\347"); n++; } else {sprintf(t,"%c",*c); strcat(s,t); n++; } } else if (*c=='~') { /* n-twiddle */ c++; w+=cwid(*c); if (*c=='N') { strcat(s,"\\321"); n++; } else if (*c=='n') { strcat(s,"\\361"); n++; } else { sprintf(t,"%c",*c); strcat(s,t); n++; } } else /* \-something-else; pass through */ {sprintf(t,"\\%c",*c); strcat(s,t); w+=cwid('A'); n++; } break; case '{': case '}': break; default: /* other characters: pass though */ sprintf(t,"%c",*c); strcat(s,t); n++; w+=cwid(*c); } c++; } *wid = w; return n; } /* ----- put_str: output a string in postscript ----- */ static void put_str(char *str) { char s[801]; float w; tex_str(str, s, &w); PUT1("%s", s); } /* ----- set_font ----- */ void set_font(struct FONTSPEC font, int add_bracket) { int fnum; for (fnum = nfontnames; fnum >= 0; fnum--) { if (!strcmp(font.name,fontnames[fnum])) break; } if (fnum<0) { printf("\n++++ Font \"%s\" not predefined; using first in list\n", font.name); fnum=0; } PUT2(" %.1f F%d ", font.size, fnum); if (add_bracket) PUT0("("); } /* ----- set_font_str ----- */ static void set_font_str(char str[], struct FONTSPEC font) { int fnum; for (fnum = nfontnames; fnum >= 0; fnum--) { if (!strcmp(font.name,fontnames[fnum])) break; } sprintf(str,"%.1f F%d ", font.size, fnum); } /* ----- check_margin: do horizontal shift if needed ---- */ void check_margin(float new_posx) { float dif; dif=new_posx-posx; if (dif*dif<0.001) return; PUT1("%.2f 0 T\n", dif); posx=new_posx; } /* ----- epsf_title ------ */ void epsf_title(char title[], char fnm[]) { char *p,*q; p=title; q=fnm; while (*p != '\0') { if (*p == ' ') *q++ = '_'; else *q++ = *p; p++; } *q = '\0'; } /* ----- close_output_file ------ */ void close_output_file() { int m; if (!file_open) return; if (interactive) printf("(close %s)\n", outfnam); close_page(fout); close_ps(fout); fclose(fout); if (tunenum==0) printf("++++ Warning: no tunes written to output file\n"); m=get_file_size (outfnam); printf("Output written on %s (%d page%s, %d title%s, %d byte%s)\n", outfnam, pagenum, pagenum==1 ? "" : "s", tunenum, tunenum==1 ? "" : "s", m, m==1 ? "" : "s"); file_open=0; file_initialized=0; } /* ----- open_output_file ------ */ void open_output_file(char fnam[], char tstr[]) { if (!strcmp(fnam,outfnam)) return; if (file_open) close_output_file(); if (interactive) printf("(open %s)\n", fnam); strcpy(outfnam, fnam); if ((fout = fopen(outfnam,"w")) == NULL) rx("Cannot open output file ", outf); tunenum=tnum1=tnum2=0; pagenum=0; file_open=1; file_initialized=0; } /* ----- add_to_text_block ----- */ void add_to_text_block(char ln[], int add_final_nl) { char *c,*a; char word[81]; int nt,nl; nt=ntxt; c=ln; for (;;) { while (*c==' ') c++; if (*c=='\0') break; a=word; nl=0; while ((*c!=' ')&&(*c!='\0')&&(*c!='\n')) { if ((*c=='\\')&&(*(c+1)=='\\')) { nl=1; c+=2; break; } *a++=*c++; } *a='\0'; if (strlen(word)>MAXWLEN) { word[MAXWLEN-1]='\0'; printf("++++ Insanely long word truncated to %d chars: %s\n", MAXWLEN-1,word); } if (nt>=MAXNTEXT) { printf("\n++++ Line %d: %s\n", linenum,ln); rx("Text overflow; increase MAXNTEXT and recompile.",""); } if (word[0] != '\0') { strcpy(txt[nt],word); nt++; } if (nl) { strcpy(txt[nt],"$$NL$$"); nt++; } } if (add_final_nl) { strcpy(txt[nt],"$$NL$$"); nt++; } ntxt=nt; } /* ----- write_text_block ----- */ void write_text_block(FILE *fp, int job) { int i,i1,i2,ntline,nc,mc,nbreak; float textwidth,ftline,ftline0,swfac,baseskip,parskip; float wwidth,wtot,spw; char str[81]; if (ntxt<=0) return; baseskip = cfmt.textfont.size * cfmt.lineskipfac; parskip = cfmt.textfont.size * cfmt.parskipfac; set_font_str(page_init,cfmt.textfont); /* estimate text widths.. ok for T-R, wild guess for other fonts */ swfac=(float)1.0; if (strstr(cfmt.textfont.name,"Times-Roman")) swfac=(float)1.00; else if (strstr(cfmt.textfont.name,"Times-Bold")) swfac=(float)1.05; else if (strstr(cfmt.textfont.name,"Helvetica")) swfac=(float)1.10; else if (strstr(cfmt.textfont.name,"Helvetica-Bold")) swfac=(float)1.15; else if (strstr(cfmt.textfont.name,"Palatino")) swfac=(float)1.10; /* swfac=1.0; ??*/ spw=cwid(' '); PUT1("/LF {0 %.1f rmoveto} bind def\n",-baseskip); /* output by pieces, separate at newline token */ i1=0; while (i1=i1;i--) { mc+=tex_str(txt[i],str,&wwidth)+1; wtot+=wwidth+spw; nc+=strlen(str)+2; if (nc>=72) { nc=0; PUT0("\n"); } PUT1 ("(%s)",str); } if (job==RAGGED) PUT1(" %.1f P1\n",cfmt.staffwidth); else PUT1(" %.1f P2\n",cfmt.staffwidth); /* first estimate: (total textwidth)/(available width) */ textwidth=wtot*swfac*cfmt.textfont.size; if (strstr(cfmt.textfont.name,"Courier")) textwidth=(float)0.60*mc*cfmt.textfont.size; ftline0=textwidth/cfmt.staffwidth; /* revised estimate: assume some chars lost at each line end */ nbreak=(int)ftline0; textwidth=textwidth+5*nbreak*cwid('a')*swfac*cfmt.textfont.size; ftline=textwidth/cfmt.staffwidth; ntline=(int)ftline+1; if (vb>=10) { printf("first estimate %.2f, revised %.2f\n", ftline0,ftline); printf("Output %d word%s, about %.2f lines (fac %.2f)\n", i2-i1, i2-i1==1?"":"s", ftline,swfac); } bskip((ntline-1)*baseskip); } buffer_eob(fp); /* next line to allow pagebreak after each text "line" */ /* if (!epsf && !within_tune) write_buffer(fp); */ i1=i2+1; } bskip(parskip); buffer_eob(fp); /* next line to allow pagebreak after each paragraph */ if (!epsf && !within_tune) write_buffer(fp); page_init[0] = '\0'; } /* ----- put_words ------- */ void put_words(FILE *fp) { int i,nw,n; char str[81]; char *p,*q; set_font(cfmt.wordsfont, 0); set_font_str(page_init,cfmt.wordsfont); n=0; for (i=0;i0) { if (str[0] != '\0') { PUT0("45 0 M ("); put_str(str); PUT0(") lshow\n"); } if (*p != '\0') { PUT0("50 0 M ("); put_str(p); PUT0(") show\n"); } } } } buffer_eob(fp); page_init[0] = '\0'; } /* ----- put_text ------- */ void put_text(FILE *fp, int type, char str[]) { int i,n; float baseskip,parskip; n=0; for (i=0;i15) printf("write inside title <%s>\n", t); if (cfmt.titlecaps) cap_str(t); PUT0(" ("); put_str(t); if (cfmt.titleleft) PUT0(") 0 0 M show\n"); else PUT1(") %.1f 0 M cshow\n", cfmt.staffwidth/2); bskip(cfmt.musicspace+(float)0.2*CM); } /* ----- write_tunetop ----- */ void write_tunetop(void) { PUT2("\n\n%% --- tune %d %s\n", tunenum+1, info.title); if (!epsf) bskip(cfmt.topspace); } /* ----- write_tempo ----- */ static void write_tempo(char tempo[]) { char *r, *q; char text[STRL]; int top,bot,value,len,i; struct SYMBOL s; float stem,dotx,doty,sc,dx; if (!cfmt.ShowTempo) return; p0 = tempo; /* (for syntax error) */ if (vb>15) printf("write tempo <%s>\n", info.tempo); r=tempo; q=text; set_font(cfmt.tempofont,0); /* Skip leading spaces, changed to skip all whitespace. */ while (isspace(*r)) r++; if (*r == '"') { r++; while ((*r != '"') && (*r != '\0')) *q++ = *r++; if (*r == '"') r++; } *q = '\0'; while (isspace(*r)) r++; PUT0(" 25 0 M "); /* draw the string, if specified */ if (text[0] != '\0') { PUT0(" ("); put_str(text); PUT0(" ) show\n"); } /* draw the tempo indication, if specified */ if (*r != '\0') { if (*r=='C' || *r=='c') { p=r+1; len=parse_length(); if (*p!='=') printf("\n++++ invalid tempo specifier: %s\n", r); sscanf(p,"=%d", &value); } else if (isdigit(*r)) { if (sscanf(r,"%d/%d=%d", &top,&bot,&value) == 3) len=(BASE*top)/bot; else { value = atoi(r); p = &tempo[strlen(tempo)]; /* empty string */ len = parse_length(); /* get default length */ } } else { printf("\n++++ invalid tempo specifier: %s\n", r); len = EIGHTH; } s.len=len; identify_note(&s,r); /* draw the note */ sc=(float)0.55*cfmt.tempofont.size/(float)10.0; PUT2("gsave %.2f %.2f scale 15 3 rmoveto currentpoint\n", sc,sc); if (s.head==H_OVAL) PUT0("HD"); else if (s.head==H_EMPTY) PUT0("Hd"); else if (s.head==H_FULL) PUT0("hd"); dx=4.0; if (s.dots) { dotx=8; doty=0; if (s.flags>0) dotx += 4; if (s.head==H_EMPTY) dotx += 1; else if (s.head==H_OVAL) dotx += 2; for (i=0;i0) PUT2(" %.1f f%du",stem,s.flags); if ((s.flags>0) && (dx<6.0)) dx=6.0; dx=(dx+18)*sc; PUT2(" grestore %.2f 0 rmoveto ( = %d) show\n", dx,value); } } /* ----- write_inside_tempo ----- */ void write_inside_tempo(void) { bskip(cfmt.partsfont.size); write_tempo(info.tempo); bskip((float)0.1*CM); } /* ----- write_heading ----- */ void write_heading(void) { float lwidth,down1,down2; int i,ncl; char t[201], tbuff[256], *tptr; lwidth=cfmt.staffwidth; /* write the main title */ bskip(cfmt.titlefont.size+cfmt.titlespace); set_font(cfmt.titlefont,1); if (cfmt.withxrefs) PUT1("%d. ", xrefnum); strcpy(t,info.title); if (cfmt.titlecaps) cap_str(t); put_str(t); if (cfmt.titleleft) PUT0(") 0 0 M show\n"); else PUT1(") %.1f 0 M cshow\n", lwidth/2); /* write second title */ if (numtitle>=2) { bskip(cfmt.subtitlespace+cfmt.subtitlefont.size); set_font(cfmt.subtitlefont,1); strcpy(t,info.title2); if (cfmt.titlecaps) cap_str(t); put_str(t); if (cfmt.titleleft) PUT0(") 0 0 M show\n"); else PUT1(") %.1f 0 M cshow\n", lwidth/2); } /* write third title */ if (numtitle>=3) { bskip(cfmt.subtitlespace+cfmt.subtitlefont.size); set_font(cfmt.subtitlefont,1); strcpy(t,info.title3); if (cfmt.titlecaps) cap_str(t); put_str(t); if (cfmt.titleleft) PUT0(") 0 0 M show\n"); else PUT1(") %.1f 0 M cshow\n", lwidth/2); } /* write composer, origin */ /* Changed to split composer to left/right side of page when it */ /* contains a forward slash "/" and drops the origin to the */ /* next line (if present). If there is not either an embedded */ /* slash or exactly two composer entries, the behaviour remains */ /* unchanged from abcm2ps. [JSA] */ set_font(cfmt.composerfont,0); bskip(cfmt.composerspace); down1=cfmt.composerspace + cfmt.musicspace + cfmt.composerfont.size; if (info.ncomp == 2) { // Put the first comp on the left and the second on the right. bskip(cfmt.composerfont.size); PUT0("0 0 M ("); put_str(info.comp[0]); PUT0(") show\n"); PUT1("%.1f 0 M (", lwidth); put_str(info.comp[1]); PUT0(") lshow\n"); } else if ((info.ncomp == 1) && (strchr(info.comp[0], '/'))) { // Divide the string into left and right sections at the '/' strncpy(tbuff, info.comp[0], 255); tptr = strchr(tbuff, '/'); if (tptr) *tptr = 0; bskip(cfmt.composerfont.size); PUT0("0 0 M ("); put_str(tbuff); PUT0(") show\n"); if (tptr) { tptr++; if (*tptr) { PUT1("%.1f 0 M (", lwidth); put_str(tptr); PUT0(") lshow\n"); } } } else if ((info.ncomp>0) || info.orig[0] != '\0') { ncl=info.ncomp; if (info.orig[0] != '\0' && ncl < 1) ncl=1; for (i=0;idown1) bskip(down2-down1); /* write tempo and parts */ if (info.parts[0] != '\0' || info.tempo[0] != '\0') { if (info.tempo[0] != '\0') { bskip((float)-0.3*CM); write_tempo(info.tempo); bskip(-cfmt.tempofont.size); } if (info.parts[0] != '\0') { bskip(-cfmt.partsspace); set_font(cfmt.partsfont,0); PUT0("0 0 M ("); put_str(info.parts); PUT0(") show\n"); bskip(cfmt.partsspace); } if (info.tempo[0] != '\0') bskip(cfmt.tempofont.size+(float)0.3*CM); } } /* ----- write_parts ----- */ void write_parts(void) { if (info.parts[0] != '\0') { bskip(cfmt.partsfont.size); set_font(cfmt.partsfont,0); PUT0("0 0 M ("); put_str(info.parts); PUT0(") show\n"); bskip(cfmt.partsspace); } }