chiark / gitweb /
Merge branch 'arkkra' into shiny
[mup] / mup / mup / nxtstrch.c
CommitLineData
69695f33
MW
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. */
23struct 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 */
35struct 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
53struct 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() */
64static 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. */
69static struct PILED Pile_info;
70
71static void begin_pile P((struct PILE_INFO *pile_info_p));
72static char *prep_pile P((char *string, struct PILE_INFO *pile_info_p));
73static int get_piled P((struct PILE_INFO *pile_info_p));
74static 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
86static char *
87prep_pile(string, pile_info_p)
88
89char *string; /* place in string where piling begins */
90struct 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
369static void
370begin_pile(pile_info_p)
371
372struct 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
409static int
410get_piled(pile_info_p)
411
412struct 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
598int
599next_str_char(str_p_p, font_p, size_p)
600
601char **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 */
605int *font_p; /* pointer to font. Will be updated to new font if
606 * appropriate */
607int *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
625int
626nxt_str_char(str_p_p, font_p, size_p, textfont_p, vertical_p, horizontal_p, in_pile_p, slash)
627
628char **str_p_p;
629int *font_p;
630int *size_p;
631int *textfont_p; /* font disregarding music characters */
632double *vertical_p; /* if non-zero, do vertical motion */
633double *horizontal_p; /* if non-zero, do horizontal motion */
634int *in_pile_p; /* will be set to YES if inside a pile */
635int 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
834static double
835vert_distance(vert_arg, font, size)
836
837int vert_arg;
838int font;
839int 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
857double
858backsp_width(size)
859
860int 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
870double
871pile_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
881int
882pile_size(size, in_pile)
883
884int size; /* current size */
885int 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
924double
925align_distance(string, font, size)
926
927char *string; /* pile starts here */
928int font;
929int 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}