Added hint about removed "freepascal" to a file which is supposed to have that inform...
[AROS-Contrib.git] / MultiMedia / abcm2ps / subs.c
blob33cf98bae05efd6dbe54366642f0c40c8c57e582
1 /*
2 * Low-level utilities.
4 * This file is part of abcm2ps.
6 * Copyright (C) 1998-2008 Jean-François Moine
7 * Adapted from abc2ps, Copyright (C) 1996,1997 Michael Methfessel
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <time.h>
28 #include <string.h>
29 #include <ctype.h>
31 #include "abcparse.h"
32 #include "abc2ps.h"
34 char tex_buf[TEX_BUF_SZ]; /* result of tex_str() */
35 int outft = -1; /* last font in the output file */
37 static char *strop; /* current string output operation */
38 static float strlw; /* line width */
39 static float strtw = -1; /* current text width */
40 static int strns; /* number of spaces (justify) */
41 static int curft; /* current (wanted) font */
42 static int defft; /* default font */
43 static int strtx; /* PostScript text outputing */
45 /* width of characters according to the encoding */
46 /* these are the widths for Times-Roman, extracted from the 'a2ps' package */
47 static short ISOLatin1_w[256] = {
48 0, 0, 0, 0, 0, 0, 0, 0, /* \002: hyphen in lyrics */
49 0, 0, 0, 0, 0, 0, 0, 0,
50 0, 0, 0, 0, 0, 0, 0, 0,
51 0, 0, 0, 0, 0, 0, 0, 0,
52 250,333,408,500,500,833,778,333,
53 333,333,500,564,250,564,250,278,
54 500,500,500,500,500,500,500,500,
55 500,500,278,278,564,564,564,444,
56 921,722,667,667,722,611,556,722,
57 722,333,389,722,611,889,722,722,
58 556,722,667,556,611,722,722,944,
59 722,722,611,333,278,333,469,500,
60 333,444,500,444,500,444,333,500,
61 500,278,278,500,278,778,500,500,
62 500,500,333,389,278,500,500,722,
63 500,500,444,480,200,480,541, 0,
64 0,500,500,500, 0, 0, 0, 0, /* \201..\203: sharp, flat and natural signs */
65 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 0, 0, 0, 0,
67 0, 0, 0, 0, 0, 0, 0, 0,
68 250,333,500,500,500,500,200,500,
69 333,760,276,500,564,333,760,333,
70 400,564,300,300,333,500,453,350,
71 333,278,310,500,750,750,750,444,
72 722,722,722,722,722,722,889,667,
73 611,611,611,611,333,333,333,333,
74 722,722,722,722,722,722,722,564,
75 722,722,722,722,722,722,556,500,
76 444,444,444,444,444,444,667,444,
77 444,444,444,444,278,278,278,278,
78 500,500,500,500,500,500,500,564,
79 500,500,500,500,500,500,500,500,
81 static short ISOLatin2_w[96] = {
82 250,500,333,611,500,500,500,500,
83 333,556,500,500,500,333,611,500,
84 400,500,333,278,333,500,500,333,
85 333,389,500,500,500,333,444,500,
86 500,722,722,500,722,500,500,667,
87 500,611,500,611,500,333,333,500,
88 500,500,500,722,722,500,722,564,
89 500,500,722,500,722,722,500,500,
90 500,444,444,500,444,500,500,444,
91 500,444,500,444,500,278,278,500,
92 500,500,500,500,500,500,500,564,
93 500,500,500,500,500,500,500,333,
95 static short ISOLatin3_w[96] = {
96 250,500,333,500,500,500,500,500,
97 333,500,500,500,500,333,760,500,
98 400,500,300,300,333,500,500,350,
99 333,278,500,500,500,750,750,500,
100 722,722,722,722,722,500,500,667,
101 611,611,611,611,333,333,333,333,
102 722,722,722,722,722,500,722,564,
103 500,722,722,722,722,500,500,500,
104 444,444,444,444,444,500,500,444,
105 444,444,444,444,278,278,278,278,
106 500,500,500,500,500,500,500,564,
107 500,500,500,500,500,500,500,333,
109 static short ISOLatin4_w[96] = {
110 250,500,500,500,500,500,500,500,
111 333,556,500,500,500,333,611,333,
112 400,500,333,500,333,500,500,333,
113 333,389,500,500,500,500,444,500,
114 500,722,722,722,722,722,889,500,
115 500,611,500,611,500,333,333,500,
116 722,500,500,500,722,722,722,564,
117 722,500,722,722,722,500,500,500,
118 500,444,444,444,444,444,667,500,
119 500,444,500,444,500,278,278,500,
120 500,500,500,500,500,500,500,564,
121 500,500,500,500,500,500,500,333,
123 static short ISOLatin5_w[96] = {
124 250,333,500,500,500,500,200,500,
125 333,760,276,500,564,333,760,333,
126 400,564,300,300,333,500,453,350,
127 333,278,310,500,750,750,750,444,
128 722,722,722,722,722,722,889,667,
129 611,611,611,611,333,333,333,333,
130 500,722,722,722,722,722,722,564,
131 722,722,722,722,722,500,500,500,
132 444,444,444,444,444,444,667,444,
133 444,444,444,444,278,278,278,278,
134 500,500,500,500,500,500,500,564,
135 500,500,500,500,500,278,500,500,
137 static short ISOLatin6_w[96] = {
138 250,500,500,500,500,500,500,500,
139 333,500,556,500,611,333,500,500,
140 500,500,500,500,500,500,500,500,
141 500,500,389,500,444,500,500,500,
142 500,722,722,722,722,722,889,500,
143 500,611,500,611,500,333,333,333,
144 500,500,500,722,722,722,722,500,
145 722,500,722,722,722,722,556,500,
146 500,444,444,444,444,444,667,500,
147 500,444,500,444,500,278,278,278,
148 500,500,500,500,500,500,500,500,
149 500,500,500,500,500,500,500,500,
152 static short *cw_tb[] = {
153 ISOLatin1_w, /* 0 = ascii */
154 ISOLatin1_w,
155 ISOLatin2_w - 160,
156 ISOLatin3_w - 160,
157 ISOLatin4_w - 160,
158 ISOLatin5_w - 160,
159 ISOLatin6_w - 160
162 /* escaped character table */
163 /* adapted from the 'recode' package - first index is 128 + 32 */
164 static char ISOLatin1_c[] =
165 "NS!!CtPdCuYeBBSE':Co-a<<NO--Rg'-DG+-2S3S''MyPI.M',1S-o>>141234?I"
166 "A!A'A>A?A:AAAEC,E!E'E>E:I!I'I>I:D-N?O!O'O>O?O:*XO/U!U'U>U:Y'THss"
167 "a!a'a>a?a:aaaec,e!e'e>e:i!i'i>i:d-n?o!o'o>o?o:-:o/u!u'u>u:y'thy:";
168 static char ISOLatin2_c[] =
169 "NSA;'(L/CuL<S'SE':S<S,T<Z'--Z<Z.DGa;';l/''l<s''<',s<s,t<z''\"z<z."
170 "R'A'A>A(A:L'C'C,C<E'E;E:E<I'I>D<D/N'N<O'O>O\"O:*XR<U0U'U\"U:Y'T,ss"
171 "r'a'a>a(a:l'c'c,c<e'e;e:e<i'i>d<d/n'n<o'o>o\"o:-:r<u0u'u\"u:y't,'.";
172 static char ISOLatin3_c[] =
173 "NSH/'(PdCu H>SE':I.S,G(J>-- Z.DGh/2S3S''Myh>.M',i.s,g(j>12 z."
174 "A!A'A> A:C.C>C,E!E'E>E:I!I'I>I: N?O!O'O>G.O:*XG>U!U'U>U:U(S>ss"
175 "a!a'a> a:c.c>c,e!e'e>e:i!i'i>i: n?o!o'o>g.o:-:g>u!u'u>u:u(s>'.";
176 static char ISOLatin4_c[] =
177 "NSA;kkR,CuI?L,SE':S<E-G,T/--Z<'-DGa;';r,''i?l,'<',s<e-g,t/NGz<ng"
178 "A-A'A>A?A:AAAEI;C<E'E;E:E.I'I>I-D/N,O-K,O>O?O:*XO/U;U'U>U:U?U-ss"
179 "a-a'a>a?a:aaaei;c<e'e;e:e.i'i>i-d/n,o-k,o>o?o:-:o/u;u'u>u:u?u-'.";
180 static char ISOLatin5_c[] =
181 "NS!!CtPdCuYeBBSE':Co-a<<NO--Rg'-DG+-2S3S''MyPI.M',1S-o>>141234?I"
182 "A!A'A>A?A:AAAEC,E!E'E>E:I!I'I>I:G(N?O!O'O>O?O:*XO/U!U'U>U:I.S,ss"
183 "a!a'a>a?a:aaaec,e!e'e;e:e.i'i>i-g(n?o!o'o>o?o:-:o/u!u'u>u:i.s,y:";
184 static char ISOLatin6_c[] =
185 "NSA;E-G,I-I?K,L,N'R,S<T/Z<--kkNGd/a;e-g,i-i?k,l,n'r,s<t/z<SEssng"
186 "A-A'A>A?A:AAAEI;C<E'E;E:E.I'I>I:D/N,O-O'O>O?O:U?O/U;U'U>U:Y'THU-"
187 "a-a'a>a?a:aaaei;c<e'e;e:e.i'i>i:d-n,o-o'o>o?o:u?o/u;u'u>u:y'thu-";
188 static char *esc_tb[] = {
189 ISOLatin1_c, /* 0 = ascii */
190 ISOLatin1_c,
191 ISOLatin2_c,
192 ISOLatin3_c,
193 ISOLatin4_c,
194 ISOLatin5_c,
195 ISOLatin6_c
198 static struct u_ps {
199 struct u_ps *next;
200 char text[2];
201 } *user_ps;
203 static char *trim_title(char *p, int first);
205 /* -- print message for internal error and maybe stop -- */
206 void bug(char *msg, int fatal)
208 error(1, 0, "Internal error: %s.", msg);
209 if (fatal) {
210 fprintf(stderr, "Emergency stop.\n\n");
211 exit(3);
213 fprintf(stderr, "Trying to continue...\n");
216 /* -- print an error message -- */
217 void error(int sev, /* 0: warning, 1: error */
218 struct SYMBOL *s,
219 char *fmt, ...)
221 va_list args;
222 static struct SYMBOL *t;
224 if (t != info['T' - 'A']) {
225 char *p;
227 t = info['T' - 'A'];
228 p = &t->as.text[2];
229 while (isspace((unsigned char) *p))
230 p++;
231 fprintf(stderr, " - In tune `%s':\n", p);
233 fprintf(stderr, sev == 0 ? "Warning " : "Error ");
234 if (s != 0) {
235 fprintf(stderr, "in line %d.%d",
236 s->as.linenum, s->as.colnum);
237 if (showerror) {
238 s->as.flags |= ABC_F_ERROR;
239 showerror++;
242 fprintf(stderr, ": ");
243 va_start(args, fmt);
244 vfprintf(stderr, fmt, args);
245 va_end(args);
246 fprintf(stderr, "\n");
249 /* -- read a number with a unit -- */
250 float scan_u(char *str)
252 float a;
253 int nch;
255 if (sscanf(str, "%f%n", &a, &nch) == 1) {
256 if (str[nch] == '\0' || str[nch] == ' ')
257 return a PT;
258 if (!strncasecmp(str + nch, "cm", 2))
259 return a CM;
260 if (!strncasecmp(str + nch, "in", 2))
261 return a IN;
262 if (!strncasecmp(str + nch, "pt", 2))
263 return a PT;
265 error(1, 0, "\n++++ Unknown unit value \"%s\"", str);
266 return 20 PT;
269 /* -- capitalize a string -- */
270 static void cap_str(char *p)
272 while (*p != '\0') {
273 #if 1
274 /* pb with toupper - works with ASCII only */
275 unsigned char c;
277 c = (unsigned char) *p;
278 if ((c >= 'a' && c <= 'z')
279 || (c >= 0xe0 && c <= 0xfe))
280 *p = c & ~0x20;
281 #else
282 *p = toupper((unsigned char) *p);
283 #endif
284 p++;
288 /* -- return the character width -- */
289 float cwid(unsigned char c)
291 short *w;
292 unsigned enc;
294 if ((enc = cfmt.encoding) >= sizeof cw_tb / sizeof cw_tb[0])
295 enc = 0;
296 if (c < 160) /* (0xa0) */
297 w = ISOLatin1_w;
298 else w = cw_tb[enc];
299 return (float) w[c] / 1000.;
302 /* -- change string taking care of some tex-style codes -- */
303 /* Puts \ in front of ( and ) in case brackets are not balanced,
304 * interprets all ISOLatin1..6 escape sequences as defined in rfc1345.
305 * Returns an estimated width of the string. */
306 float tex_str(char *s)
308 char *d, c1, c2, *p_enc, *p;
309 int maxlen, i;
310 float w, swfac;
312 w = 0;
313 d = tex_buf;
314 maxlen = sizeof tex_buf - 1; /* have room for EOS */
315 if ((i = curft) <= 0)
316 i = defft;
317 swfac = cfmt.font_tb[i].swfac;
318 i = font_enc[cfmt.font_tb[i].fnum];
319 if ((unsigned) i >= sizeof esc_tb / sizeof esc_tb[0])
320 i = 0;
321 p_enc = esc_tb[i];
322 while ((c1 = *s++) != '\0') {
323 switch (c1) {
324 case '\\': /* backslash sequences */
325 if (*s == '\0')
326 continue;
327 c1 = *s++;
328 if (c1 == ' ')
329 break;
330 if (c1 == 't') {
331 c1 = '\t';
332 break;
334 if (c1 == '\\' || (c2 = *s) == '\0') {
335 if (--maxlen <= 0)
336 break;
337 *d++ = '\\';
338 break;
340 /* treat escape with octal value */
341 if ((unsigned) (c1 - '0') <= 3
342 && (unsigned) (c2 - '0') <= 7
343 && (unsigned) (s[1] - '0') <= 7) {
344 c1 = ((c1 - '0') << 6) + ((c2 - '0') << 3) + s[1] - '0';
345 s += 2;
346 break;
348 /* convert to rfc1345 */
349 switch (c1) {
350 case '`': c1 = '!'; break;
351 case '^': c1 = '>'; break;
352 case '~': c1 = '?'; break;
353 case '"': c1 = ':'; break;
354 /* special TeX sequences */
355 case 'O': c1 = '/'; c2 = 'O'; s--; break;
356 case 'o': c1 = '/'; c2 = 'o'; s--; break;
357 case 'c': if (c2 == 'c' || c2 == 'C')
358 c1 = ',';
359 break;
361 switch (c2) {
362 case '`': c2 = '!'; break;
363 case '^': c2 = '>'; break;
364 case '~': c2 = '?'; break;
365 case '"': c2 = ':'; break;
367 for (i = 32 * 3, p = p_enc; --i >= 0; p += 2) {
368 if ((*p == c1 && p[1] == c2)
369 || (*p == c2 && p[1] == c1)) {
370 s++;
371 c1 = (p - p_enc) / 2 + 128 + 32;
372 break;
375 if (i < 0)
376 c1 = s[-1];
377 break;
378 case '$':
379 if (isdigit((unsigned char) *s)
380 && (unsigned) (*s - '0') < FONT_UMAX) {
381 i = *s - '0';
382 if (i == 0)
383 i = defft;
384 swfac = cfmt.font_tb[i].swfac;
385 i = cfmt.font_tb[i].fnum;
386 i = font_enc[i];
387 if ((unsigned) i >= sizeof esc_tb / sizeof esc_tb[0])
388 i = 0;
389 p_enc = esc_tb[i];
390 if (--maxlen <= 0)
391 break;
392 *d++ = c1;
393 c1 = *s++;
394 goto addchar_nowidth;
396 if (*s == '$') {
397 if (--maxlen <= 0)
398 break;
399 *d++ = c1;
400 s++;
402 break;
403 case '(':
404 case ')': /* ( ) becomes \( \) */
405 if (--maxlen <= 0)
406 break;
407 *d++ = '\\';
408 break;
410 w += cwid((unsigned char) c1) * swfac;
411 addchar_nowidth:
412 if (--maxlen <= 0)
413 break;
414 *d++ = c1;
416 *d = '\0';
417 return w;
420 /* -- set the default font of a string -- */
421 void str_font(int ft)
423 curft = defft = ft;
426 /* -- get the current default font -- */
427 int get_str_font(void)
429 return defft;
432 /* -- output one string -- */
433 static void str_ft_out1(char *p, int l)
435 if (curft != outft) {
436 if (strtx) {
437 PUT1(")%s ", strop);
438 strtx = 0;
440 if (curft == 0)
441 curft = defft;
442 set_font(curft);
444 if (!strtx) {
445 PUT0("(");
446 strtx = 1;
448 PUT2("%.*s", l, p);
451 /* -- output a string and the font changes -- */
452 static void str_ft_out(char *p, int end)
454 char *q;
456 q = p;
457 while (*p != '\0') {
458 if (*p == '$') {
459 if (isdigit((unsigned char) p[1])
460 && (unsigned) (p[1] - '0') < FONT_UMAX) {
461 if (p > q)
462 str_ft_out1(q, p - q);
463 if (curft != p[1] - '0') {
464 curft = p[1] - '0';
465 if (curft == 0)
466 curft = defft;
468 p += 2;
469 q = p;
470 continue;
472 if (p[1] == '$') {
473 str_ft_out1(q, p - q);
474 q = ++p;
477 p++;
479 if (p > q)
480 str_ft_out1(q, p - q);
481 if (end && strtx) {
482 PUT1(")%s ", strop);
483 strtx = 0;
487 /* -- output a string, handling the font changes -- */
488 void str_out(char *p, int action)
490 if (curft <= 0) /* first call */
491 curft = defft;
493 /* special case when font change at start of text */
494 /*---fixme: authorize 2 chars?*/
495 if (*p == '$' && isdigit((unsigned char) p[1])
496 && (unsigned) (p[1] - '0') < FONT_UMAX) {
497 if (curft != p[1] - '0') {
498 curft = p[1] - '0';
499 if (curft == 0)
500 curft = defft;
502 p += 2;
505 /* direct output if no font change */
506 if (strchr(p, '$') == 0) {
507 char *op;
509 set_font(curft);
510 if (action == A_CENTER)
511 op = "c";
512 else if (action == A_RIGHT)
513 op = "r";
514 else op = "";
515 PUT2("(%s)show%s ", p, op);
516 return;
519 /* if not left aligned, build a PS function */
520 if (action == A_LEFT)
521 strop = "show";
522 else {
523 PUT0("/str{");
524 outft = -1;
525 strop = "strop";
528 str_ft_out(p, 1); /* output the string */
530 /* if not left aligned, call the PS function */
531 if (action == A_LEFT)
532 return;
533 PUT0("}def\n"
534 "/strop/strw load def/w 0 def str w ");
535 if (action == A_CENTER)
536 PUT0("0.5 mul ");
537 PUT0("neg 0 RM/strop/show load def str ");
540 /* -- output a string with TeX translation -- */
541 void put_str(char *str, int action)
543 tex_str(str);
544 str_out(tex_buf, action);
545 PUT0("\n");
548 /* -- output a header information -- */
549 static void put_inf(struct SYMBOL *s)
551 char *p;
553 p = s->as.text;
554 if (p[1] == ':')
555 p += 2;
556 while (isspace((unsigned char) *p))
557 p++;
558 put_str(p, A_LEFT);
561 /* -- output a header format '111 (222)' -- */
562 static void put_inf2r(struct SYMBOL *s1,
563 struct SYMBOL *s2,
564 int action)
566 char buf[256], *p, *q;
568 if (s1 == 0) {
569 s1 = s2;
570 s2 = 0;
572 p = s1->as.text;
573 if (p[1] == ':')
574 p += 2;
575 while (isspace((unsigned char) *p))
576 p++;
577 if (s1->as.text[0] == 'T' && s1->as.text[1] == ':')
578 p = trim_title(p, s1 == info['T' - 'A']);
579 if (s2 != 0) {
580 buf[sizeof buf - 1] = '\0';
581 strncpy(buf, p, sizeof buf - 1);
582 q = buf + strlen(buf);
583 if (q < buf + sizeof buf - 4) {
584 *q++ = ' ';
585 *q++ = '(';
586 p = s2->as.text;
587 if (p[1] == ':')
588 p += 2;
589 while (isspace((unsigned char) *p))
590 p++;
591 strncpy(q, p, buf + sizeof buf - 2 - q);
592 q += strlen(q);
593 *q++ = ')';
594 *q = '\0';
596 p = buf;
598 put_str(p, action);
601 /* -- add text to a block -- */
602 void add_to_text_block(char *s, int job)
604 float baseskip, lw;
605 char *p, sep;
606 struct FONTSPEC *f;
608 /* if first line, set the fonts */
609 if (strtw < 0) {
610 str_font(TEXTFONT);
611 strlw = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
612 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
615 if (curft > 0)
616 f = &cfmt.font_tb[curft];
617 else f = &cfmt.font_tb[defft];
618 baseskip = f->size * cfmt.lineskipfac;
620 /* follow lines */
621 if (job == T_LEFT || job == T_CENTER || job == T_RIGHT) {
622 if (*s != '\0') {
623 bskip(baseskip);
624 if (job == T_LEFT) {
625 PUT0("0 0 M ");
626 put_str(s, A_LEFT);
627 } else if (job == T_CENTER) {
628 PUT1("%.1f 0 M ", strlw * 0.5);
629 put_str(s, A_CENTER);
630 } else {
631 PUT1("%.1f 0 M ", strlw);
632 put_str(s, A_RIGHT);
634 } else {
635 bskip(baseskip * 0.5);
636 buffer_eob();
638 strtw = 0;
639 return;
642 /* fill or justify lines */
643 if (strtw < 0) { /* if first line */
644 curft = defft;
645 bskip(baseskip);
646 PUT0("0 0 M ");
647 if (job == T_FILL)
648 strop = "show";
649 else {
650 PUT0("/str{");
651 outft = -1;
652 strop = "strop";
654 strns = 0;
655 strtw = 0;
658 if (*s == '\0') { /* empty line */
659 if (strtx) {
660 PUT1(")%s", strop);
661 strtx = 0;
663 if (job == T_JUSTIFY)
664 PUT0("}def\n"
665 "/strop/show load def str\n");
666 else PUT0("\n");
667 bskip(f->size * cfmt.lineskipfac * 1.5);
668 buffer_eob();
669 PUT0("0 0 M ");
670 if (job == T_JUSTIFY) {
671 PUT0("/str{");
672 outft = -1;
674 strns = 0;
675 strtw = 0;
676 return;
679 p = s;
680 for (;;) {
681 while (*p != ' ' && *p != '\0')
682 p++;
683 sep = *p;
684 *p = '\0';
685 lw = tex_str(s);
686 if (strtw + lw > strlw) {
687 if (strtx) {
688 PUT1(")%s ", strop);
689 strtx = 0;
691 if (job == T_JUSTIFY) {
692 if (strns == 0)
693 strns = 1;
694 PUT2("}def\n"
695 "/strop/strw load def/w 0 def str"
696 "/w %.1f w sub %d div def"
697 "/strop/jshow load def str ",
698 strlw, strns);
699 strns = 0;
701 bskip(cfmt.font_tb[curft].size * cfmt.lineskipfac);
702 PUT0("0 0 M ");
703 if (job == T_JUSTIFY) {
704 PUT0("/str{");
705 outft = -1;
707 strtw = 0;
709 if (strtw != 0) {
710 str_ft_out(" ", 0);
711 strtw += cwid(' ') * cfmt.font_tb[curft].swfac;
712 strns++;
714 str_ft_out(tex_buf, 0);
715 strtw += lw;
716 *p = sep;
717 while (*p == ' ')
718 p++;
719 if (*p == '\0')
720 break;
721 s = p;
725 /* -- write a text block -- */
726 void write_text_block(int job, int abc_state)
728 if (strtw < 0)
729 return;
731 if (strtx) {
732 PUT1(")%s", strop);
733 strtx = 0;
735 if (job == T_JUSTIFY)
736 PUT0("}def\n"
737 "/strop/show load def str\n");
738 else if (job == T_FILL)
739 PUT0("\n");
740 bskip(cfmt.font_tb[TEXTFONT].size * cfmt.parskipfac);
741 buffer_eob();
743 /* next line to allow pagebreak after each paragraph */
744 if (!epsf && abc_state != ABC_S_TUNE)
745 write_buffer();
746 strtw = -1;
749 /* -- output a line of words after tune -- */
750 static int put_wline(char *p,
751 float x,
752 int right)
754 char *q, *r, sep;
756 while (isspace((unsigned char) *p))
757 p++;
758 if (*p == '$' && isdigit((unsigned char) p[1])
759 && (unsigned) (p[1] - '0') < FONT_UMAX) {
760 if (curft != p[1] - '0') {
761 curft = p[1] - '0';
762 if (curft == 0)
763 curft = defft;
765 p += 2;
767 r = 0;
768 q = p;
769 if (isdigit((unsigned char) *p) || p[1] == '.') {
770 while (*p != '\0') {
771 p++;
772 if (*p == ' '
773 || p[-1] == ':'
774 || p[-1] == '.')
775 break;
777 r = p;
778 while (*p == ' ')
779 p++;
782 /* on the left side, permit page break at empty lines or stanza start */
783 if (!right
784 && (*p == '\0' || r != 0))
785 buffer_eob();
787 if (r != 0) {
788 sep = *r;
789 *r = '\0';
790 PUT1("%.1f 0 M ", x);
791 put_str(q, A_RIGHT);
792 *r = sep;
794 if (*p != '\0') {
795 PUT1("%.1f 0 M ", x + 5);
796 put_str(p, A_LEFT);
798 return *p == '\0' && r == 0;
801 /* -- output the words after tune -- */
802 void put_words(struct SYMBOL *words)
804 struct SYMBOL *s, *s_end, *s2;
805 char *p;
806 int i, n, have_text, max2col;
807 float middle;
809 str_font(WORDSFONT);
811 /* see if we may have 2 columns */
812 middle = 0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
813 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
814 max2col = (int) ((middle - 45.) / (cwid('a') * cfmt.font_tb[WORDSFONT].swfac));
815 n = 0;
816 have_text = 0;
817 for (s = words; s != 0; s = s->next) {
818 p = &s->as.text[2];
819 while (isspace((unsigned char) *p))
820 p++;
821 if (strlen(p) > max2col) {
822 n = 0;
823 break;
825 if (*p == '\0') {
826 if (have_text) {
827 n++;
828 have_text = 0;
830 } else have_text = 1;
832 if (n > 0) {
833 n++;
834 n /= 2;
835 i = n;
836 have_text = 0;
837 s_end = words;
838 for (;;) {
839 p = &s_end->as.text[2];
840 while (isspace((unsigned char) *p))
841 p++;
842 if (*p == '\0') {
843 if (have_text && --i <= 0)
844 break;
845 have_text = 0;
846 } else have_text = 1;
847 s_end = s_end->next;
849 s2 = s_end->next;
850 } else {
851 s_end = 0;
852 s2 = 0;
855 /* output the text */
856 bskip(cfmt.wordsspace);
857 for (s = words; s != 0 || s2 != 0; ) {
858 bskip(cfmt.lineskipfac * cfmt.font_tb[WORDSFONT].size);
859 if (s != 0) {
860 put_wline(&s->as.text[2], 45., 0);
861 s = s->next;
862 if (s == s_end)
863 s = 0;
865 if (s2 != 0) {
866 if (put_wline(&s2->as.text[2], 20. + middle, 1)) {
867 if (--n == 0) {
868 if (s != 0)
869 n++;
870 else if (s2->next != 0) {
872 /* center the last words */
873 /*fixme: should compute the width average.. */
874 middle *= 0.6;
878 s2 = s2->next;
881 buffer_eob();
884 /* -- output history -- */
885 void put_history(void)
887 struct SYMBOL *s, *s2;
888 float h;
890 bskip(cfmt.textspace);
891 str_font(HISTORYFONT);
892 for (s = info['I' - 'A']; s != 0; s = s->next) {
893 if ((s2 = info[s->as.text[0] - 'A']) == 0)
894 continue;
895 get_str(tex_buf, &s->as.text[1], 256);
896 h = cfmt.font_tb[HISTORYFONT].size * cfmt.lineskipfac;
897 set_font(HISTORYFONT);
898 PUT1("0 0 M(%s)show ", tex_buf);
899 for (;;) {
900 put_inf(s2);
901 if ((s2 = s2->next) == 0)
902 break;
903 bskip(h);
904 PUT0("50 0 M ");
906 bskip(h * 1.2);
907 buffer_eob();
911 /* -- move trailing "The" to front, set to uppercase letters or add xref -- */
912 static char *trim_title(char *p, int first)
914 char *b, *q;
915 int l;
916 static char buf[256];
918 q = 0;
919 if (cfmt.titletrim) {
920 q = strrchr(p, ',');
921 if (q != 0) {
922 if (q[1] != ' ' || !isupper(q[2])
923 || strchr(q + 2, ' ') != 0)
924 q = 0;
927 if (q == 0 && !cfmt.titlecaps && !(first && cfmt.withxrefs))
928 return p; /* keep the title as it is */
929 b = buf;
930 if (first && cfmt.withxrefs)
931 b += sprintf(b, "%s. ", &info['X' - 'A']->as.text[2]);
932 if (q != 0) {
933 strcpy(b, q + 2);
934 b += strlen(q + 2);
935 *b++ = ' ';
936 l = q - p;
937 if (l > buf + sizeof buf - b - 1)
938 l = buf + sizeof buf - b - 1;
939 } else l = buf + sizeof buf - b - 1;
940 strncpy(b, p, l);
941 b[l] = '\0';
942 if (cfmt.titlecaps)
943 cap_str(buf);
944 return buf;
947 /* -- write a title -- */
948 void write_title(struct SYMBOL *s)
950 char *p;
952 p = &s->as.text[2];
953 while (isspace((unsigned char) *p))
954 p++;
955 if (*p == '\0')
956 return;
957 p = trim_title(p, s == info['T' - 'A']);
958 if (s == info['T' - 'A']) {
959 bskip(cfmt.titlespace + cfmt.font_tb[TITLEFONT].size);
960 set_font(TITLEFONT);
961 } else {
962 bskip(cfmt.subtitlespace + cfmt.font_tb[SUBTITLEFONT].size);
963 set_font(SUBTITLEFONT);
965 if (cfmt.titleleft)
966 PUT0("0 0 M(");
967 else PUT1("%.1f 0 M(",
968 0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
969 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale);
970 tex_str(p);
971 PUT2("%s)show%s\n", tex_buf, cfmt.titleleft ? "" : "c");
974 /* -- write heading with format -- */
975 static void write_headform(float lwidth)
977 char *p, *q;
978 struct SYMBOL *s;
979 struct FONTSPEC *f;
980 int align, i, j;
981 float x, y, xa[3], ya[3], sz, yb[3];
982 char inf_nb[26];
983 INFO inf_s;
984 char inf_ft[26];
985 float inf_sz[26];
986 char fmt[64];
988 memset(inf_nb, 0, sizeof inf_nb);
989 memset(inf_ft, HISTORYFONT, sizeof inf_ft);
990 inf_ft['A' - 'A'] = INFOFONT;
991 inf_ft['C' - 'A'] = COMPOSERFONT;
992 inf_ft['O' - 'A'] = COMPOSERFONT;
993 inf_ft['P' - 'A'] = PARTSFONT;
994 inf_ft['Q' - 'A'] = TEMPOFONT;
995 inf_ft['R' - 'A'] = INFOFONT;
996 inf_ft['T' - 'A'] = TITLEFONT;
997 inf_ft['X' - 'A'] = TITLEFONT;
998 memcpy(inf_s, info, sizeof inf_s);
999 memset(inf_sz, 0, sizeof inf_sz);
1000 inf_sz['A' - 'A'] = cfmt.infospace;
1001 inf_sz['C' - 'A'] = cfmt.composerspace;
1002 inf_sz['O' - 'A'] = cfmt.composerspace;
1003 inf_sz['R' - 'A'] = cfmt.infospace;
1004 p = cfmt.titleformat;
1005 j = 0;
1006 for (;;) {
1007 while (isspace((unsigned char) *p))
1008 p++;
1009 if (*p == '\0')
1010 break;
1011 i = *p - 'A';
1012 if ((unsigned) i < 26) {
1013 inf_nb[i]++;
1014 switch (p[1]) {
1015 default:
1016 align = A_CENTER;
1017 break;
1018 case '1':
1019 align = A_RIGHT;
1020 p++;
1021 break;
1022 case '-':
1023 align = A_LEFT;
1024 p++;
1025 break;
1027 if (j < sizeof fmt - 4) {
1028 fmt[j++] = i;
1029 fmt[j++] = align;
1031 } else if (*p == ',') {
1032 if (j < sizeof fmt - 3)
1033 fmt[j++] = 126; /* next line */
1034 } else if (*p == '+') {
1035 if (j > 0 && fmt[j - 1] < 125
1036 && j < sizeof fmt - 3)
1037 fmt[j++] = 125; /* concatenate */
1038 /*new fixme: add free text "..." ?*/
1040 p++;
1042 fmt[j++] = 126; /* newline */
1043 fmt[j] = 127; /* end of format */
1045 ya[0] = ya[1] = ya[2] = cfmt.titlespace;;
1046 xa[0] = 0;
1047 xa[1] = lwidth * 0.5;
1048 xa[2] = lwidth;
1050 p = fmt;
1051 for (;;) {
1052 yb[0] = yb[1] = yb[2] = y = 0;
1053 q = p;
1054 for (;;) {
1055 i = *q++;
1056 if (i >= 126) /* if newline */
1057 break;
1058 align = *q++;
1059 if (yb[align + 1] != 0)
1060 continue;
1061 s = inf_s[i];
1062 if (s == 0 || inf_nb[i] == 0)
1063 continue;
1064 j = inf_ft[i];
1065 f = &cfmt.font_tb[j];
1066 sz = f->size * 1.1 + inf_sz[i];
1067 if (y < sz)
1068 y = sz;
1069 yb[align + 1] = sz;
1070 /*fixme:should count the height of the concatenated field*/
1071 if (*q == 125)
1072 q++;
1074 for (i = 0; i < 3; i++)
1075 ya[i] += y - yb[i];
1076 for (;;) {
1077 i = *p++;
1078 if (i >= 126) /* if newline */
1079 break;
1080 align = *p++;
1081 s = inf_s[i];
1082 if (s == 0 || inf_nb[i] == 0)
1083 continue;
1084 j = inf_ft[i];
1085 str_font(j);
1086 x = xa[align + 1];
1087 f = &cfmt.font_tb[j];
1088 sz = f->size * 1.1 + inf_sz[i];
1089 y = ya[align + 1] + sz;
1090 PUT2("%.1f %.1f M ", x, -y);
1091 if (*p == 125) { /* concatenate */
1092 p++;
1093 /*fixme: do it work with different fields*/
1094 if (*p == i && p[1] == align
1095 && s->next != 0) {
1096 char buf[256], *r;
1098 q = s->as.text;
1099 if (q[1] == ':')
1100 q += 2;
1101 while (isspace((unsigned char) *q))
1102 q++;
1103 if (i == 'T' - 'A')
1104 q = trim_title(q, s == inf_s['T' - 'A']);
1105 strncpy(buf, q, sizeof buf - 1);
1106 buf[sizeof buf - 1] = '\0';
1107 j = strlen(buf);
1108 if (j < sizeof buf - 1) {
1109 buf[j] = ' ';
1110 buf[j + 1] = '\0';
1112 s = s->next;
1113 q = s->as.text;
1114 if (q[1] == ':')
1115 q += 2;
1116 while (isspace((unsigned char) *q))
1117 q++;
1118 if (s->as.text[0] == 'T' && s->as.text[1] == ':')
1119 q = trim_title(q, 0);
1120 r = buf + strlen(buf);
1121 strncpy(r, q, buf + sizeof buf - r - 1);
1122 tex_str(buf);
1123 str_out(tex_buf, align);
1124 PUT0("\n");
1125 inf_nb[i]--;
1126 p += 2;
1128 } else if (i == 'Q' - 'A') { /* special case for tempo */
1129 if (align != A_LEFT) {
1130 float w;
1132 w = tempo_width(s);
1133 if (align == A_CENTER)
1134 PUT1("-%.1f 0 RM ", w * 0.5);
1135 else PUT1("-%.1f 0 RM ", w);
1137 write_tempo(s, 0, 0.75);
1138 } else put_inf2r(s, 0, align);
1139 if (inf_s[i] == info['T' - 'A']) {
1140 inf_ft[i] = SUBTITLEFONT;
1141 str_font(SUBTITLEFONT);
1142 f = &cfmt.font_tb[SUBTITLEFONT];
1143 inf_sz[i] = cfmt.subtitlespace;
1144 sz = f->size * 1.1 + inf_sz[i];
1146 s = s->next;
1147 if (inf_nb[i] == 1) {
1148 while (s != 0) {
1149 y += sz;
1150 PUT2("%.1f %.1f M ", x, -y);
1151 put_inf2r(s, 0, align);
1152 s = s->next;
1155 inf_s[i] = s;
1156 inf_nb[i]--;
1157 ya[align + 1] = y;
1159 if (ya[1] > ya[0])
1160 ya[0] = ya[1];
1161 if (ya[2] > ya[0])
1162 ya[0] = ya[2];
1163 if (*p == 127) {
1164 bskip(ya[0]);
1165 break;
1167 ya[1] = ya[2] = ya[0];
1171 /* -- output the tune heading -- */
1172 void write_heading(struct abctune *t)
1174 struct SYMBOL *s, *rhythm, *area, *author;
1175 float lwidth, down1, down2;
1177 lwidth = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
1178 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
1180 if (cfmt.titleformat != 0) {
1181 write_headform(lwidth);
1182 bskip(cfmt.musicspace);
1183 return;
1186 /* titles */
1187 for (s = info['T' - 'A']; s != 0; s = s->next)
1188 write_title(s);
1190 /* rhythm, composer, origin */
1191 down1 = cfmt.composerspace + cfmt.font_tb[COMPOSERFONT].size;
1192 rhythm = (first_voice->key.bagpipe && !cfmt.infoline) ? info['R' - 'A'] : 0;
1193 if (rhythm) {
1194 str_font(COMPOSERFONT);
1195 PUT1("0 %.1f M ",
1196 -(cfmt.composerspace + cfmt.font_tb[COMPOSERFONT].size));
1197 put_inf(rhythm);
1198 down1 -= cfmt.font_tb[COMPOSERFONT].size;
1200 area = author = 0;
1201 if (t->abc_vers < 2)
1202 area = info['A' - 'A'];
1203 else author = info['A' - 'A'];
1204 if ((s = info['C' - 'A']) != 0 || info['O' - 'A'] || author != 0) {
1205 float xcomp;
1206 int align;
1208 str_font(COMPOSERFONT);
1209 bskip(cfmt.composerspace);
1210 if (cfmt.aligncomposer < 0) {
1211 xcomp = 0;
1212 align = A_LEFT;
1213 } else if (cfmt.aligncomposer == 0) {
1214 xcomp = lwidth * 0.5;
1215 align = A_CENTER;
1216 } else {
1217 xcomp = lwidth;
1218 align = A_RIGHT;
1220 down2 = down1;
1221 if (author != 0) {
1222 for (;;) {
1223 bskip(cfmt.font_tb[COMPOSERFONT].size);
1224 down2 += cfmt.font_tb[COMPOSERFONT].size;
1225 PUT0("0 0 M ");
1226 put_inf(author);
1227 if ((author = author->next) == 0)
1228 break;
1231 if ((s = info['C' - 'A']) != 0 || info['O' - 'A']) {
1232 if (cfmt.aligncomposer >= 0
1233 && down1 != down2)
1234 bskip(down1 - down2);
1235 for (;;) {
1236 bskip(cfmt.font_tb[COMPOSERFONT].size);
1237 PUT1("%.1f 0 M ", xcomp);
1238 put_inf2r(s,
1239 (s == 0 || s->next == 0) ? info['O' - 'A'] : 0,
1240 align);
1241 if (s == 0)
1242 break;
1243 if ((s = s->next) == 0)
1244 break;
1245 down1 += cfmt.font_tb[COMPOSERFONT].size;
1247 if (down2 > down1)
1248 bskip(down2 - down1);
1251 rhythm = rhythm ? 0 : info['R' - 'A'];
1252 if ((rhythm || area) && cfmt.infoline) {
1254 /* if only one of rhythm or area then do not use ()'s
1255 * otherwise output 'rhythm (area)' */
1256 str_font(INFOFONT);
1257 bskip(cfmt.font_tb[INFOFONT].size + cfmt.infospace);
1258 PUT1("%.1f 0 M ", lwidth);
1259 put_inf2r(rhythm, area, A_RIGHT);
1260 down1 += cfmt.font_tb[INFOFONT].size + cfmt.infospace;
1262 down2 = 0;
1263 } else down2 = cfmt.composerspace + cfmt.font_tb[COMPOSERFONT].size;
1265 /* parts */
1266 if (info['P' - 'A']) {
1267 down1 = cfmt.partsspace + cfmt.font_tb[PARTSFONT].size - down1;
1268 if (down1 > 0)
1269 down2 += down1;
1270 if (down2 > 0.01)
1271 bskip(down2);
1272 str_font(PARTSFONT);
1273 PUT0("0 0 M ");
1274 put_inf(info['P' - 'A']);
1275 down2 = 0;
1277 bskip(down2 + cfmt.musicspace);
1280 /* -- memorize a PS line -- */
1281 void user_ps_add(char *s)
1283 struct u_ps *t, *r;
1284 int l;
1286 l = strlen(s);
1287 t = (struct u_ps *) malloc(sizeof *user_ps - sizeof user_ps->text
1288 + l + 1);
1289 strcpy(t->text, s);
1290 t->next = 0;
1291 if ((r = user_ps) == 0)
1292 user_ps = t;
1293 else {
1294 while (r->next != 0)
1295 r = r->next;
1296 r->next = t;
1300 /* -- output the user defined postscript sequences -- */
1301 void user_ps_write(void)
1303 struct u_ps *t, *r;
1305 if ((t = user_ps) == 0)
1306 return;
1307 user_ps = 0;
1308 for (;;) {
1309 if (t->text[0] == '\001') { /* PS file */
1310 FILE *f;
1311 char line[BSIZE];
1313 if ((f = fopen(&t->text[1], "r")) == 0) {
1314 error(1, 0, "Cannot open PS file '%s'",
1315 &t->text[1]);
1316 } else {
1317 while (fgets(line, sizeof line, f)) /* copy the file */
1318 fwrite(line, 1, strlen(line), fout);
1319 fclose(f);
1321 } else fprintf(fout, "%s\n", t->text);
1322 r = t->next;
1323 free(t);
1324 if ((t = r) == 0)
1325 break;