chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / nxtstrch.c
1 /* Copyright (c) 1999, 2000, 2002, 2004, 2005 by Arkkra Enterprises */
2 /* All rights reserved */
3
4 /* Functions related to walking through strings, to get each character
5  * in turn, along with any font/size changes and horizontal/vertical
6  * motions that should be done before printing that character.
7  */
8
9 #include "defines.h"
10 #include "structs.h"
11 #include "globals.h"
12
13
14 /* when entering pile, adjust size by PILE_A / PILE_B and when
15  * exiting pile, adjust by the recipocal of that. */
16 #define PILE_A  65
17 #define PILE_B  100
18
19
20 /* This structure contains information about one line in a set of lines
21  * that are piled on top of one another. This might be used for figured
22  * bass or superscript/subscript or things like that. */
23 struct PILED_LINE {
24         char *text;             /* points to start of piled line */
25         short align_offset;     /* where the alignment is, relative to text */
26         short length;           /* bytes in this line */
27         short last_char_offset; /* where the last character is */
28         short last_digit_offset;/* where the last digit is */
29         float width_left_of_align;      /* width for this line */
30         float width_right_of_align;
31         struct PILED_LINE *next;/* the next line in the pile */
32 };
33
34 /* This keep track of an entire piled section of a string */
35 struct PILED {
36         float curr_x, curr_y;   /* where we are now, relative to pile start */
37         struct PILED_LINE *lines_p;     /* list of lines in pile */
38         struct PILED_LINE *curr_line_p; /* which piled line we're processing */
39         short curr_offset;              /* offset into curr_line_p->text */
40         float width_left_of_align;      /* width of the entire pile */
41         float width_right_of_align;
42         short orig_font;                /* font coming into the pile */
43         short orig_size;                /* size coming into the pile */
44         short new_size;                 /* size at beginning of pile */
45         char *pile_end;                 /* last character of pile */
46 };
47
48 /* One of these structs is passed to get_piled() with the current
49  * font and size filled in. If we are in piled mode, it will return 1 and
50  * will have filled in all the field. Otherwise it will return 0.
51  */
52
53 struct PILE_INFO {
54         char    font;
55         char    size;
56         char    ch;     /* the next character in the pile, or \0 if
57                          * this only indicates motion */
58         double  hor;    /* how much to adjust before printing ch */
59         double  vert;   /* how much to adjust before printing ch */
60 };
61
62 /* When a backspace is returned, this keeps track of its width, to
63  * potentially be returned via backsp_width() */
64 static int Backspace_dist;
65
66 /* This is all the info about the piled section we are currently processing.
67  * We only deal with one pile at a time, so a single static structure
68  * meets our needs. */
69 static struct PILED Pile_info;
70
71 static void begin_pile P((struct PILE_INFO *pile_info_p));
72 static char *prep_pile P((char *string, struct PILE_INFO *pile_info_p));
73 static int get_piled P((struct PILE_INFO *pile_info_p));
74 static double vert_distance P((int vert_arg, int font, int size));
75 \f
76
77 /* When a STR_PILE is encountered, this should be called to gather all
78  * the information we might need about how to align everything in the pile.
79  * Then other functions like get_piled() can be called to retrieve info.
80  * The passed-in pile_info_p contains the font and size at the beginning
81  * of the pile, and on return will contain the new size and the vertical
82  * and horizontal offsets before the first character of the pile.
83  * Return value points to the last character that was processed from string.
84  */
85
86 static char *
87 prep_pile(string, pile_info_p)
88
89 char *string;           /* place in string where piling begins */
90 struct PILE_INFO *pile_info_p;  /* passed in font and size, returns size and vert */
91
92 {
93         struct PILED_LINE **piled_line_p_p;     /* where to link on next line */
94         struct PILED_LINE *line_p;              /* the current line of pile */
95         char *s;                                /* to walk through string */
96         int alignments = 0;                     /* how many align points found */
97         int lines = 0;                          /* how many lines in pile */
98         float width_contribution;               /* width of current char */
99         int i;                                  /* index though a line */
100         int orig_i;                             /* value of i when current
101                                                  * character began (some
102                                                  * things take up multiple
103                                                  * bytes and we need to know
104                                                  * where it began. */
105         int font, size;
106
107         /* initialize */
108         Pile_info.width_left_of_align = Pile_info.width_right_of_align = 0.0;
109         Pile_info.lines_p = (struct PILED_LINE *) 0;
110         Pile_info.curr_line_p = (struct PILED_LINE *) 0;
111         Pile_info.curr_offset = 0;
112         piled_line_p_p = &(Pile_info.lines_p);
113         line_p = *piled_line_p_p;
114
115         Pile_info.orig_font = font = pile_info_p->font;
116
117         /* Adjust size for entering the pile */
118         size = Pile_info.new_size = pile_size(pile_info_p->size, NO);
119
120         /* Walk through the piled things, skipping past the initial STR_PILE,
121          * this time just to find the line boundaries and alignment offsets. */
122         for (s = string + 1; *s != '\0'; s++) {
123
124                 /* if end piling, jump out */
125                 int curr_char;  
126                 curr_char = *s & 0xff;
127                 if ( curr_char == STR_PILE || curr_char == STR_BOX_END
128                                                 || curr_char == STR_CIR_END) {
129                         if (line_p != (struct PILED_LINE *) 0) {
130                                 line_p->length = s - line_p->text;
131                         }
132                         line_p = (struct PILED_LINE *) 0;
133                         s++;
134                         break;
135                 }
136
137                 /* If we don't have a current PILED_LINE struct,
138                  * get one, and initialize it. */
139                 if (line_p == (struct PILED_LINE *) 0) {
140                         MALLOC(PILED_LINE, *piled_line_p_p, 1);
141                         line_p = *piled_line_p_p;
142                         line_p->text = s;
143                         line_p->align_offset = -1;
144                         line_p->last_digit_offset = -1;
145                         line_p->last_char_offset = -1;
146                         line_p->length = -1;
147                         line_p->width_left_of_align = 0.0;
148                         line_p->width_right_of_align = 0.0;
149                         line_p->next = (struct PILED_LINE *) 0;
150                         lines++;
151                 }
152
153                 switch (*s & 0xff) {
154
155                 case STR_C_ALIGN:
156                 case STR_L_ALIGN:
157                         /* Point the offset at the following character,
158                          * because for C_ALIGN that character will straddle
159                          * the alignment point, and we can easily make
160                          * L_ALIGN work with that convention too. */
161                         line_p->align_offset = s - line_p->text + 1;
162                         alignments++;
163                         break;
164
165                 case '\n':
166                         /* if newline, finish this line by saving its length,
167                          * then prepare for next line by moving the place at
168                          * which to add a line to the current line's 'next' */
169                         line_p->length = s - line_p->text;
170                         piled_line_p_p = &(line_p->next);
171                         line_p = (struct PILED_LINE *) 0;
172                         break;
173
174                 case STR_FONT:
175                 case STR_SIZE:
176                 case STR_BACKSPACE:
177                 case STR_VERTICAL:
178                         /* need to skip past its argument byte */
179                         s++;
180                         break;
181
182                 case STR_MUS_CHAR:
183                 case STR_MUS_CHAR2:
184                         /* Mark as last character so far, in case we need
185                          * to use that as default alignment */
186                         line_p->last_char_offset = s - line_p->text;
187                         /* need to skip past its 2 argument bytes */
188                         s += 2;
189                         break;
190
191                 case STR_PAGENUM:
192                 case STR_NUMPAGES:
193                         /* We actually don't allow this, but can get to this
194                          * code in error cases. Skip past argument. */
195                         s++;
196                         break;
197
198                 default:
199                         /* Mark as last character so far, in case we need
200                          * to use that as default alignment */
201                         line_p->last_char_offset = s - line_p->text;
202
203                         /* remember location of last digit, in case we
204                          * need to use as default aligment */
205                         if ( isdigit(*s) ) {
206                                 line_p->last_digit_offset = s - line_p->text;
207                         }
208                         break;
209                 }
210         }
211
212         if (*s == '\0' && line_p != (struct PILED_LINE *) 0) {
213                 line_p->length = s - line_p->text;
214         }
215
216         /* if there are lines with unspecified alignment points, find them */
217         if (alignments < lines) {
218                 for (line_p = Pile_info.lines_p;
219                                 line_p != (struct PILED_LINE *) 0;
220                                 line_p = line_p->next) {
221                         /* If no user-specified alignment point in this line,
222                          * use last digit if any, else last char. If there was
223                          * no last char (empty line, maybe to get subscript
224                          * without superscript, for example) this will end
225                          * up with an offset of -1. */
226                         if (line_p->align_offset < 0) {
227                                 line_p->align_offset =
228                                     ((line_p->last_digit_offset >= 0)
229                                     ? line_p->last_digit_offset
230                                     : line_p->last_char_offset);
231                         }
232                 }
233         }
234
235         /* Find left and right of alignment for each line and the
236          * widest overall for the pile. */
237         for (line_p = Pile_info.lines_p;
238                                         line_p != (struct PILED_LINE *) 0;
239                                         line_p = line_p->next) {
240
241                 /* Some items take more than one byte. In that case,
242                  * the index 'i' in this 'for' loop will get more added to it
243                  * than just the increment at the end of each loop.
244                  * When this happens, orig_i keeps track of where the
245                  * multi-byte item began. */
246                 for (i = 0; i < line_p->length; i++) {
247                         
248                         orig_i = i;
249                         width_contribution = 0.0;
250
251                         switch (line_p->text[i] & 0xff) {
252
253                         case STR_MUS_CHAR:
254                         case STR_MUS_CHAR2:
255                                 /* get its width and skip past its args */
256                                 width_contribution += width(
257                                                 str2mfont(line_p->text[i] & 0xff),
258                                                 line_p->text[i+1],
259                                                 line_p->text[i+2]);
260                                 i += 2;
261                                 break;
262
263                         case STR_FONT:
264                                 /* Remember the font, may need for getting
265                                  * widths of future characters */
266                                 font = line_p->text[++i];
267                                 break;
268
269                         case STR_SIZE:
270                                 /* remember the size, may need for getting
271                                  * widths of future characters */
272                                 size = line_p->text[++i];
273                                 break;
274
275                         case STR_C_ALIGN:
276                         case STR_L_ALIGN:
277                         case STR_SLASH:
278                                 /* irrelevant for width, no args */
279                                 break;
280
281                         case STR_VERTICAL:
282                                 /* irrelevant for width, 1 byte arg to skip */
283                                 i++;
284                                 break;
285
286                         case STR_BACKSPACE:
287                                 width_contribution -= ((line_p->text[++i] *
288                                         size) / DFLT_SIZE) / BACKSP_FACTOR;
289                                 break;
290
291                         case STR_BOX:
292                         case STR_BOX_END:
293                         case STR_CIR:
294                         case STR_CIR_END:
295                                 /* not relevant here */
296                                 break;
297
298                         case STR_PAGENUM:
299                         case STR_NUMPAGES:
300                                 /* we don't really know how wide this will
301                                  * end up being, so just use the width of
302                                  * the following byte (% or #).
303                                  */
304                                 width_contribution = width(font, size,
305                                                 line_p->text[++i]);
306                                 break;
307                         default:
308                                 if ( IS_STR_COMMAND(line_p->text[i]) & 0xff) {
309                                         pfatal("unexpected STR_ value 0x%x in prep_pile()",
310                                                 line_p->text[i] & 0xff);
311                                 }
312
313                                 /* ordinary character, look up its width */
314                                 width_contribution = width(font, size,
315                                                 line_p->text[i]);
316                                 break;
317                         }
318
319                         /* Figure out which side of alignment.
320                          * Anything before align_offset goes on left,
321                          * while anything after goes on the right.
322                          * If we're right at the alignment offset,
323                          * and the alignment type is STR_C_ALIGN,
324                          * split the width. Otherwise goes on right. */
325                         if (orig_i < line_p->align_offset) {
326                                 line_p->width_left_of_align
327                                         += width_contribution;
328                         }
329                         else if (orig_i == line_p->align_offset &&
330                                         (line_p->text[orig_i - 1] & 0xff)
331                                         != STR_L_ALIGN) {
332                                 /* put half on each side */
333                                 width_contribution /= 2.0;
334                                 line_p->width_left_of_align
335                                         += width_contribution;
336                                 line_p->width_right_of_align
337                                         += width_contribution;
338                         }
339                         else {
340                                 line_p->width_right_of_align
341                                         += width_contribution;
342                         }
343                 }
344
345                 /* find widest line */
346                 if (line_p->width_left_of_align
347                                         > Pile_info.width_left_of_align) {
348                         Pile_info.width_left_of_align
349                                                 = line_p->width_left_of_align;
350                 }
351                 if (line_p->width_right_of_align
352                                         > Pile_info.width_right_of_align) {
353                         Pile_info.width_right_of_align
354                                                 = line_p->width_right_of_align;
355                 }
356         }
357
358         /* start at first character of first line of pile */
359         begin_pile(pile_info_p);
360
361         Pile_info.pile_end = s;
362         return(s);
363 }
364 \f
365
366 /* fill in the fields of pile_info_p for starting at the beginning of
367  * the current pile. */
368
369 static void
370 begin_pile(pile_info_p)
371
372 struct PILE_INFO *pile_info_p;
373
374 {
375         /* begin at first line of pile */
376         Pile_info.curr_line_p = Pile_info.lines_p;
377         Pile_info.curr_offset = -1;
378
379         /* if user ended a string with a pile indicator (which is a bit
380          * silly, but could happen...) get out now,
381          * to avoid dereferencing a line that doesn't exist. */
382         if (Pile_info.curr_line_p == (struct PILED_LINE *) 0) {
383                 return;
384         }
385
386         /* fill in rest of returned data */
387         pile_info_p->hor = Pile_info.curr_x = Pile_info.width_left_of_align
388                                 - Pile_info.lines_p->width_left_of_align;
389         /* Adjust vertical by half the unpiled line height, but less a little
390          * (there is some white space padding, usually piles are numbers
391          * without descenders, and they are smaller anyway, so looks better
392          * to jam things together a bit more than normal). */
393         pile_info_p->vert = Pile_info.curr_y
394                 = fontheight(Pile_info.orig_font, Pile_info.orig_size) / 2.0 
395                 - POINT * Pile_info.new_size / DFLT_SIZE;
396         pile_info_p->size = Pile_info.new_size;
397
398         pile_info_p->ch = '\0';
399 }
400 \f
401
402 /* Get the next character in piled segment, along with any horizontal
403  * and/or vertical motion that should be done before printing that
404  * character. Info is placed into the struct passed in. If the returned
405  * ->ch is '\0', that means this call only returned motion information.
406  * Returns NO at end of pile, otherwise YES.
407  */
408
409 static int
410 get_piled(pile_info_p)
411
412 struct PILE_INFO *pile_info_p;
413
414 {
415         char *text = 0;         /* Piled text to process. The initialization
416                                  * is just to avoid bogus "used before set"
417                                  * warning. It will always get set to a real
418                                  * string before actually being used. */
419         float backdist;         /* width of backspace */
420         int mfont;              /* FONT_MUSIC*  */
421
422
423         /* Move on to next character. */
424         ++(Pile_info.curr_offset);
425
426         if (Pile_info.curr_line_p != (struct PILED_LINE *) 0) {
427
428                 /* Check if we've gotten to end of pile. We are if we're
429                  * past the last character of the last line of the pile */
430                 if (Pile_info.curr_line_p->next == (struct PILED_LINE *) 0 &&
431                                 Pile_info.curr_offset
432                                 >= Pile_info.curr_line_p->length) {
433                         /* yes, at end of pile */
434                         Pile_info.curr_line_p = (struct PILED_LINE *) 0;
435                 }
436                 else {
437                         text = Pile_info.curr_line_p->text;
438                 }
439         }
440
441         /* check for end of pile */
442         if (Pile_info.curr_line_p == (struct PILED_LINE *) 0) {
443
444                 /* Adjust vertical and horizontal to past pile.
445                  * Horizontal adjust is the total width of the
446                  * pile minus where we are currently. */
447                 pile_info_p->hor = (Pile_info.width_left_of_align
448                                         + Pile_info.width_right_of_align)
449                                         - Pile_info.curr_x;
450                 /* Vertical adjust is the negative of the current
451                  * y, since that gets us back to zero relative to
452                  * where we started. */
453                 pile_info_p->vert = - (Pile_info.curr_y);
454
455                 /* Restore size for leaving the pile. */
456                 pile_info_p->size = pile_size(pile_info_p->size, YES);
457
458                 /* mark as not being an actual character, just motion */
459                 pile_info_p->ch = '\0';
460
461                 return(NO);
462         }
463
464         /* handle newline -- go to next line of pile */
465         if (text[Pile_info.curr_offset] == '\n') {
466                 /* prepare for next line, if any */
467                 Pile_info.curr_line_p = Pile_info.curr_line_p->next;
468                 Pile_info.curr_offset = -1;
469
470                 /* Adjust vertical and horizontal to start next line of pile.
471                  * For horizontal, go back to beginning of pile,
472                  * then add the difference between this line's
473                  * left of align and the pile's. */
474                 if (Pile_info.curr_line_p != (struct PILED_LINE *) 0) {
475                         pile_info_p->hor = - Pile_info.curr_x;
476                         Pile_info.curr_x = Pile_info.width_left_of_align
477                                 - Pile_info.curr_line_p->width_left_of_align;
478                         pile_info_p->hor += Pile_info.curr_x;
479
480                         /* vertical is adjusted by height of font, less
481                          * a little to jam together to look better. */
482                         pile_info_p->vert = - fontheight(pile_info_p->font,
483                                 pile_info_p->size)
484                                 + POINT * pile_info_p->size / DFLT_SIZE;
485                         Pile_info.curr_y += pile_info_p->vert;
486                 }
487                 else {
488                         pile_info_p->vert = pile_info_p->hor = 0.0;
489                 }
490
491                 /* only motion */
492                 pile_info_p->ch = '\0';
493
494         }
495
496         else {
497                 pile_info_p->hor = pile_info_p->vert = 0.0;
498
499                 switch (text[Pile_info.curr_offset] & 0xff) {
500
501                 case STR_MUS_CHAR:
502                 case STR_MUS_CHAR2:
503                         mfont = str2mfont( text[Pile_info.curr_offset] & 0xff);
504                         pile_info_p->font = mfont;
505                         pile_info_p->size = text[Pile_info.curr_offset+1];
506                         pile_info_p->ch = text[Pile_info.curr_offset+2];
507                         Pile_info.curr_x += width(mfont,
508                                         pile_info_p->size, pile_info_p->ch);
509                         Pile_info.curr_offset += 2;
510                         break;
511
512                 case STR_FONT:
513                         (Pile_info.curr_offset)++;
514                         pile_info_p->font = text[Pile_info.curr_offset];
515                         pile_info_p->ch = '\0';
516                         break;
517
518                 case STR_SIZE:
519                         (Pile_info.curr_offset)++;
520                         pile_info_p->size = text[Pile_info.curr_offset];
521                         pile_info_p->ch = '\0';
522                         break;
523
524                 case STR_SLASH:
525                         pile_info_p->ch = (char) STR_SLASH;
526                         break;
527
528                 case STR_C_ALIGN:
529                 case STR_L_ALIGN:
530                         pile_info_p->ch = '\0';
531                         break;
532
533                 case STR_VERTICAL:
534                         (Pile_info.curr_offset)++;
535                         pile_info_p->vert = vert_distance(
536                                 DECODE_VERT(text[Pile_info.curr_offset]),
537                                 pile_info_p->font, pile_info_p->size);
538                         pile_info_p->ch = '\0';
539                         break;
540
541                 case STR_BACKSPACE:
542                         (Pile_info.curr_offset)++;
543                         Backspace_dist = text[Pile_info.curr_offset];
544                         backdist = backsp_width(pile_info_p->size);
545                         pile_info_p->hor -= backdist;
546                         Pile_info.curr_x -= backdist;
547                         pile_info_p->ch = '\0';
548                         break;
549
550                 case STR_PAGENUM:
551                 case STR_NUMPAGES:
552                         /* These aren't really allowed in piles,
553                          * but we can hit this code under some error cases,
554                          * so we recognize them and do something reasonable.
555                          */
556                         (Pile_info.curr_offset)++;
557                         pile_info_p->ch = text[Pile_info.curr_offset];
558                         break;
559                 
560                 default:
561                         if ( IS_STR_COMMAND(text[Pile_info.curr_offset]) & 0xff) {
562                                 pfatal("unexpected STR_ value 0x%x in get_piled()",
563                                         text[Pile_info.curr_offset] & 0xff);
564                         }
565
566                         /* ordinary character */
567                         Pile_info.curr_x += width(pile_info_p->font,
568                                         pile_info_p->size,
569                                         text[Pile_info.curr_offset]);
570                         pile_info_p->ch = text[Pile_info.curr_offset];
571                         break;
572                 }
573         }
574
575         return(YES);
576 }
577 \f
578
579 /* given a pointer into a string, return the code for the next
580  * character in the string. This will be either the ASCII code
581  * or the music symbol code. This function will also update the
582  * pointer into the string and the font and size if appropriate.
583  * If there happens to be a music character in the middle of a string,
584  * the font (and maybe size) will temporarily be changed for the music
585  * font. This function keeps track of that.
586  *
587  * Restrictions: This function must be used by calling it on the beginning
588  * of a string, then to walk through that string. You don't have to go all the
589  * way to the end of a given string, but once you switch to a new string
590  * (from which you must begin at its beginning) you cannot go back to the
591  * middle of some other string that you had been working on before.
592  *
593  * This function calls the more general function which can also update
594  * vertical motion and alignment information. It is retained for backward
595  * compatibility for the places that don't care about those things.
596  */
597
598 int
599 next_str_char(str_p_p, font_p, size_p)
600
601 char **str_p_p; /* address of pointer into string. Will be incremented
602                  * to beyond current character on return (this might
603                  * be several bytes into the string if current char
604                  * is a special string command */
605 int *font_p;    /* pointer to font. Will be updated to new font if
606                  * appropriate */
607 int *size_p;    /* pointer to size. Will be updated to new size if
608                  * appropriate */
609
610 {
611         /* place to hold things we don't care about from general function */
612         double dummy_vertical, dummy_horizontal;
613         int dummy_in_pile;
614         int dummy_textfont;
615
616         return(nxt_str_char(str_p_p, font_p, size_p, &dummy_textfont,
617                 &dummy_vertical, &dummy_horizontal, &dummy_in_pile, NO));
618 }
619 \f
620
621 /* returns the next character in string, along with any font/size changes
622  * and horizontal/vertical motions that should happen before that character.
623  */
624
625 int
626 nxt_str_char(str_p_p, font_p, size_p, textfont_p, vertical_p, horizontal_p, in_pile_p, slash)
627
628 char **str_p_p;
629 int *font_p;
630 int *size_p;
631 int *textfont_p;        /* font disregarding music characters */
632 double *vertical_p;     /* if non-zero, do vertical motion */
633 double *horizontal_p;   /* if non-zero, do horizontal motion */
634 int *in_pile_p;         /* will be set to YES if inside a pile */
635 int slash;              /* if YES, return any STR_SLASH's found,
636                          * else ignore them */
637
638 {
639         static char *prev_str_p;        /* most recent value of *str_p_p */
640         static int textfont;            /* current font, ignoring music chars */
641         static int textsize;            /* current size, ignoring music chars */
642         static int pile_mode;           /* if inside a pile */
643         int code;
644         struct PILE_INFO pile_info;
645
646         /* initialize to assume no vertical motion or alignment at this char */
647         *horizontal_p = *vertical_p = 0.0;
648
649         if (*str_p_p == (char *) 0) {
650                 return(0);
651         }
652
653         for ( ; ; ) {
654
655                 if (prev_str_p != *str_p_p) {
656                         /* must be starting on a different string. Our
657                          * remembered text font/size are no longer valid */
658                         textfont = *font_p;
659                         *textfont_p = textfont;
660                         textsize = *size_p;
661                         pile_mode = NO;
662                 }
663
664                 if (pile_mode == YES) {
665                         /* call the special function for piled to get next */
666                         pile_info.font = textfont;
667                         pile_info.size = textsize;
668                         pile_mode = get_piled(&pile_info);
669
670                         /* save what it returned */
671                         *font_p = pile_info.font;
672                         *size_p = pile_info.size;
673                         if (pile_info.font != FONT_MUSIC) {
674                                 textfont = pile_info.font;
675                                 *textfont_p = textfont;
676                                 textsize = pile_info.size;
677                         }
678
679                         /* Add motion to any accumulated so far */
680                          *vertical_p += pile_info.vert;
681                          *horizontal_p += pile_info.hor;
682
683                         if (pile_info.ch == '\0') {
684                                 /* only motion this time, keep going */
685                                 continue;
686                         }
687                         else if ( (pile_info.ch & 0xff) == STR_SLASH &&
688                                                 slash == NO) {
689                                 /* caller doesn't care about slashes */
690                                 continue;
691                         }
692                         else {
693                                 *in_pile_p = pile_mode;
694                                 return (pile_info.ch);
695                         }
696                 }
697                 *in_pile_p = NO;
698
699                 if (**str_p_p == '\0') {
700                         /* end of string */
701                         *font_p = textfont;
702                         *size_p = textsize;
703                         return(0);
704                 }
705
706                 if ( ! IS_STR_COMMAND(**str_p_p)) {
707                         /* just an ordinary character. Return it after updating
708                          * pointer to point past it */
709                         code = **str_p_p & 0xff;
710                         ++(*str_p_p);
711                         prev_str_p = *str_p_p;
712
713                         /* if last chacter was music character, put back real                            * font and size */
714                         if (IS_MUSIC_FONT(*font_p)) {
715                                 *font_p = textfont;
716                                 *size_p = textsize;
717                         }
718                         return(code);
719                 }
720
721                 /* special command. see which one */
722                 switch ( (unsigned) (**str_p_p & 0xff) ) {
723
724                 case STR_FONT:
725                         /* change font to specified font and point past it */
726                         *font_p = *++(*str_p_p);
727                         ++(*str_p_p);
728                         textfont = *font_p;
729                         *textfont_p = textfont;
730                         break;
731
732                 case STR_SIZE:
733                         /* change size to specified size and point past it */
734                         *size_p = *++(*str_p_p);
735                         ++(*str_p_p);
736                         textsize = *size_p;
737                         break;
738
739                 case STR_PAGENUM:
740                         /* Page number and number of pages are strange cases.
741                          * We don't necessarily know at this point
742                          * how many digits long they will be. So we punt:
743                          * just return % or #. For height, ascent, and descent,
744                          * the % or # will be the same as any number
745                          * of digits (or at least close enough, we hope).
746                          * That means the only problem is when we are getting
747                          * the width of a string or actually printing
748                          * it, so we'll deal with those as special cases */
749                         *str_p_p += 2;
750                         prev_str_p = *str_p_p;
751                         return('%');
752                 case STR_NUMPAGES:
753                         *str_p_p += 2;
754                         prev_str_p = *str_p_p;
755                         return('#');
756
757
758                 case STR_MUS_CHAR:
759                 case STR_MUS_CHAR2:
760                         /* music character */
761                         *font_p = str2mfont( (unsigned) (**str_p_p & 0xff) );
762                         *size_p = *++(*str_p_p);
763                         code = (*++(*str_p_p)) & 0xff;
764                         ++(*str_p_p);
765                         prev_str_p = *str_p_p;
766                         return(code);
767
768                 case STR_BACKSPACE:
769                         Backspace_dist = (*++(*str_p_p)) & 0xff;
770                         ++(*str_p_p);
771                         prev_str_p = *str_p_p;
772                         *font_p = textfont;
773                         *size_p = textsize;
774                         return('\b');
775
776                 case STR_PRE:
777                 case STR_U_PRE:
778                 case STR_PRE_END:
779                 case STR_PST:
780                 case STR_U_PST:
781                 case STR_PST_END:
782                 case STR_BOX:
783                 case STR_BOX_END:
784                 case STR_CIR:
785                 case STR_CIR_END:
786                         ++(*str_p_p);
787                         break;
788
789                 case STR_VERTICAL:
790                         *vertical_p += vert_distance(
791                                         DECODE_VERT( *++(*str_p_p) ),
792                                         textfont, textsize);
793                         ++(*str_p_p);
794                         break;
795
796                 case STR_C_ALIGN:
797                 case STR_L_ALIGN:
798                         ++(*str_p_p);
799                         break;
800
801                 case STR_PILE:
802                         pile_info.font = textfont;
803                         pile_info.size = textsize;
804                         *str_p_p = prep_pile(*str_p_p, &pile_info);
805                         *vertical_p += pile_info.vert;
806                         *horizontal_p += pile_info.hor;
807                         *size_p = pile_info.size;
808                         textsize = *size_p;
809                         pile_mode = YES;
810                         break;
811
812                 case STR_SLASH:
813                         ++(*str_p_p);
814                         prev_str_p = *str_p_p;
815                         if (slash == YES) {
816                                 return(STR_SLASH);
817                         }
818                         break;
819
820                 default:
821                         pfatal("bad internal format for string (%x)",
822                                         **str_p_p & 0xff);
823                         /*NOTREACHED*/
824                         break;
825                 }
826                 prev_str_p = *str_p_p;
827         }
828 }
829 \f
830
831 /* Given an argument to STR_VERTICAL and a font and size,
832  * return the distance in inches to move vertically. */
833
834 static double
835 vert_distance(vert_arg, font, size)
836
837 int vert_arg;
838 int font;
839 int size;
840
841 {
842         double ratio;
843
844         /* first convert to ratio relative to font height */
845         if (vert_arg >= 0) {
846                 ratio = (double) vert_arg / (double) MAXVERTICAL;
847         }
848         else {
849                 ratio = (double) vert_arg / (double) (-MINVERTICAL);
850         }
851         return(ratio * fontheight(font, size));
852 }
853 \f
854
855 /* return width of most recent backspace returned by next_str_char() */
856
857 double
858 backsp_width(size)
859
860 int size;       /* adjust to this point size */
861
862 {
863         return ( (double) Backspace_dist * ((double) size / (double) DFLT_SIZE)
864                                         / BACKSP_FACTOR);
865 }
866
867
868 /* Return width of current pile */
869
870 double
871 pile_width()
872
873 {
874         return(Pile_info.width_left_of_align + Pile_info.width_right_of_align);
875 }
876 \f
877
878 /* adjust size when entering/leaving a pile. Called with current size and
879  * whether we are entering or leaving. It returns the new size. */
880
881 int
882 pile_size(size, in_pile)
883
884 int size;       /* current size */
885 int in_pile;    /* if YES, we are currently in a pile and are about to leave
886                  * it; if NO, we are about to enter the pile */
887
888 {
889         int new_size;
890
891         if (in_pile == NO) {
892                 /* entering pile. Save current size, and return new size */
893                 Pile_info.orig_size = size;
894                 /* constrain new size to be at least MINSIZE */
895                 if ((Pile_info.new_size = PILE_A * size / PILE_B) < MINSIZE) {
896                         Pile_info.new_size = MINSIZE;
897                 }
898                 return(Pile_info.new_size);
899         }
900         else {
901                 /* leaving pile */
902                 /* Restore size. If we're still at the size at the beginning
903                  * of the pile, use the orginal value before the pile to avoid
904                  * any possible roundoff errors going back and forth,
905                  * otherwise adjust by inverse of the factor used when
906                  * entering the pile. */
907                 if (size == Pile_info.new_size) {
908                         return(Pile_info.orig_size);
909                 }
910                 else {
911                         if ((new_size = (size * PILE_B) / PILE_A) > MAXSIZE) {
912                                 /* constrain to no larger than MAXSIZE */
913                                 new_size = MAXSIZE;
914                         }
915                         return(new_size);
916                 }
917         }
918 }
919 \f
920
921 /* given where a pile begins and the font/size at that point, return the
922  * width from the beginning to the pile to the alignment point of the pile. */
923
924 double
925 align_distance(string, font, size)
926
927 char *string;   /* pile starts here */
928 int font;
929 int size;
930
931 {
932         struct PILE_INFO pile_info;
933
934         pile_info.font = font;
935         pile_info.size = size;
936         (void) prep_pile(string, &pile_info);
937         return(Pile_info.width_left_of_align);
938 }