2 /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
3 /* All rights reserved */
5 /* functions related to characters: information about the size and shape of
6 * characters to be printed, to initialize the internal tables
7 * to tell how big each character is, etc.
16 /* code for invalid music character */
17 #define BAD_CHAR '\377'
18 /* code for invalid number in user input string */
19 #define BAD_NUMBER -30000
21 /* machine-generated sorted list for translating
22 * music character names to internal code numbers. */
23 /* The +1 is because there is an "end-of-list" entry with charname == 0 */
24 extern struct SPECCHAR Mus_char_table
[NUM_MFONTS
][CHARS_IN_FONT
+1];
26 extern struct SPECCHAR Ext_char_table
[];
30 /* save information about characters in string as we go, in order to be
31 * able to backspace back over them */
32 struct BACKSPACEINFO
{
39 extern char *bsearch(); /* binary search library function */
43 /* static functions */
44 static char *get_font
P((char *string
, int *font_p
, int prev_font
, char *fname
,
46 static char *get_num
P((char *string
, int *num_p
));
47 static int sc_compare
P((const void *item1_p
, const void *item2_p
));
49 static unsigned char ext_name2num
P((char *name
));
51 static int starts_piled
P((char *string
, int *font_p
, int *size_p
,
52 char **pile_start_p_p
));
53 static int str_cmd
P((char *str
, int *size_p
, int *in_pile_p
));
54 static int get_accidental
P((unsigned char *str
, char *accidental_p
,
55 int *acc_size_p
, int trans_natural
, int *escaped_p
));
56 static int add_accidental
P((char *buff
, int acc_character
, int acc_size
,
58 static int dim_tri
P((unsigned char *str
, char *replacement
,
59 int size
, int is_chord
));
60 static int smallsize
P((int size
));
61 static int accsize
P((int size
));
64 /* return the height (in inches) of a character of specified font and size */
67 height(font
, size
, ch
)
71 int ch
; /* which character */
78 /* control characters have no height */
79 if (chval
< FIRST_CHAR
) {
83 return((Fontinfo
[ font_index(font
) ].ch_height
[ CHAR_INDEX(chval
) ]
84 / FONTFACTOR
) * ((float)size
/ (float)DFLT_SIZE
) );
88 /* return the width (in inches) of a character of specified font and size */
95 int ch
; /* which character */
102 /* control characters have no width */
103 if (chval
< FIRST_CHAR
) {
107 return((Fontinfo
[ font_index(font
) ].ch_width
[ CHAR_INDEX(chval
) ]
108 / FONTFACTOR
) * ((float)size
/ (float)DFLT_SIZE
) );
112 /* return the ascent (in inches) of a character of specified font and size */
115 ascent(font
, size
, ch
)
119 int ch
; /* which character */
126 /* control characters have no ascent */
127 if (chval
< FIRST_CHAR
) {
131 return((Fontinfo
[ font_index(font
) ].ch_ascent
[ CHAR_INDEX(chval
) ]
132 / FONTFACTOR
) * ((float) size
/ (float)DFLT_SIZE
) );
136 /* return the descent (in inches) of a character of specified font and size */
139 descent(font
, size
, ch
)
143 int ch
; /* which character */
146 return ( height(font
, size
, ch
) - ascent(font
, size
, ch
) );
150 /* given a user input string, normalize it. This means:
151 * Put the default font in [0] and default size in [1] of the string.
152 * Change backslashed things to internal format. Each starts with a
153 * hyper-ASCII code byte and is followed by one or more data bytes.
154 * Note that in all cases in internal format is no longer than the
156 * Change any \f(XX) to STR_FONT font_number
157 * Change any \s(NN) to STR_SIZE actual_size
158 * Note that NN might have a sign to indicate relative size change.
159 * Change any \v(NN) to STR_VERTICAL vertical_offset
160 * Note that NN might have a sign to indicate direction,
161 * negative means downward.
162 * Change any \/ to STR_SLASH
163 * Change any \| to STR_L_ALIGN (piled mode only)
164 * Change any \^ to STR_C_ALIGN (piled mode only)
165 * Change any backslashed space to space when in piled mode
166 * Change any space to newline while in piled mode
167 * Change \(xxxx) to STR_MUS_CHAR size mus_char_code
168 * Change \% to STR_PAGENUM %
169 * Change \# to STR_NUMPAGES #
170 * Change \n to newline
171 * Change \b to STR_BACKSPACE n
172 * where n is how much to back up for the
173 * default size, in BACKSP_FACTORths of an inch
174 * Change backslashed backslash or double quote to just be themselves.
175 * Reject any other control characters or illegal backslash escapes.
176 * The string is null-terminated.
178 * The normalized string is put back into the original string buffer
179 * that was passed in, and a pointer to it is returned.
181 * Note that some functions in lyrics.c, and prntdata.c
182 * also have knowledge of the escape conventions,
183 * so if these change, check there too. But it is intended
184 * that all the rest of the code gets at strings indirectly
185 * via functions in this file, so the details can be hidden here.
189 fix_string(string
, font
, size
, fname
, lineno
)
191 char *string
; /* original string */
192 int font
; /* default font for string */
193 int size
; /* default size for string */
194 char *fname
; /* file name, for error messages */
195 int lineno
; /* input line number, for error messages */
198 char *tmpbuff
; /* for normalized string */
199 int leng
; /* strlen(string) + 1 */
200 char *inp_p
, *out_p
; /* walk thru orig & normalized string */
201 int nsize
; /* new size */
202 int prevsize
; /* previous size */
203 int msize
; /* size for music character */
204 int vert
; /* argument to \v without sign */
205 int vertval
= 0; /* signed argument to \v */
206 int has_vertical
= NO
; /* YES if \v or pile found */
207 int has_newline
= NO
; /* YES if \n somewhere in string */
209 int align_points
= 0; /* how many aligments points found */
210 int error
; /* YES if have found an error */
211 char spec_name
[100], *sc_p
; /* name of special music character, or
212 * extended character set character */
213 unsigned char extchar
; /* value for extended character */
214 unsigned char muschar
; /* value for music character */
215 int now_font
; /* current font */
216 int newfont
; /* proposed new font */
217 int prevfont
; /* previous font */
218 int mfont
; /* music font */
219 struct BACKSPACEINFO
*backspaceinfo
; /* font/size for backspacing */
220 int backspaceindex
= 0; /* index into backspaceinfo */
221 float backup
; /* backspace distance in inches
222 * for default size */
223 int backupval
; /* value to store for backspace
227 /* fill in default font and size */
228 string
[0] = (char) font
;
229 if (rangecheck(size
, MINSIZE
, MAXSIZE
, "size") == NO
) {
232 string
[1] = (char) size
;
233 now_font
= prevfont
= font
;
236 leng
= strlen(string
) + 1;
237 MALLOCA(char, tmpbuff
, leng
);
238 MALLOC(BACKSPACEINFO
, backspaceinfo
, leng
);
239 /* walk through incoming string, creating normalized string */
240 for (error
= NO
, out_p
= tmpbuff
+ 2, inp_p
= string
+ 2;
241 (error
== NO
) && (*inp_p
!= '\0');
244 /* handle backslashed stuff */
245 if (*inp_p
== '\\') {
247 /* skip past the backslash */
253 /* ignore the backslashed newline */
258 if (*(inp_p
) == '\n') {
266 inp_p
= get_font(++inp_p
, &newfont
, prevfont
,
268 if (newfont
== FONT_UNKNOWN
) {
272 *out_p
++ = (char) STR_FONT
;
273 *out_p
= (char) newfont
;
281 if (*++inp_p
== '(') {
284 inp_p
= get_num(++inp_p
, &nsize
);
290 inp_p
= get_num(++inp_p
, &nsize
);
292 nsize
= size
- nsize
;
296 if (strncmp(inp_p
, "PV)", 3) == 0) {
305 if (strncmp(inp_p
, "previous)", 9) == 0) {
314 inp_p
= get_num(inp_p
, &nsize
);
322 /* if got valid size, store it */
323 if (nsize
== BAD_NUMBER
) {
324 l_yyerror(fname
, lineno
,
325 "Invalid format for size value");
328 else if (rangecheck(nsize
, MINSIZE
,
329 MAXSIZE
, "size") == YES
) {
330 *out_p
++ = (char) STR_SIZE
;
331 *out_p
= (char) nsize
;
343 /* vertical motion */
344 if (*++inp_p
== '(') {
347 inp_p
= get_num(++inp_p
, &vert
);
357 inp_p
= get_num(inp_p
, &vert
);
368 if (vert
== BAD_NUMBER
) {
369 l_yyerror(fname
, lineno
,
370 "Invalid format for vertical motion value");
373 else if (rangecheck(vertval
, -100, 100,
374 "vertical") == YES
) {
375 /* if motion is zero, don't even bother
376 * to save it, else do */
378 /* convert percentage to
379 * STR_VERTICAL units */
388 *out_p
++ = (char) STR_VERTICAL
;
389 *out_p
= (char) ENCODE_VERT(
397 /* we don't allow backspacing to something
398 * before a vertical motion--this is almost
407 /* If this begins a pile, and the next thing
408 * in input ends the pile, just ignore them
409 * both to keep things simpler later. */
410 if (pile_mode
== NO
&& *(inp_p
+1) == '\\'
411 && *(inp_p
+2) == ':') {
413 /* no output character */
417 *out_p
= (char) STR_PILE
;
419 pile_mode
= (pile_mode
== YES ? NO
: YES
);
426 if (pile_mode
== NO
) {
427 l_yyerror(fname
, lineno
,
428 "alignment point only allowed in piled mode");
432 else if (++align_points
> 1) {
433 l_yyerror(fname
, lineno
,
434 "only one alignment point allowed per line");
438 else if (*inp_p
== '^') {
440 *out_p
= (char) STR_C_ALIGN
;
441 next_ch
= *(inp_p
+1) & 0xff;
442 /* it's too much trouble to handle
443 * things like font changes between
444 * the \^ and the character that
445 * will be allowed, so disallow them,
446 * since user can easily put them
447 * before the \^ anyway. */
448 if ( (IS_STR_COMMAND(next_ch
)
449 && next_ch
!= STR_MUS_CHAR
)
451 || iscntrl(*(inp_p
+1)) ) {
452 l_yyerror(fname
, lineno
,
453 "\\^ must be followed by normal character");
457 *out_p
= (char) STR_L_ALIGN
;
463 if (pile_mode
== NO
) {
464 l_yyerror(fname
, lineno
,
465 "backslashed space only allowed in piled mode");
471 /* This is only allowed after one
473 if ( inp_p
- string
< 4 ||
474 ! isdigit( *(inp_p
- 2)) ) {
475 l_yyerror(fname
, lineno
,
476 "slash can only be used after digit(s)");
478 *out_p
= (char) STR_SLASH
;
482 /* real backslash or embedded quote, copy it */
483 backspaceinfo
[backspaceindex
].code
= *inp_p
;
484 backspaceinfo
[backspaceindex
++].font
490 /* special music character or extended
491 * character set character */
492 /* make copy of name */
493 for ( sc_p
= spec_name
, inp_p
++;
494 *inp_p
!= ')' && *inp_p
!= '\0';
501 /* first see if it is a character in the
502 * extended character set */
503 if ((extchar
= ext_name2num(spec_name
))
504 != (unsigned char) BAD_CHAR
) {
505 /* temporarily change to the extended
506 * character set font that corresponds
507 * to the current normal ASCII font,
508 * and output the extended character
509 * set code for the desired character.
510 * Then go back to original font */
511 *out_p
++ = (char) STR_FONT
;
513 (now_font
+ EXT_FONT_OFFSET
);
515 *out_p
++ = (char) STR_FONT
;
516 *out_p
= (char) now_font
;
517 backspaceinfo
[backspaceindex
].code
519 backspaceinfo
[backspaceindex
++].font
520 = now_font
+ EXT_FONT_OFFSET
;
522 /* mark that this extended character
523 * set font has been used */
524 Font_used
[now_font
+ EXT_FONT_OFFSET
] = YES
;
529 /* look up music character with this name */
531 if ((muschar
= mc_name2num(spec_name
, fname
,
532 lineno
, &msize
, &mfont
))
533 != (unsigned char) BAD_CHAR
) {
534 *out_p
++ = (char) mfont2str(mfont
);
535 *out_p
++ = (char) msize
;
537 backspaceinfo
[backspaceindex
].code
539 backspaceinfo
[backspaceindex
++].font
545 /* start of boxed text. We only allow this at
546 * the beginning of a string */
547 if (inp_p
!= string
+ 3) {
548 l_yyerror(fname
, lineno
,
549 "\\[ only allowed at beginning of string");
553 *out_p
= (char) STR_BOX
;
558 /* end of boxed text. Only allowed at end of
559 * string, and only if the string began
560 * with a box start. */
561 if (*(inp_p
+ 1) != '\0') {
562 l_yyerror(fname
, lineno
,
563 "\\] only allowed at end of string");
566 else if (IS_BOXED(tmpbuff
) == NO
) {
567 l_yyerror(fname
, lineno
,
568 "no matching \\[ for \\]");
572 *out_p
= (char) STR_BOX_END
;
577 /* start of circled text. We only allow this at
578 * the beginning of a string */
579 if (inp_p
!= string
+ 3) {
580 l_yyerror(fname
, lineno
,
581 "\\{ only allowed at beginning of string");
585 *out_p
= (char) STR_CIR
;
590 /* end of circled text. Only allowed at end of
591 * string, and only if the string began
592 * with a circle start. */
593 if (*(inp_p
+ 1) != '\0') {
594 l_yyerror(fname
, lineno
,
595 "\\} only allowed at end of string");
598 else if (IS_CIRCLED(tmpbuff
) == NO
) {
599 l_yyerror(fname
, lineno
,
600 "no matching \\{ for \\}");
604 *out_p
= (char) STR_CIR_END
;
609 /* too hard to deal with inside a pile... */
610 if (pile_mode
== YES
) {
611 l_yyerror(fname
, lineno
,
612 "\\%c not allowed inside a pile\n", '%');
615 /* page number -- change to STR_PAGENUM-% */
616 *out_p
++ = (char) STR_PAGENUM
;
618 /* we really don't know at this point how far
619 * to backspace over pagenum because we don't
620 * know yet how many digits it is, etc, so we
621 * punt and just use the % character
623 backspaceinfo
[backspaceindex
].code
= '%';
624 backspaceinfo
[backspaceindex
++].font
629 /* code basically the same as for % */
630 if (pile_mode
== YES
) {
631 l_yyerror(fname
, lineno
,
632 "\\# not allowed inside a pile\n");
635 /* number of pages -- change to STR_NUMPAGES-# */
636 *out_p
++ = (char) STR_NUMPAGES
;
638 /* We really don't know at this point how far
639 * to backspace, because we don't know yet
640 * how many digits it is, etc, so we punt
641 * and just use the # character for width. */
642 backspaceinfo
[backspaceindex
].code
= '#';
643 backspaceinfo
[backspaceindex
++].font
650 /* can't back up to previous line */
656 /* can't back up past beginning of string */
657 if (backspaceindex
== 0) {
658 if (has_newline
== YES
|| has_vertical
== YES
) {
659 l_yyerror(fname
, lineno
,
660 "can't backspace before newline or vertical motion");
663 l_yyerror(fname
, lineno
,
664 "can't backspace before beginning of line");
670 backup
= width(backspaceinfo
671 [backspaceindex
].font
,
672 DFLT_SIZE
, backspaceinfo
673 [backspaceindex
].code
);
674 *out_p
++ = (char) STR_BACKSPACE
;
675 /* calculate backup value to store */
676 backupval
= (int) (backup
* BACKSP_FACTOR
);
680 else if (backupval
> 127) {
683 *out_p
= (char) backupval
;
688 yyerror("illegal \\ escape");
694 else if (iscntrl(*inp_p
) ) {
695 if (*inp_p
== '\n') {
700 else if (*inp_p
== '\r' && *(inp_p
+1) == '\n') {
701 /* ignore DOS's extraneous \r */
705 /* We don't support any other control
706 * characters, but just convert others to
707 * space and continue. That way user at least
708 * gets something. Tab is something user may
709 * expect to work, so we give a more clear
710 * and specific error for that.
712 l_warning(fname
, lineno
,
713 "unsupported control character '\\0%o' %sin string replaced with space",
714 *inp_p
, *inp_p
=='\t' ?
"(tab) ": "");
718 else if (pile_mode
== YES
&& *inp_p
== ' ') {
719 /* in piled mode, space means move down for next
727 /* normal character -- copy as is */
729 backspaceinfo
[backspaceindex
].code
= *inp_p
;
730 backspaceinfo
[backspaceindex
++].font
= (char) now_font
;
733 /* If we got an error, we would not have put anything into the
734 * final output position before incrementing out_p in the 'for' loop,
735 * so compensate, so we don't leave a garbage character. */
741 if (error
== NO
&& IS_BOXED(tmpbuff
) == YES
&&
742 (*(out_p
- 1) & 0xff) != (STR_BOX_END
& 0xff)) {
743 l_yyerror(fname
, lineno
, "no matching \\] for \\[");
746 if (error
== NO
&& IS_CIRCLED(tmpbuff
) == YES
&&
747 (*(out_p
- 1) & 0xff) != (STR_CIR_END
& 0xff)) {
748 l_yyerror(fname
, lineno
, "no matching \\} for \\{");
751 /* to keep things simple, we don't allow
752 * mixing newlines with vertical motion */
753 if (has_vertical
== YES
&& has_newline
== YES
) {
754 l_yyerror(fname
, lineno
,
755 "can't have newline in same string with vertical motion or alignment");
758 /* now copy normalized string back onto original */
759 (void) strcpy(string
+ 2, tmpbuff
+ 2);
766 /* given pointer into a string, read a font name exclosed in parentheses.
767 * Return the corresponding font number, or
768 * FONT_UNKNOWN if name is invalid. Return pointer to last character
769 * processed in string */
772 get_font(string
, font_p
, prev_font
, fname
, lineno
)
774 char *string
; /* get font from this string */
775 int *font_p
; /* return new font via this pointer */
776 int prev_font
; /* previous font */
777 char *fname
; /* file name for errors */
778 int lineno
; /* line number, for error messages */
781 char fontname
[BUFSIZ
];
782 int font
= FONT_UNKNOWN
;
783 char *endparen
; /* where ')' is in string */
784 int length
; /* of font name */
787 if (*string
== '(') {
789 if ((endparen
= strchr(string
, ')')) != (char *) 0) {
790 length
= endparen
- string
;
791 (void) strncpy(fontname
, string
, (unsigned) length
);
792 fontname
[length
] = '\0';
794 if (strcmp(fontname
, "PV") == 0
795 || strcmp(fontname
, "previous") == 0) {
796 /* special case of "previous" font */
800 font
= lookup_font(fontname
);
806 if (font
== FONT_UNKNOWN
) {
807 l_yyerror(fname
, lineno
, "unknown font specified");
813 /* given a pointer into a string, get a number followed by close parenthesis.
814 * Return the number via pointer, or BAD_NUMBER on error.
815 * Return pointer to the last character processed
816 * in the incoming string */
819 get_num(string
, num_p
)
821 char *string
; /* get number from this string */
822 int *num_p
; /* return number via this pointer, or -1 on error */
825 if (isdigit(*string
)) {
826 *num_p
= strtol(string
, &string
, 10);
827 if (*string
!= ')') {
838 /* compare the charname fields of 2 SPECCHAR structs and return
839 * their proper order, for comparison by bsearch() */
842 sc_compare(item1_p
, item2_p
)
845 const void *item1_p
; /* there are really struct SPECCHAR *, but bsearch
846 * passes them as char * and we have to
847 * cast appropriately */
850 char *item1_p
; /* there are really struct SPECCHAR *, but bsearch passes them
851 * as char * and we have to cast appropriately */
856 return(strcmp( ((struct SPECCHAR
*)(item1_p
))->charname
,
857 ((struct SPECCHAR
*)(item2_p
))->charname
));
861 /* given the name of a music character, return its code number.
862 * If the name is not a valid name, return BAD_CHAR.
863 * Just do a binary search in the name-to-code translation table.
867 mc_name2num(name
, fname
, lineno
, size_p
, font_p
)
869 char *name
; /* name for a music character */
870 char *fname
; /* file name for error messages */
871 int lineno
; /* input line number for error messages */
872 int *size_p
; /* points to current size, proper size for music character
873 * is returned through here */
874 int *font_p
; /* FONT_MUSIC* is returned here */
877 struct SPECCHAR
*info_p
;/* translation entry for the given name */
878 struct SPECCHAR key
; /* what to look for */
879 int f
; /* font index */
880 static unsigned int numch
[NUM_MFONTS
]; /* how many items in font */
883 /* first time through, find size of name-to-code table */
885 for (f
= 0; f
< NUM_MFONTS
; f
++) {
886 for ( ; Mus_char_table
[f
][numch
[f
]].charname
!= (char *)0;
893 /* check for "small" characters */
894 if (name
[0] == 's' && name
[1] == 'm') {
895 key
.charname
= name
+ 2;
896 *size_p
= smallsize(*size_p
);
902 /* do binary search for code */
903 for (f
= 0; f
< NUM_MFONTS
; f
++) {
904 if ((info_p
= (struct SPECCHAR
*) bsearch((char *) &key
, Mus_char_table
[f
],
905 numch
[f
], sizeof(struct SPECCHAR
), sc_compare
))
906 != (struct SPECCHAR
*) 0) {
907 *font_p
= FONT_MUSIC
+ f
;
908 return( (unsigned char) info_p
->code
);
912 l_yyerror(fname
, lineno
, "unknown music character '%s'", name
);
913 *font_p
= FONT_MUSIC
;
914 return( (unsigned char) BAD_CHAR
);
919 /* given the name of an extended character set character,
920 * return its code number.
921 * If the name is not a valid name, return BAD_CHAR.
922 * Just do a binary search in the name-to-code translation table.
928 char *name
; /* name for an extended character set character */
931 struct SPECCHAR
*info_p
;/* translation entry for the given name */
932 struct SPECCHAR key
; /* what to look for */
933 static unsigned int numch
= 0; /* how many items in xlation table */
934 char shortcut
[12]; /* full name of shortcutted character */
937 /* find size of name-to-code table */
939 for ( ; Ext_char_table
[numch
].charname
!= (char *) 0;
947 /* allow some shortcuts for common diacritical marks. A letter
948 * followed by one of '`^~:/,vo represents acute, grave, circumflex,
949 * tilde, dieresis, slash, cedilla, caron, and ring.
950 * And as a special case, ss represents germandbls */
951 if (strlen(name
) == 2 && isalpha(name
[0])) {
954 (void) sprintf(shortcut
, "%cacute", name
[0]);
955 key
.charname
= shortcut
;
958 (void) sprintf(shortcut
, "%cgrave", name
[0]);
959 key
.charname
= shortcut
;
962 (void) sprintf(shortcut
, "%ccircumflex", name
[0]);
963 key
.charname
= shortcut
;
966 (void) sprintf(shortcut
, "%ctilde", name
[0]);
967 key
.charname
= shortcut
;
970 (void) sprintf(shortcut
, "%cdieresis", name
[0]);
971 key
.charname
= shortcut
;
974 (void) sprintf(shortcut
, "%cslash", name
[0]);
975 key
.charname
= shortcut
;
978 (void) sprintf(shortcut
, "%ccedilla", name
[0]);
979 key
.charname
= shortcut
;
982 (void) sprintf(shortcut
, "%ccaron", name
[0]);
983 key
.charname
= shortcut
;
986 (void) sprintf(shortcut
, "%cring", name
[0]);
987 key
.charname
= shortcut
;
990 if (name
[0] == 's') {
991 (void) sprintf(shortcut
, "germandbls");
992 key
.charname
= shortcut
;
996 /* not a special shortcut, leave as is */
1000 /* Some more special case shortcuts: `` and '' are shortcuts for
1001 * quotedblleft and quotedblright, and << and >> for guillemots */
1002 if (strcmp(name
, "``") == 0) {
1003 key
.charname
= "quotedblleft";
1005 else if (strcmp(name
, "''") == 0) {
1006 key
.charname
= "quotedblright";
1008 else if (strcmp(name
, "<<") == 0) {
1009 key
.charname
= "guillemotleft";
1011 else if (strcmp(name
, ">>") == 0) {
1012 key
.charname
= "guillemotright";
1015 /* do binary search for code */
1016 if ((info_p
= (struct SPECCHAR
*) bsearch((char *) &key
, Ext_char_table
,
1017 numch
, sizeof(struct SPECCHAR
), sc_compare
))
1018 != (struct SPECCHAR
*) 0) {
1019 return( (unsigned char) info_p
->code
);
1023 /* don't do error message here, because it could just be a
1024 * music character rather than an extended character set char */
1025 return( (unsigned char) BAD_CHAR
);
1031 /* given the C_XXX code value for a music character, return the
1032 * user name for the character. The first time this function gets
1033 * called it sets up a translation array. Then it can just look up
1034 * the name by using the code as an index into the array */
1037 mc_num2name(code
, font
)
1039 int code
; /* the code for the music character */
1040 int font
; /* FONT_MUSIC* */
1043 static int xlate_tbl
[NUM_MFONTS
][CHARS_IN_FONT
+ FIRST_CHAR
];
1044 /* translate music char #define
1045 * values to offset in Mus_char_table
1047 int f
; /* font index */
1048 static int called
= NO
; /* boolean, YES if this function
1049 * has been called before */
1050 register int numch
; /* how many music characters to do */
1055 /* first time. need to build table */
1057 /* For each item in the Mus_char_table, fill in the
1058 * element of the xlate_tbl array with its offset,
1059 * or fill in -1 if no valid character with that code. */
1060 for (f
= 0; f
< NUM_MFONTS
; f
++) {
1061 for ( numch
= 0; numch
< CHARS_IN_FONT
+ FIRST_CHAR
; numch
++) {
1062 xlate_tbl
[f
][numch
] = -1;
1065 for (f
= 0; f
< NUM_MFONTS
; f
++) {
1066 for ( numch
= 0; numch
< CHARS_IN_FONT
+ FIRST_CHAR
; numch
++) {
1067 if (Mus_char_table
[f
][numch
].charname
!= 0) {
1068 xlate_tbl
[f
] [ Mus_char_table
[f
][numch
].code
& 0xff ] =
1078 /* now we just look up the name */
1079 if ((numch
= xlate_tbl
[font
- FONT_MUSIC
][code
& 0xff]) < 0) {
1080 pfatal("bad music character [%d][%d] in mc_num2name",
1081 font
- FONT_MUSIC
, code
& 0xff);
1084 return( Mus_char_table
[font
- FONT_MUSIC
][numch
].charname
);
1089 /* given the C_XXX code value for an extended character set char, return the
1090 * user name for the character. The first time this function gets
1091 * called it sets up a translation array. Then it can just look up
1092 * the name by using the code as an index into the array */
1097 int code
; /* the code for the extended character set character */
1100 static int xlate_tbl
[CHARS_IN_FONT
+ FIRST_CHAR
];
1101 /* translate extended char
1102 * #define values to offset in
1103 * Ext_char_table array */
1104 static int called
= NO
; /* boolean, YES if this function
1105 * has been called before */
1106 register int numch
; /* how many extended characters to do */
1111 /* first time. need to build table */
1113 /* initialize table to have nothing set */
1114 for ( numch
= 0; numch
< CHARS_IN_FONT
+ FIRST_CHAR
; numch
++) {
1115 xlate_tbl
[numch
] = -1;
1118 /* for each item in the Ext_char_table, fill in the
1119 * element of the xlate_tbl array with its offset */
1120 for (numch
= 0; Ext_char_table
[numch
].charname
!= (char *) 0;
1122 xlate_tbl
[ Ext_char_table
[numch
].code
& 0xff ] =
1127 /* now we just look up the name */
1128 if ((numch
= xlate_tbl
[code
& 0xff]) < 0) {
1129 pfatal("bad extended character set character (%d) in ext_num2name", code
& 0xff);
1132 return( Ext_char_table
[numch
].charname
);
1137 /* return YES if string passed in consists solely of a music symbol, otherwise
1141 is_music_symbol(str
)
1143 char *str
; /* which string to check */
1151 if (str
== (char *) 0) {
1159 /* has to be music char followed by null to be YES */
1160 if (next_str_char(&string
, &font
, &size
) == '\0') {
1163 if ( ! IS_MUSIC_FONT(font
)) {
1166 if (next_str_char(&string
, &font
, &size
) == '\0') {
1173 /* return the ascent of a string in inches. This is the largest ascent of any
1174 * character in the string */
1179 char *str
; /* which string to process */
1182 float max_ascent
, a
; /* tallest and current ascent */
1183 char *s
; /* to walk through string */
1184 int font
, size
, code
;
1186 double vertical
, horizontal
;
1187 float baseline_offset
; /* to account for vertical motion */
1189 int only_mus_sym
; /* YES if string consists solely
1190 * of a music char */
1193 if (str
== (char *) 0) {
1197 only_mus_sym
= is_music_symbol(str
);
1199 /* first 2 bytes are font and size. */
1203 /* Walk through the string. */
1204 for (max_ascent
= 0.0, baseline_offset
= 0.0, s
= str
+ 2;
1205 (code
= nxt_str_char(&s
, &font
, &size
, &textfont
,
1206 &vertical
, &horizontal
, &in_pile
, NO
)) > 0; ) {
1208 /* A newline goes to following line, so we probably won't
1209 * get any higher ascent than we have so far, but if
1210 * user gives enough vertical motion, we might, so continue. */
1212 baseline_offset
-= fontheight(font
, size
);
1215 /* adjust for any vertical motion */
1216 if (vertical
!= 0.0) {
1217 baseline_offset
+= vertical
;
1220 /* music characters inside strings get moved up to the baseline,
1221 * so use their height as ascent.
1222 * Regular characters use the
1223 * ascent of the character */
1224 if ((IS_MUSIC_FONT(font
)) && (only_mus_sym
== NO
)) {
1225 a
= height(font
, size
, code
);
1228 a
= ascent(font
, size
, code
);
1230 a
+= baseline_offset
;
1232 /* if tallest seen save this height */
1233 if (a
> max_ascent
) {
1238 /* if boxed, allow space for that */
1239 if (IS_BOXED(str
) == YES
) {
1240 max_ascent
+= 2.5 * STDPAD
;
1242 /* similarly, allow space for circle */
1243 if (IS_CIRCLED(str
) == YES
) {
1244 float ascent_adjust
;
1245 max_ascent
+= circled_dimensions(str
, (float *) 0, (float *) 0,
1246 &ascent_adjust
, (float *) 0);
1247 max_ascent
+= ascent_adjust
;
1253 /* return the descent of a string in inches. This is the largest descent of any
1254 * character in the string */
1259 char *str
; /* which string to process */
1262 float max_descent
, d
; /* largest and current descent */
1263 float line_descent
; /* descent caused by newlines */
1264 double vertical
, horizontal
;
1266 char *s
; /* to walk through string */
1267 int font
, size
, code
;
1269 int only_mus_sym
; /* YES if string consists solely
1270 * of a music char */
1273 if (str
== (char *) 0) {
1277 only_mus_sym
= is_music_symbol(str
);
1279 /* first 2 bytes are font and size. */
1283 /* walk through the string. */
1284 for (max_descent
= line_descent
= 0.0, s
= str
+ 2;
1285 (code
= nxt_str_char(&s
, &font
, &size
, &textfont
,
1286 &vertical
, &horizontal
, &in_pile
, NO
)) > 0
1287 || vertical
!= 0.0; ) {
1289 /* Adjust for vertical motion. Since line_descent is
1290 * measured downward and vertical is upward, have to
1291 * substract the vertical, then adjust max_descent
1293 if (vertical
!= 0.0) {
1294 line_descent
-= vertical
;
1295 max_descent
+= vertical
;
1303 /* at newline, descent goes down to next baseline,
1304 * which will be down from current baseline
1305 * by height of font */
1306 line_descent
+= fontheight(font
, size
);
1311 /* music characters inside strings get moved up to the
1312 * baseline, so have no descent. */
1313 if ( ! (IS_MUSIC_FONT(font
)) || (only_mus_sym
== YES
)) {
1314 d
= descent(font
, size
, code
);
1320 /* if largest descent seen, save this descent */
1321 if (d
> max_descent
) {
1326 /* if boxed, allow space for that */
1327 if (IS_BOXED(str
) == YES
) {
1328 max_descent
+= 3.5 * STDPAD
;
1330 /* similarly, allow space for circle */
1331 if (IS_CIRCLED(str
) == YES
) {
1332 max_descent
+= circled_dimensions(str
, (float *) 0, (float *) 0,
1333 (float *) 0, (float *) 0);
1335 return(max_descent
+ line_descent
);
1339 /* return the height of a string in inches. This is the maximum ascent plus the
1340 * maximum descent */
1345 char *str
; /* which string to process */
1347 /* Since letters may not
1348 * align because of ascent/descent, we get the tallest extent
1349 * by adding the largest ascent to the largest descent */
1350 return( strascent(str
) + strdescent(str
));
1354 /* return the width of a string. This is the sum of the widths of the
1355 * individual characters in the string */
1362 float widest_line
; /* for multi-line strings */
1364 double horizontal
, vertical
;
1365 int was_in_pile
; /* if in pile last time through loop */
1366 int in_pile_now
; /* if current character is inside a pile */
1367 char *s
; /* to walk through string */
1368 int font
, size
, code
;
1372 if (str
== (char *) 0) {
1376 /* first 2 bytes are font and size. */
1380 /* walk through string */
1382 for (curr_width
= tot_width
= widest_line
= 0.0, s
= str
+ 2;
1383 (code
= nxt_str_char(&s
, &font
, &size
, &textfont
,
1384 &vertical
, &horizontal
, &in_pile_now
, NO
)) > 0;
1385 was_in_pile
= in_pile_now
) {
1387 /* Piles are handled specially. As soon as we enter a pile,
1388 * we call the function to get its entire width. Then for
1389 * the rest of the pile, we just skip past everything */
1390 if (in_pile_now
== YES
) {
1391 if (was_in_pile
== NO
) {
1392 curr_width
+= pile_width();
1393 if (curr_width
> tot_width
) {
1394 tot_width
= curr_width
;
1400 /* the horizontal movement coming out of a pile doesn't count,
1401 * since it was included in the pile, otherwise it does */
1402 if (was_in_pile
== NO
) {
1403 curr_width
+= horizontal
;
1405 if (curr_width
> tot_width
) {
1406 tot_width
= curr_width
;
1410 /* keep track of width line of multi-line string */
1411 if (tot_width
> widest_line
) {
1412 widest_line
= tot_width
;
1421 tot_width
-= backsp_width(size
);
1422 curr_width
-= backsp_width(size
);
1426 /* If we have the special "page number" character,
1427 * or special "total number of pages" character,
1428 * we deal with that here. */
1429 if ( (code
== '%' || code
== '#') && (s
> str
+ 3)
1430 && ( (*(s
-2) & 0xff) == STR_PAGENUM
1431 || (*(s
-2) & 0xff) == STR_NUMPAGES
) ) {
1433 char pgnumbuff
[8], *pgnum_p
;
1435 /* convert page number to a string and
1436 * add the width of each character in
1438 (void) sprintf(pgnumbuff
, "%d",
1439 code
== '%' ? Pagenum
: Last_pagenum
);
1441 for ( pgnum_p
= pgnumbuff
; *pgnum_p
!= '\0';
1443 curr_width
+= width(font
, size
, *pgnum_p
);
1448 /* Oh good. This is a normal case. Just add
1449 * width of this character to width so far */
1450 curr_width
+= width(font
, size
, code
);
1453 if (curr_width
> tot_width
) {
1454 tot_width
= curr_width
;
1457 if (tot_width
< widest_line
) {
1458 tot_width
= widest_line
;
1460 /* if string is boxed, allow space for the box */
1461 if (IS_BOXED(str
) == YES
) {
1462 tot_width
+= 6.0 * STDPAD
;
1464 /* similarly, allow space for circled */
1465 if (IS_CIRCLED(str
) == YES
) {
1466 (void) circled_dimensions(str
, (float *) 0, &tot_width
,
1467 (float *) 0, (float *) 0);
1473 /* Return the width to the "anchor" point of a string. For most strings,
1474 * this will be half the width of the first character. But for a string
1475 * that begins with things piled atop one another, it is the alignment point.
1476 * And for boxed or circled strings, the box or circle must be considered.
1487 char *pile_start_p
; /* where pile begins, if any */
1489 if (starts_piled(string
, &font
, &size
, &pile_start_p
) == YES
) {
1490 return(align_distance(pile_start_p
, font
, size
));
1494 float extra
; /* space for box or circle, if any */
1496 /* For boxed or circled strings,
1497 * the space for the box or circle must be added in */
1498 if (IS_BOXED(string
) == YES
) {
1499 extra
= 3.5 * STDPAD
;
1501 else if (IS_CIRCLED(string
) == YES
) {
1502 (void) circled_dimensions(string
, (float *) 0,
1503 (float *) 0, (float *) 0, &extra
);
1509 /* Get half the width of the first character in the string */
1512 ch
= next_str_char(&string
, &font
, &size
);
1513 return(width(font
, size
, ch
) / 2.0 + extra
);
1518 /* If string begins with piled text, return YES, otherwise NO,
1519 * If YES, also return via pointers the start of the pile and the
1520 * font and size at that point. */
1523 starts_piled(string
, font_p
, size_p
, pile_start_p_p
)
1528 char **pile_start_p_p
;
1531 *font_p
= *string
++;
1532 *size_p
= *string
++;
1534 /* walk through string, skipping any leading box/size/font */
1535 for ( ; *string
!= '\0'; string
++) {
1536 if (IS_STR_COMMAND(*string
)) {
1537 switch(*string
& 0xff) {
1540 *font_p
= *(++string
);
1544 *size_p
= *(++string
);
1552 /* The first thing we found that was not to be
1553 * ignored is the beginning of a pile */
1554 *pile_start_p_p
= string
;
1569 /* given a string representing a chord mark, transpose it. For each letter
1570 * 'A' to 'G' optionally followed by an accidental, call function to
1571 * get transposed value. Build new string with transposed values. Free up
1572 * the old string and return the new one. Also, if the accidental was
1573 * of the form &, #, x, or && instead of \(smflat) etc, change to proper
1574 * music symbol. Also handles translation of o, o/ and ^ to dim, halfdim,
1575 * and triangle symbols, and does translation of unescaped accidentals. */
1578 tranchstr(chordstring
, staffno
)
1580 char *chordstring
; /* untransposed string */
1581 int staffno
; /* which staff it is associated with */
1582 /* A staffno of -1 means no transpose, just translate */
1585 char tmpbuff
[128]; /* temporary copy of transposed string */
1586 char replacement
[4]; /* for dim/halfdim/triangle */
1587 short i
; /* index into tmpbuff */
1588 unsigned char *str
; /* walk through chordstring */
1589 char *transposed
; /* new version of letter[accidental] */
1590 char tranbuff
[4]; /* to point 'transposed' at if not really
1592 char letter
; /* A to G */
1594 int escaped
; /* YES is accidental was escaped */
1595 char literal_accidental
; /* what would normally be translated */
1596 int nprocessed
; /* how many character processed by subroutine */
1597 char *newstring
; /* final copy of transposed string */
1600 int in_pile
; /* YES if inside a pile */
1601 int acc_size
; /* size for accidentals */
1604 /* get font/size info */
1605 tmpbuff
[0] = chordstring
[0];
1606 tmpbuff
[1] = chordstring
[1];
1607 size
= chordstring
[1];
1609 str
= (unsigned char *) (chordstring
+ 2);
1610 literal_accidental
= '\0'; /* avoids bogus "used before set" warning */
1612 /* walk through original string */
1613 for (i
= 2; *str
!= '\0'; str
++) {
1615 /* Be safe. Bail out a little before we reach end,
1616 * because some things take several bytes,
1617 * and it's easiest to just check once per loop. */
1618 if (i
> sizeof(tmpbuff
) - 8) {
1619 ufatal("chord string too long: '%s'", chordstring
+ 2);
1622 acc_size
= accsize(size
);
1624 /* If a STR_*, deal with that */
1625 if ((n
= str_cmd((char *) str
, &size
, &in_pile
)) > 0) {
1626 strncpy(tmpbuff
+ i
, (char *) str
, (unsigned) n
);
1631 /* handle backslashed o and ^ */
1632 else if (*str
== '\\' && ( *(str
+1) == 'o' || *(str
+1) == '^' ) ) {
1634 tmpbuff
[i
++] = *str
;
1637 else if (*str
>= 'A' && *str
<= 'G') {
1639 /* Aha! Something to transpose. */
1642 str
+= get_accidental( (unsigned char *) (str
+ 1),
1643 &accidental
, &acc_size
, NO
, &escaped
);
1644 if (escaped
== YES
) {
1645 /* not *really* an accidental, so save to
1647 literal_accidental
= accidental
;
1650 if (staffno
== -1) {
1651 /* not to be transposed, so make a string
1652 * that would be like what tranchnote() would
1653 * return, but with no transposition. */
1654 tranbuff
[0] = letter
;
1655 tranbuff
[1] = accidental
;
1657 transposed
= tranbuff
;
1660 /* get the transposed value */
1661 transposed
= tranchnote(letter
, accidental
, staffno
);
1664 /* put transposed letter into output */
1665 tmpbuff
[i
++] = *transposed
;
1667 /* now add accidental if any */
1668 i
+= add_accidental(tmpbuff
+ i
, (int) *++transposed
,
1671 /* add on any escaped pseudo-accidental */
1672 if (escaped
== YES
) {
1673 i
+= add_accidental(tmpbuff
+ i
,
1674 (int) literal_accidental
,
1679 /* handle dim/halfdim/triangle transformations */
1680 if ((n
= dim_tri(str
+ 1, replacement
, size
, YES
)) > 0) {
1681 strcpy(tmpbuff
+ i
, replacement
);
1682 i
+= strlen(replacement
);
1687 /* Originally we only translated things like # and &
1688 * in chords to musical accidental symbols if they
1689 * immediately followed a letter A-G. But due to
1690 * popular demand, they are now translated everywhere,
1691 * unless escaped. */
1692 nprocessed
= get_accidental( (unsigned char *) str
,
1693 &accidental
, &acc_size
, NO
, &escaped
);
1694 if (nprocessed
> 0) {
1695 i
+= add_accidental(tmpbuff
+ i
,
1698 /* the -1 is because str will get incremented
1699 * at the top of the 'for' */
1700 str
+= nprocessed
- 1;
1703 /* something boring. Just copy */
1704 tmpbuff
[i
++] = *str
;
1709 /* need to make permanent copy of new string */
1710 tmpbuff
[i
++] = '\0';
1711 MALLOCA(char, newstring
, i
+ 1);
1712 (void) memcpy(newstring
, tmpbuff
, (unsigned) i
);
1714 /* free original version */
1717 /* return new, transposed version */
1722 /* If there is a STR_* command in chord/analysis/figbass, return how
1723 * many characters long it is. Also update the size if the
1724 * command was one to change size, and update pile status if necessary. */
1727 str_cmd(str
, size_p
, in_pile_p
)
1729 char *str
; /* check string starting here */
1731 int *in_pile_p
; /* YES if in pile, may be updated */
1734 if (IS_STR_COMMAND(*str
)) {
1735 switch(*str
& 0xff) {
1739 *size_p
= *(str
+ 1);
1740 /* command plus 1 argument byte */
1748 /* command plus 1 argument byte */
1752 /* command plus 2 argument bytes */
1756 /* entering/leaving a pile alters the size */
1757 *size_p
= pile_size(*size_p
, *in_pile_p
);
1758 *in_pile_p
= (*in_pile_p ? NO
: YES
);
1762 /* others have no argument bytes */
1770 /* Check the first character of the given string to see if it is an accidental
1771 * or something that should be translated to an accidental (# & x && and
1772 * maybe n). If so, fill in the accidental. If the accidental was specified
1773 * via a STR_MUS_CHAR, also update the accidental size.
1774 * If no accidental, accidental_p will will filled in
1775 * with '\0'. In any case return how many bytes were processed.
1779 get_accidental(string
, accidental_p
, acc_size_p
, trans_natural
, escaped_p
)
1781 unsigned char *string
; /* check this for an accidental */
1782 char *accidental_p
; /* return the accidental here, or \0 if none */
1783 int *acc_size_p
; /* return the accidental size here */
1784 int trans_natural
; /* if YES, translate n to natural, else leave as n */
1785 int *escaped_p
; /* Return value: YES if the symbol was backslashed */
1788 unsigned char *str_p
;
1792 /* assume no accidental */
1793 *accidental_p
= '\0';
1795 /* check if escaped */
1796 if (*str_p
== '\\') {
1804 /* See if the following character is an accidental */
1809 *accidental_p
= *str_p
++;
1812 *accidental_p
= *str_p
++;
1813 /* have to peek ahead to check for double flat,
1814 * but not if escaped, so person can get a literal
1815 * ampersand followed by a flat. */
1816 if (*escaped_p
== NO
&& *str_p
== '&') {
1817 /* double flat is 'B' internally */
1818 *accidental_p
= 'B';
1824 /* naturals are not translated in chords, but are
1825 * in analysis and figbass */
1826 if (trans_natural
== YES
) {
1827 *accidental_p
= *str_p
++;
1832 if (*escaped_p
== YES
) {
1835 /* Check if user put in \(flat) or something
1836 * similar. If so, use that. */
1837 switch (*(str_p
+ 2)) {
1839 *acc_size_p
= *(str_p
+ 1);
1840 *accidental_p
= '&';
1845 *acc_size_p
= *(str_p
+ 1);
1846 *accidental_p
= '#';
1851 *acc_size_p
= *(str_p
+ 1);
1852 *accidental_p
= 'B';
1857 *acc_size_p
= *(str_p
+ 1);
1858 *accidental_p
= 'x';
1863 /* Always translate the natural symbol,
1864 * even when trans_natural is NO. That really
1865 * applies just to the use of 'n' which is
1866 * likely to be wanted as a real n, whereas
1867 * a music symbol natural is unambiguous. */
1868 *acc_size_p
= *(str_p
+ 1);
1869 *accidental_p
= 'n';
1874 /* false alarm. Some other
1875 * music character. */
1881 /* nothing special */
1885 /* If all we saw was a backslash,
1886 * then there wasn't really an accidental */
1887 if (*escaped_p
== YES
&& str_p
== string
+ 1) {
1892 return(str_p
- string
);
1896 /* Write the given accidental in the given size to the given string.
1897 * Return how many bytes were added. */
1900 add_accidental(buff
, acc_character
, acc_size
, escaped
)
1902 char *buff
; /* write into this buffer */
1903 int acc_character
; /* write this accidental */
1904 int acc_size
; /* make accidental this big */
1905 int escaped
; /* if YES, was escaped, so not really an accidental;
1906 * print it as a normal character */
1909 if (acc_character
!= '\0') {
1911 /* if escaped, just treat like normal character. */
1912 if (escaped
== YES
) {
1913 buff
[0] = acc_character
;
1917 /* sharps and naturals are tall enough that they can
1918 * make things not line up, so move them down some */
1919 if (acc_character
== '#' || acc_character
== 'n') {
1920 buff
[0] = (char) STR_VERTICAL
;
1921 buff
[1] = (char) ENCODE_VERT(-4);
1924 /* has accidental. Add STR_MUS_CHAR-size-code */
1925 buff
[0] = (char) STR_MUS_CHAR
;
1927 /* double sharp is special. It is too small,
1928 * so make it bigger */
1929 if (acc_character
== 'x') {
1930 acc_size
= (int) ( (float) acc_size
1933 buff
[1] = (char) acc_size
;
1935 /* use accidental of appropriate type */
1936 switch (acc_character
) {
1947 buff
[2] = C_DBLSHARP
;
1951 buff
[2] = C_DBLFLAT
;
1959 pfatal("illegal accidental on transposed chord");
1962 if (acc_character
== '#' || acc_character
== 'n') {
1963 buff
[3] = (char) STR_VERTICAL
;
1964 buff
[4] = (char) ENCODE_VERT(4);
1965 /* We added 3 bytes for the accidental, plus
1966 * 2 bytes before and after for vertical motion. */
1970 return(3); /* we added 3 bytes */
1978 /* In chords and such, "o" becomes \(dim), "o/" becomes \(halfdim)
1979 * unless followed by [A-G] in which case it becomes "\(dim)/",
1980 * and "^" becomes \(triangle). Return number of characters processed.
1984 dim_tri(str_p
, replacement
, size
, is_chord
)
1986 unsigned char *str_p
; /* check string at this point */
1987 char *replacement
; /* return the replacement in this buffer,
1988 * which needs to be at least 4 bytes long */
1989 int size
; /* use this size for music character */
1990 int is_chord
; /* YES for chord, NO for analysis/figbass */
1993 if (*str_p
== '^') {
1994 replacement
[0] = (char) STR_MUS_CHAR
;
1995 replacement
[1] = size
;
1996 replacement
[2] = C_TRIANGLE
;
1997 replacement
[3] = '\0';
2000 else if (*str_p
== 'o') {
2001 replacement
[0] = (char) STR_MUS_CHAR
;
2002 replacement
[1] = size
;
2003 replacement
[3] = '\0';
2004 if ( *(str_p
+1) == '/' && (is_chord
== NO
||
2005 (*(str_p
+2) < 'A' || *(str_p
+2) > 'G'))) {
2006 replacement
[2] = C_HALFDIM
;
2010 replacement
[2] = C_DIM
;
2018 /* Given a string for analysis or figbass, transform the accidentals
2019 * & # && x n to their music characters.
2028 char buffer
[128]; /* output buffer for transformed string */
2029 char *out_p
; /* current location in output buffer */
2030 char replacement
[4]; /* space for dim, halfdim, etc */
2033 char accidental
; /* #, &, x, etc */
2034 int escaped
; /* YES is accidental was escaped */
2035 int in_pile
; /* YES if inside a pile */
2038 buffer
[0] = string
[0];
2039 buffer
[1] = string
[1];
2043 /* walk through string, transforming any accidentals along the way */
2044 for ( string
+= 2, out_p
= buffer
+ 2; *string
!= '\0'; ) {
2045 /* Be safe. Bail out a little before we reach end,
2046 * because some things take several bytes,
2047 * and it's easiest to just check once per loop. */
2048 if (out_p
- buffer
> sizeof(buffer
) - 8) {
2049 l_ufatal(Curr_filename
, yylineno
,
2050 "analysis or figbass string too long");
2053 acc_size
= accsize(size
);
2054 if ((n
= get_accidental((unsigned char *) string
,
2055 &accidental
, &acc_size
, YES
, &escaped
)) > 0 ) {
2056 out_p
+= add_accidental(out_p
, (int) accidental
,
2060 else if (*string
== '\\' && ( *(string
+1) == 'o' || *(string
+1) == '^') ) {
2061 *out_p
++ = *++string
;
2064 else if ((n
= dim_tri((unsigned char *) string
, replacement
,
2066 strcpy(out_p
, replacement
);
2067 out_p
+= strlen(replacement
);
2070 else if ((n
= str_cmd(string
, &size
, &in_pile
)) > 0) {
2071 strncpy(out_p
, string
, (unsigned) n
);
2076 *out_p
++ = *string
++;
2081 return(copy_string(buffer
+ 2, buffer
[0], buffer
[1]));
2084 /* Given a chord, analysis or figbass string,
2085 * transform according to their special rules:
2086 * - : gets translated to \: and vice-versa
2087 * - figbass starts in piled mode
2088 * - in figbass, a / gets translated to \/ and vice-versa
2089 * This string will be in half transformed state: the first 2 bytes
2090 * are font/size, but the rest is still all ASCII, not internal format.
2094 modify_chstr(string
, modifier
)
2100 int length
; /* of modified string */
2101 char *s
; /* walk through string */
2103 char *new_p
; /* walk through newstring */
2104 int need_new
; /* if we need to make a new string */
2107 length
= strlen(string
);
2108 if (modifier
== TM_FIGBASS
) {
2109 /* We'll need two extra bytes for
2110 * the leading \: for pile mode. */
2115 /* Only need a new string if the original has colons,
2116 * so assume for now we won't need a new string */
2120 /* Figure out how much space we'll need for the modified string.
2121 * Any unbackslashed colons will take up an extra byte once
2122 * we backslash it. But any backslashed one will take up one
2123 * less when we unescape it. Similar for slashes in figbass. */
2124 for (s
= string
+ 2; *s
!= '\0'; s
++) {
2129 else if (modifier
== TM_FIGBASS
&& *s
== '/') {
2130 /* o/ means half diminished so that doesn't count */
2131 if (s
> string
+ 2 && *(s
-1) == 'o') {
2137 else if (*s
== '\\') {
2139 /* things that occur inside \(...) don't count */
2141 for (s
++; *s
!= '\0' && *s
!= ')'; s
++) {
2144 /* If no closing parenthesis, return as is;
2145 * later code will catch that */
2150 else if (*s
== ':') {
2154 else if (modifier
== TM_FIGBASS
&& *s
== '/') {
2161 /* If string is okay as is, we are done here */
2162 if (need_new
== NO
) {
2166 /* get enough space for new string */
2167 MALLOCA(char, newstring
, length
+ 1);
2169 /* copy font/size */
2170 newstring
[0] = string
[0];
2171 newstring
[1] = string
[1];
2173 new_p
= newstring
+ 2;
2175 if (modifier
== TM_FIGBASS
) {
2176 /* add \: but after box, if any */
2177 if (string
[2] == '\\' && string
[3] == '[') {
2185 /* walk through rest of string, copying, but transforming
2186 * any slashes and colons along the way */
2187 for ( ; *s
!= '\0'; s
++, new_p
++) {
2191 /* add a backslash */
2194 else if (*s
== '\\' && *(s
+1) == ':') {
2195 /* skip past the backslash */
2199 /* handle slashes in figbass */
2200 else if (modifier
== TM_FIGBASS
) {
2202 /* o/ means half diminished
2203 * so that doesn't count */
2204 if (s
<= string
+ 2 || *(s
-1) != 'o') {
2205 /* add a backslash */
2209 else if (*s
== '\\' && *(s
+1) == '/') {
2210 /* skip past the backslash */
2215 /* copy from original string to new one */
2219 /* original is now no longer needed */
2222 /* terminate and return the modified string */
2228 /* given an integer point size, return the integer point size appropriate
2229 * for a "small" version. This is SM_FACTOR times the size, rounded, but
2230 * not less than 1. */
2238 size
= (int) ( (float) size
* SM_FACTOR
);
2246 /* accidentals in chords need to be scaled. Given a size, return the size
2247 * that an accidental should be. This is 60% of given size, rounded to
2248 * an integer, but no smaller than 1. */
2256 size
= (int) ( (float) size
* 0.6);
2264 /* return which character to use for rest, based on basictime */
2272 if (basictime
< -1) {
2273 pfatal("tried to get rest character for multirest");
2278 else if (basictime
== -1) {
2283 else if (basictime
== 0) {
2284 /* double whole rest */
2289 /* other non-multirest */
2290 return (Resttab
[ drmo(basictime
) ] );
2295 /* return YES if given font is an italic font (includes boldital too) */
2303 return(Fontinfo
[ font_index(font
) ].is_ital
);
2307 /* given a string, return, via pointers the font and size in effect at the
2308 * end of the string */
2311 end_fontsize(str
, font_p
, size_p
)
2313 char *str
; /* check this string */
2314 int *font_p
; /* return font at end of str via this pointer */
2315 int *size_p
; /* return size at end of str via this pointer */
2318 if (str
== (char *) 0) {
2319 /* empty string, use defaults */
2321 *size_p
= DFLT_SIZE
;
2325 /* find the font/size in effect at end of given string */
2328 while (next_str_char(&str
, font_p
, size_p
) != '\0') {
2334 /* given a string, return a string made up of a dash in the font and size
2335 * of the end of the given string. However, if the string ends with a ~ or _
2336 * return a string containing that instead */
2341 char *str
; /* return dash with same font/size as end of this string */
2346 int ch
; /* character to use */
2349 end_fontsize(str
, &font
, &size
);
2350 ch
= last_char(str
);
2351 if (ch
!= '~' && ch
!= '_') {
2355 /* allocate space for dash string and fill it in */
2356 MALLOCA(char, newstring
, 4);
2357 newstring
[0] = (char) font
;
2358 newstring
[1] = (char) size
;
2359 newstring
[2] = (char) ch
;
2360 newstring
[3] = '\0';
2365 /* Given an internal format string, create an ASCII-only string. Flags
2366 * tell how complete a conversion to do. If verbose is YES, try to convert
2367 * everything back to user's original input, otherwise ignore special things
2368 * other than music characters, extended characters, and backspace.
2369 * If pagenum is YES, interpolate the current page number rather than using %.
2371 * Recreating the original user string is not perfect, but is usually right.
2372 * Where there are shortcuts, we can't tell if user used them or not.
2373 * Extended characters are output by name even if user put them in as single
2374 * Latin-1 characters. But we couldn't use the Latin-1 hyper-ASCII in midi
2375 * anyway, because they have high bit set.
2377 * Returns the ASCII-ized string, which is stored in an area that will get
2378 * overwritten on subsequent calls, so if caller needs a permanent copy,
2379 * they have to make it themselves.
2382 /* This is how much to malloc at a time to hold the ASCII-ized string */
2383 #define ASCII_BSIZE 512
2386 ascii_str(string
, verbose
, pagenum
, textmod
)
2388 char *string
; /* internal format string to convert */
2389 int verbose
; /* If YES, try to reproduce user's original input */
2390 int pagenum
; /* YES (interpolate number for \%) or NO (leave \% as is) */
2391 int textmod
; /* TM_ value */
2394 static char *buff
= 0; /* for ASCII-ized string */
2395 static unsigned buff_length
= 0;/* how much is malloc-ed */
2396 int i
; /* index into ASCII-ized string */
2397 char *musname
; /* music character name */
2399 char *str
; /* walk through string */
2400 int musfont
; /* FONT_MUSIC* */
2403 /* first time, get some space */
2404 if (buff_length
== 0) {
2405 buff_length
= ASCII_BSIZE
;
2406 MALLOCA(char, buff
, buff_length
);
2409 /* walk through string */
2411 /* special case: normally we implicitly begin a figbass with a
2412 * pile start, but if users cancels that, it won't be there */
2413 if (textmod
== TM_FIGBASS
&&
2414 (((unsigned char) *(string
+2)) & 0xff) != STR_PILE
) {
2417 for (str
= string
+ 2; *str
!= '\0'; str
++) {
2418 switch ( ((unsigned char) *str
) & 0xff) {
2423 if ( (int) *str
> EXT_FONT_OFFSET
) {
2425 /* translate to Mup name */
2426 (void) sprintf(buff
+ i
, "\\(%s)",
2427 ext_num2name((int) *str
));
2428 while (buff
[i
] != '\0') {
2431 /* skip past the return to original font */
2434 else if (verbose
== YES
) {
2436 if (verbose
== YES
) {
2438 (void) sprintf(buff
+ i
, "\\f(%s)",
2439 fontnum2name((int) *str
));
2440 while (buff
[i
] != '\0') {
2448 if (verbose
== YES
) {
2449 (void) sprintf(buff
+ i
, "\\s(%d)", (int) *str
);
2450 while (buff
[i
] != '\0') {
2458 if (verbose
== YES
) {
2459 (void) sprintf(buff
+ i
, "\\v(%d)",
2460 DECODE_VERT((int) *str
) * 100
2462 while (buff
[i
] != '\0') {
2470 musfont
= str2mfont( ((unsigned char) *str
) & 0xff);
2472 /* skip past the size byte,
2473 * and on to the character code. */
2475 /* In chordlike stuffs, we translate things like
2476 * # and &&, so translate them back. It's possible
2477 * the user used the names explicitly rather than us
2478 * translating, in which case this won't be
2479 * strictly what they put in, but it will be
2480 * consistent, so that a caller of this function
2481 * can easily sort or compare values
2482 * without having to know (for example)
2483 * that '#' and \(smsharp) are the same thing. */
2485 if (IS_CHORDLIKE(textmod
) == YES
2486 && musfont
== FONT_MUSIC
) {
2487 switch( ((unsigned char) *str
) & 0xff) {
2501 if (textmod
!= TM_CHORD
) {
2519 (void) sprintf(buff
+ i
, musname
);
2522 (void) sprintf(buff
+ i
, "\\(%s)",
2523 mc_num2name((int) *str
, musfont
));
2525 while (buff
[i
] != '\0') {
2532 if (verbose
== YES
) {
2536 /* ignore this and following char */
2542 if (verbose
== YES
) {
2549 if (verbose
== YES
) {
2557 if (verbose
== YES
) {
2563 if (verbose
== YES
) {
2570 if (verbose
== YES
) {
2577 if (verbose
== YES
) {
2584 if (verbose
== YES
) {
2591 if (verbose
== YES
) {
2598 if (verbose
== YES
) {
2605 if (verbose
== YES
) {
2606 /* On figbass, we implictly add a pile start */
2607 if (textmod
== TM_FIGBASS
&& string
+ 2 == str
) {
2610 /* if this is at the end of a padded string,
2611 * there is a high probability it is one
2612 * we added implicitly, so skip it */
2613 else if (in_pile
== YES
&& *(str
+1) == ' ' &&
2618 /* in chordlike things, user didn't
2619 * use a backslash, else they did */
2620 if (IS_CHORDLIKE(textmod
) == NO
) {
2626 /* keep track of toggle state */
2627 in_pile
= (in_pile
== YES ? NO
: YES
);
2631 if (verbose
== YES
&& textmod
!= TM_FIGBASS
) {
2639 if (pagenum
== YES
) {
2640 /* Write page number and update length.
2641 * Actually, we don't have the correct values
2642 * for this until late in program execution,
2643 * and for MIDI, there are no pages at all,
2644 * and this can be called from MIDI, so
2645 * this is probably not really very useful,
2646 * but this is the best we can do... */
2647 (void) sprintf(buff
+ i
, "%d",
2648 (((unsigned char) *str
) & 0xff)
2650 Pagenum
: Last_pagenum
);
2651 while (buff
[i
] != '\0') {
2657 buff
[i
++] = *(str
+1);
2669 if (in_pile
== YES
) {
2670 if ( *(str
+1) != '\0') {
2679 else if (IS_CHORDLIKE(textmod
) == YES
&& *str
== ':') {
2683 else if (textmod
== TM_FIGBASS
&& *str
== '/') {
2687 else if (*str
== ' ' && *(str
+1) == '\0') {
2688 /* This is probably a space padding
2689 * that we added implicitly,
2690 * so don't print it. If this is
2691 * called on a 'with' item or 'print' item
2692 * where user explicitly added a space,
2693 * this will strip that off, which, strictly
2694 * speaking, it shouldn't. But that would
2695 * only be for debugging anyway, and a
2696 * strange case, so don't worry about it. */
2700 /* ordinary character */
2705 /* If running low on space, get some more. Could probably
2706 * just truncate, since this is used for things like error
2707 * messages, but alloc-ing more is easy enough. */
2708 if (i
> buff_length
- 20) {
2709 buff_length
+= ASCII_BSIZE
;
2710 REALLOCA(char, buff
, buff_length
);
2720 * Given a text string and a maximum desired width, try adding newlines at
2721 * white space to bring the width down under the desired width. If that's
2722 * not possible, do the best we can. Return pointer to the possibly
2727 split_string(string
, desired_width
)
2730 double desired_width
;
2733 char *last_white_p
; /* where last white space was */
2734 char *curr_white_p
; /* white space we're dealing with now */
2735 char *str
; /* to walk through string */
2736 double proposed_width
; /* width of string so far */
2738 int c
; /* the current character in string */
2739 int save_c
; /* temporary copy of c */
2740 int save_str
; /* temporary copy of character from string */
2743 /* Piles are incompatible with newlines, so we don't want to
2744 * even attempt to split a string with a pile in it. */
2745 for (str
= string
+ 2; *str
!= '\0'; str
++) {
2746 if ((*str
& 0xff) == STR_PILE
) {
2747 /* string has a pile, so return it as is */
2752 /* Go through the string, until we hit white space. */
2753 last_white_p
= (char *) 0;
2757 while ((c
= next_str_char(&str
, &font
, &size
)) != '\0') {
2759 /* Are we at white space? */
2760 if ( ! IS_MUSIC_FONT(font
) && (c
== ' ' || c
== '\t')) {
2762 /* Temporarily replace with newline, and terminate
2763 * to get width so far if we were to add a newline */
2764 curr_white_p
= str
- 1;
2767 *curr_white_p
= '\n';
2769 proposed_width
= strwidth(string
);
2770 *curr_white_p
= save_c
;
2773 if (proposed_width
> desired_width
) {
2774 if (last_white_p
!= (char *) 0) {
2775 /* reduce the width of the string by
2776 * changing the most recent white space
2778 *last_white_p
= '\n';
2780 /* if the overall string is now short
2781 * enough, we are done */
2782 if (strwidth(string
) <= desired_width
) {
2785 last_white_p
= curr_white_p
;
2788 /* No previous white space, so we
2789 * can't make it short enough. So change
2790 * this current white space to a
2791 * newline, since that's the best we
2792 * can do. But also set the desired
2793 * width to our current width,
2794 * because we know we're
2795 * going to have to be at least this
2796 * wide anyway, so we might as well use
2797 * this much space on future lines */
2798 *curr_white_p
= '\n';
2799 desired_width
= proposed_width
;
2801 /* no longer have a previous
2802 * white space on the current line,
2803 * because we just started a new
2805 last_white_p
= (char *) 0;
2810 /* not too wide yet. Remember where this white
2811 * space is, in case the next word makes us
2812 * too wide and we have to change it to a
2814 last_white_p
= curr_white_p
;
2819 /* If last word went over the edge, move to next line if possible. */
2820 if (strwidth(string
) > desired_width
&& last_white_p
!= (char *) 0) {
2821 *last_white_p
= '\n';
2824 /* Return the (possibly altered) string */
2829 /* Given a point size and an adjustment factor, return a new point size.
2830 * If size would be less than MINSIZE, return MINSIZE.
2831 * If it would be greater than MAXSIZE, print error and return MAXSIZE.
2832 * Since we only use integer sizes, there may be some roundoff error.
2833 * While it would be possible to dream up a pathological case
2834 * where this roundout might be big enough to notice,
2835 * for any sane scenario you would probably need
2836 * an extremely high resolution printer and a microscope to notice.
2840 adj_size(size
, scale_factor
, filename
, lineno
)
2842 int size
; /* original point size */
2843 double scale_factor
; /* multiply original size by this factor */
2844 char *filename
; /* filename and lineno are for error messages */
2848 size
= (int) ((double) size
* scale_factor
+ 0.5);
2849 if (size
< MINSIZE
) {
2852 if (size
> MAXSIZE
) {
2853 l_warning(filename
, lineno
,
2854 "Adjusted size of string would be bigger than %d", MAXSIZE
);
2861 /* Given a string that is in internal format, and a scale factor by which to
2862 * resize that string, adjust all size bytes in the string.
2866 resize_string(string
, scale_factor
, filename
, lineno
)
2868 char *string
; /* this is the string to adjust */
2869 double scale_factor
; /* adjust sizes in string by this factor */
2870 char *filename
; /* for error messages */
2871 int lineno
; /* for error messages */
2874 char *s
; /* to walk through string */
2877 /* if string is empty, nothing to do */
2878 if (string
== (char *) 0 || *string
== '\0') {
2882 /* if factor is sufficiently close to 1.0 that it's very clear
2883 * we won't be making any changes (since we only use integer
2884 * point sizes), don't bother */
2885 if ( fabs( (double) (scale_factor
- 1.0)) < 0.01) {
2889 /* second byte is size byte, so adjust that */
2890 string
[1] = (char) adj_size( (int) string
[1], scale_factor
,
2893 /* Go through the string. For each size byte, replace it with an
2894 * adjusted size. Size bytes occur immediately after STR_SIZE
2895 * and STR_MUS_CHAR commands. Everything else can get copied as
2896 * is: STR_BACKSPACE is in terms of the default size, so it is
2897 * unaffected by this resizing, and the other special string commands
2898 * are unrelated to size and thus unaffected. */
2899 for (s
= string
+ 2; *s
!= '\0'; s
++) {
2900 switch ( (unsigned char) *s
) {
2904 *s
= (char) adj_size( (int) *s
, scale_factor
,
2916 /* Given a circled string, return how much to add to its ascent and
2917 * descent to give room for the circle. If pointer arguments are non-zero,
2918 * return additional values via those pointers.
2922 circled_dimensions(str
, height_p
, width_p
, ascent_adjust
, x_offset_p
)
2924 char *str
; /* a circled string */
2925 float *height_p
; /* if non-zero, return circled height here */
2926 float *width_p
; /* if non-zero, return circled width here */
2927 float *ascent_adjust
; /* if non-zero, return amount we added to
2928 * ascent to bring up to minimum height */
2929 float *x_offset_p
; /* if non-zero, return where to print the
2930 * actual string relative to circle edge */
2935 float adjust
; /* amount to bring up to min height */
2936 float uncirc_height
, uncirc_width
;/* dimensions of uncircled str */
2937 float circ_height
; /* height including circle */
2938 float circ_width
; /* width including circle */
2939 float circ_extra
; /* how much to add to top and
2940 * bottom to allow space for circle */
2943 /* temporarily make the string uncircled */
2944 size
= str
[2] = str
[1];
2945 font
= str
[1] = str
[0];
2946 /* Note that there is at least one circumstance (in split_string())
2947 * where a circled string is temporarily lacking the trailing END_CIR,
2948 * and strheight and strwidth don't need it, so we don't need
2949 * to blank that out. */
2951 /* get the dimensions of the uncircled version */
2952 uncirc_height
= strheight(str
+1);
2953 uncirc_width
= strwidth(str
+1);
2955 /* put the circle back */
2957 str
[2] = (char) STR_CIR
;
2959 /* If string is unusually short vertically, treat as at least as tall
2960 * as the font's ascent. That way if there are a bunch of
2961 * circled things and one is tiny, like a dot, that circle
2962 * won't be vastly smaller than the others. */
2963 min_height
= fontascent(font
, size
);
2964 if (uncirc_height
< min_height
) {
2965 adjust
= min_height
- uncirc_height
;
2966 uncirc_height
= min_height
;
2972 /* Allow 25% of the height above and below as space for the circle. */
2973 circ_extra
= 0.25 * uncirc_height
;
2974 circ_height
= 2.0 * circ_extra
+ uncirc_height
;
2976 /* If width is up to 110% of the height, use the circled
2977 * height as the circled width as well. */
2978 if (uncirc_width
<= 1.1 * uncirc_height
) {
2979 circ_width
= circ_height
;
2982 /* make a little taller to compensate for the width */
2983 circ_extra
+= circ_height
* .03 * (uncirc_width
/ uncirc_height
);
2984 circ_height
= 2.0 * circ_extra
+ uncirc_height
;
2986 /* Use 50% of the circled height as the amount to add
2987 * to the width, 25% on each end. */
2988 circ_width
= uncirc_width
+ 0.5 * circ_height
;
2990 if (height_p
!= 0) {
2991 *height_p
= circ_height
;
2994 *width_p
= circ_width
;
2996 if (x_offset_p
!= 0) {
2997 *x_offset_p
= (circ_width
- uncirc_width
) / 2.0;
2999 if (ascent_adjust
!= 0) {
3000 *ascent_adjust
= adjust
;
3007 /* Return proper version of rehearsal mark string, based on staff number.
3008 * It may be circled, boxed, or plain. If circled or boxed, a new string
3009 * is returned. If plain, the string is returned as is.
3013 get_reh_string(string
, staffnum
)
3015 char *string
; /* the plain rehearsal mark string */
3016 int staffnum
; /* which staff it is for */
3019 char reh_buffer
[100]; /* if not okay as it is, copy is put here */
3022 style
= svpath(staffnum
, REHSTYLE
)->rehstyle
;
3024 if (style
== RS_PLAIN
) {
3028 if (strlen(string
) + 3 > sizeof(reh_buffer
)) {
3029 /* Usually reh marks are very short,
3030 * so if this one is really long, too bad.
3032 ufatal("rehearsal mark is too long");
3035 (void) sprintf(reh_buffer
, "%c%s%c",
3036 style
== RS_CIRCLED ? STR_CIR
: STR_BOX
,
3038 style
== RS_CIRCLED ? STR_CIR_END
: STR_BOX_END
);
3039 return(copy_string(reh_buffer
, string
[0], string
[1]));
3043 /* Map STR_MUS_CHAR* to FONT_MUSIC* */
3048 int str
; /* STR_MUS_CHAR* */
3055 return(FONT_MUSIC2
);
3057 pfatal("impossible str 0x%x in str2mfont", str
);
3063 /* Map FONT_MUSIC* to STR_MUS_CHAR* */
3068 int mfont
; /* FONT_MUSIC* */
3073 return(STR_MUS_CHAR
);
3075 return(STR_MUS_CHAR2
);
3077 pfatal("impossible mfont %d in mfont2str", mfont
);
3079 return(STR_MUS_CHAR
);