Commit | Line | Data |
---|---|---|
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. */ | |
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 | } |