1 /*
2 * Low-level utilities.
3 *
4 * This file is part of abcm2ps.
5 *
6 * Copyright (C) 1998-2018 Jean-François Moine
7 * Adapted from abc2ps, Copyright (C) 1996,1997 Michael Methfessel
8 *
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.
13 */
14
15 #include <stdio.h>
16 #include <stdarg.h>
17 #include <stdlib.h>
18 #include <time.h>
19 #include <string.h>
20 #include <ctype.h>
21 #ifdef HAVE_PANGO
22 #include <pango/pangocairo.h>
23 #include <pango/pangofc-font.h>
24 #endif
25
26 #include "abcm2ps.h"
27
28 char tex_buf[TEX_BUF_SZ]; /* result of tex_str() */
29 int outft = -1; /* last font in the output file */
30
31 static int stropx; /* index current string output operation */
32 static float strlw; /* line width */
33 static int curft; /* current (wanted) font */
34 static int defft; /* default font */
35 static char strtx; /* PostScript text outputing (bits) */
36 #define TX_STR 1 /* string started */
37 #define TX_ARR 2 /* glyph/string array started */
38
39 /* width of characters according to the encoding */
40 /* these are the widths for Times-Roman, extracted from the 'a2ps' package */
41 /*fixme-hack: set 500 to control characters for utf-8*/
42 static short cw_tb[] = {
43 500,500,500,500,500,500,500,500, // 00
44 500,500,500,500,500,500,500,500,
45 500,500,500,500,500,500,500,500, // 10
46 500,500,500,500,500,500,500,500,
47 250,333,408,500,500,833,778,333, // 20
48 333,333,500,564,250,564,250,278,
49 500,500,500,500,500,500,500,500, // 30
50 500,500,278,278,564,564,564,444,
51 921,722,667,667,722,611,556,722, // 40
52 722,333,389,722,611,889,722,722,
53 556,722,667,556,611,722,722,944, // 50
54 722,722,611,333,278,333,469,500,
55 333,444,500,444,500,444,333,500, // 60
56 500,278,278,500,278,778,500,500,
57 500,500,333,389,278,500,500,722, // 70
58 500,500,444,480,200,480,541,500,
59 };
60
61 static struct u_ps {
62 struct u_ps *next;
63 char text[2];
64 } *user_ps;
65
66 /* -- print message for internal error and maybe stop -- */
bug(char * msg,int fatal)67 void bug(char *msg, int fatal)
68 {
69 error(1, NULL, "Internal error: %s.", msg);
70 if (fatal) {
71 fprintf(stderr, "Emergency stop.\n\n");
72 exit(EXIT_FAILURE);
73 }
74 fprintf(stderr, "Trying to continue...\n");
75 }
76
77 /* -- print an error message -- */
error(int sev,struct SYMBOL * s,char * fmt,...)78 void error(int sev, /* 0: warning, 1: error */
79 struct SYMBOL *s,
80 char *fmt, ...)
81 {
82 va_list args;
83
84 if (s) {
85 if (s->fn)
86 fprintf(stderr, "%s:%d:%d: ", s->fn,
87 s->linenum, s->colnum);
88 s->flags |= ABC_F_ERROR;
89 }
90 fprintf(stderr, sev == 0 ? "warning: " : "error: ");
91 va_start(args, fmt);
92 vfprintf(stderr, fmt, args);
93 va_end(args);
94 fprintf(stderr, "\n");
95 if (sev > severity)
96 severity = sev;
97 }
98
99 /* -- capitalize a string -- */
cap_str(char * p)100 static void cap_str(char *p)
101 {
102 while (*p != '\0') {
103 #if 1
104 /* pb with toupper - works with ASCII and some latin characters only */
105 unsigned char c;
106
107 c = (unsigned char) *p;
108 if (c >= 'a' && c <= 'z') {
109 *p = c & ~0x20;
110 } else if (c == 0xc3) {
111 p++;
112 c = *p;
113 if (c >= 0xa0 && c <= 0xbe && c != 0xb7)
114 *p = c & ~0x20;
115 } else if (c == 0xc4) {
116 p++;
117 c = *p;
118 if (c >= 0x81 && c <= 0xb7 && (c & 0x01))
119 (*p)--;
120 }
121 #else
122 *p = toupper((unsigned char) *p);
123 #endif
124 p++;
125 }
126 }
127
128 /* -- return the character width -- */
cwid(unsigned char c)129 float cwid(unsigned char c)
130 {
131 if (c >= 0x80) {
132 if (c < 0xc0)
133 return 0; // not start of utf8 character
134 c = 'a';
135 }
136 return (float) cw_tb[c] / 1000.;
137 }
138
139 /* -- change string taking care of some tex-style codes -- */
140 /* Return an estimated width of the string. */
tex_str(char * s)141 float tex_str(char *s)
142 {
143 char *d, *p;
144 unsigned char c1;
145 unsigned maxlen, i;
146 float w, swfac;
147
148 w = 0;
149 d = tex_buf;
150 maxlen = sizeof tex_buf - 1; /* have room for EOS */
151 if ((i = curft) <= 0)
152 i = defft;
153 swfac = cfmt.font_tb[i].swfac;
154 while (1) {
155 c1 = (unsigned char) *s++;
156 if (c1 == '\0')
157 break;
158 switch (c1) {
159 case '\\':
160 c1 = *s++;
161 if (c1 == '\0') {
162 *d = '\0';
163 return w;
164 }
165 switch (c1) {
166 case 'n':
167 c1 = '\n';
168 break;
169 case 't':
170 c1 = '\t';
171 break;
172 }
173 break;
174 case '$':
175 if (isdigit((unsigned char) *s)
176 && (unsigned) (*s - '0') < FONT_UMAX) {
177 i = *s - '0';
178 if (i == 0)
179 i = defft;
180 swfac = cfmt.font_tb[i].swfac;
181 if (--maxlen <= 0)
182 break;
183 *d++ = c1;
184 c1 = *s++;
185 goto addchar_nowidth;
186 }
187 if (*s == '$') {
188 if (--maxlen <= 0)
189 break;
190 *d++ = c1;
191 s++;
192 }
193 break;
194 case '&': /* treat XML characters */
195 if (svg || epsf > 1) {
196 p = strchr(s, ';');
197 if (!p || p - s >= 10)
198 break;
199 *d++ = c1;
200 while (s <= p)
201 *d++ = *s++;
202 w += cwid('a') * swfac;
203 continue;
204 }
205 if (*s == '#') {
206 int j;
207 long v;
208
209 if (s[1] == 'x')
210 i = sscanf(s, "#x%lx;%n", &v, &j);
211 else
212 i = sscanf(s, "#%ld;%n", &v, &j);
213 if (i != 1) {
214 error(0, NULL, "Bad XML char reference");
215 break;
216 }
217 if (v < 0x80) { /* convert to UTF-8 */
218 *d++ = v;
219 } else if (v < 0x800) {
220 *d++ = 0xc0 | (v >> 6);
221 *d++ = 0x80 | (v & 0x3f);
222 } else if (v < 0x10000) {
223 *d++ = 0xe0 | (v >> 12);
224 *d++ = 0x80 | ((v >> 6) & 0x3f);
225 *d++ = 0x80 | (v & 0x3f);
226 } else {
227 *d++ = 0xf0 | (v >> 18);
228 *d++ = 0x80 | ((v >> 12) & 0x3f);
229 *d++ = 0x80 | ((v >> 6) & 0x3f);
230 *d++ = 0x80 | (v & 0x3f);
231 }
232 w += cwid('a') * swfac;
233 s += j;
234 continue;
235 }
236 if (strncmp(s, "lt;", 3) == 0) {
237 c1 = '<';
238 s += 3;
239 } else if (strncmp(s, "gt;", 3) == 0) {
240 c1 = '>';
241 s += 3;
242 } else if (strncmp(s, "amp;", 4) == 0) {
243 c1 = '&';
244 s += 4;
245 } else if (strncmp(s, "apos;", 5) == 0) {
246 c1 = '\'';
247 s += 5;
248 } else if (strncmp(s, "quot;", 5) == 0) {
249 c1 = '"';
250 s += 5;
251 }
252 break;
253 }
254 if (c1 >= 0x80) {
255 if (c1 >= 0xc0)
256 w += cwid('a') * swfac; // start of unicode char
257 } else if (c1 <= 5) { /* accidentals from gchord */
258 if (--maxlen < 4)
259 break;
260 switch (c1) {
261 case 1:
262 *d++ = 0xe2;
263 *d++ = 0x99;
264 *d++ = 0xaf;
265 break;
266 case 2:
267 *d++ = 0xe2;
268 *d++ = 0x99;
269 *d++ = 0xad;
270 break;
271 case 3:
272 *d++ = 0xe2;
273 *d++ = 0x99;
274 *d++ = 0xae;
275 break;
276 case 4:
277 *d++ = 0xf0;
278 *d++ = 0x9d;
279 *d++ = 0x84;
280 *d++ = 0xaa;
281 break;
282 case 5:
283 *d++ = 0xf0;
284 *d++ = 0x9d;
285 *d++ = 0x84;
286 *d++ = 0xab;
287 break;
288 }
289 w += cwid('a') * swfac;
290 continue;
291 } else {
292 w += cwid(c1) * swfac;
293 }
294 addchar_nowidth:
295 if (--maxlen <= 0)
296 break;
297 *d++ = c1;
298 }
299 *d = '\0';
300 if (maxlen <= 0)
301 error(0, NULL, "Text too large - ignored part: '%s'", s);
302 return w;
303 }
304
305 #ifdef HAVE_PANGO
306 #define PG_SCALE (PANGO_SCALE * 72 / 96) /* 96 DPI */
307
308 static PangoFontDescription *desc_tb[MAXFONTS];
309 static PangoLayout *layout = (PangoLayout *) -1;
310 static PangoAttrList *attrs;
311 static int out_pg_ft = -1; /* current pango font */
312 static GString *pg_str;
313
314 /* -- initialize the pango mechanism -- */
pg_init(void)315 void pg_init(void)
316 {
317 static PangoContext *context;
318
319 context = pango_font_map_create_context(
320 pango_cairo_font_map_get_default());
321 if (context)
322 layout = pango_layout_new(context);
323 if (!layout) {
324 error(0, NULL, "pango disabled\n");
325 cfmt.pango = 0;
326 } else {
327 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
328 // pango_layout_set_spacing(layout, 0);
329 pg_str = g_string_sized_new(256);
330 }
331 }
pg_reset_font(void)332 void pg_reset_font(void)
333 {
334 out_pg_ft = -1;
335 }
336
desc_font(int fnum)337 static void desc_font(int fnum)
338 {
339 char font_name[128], *p;
340
341 if (desc_tb[fnum] == 0) {
342 p = font_name;
343 sprintf(p, "%s 10", fontnames[fnum]);
344 while (*p != '\0') {
345 if (*p == '-')
346 *p = ' ';
347 p++;
348 }
349 desc_tb[fnum] = pango_font_description_from_string(font_name);
350 }
351 }
352
353 /* output a line */
pg_line_output(PangoLayoutLine * line)354 static void pg_line_output(PangoLayoutLine *line)
355 {
356 GSList *runs_list;
357 PangoGlyphInfo *glyph_info;
358 char tmp[256];
359 const char *fontname = NULL;
360 int ret, glypharray;
361
362 outft = -1;
363 glypharray = 0;
364 for (runs_list = line->runs; runs_list; runs_list = runs_list->next) {
365 PangoLayoutRun *run = runs_list->data;
366 PangoItem *item = run->item;
367 PangoGlyphString *glyphs = run->glyphs;
368 PangoAnalysis *analysis = &item->analysis;
369 PangoFont *font = analysis->font;
370 PangoFcFont *fc_font = PANGO_FC_FONT(font);
371 FT_Face face = pango_fc_font_lock_face(fc_font);
372 PangoFontDescription *ftdesc =
373 pango_font_describe(font);
374 int wi = pango_font_description_get_size(ftdesc);
375 int i, c;
376
377 if (pango_font_description_get_size(ftdesc) != wi) {
378 wi = pango_font_description_get_size(ftdesc);
379 fontname = NULL;
380 }
381 for (i = 0; i < glyphs->num_glyphs; i++) {
382 glyph_info = &glyphs->glyphs[i];
383 c = glyph_info->glyph;
384 if (c == PANGO_GLYPH_EMPTY)
385 continue;
386 if (c & PANGO_GLYPH_UNKNOWN_FLAG) {
387 c &= ~PANGO_GLYPH_UNKNOWN_FLAG;
388 error(0, NULL, "char %04x not treated\n", c);
389 continue;
390 }
391
392 ret = FT_Load_Glyph(face,
393 c, // PangoGlyph = index
394 FT_LOAD_NO_SCALE);
395 if (ret != 0) {
396 error(0, NULL, "freetype error %d\n", ret);
397 } else if (FT_HAS_GLYPH_NAMES(face)) {
398 if (FT_Get_Postscript_Name(face) != fontname) {
399 fontname = FT_Get_Postscript_Name(face);
400 if (glypharray)
401 a2b("]glypharray");
402 a2b("\n/%s %.1f selectfont[",
403 fontname,
404 (float) wi / PG_SCALE);
405 glypharray = 1;
406 }
407 FT_Get_Glyph_Name((FT_FaceRec *) face, c,
408 tmp, sizeof tmp);
409 a2b("/%s", tmp);
410 } else {
411 a2b("%% glyph: %s %d\n",
412 FT_Get_Postscript_Name(face), c);
413 }
414 }
415 pango_fc_font_unlock_face(fc_font);
416 }
417 if (glypharray)
418 a2b("]glypharray");
419 }
420
str_font_change(int start,int end)421 static void str_font_change(int start,
422 int end)
423 {
424 struct FONTSPEC *f;
425 int fnum;
426 PangoAttribute *attr1, *attr2;
427
428 f = &cfmt.font_tb[curft];
429 fnum = f->fnum;
430 if (f->size == 0) {
431 error(0, NULL, "Font \"%s\" with a null size - set to 8",
432 fontnames[fnum]);
433 f->size = 8;
434 }
435 desc_font(fnum);
436
437 attr1 = pango_attr_font_desc_new(desc_tb[fnum]);
438 attr1->start_index = start;
439 attr1->end_index = end;
440 pango_attr_list_insert(attrs, attr1);
441 attr2 = pango_attr_size_new((int) (f->size * PG_SCALE));
442 attr2->start_index = start;
443 attr2->end_index = end;
444 pango_attr_list_insert(attrs, attr2);
445 }
446
str_set_font(char * p)447 static void str_set_font(char *p)
448 {
449 GString *str;
450 char *q;
451 int start;
452
453 str = pg_str;
454 start = str->len;
455 q = p;
456 while (*p != '\0') {
457 switch (*p) {
458 case '$':
459 if (isdigit((unsigned char) p[1])
460 && (unsigned) (p[1] - '0') < FONT_UMAX) {
461 if (p > q)
462 str = g_string_append_len(str, q, p - q);
463 if (curft != p[1] - '0') {
464 str_font_change(start, str->len);
465 start = str->len;
466 curft = p[1] - '0';
467 if (curft == 0)
468 curft = defft;
469 }
470 p += 2;
471 q = p;
472 continue;
473 }
474 if (p[1] == '$') {
475 str = g_string_append_len(str, q, p - q);
476 q = ++p;
477 }
478 break;
479 }
480 p++;
481 }
482 if (p > q) {
483 str = g_string_append_len(str, q, p - q);
484 str_font_change(start, str->len);
485 }
486 pg_str = str;
487 }
488
489 /* -- output a string using the pango and freetype libraries -- */
str_pg_out(char * p,int action)490 static void str_pg_out(char *p, int action)
491 {
492 PangoLayoutLine *line;
493 int wi;
494 float w;
495
496 //fixme: test
497 //a2b("\n%% t: '%s'\n", p);
498 if (out_pg_ft != curft)
499 out_pg_ft = -1;
500
501 /* guitar chord with TABs */
502 if (action == A_GCHEXP) {
503 char *q;
504
505 /* get the inter TAB width (see draw_gchord) */
506 q = mbf - 1;
507 while (q[-1] != ' ')
508 q--;
509 mbf = q;
510 w = atof(q);
511 for (;;) {
512 q = strchr(p, '\t');
513 if (!q)
514 break;
515 *q = '\0';
516 str_pg_out(p, A_LEFT);
517 a2b(" %.1f 0 RM ", w);
518 p = q + 1;
519 }
520 }
521
522 attrs = pango_attr_list_new();
523 str_set_font(p);
524
525 pango_layout_set_text(layout, pg_str->str, pg_str->len);
526 pango_layout_set_attributes(layout, attrs);
527
528 /* only one line */
529 line = pango_layout_get_line_readonly(layout, 0);
530 switch (action) {
531 case A_CENTER:
532 case A_RIGHT:
533 pango_layout_get_size(layout, &wi, NULL);
534 if (action == A_CENTER)
535 wi /= 2;
536 // w = (float) wi / PG_SCALE;
537 w = (float) wi / PANGO_SCALE;
538 a2b("-%.1f 0 RM ", w);
539 break;
540 }
541 pg_line_output(line);
542 pango_layout_set_attributes(layout, NULL);
543 pg_str = g_string_truncate(pg_str, 0);
544 pango_attr_list_unref(attrs);
545 }
546
547 /* output a justified or filled paragraph */
pg_para_output(int job)548 static void pg_para_output(int job)
549 {
550 GSList *lines, *runs_list;
551 PangoLayoutLine *line;
552 PangoGlyphInfo *glyph_info;
553 char tmp[256];
554 const char *fontname = NULL;
555 int ret, glypharray;
556 int wi;
557 float y;
558
559 pango_layout_set_text(layout, pg_str->str,
560 pg_str->len - 1); /* remove the last space */
561 pango_layout_set_attributes(layout, attrs);
562 outft = -1;
563 glypharray = 0;
564 wi = 0;
565 y = 0;
566 lines = pango_layout_get_lines_readonly(layout);
567
568 for (; lines; lines = lines->next) {
569 PangoRectangle pos;
570
571 line = lines->data;
572 pango_layout_line_get_extents(line, NULL, &pos);
573 y += (float) pos.height
574 * .87 /* magic! */
575 / PANGO_SCALE;
576
577 for (runs_list = line->runs; runs_list; runs_list = runs_list->next) {
578 PangoLayoutRun *run = runs_list->data;
579 PangoItem *item = run->item;
580 PangoGlyphString *glyphs = run->glyphs;
581 PangoAnalysis *analysis = &item->analysis;
582 PangoFont *font = analysis->font;
583 PangoFcFont *fc_font = PANGO_FC_FONT(font);
584 FT_Face face = pango_fc_font_lock_face(fc_font);
585 PangoFontDescription *ftdesc =
586 pango_font_describe(font);
587 int i, g, set_move, x;
588
589 if (pango_font_description_get_size(ftdesc) != wi) {
590 wi = pango_font_description_get_size(ftdesc);
591 fontname = NULL;
592 }
593 //printf("font size: %.2f\n", (float) wi / PG_SCALE);
594
595 pango_layout_index_to_pos(layout, item->offset, &pos);
596 x = pos.x;
597 set_move = 1;
598 for (i = 0; i < glyphs->num_glyphs; i++) {
599 glyph_info = &glyphs->glyphs[i];
600 g = glyph_info->glyph;
601 if (g == PANGO_GLYPH_EMPTY)
602 continue;
603 if (set_move) {
604 set_move = 0;
605 if (glypharray) {
606 a2b("]glypharray");
607 glypharray = 0;
608 }
609 a2b("\n");
610 a2b("%.2f %.2f M ",
611 (float) x / PANGO_SCALE, -y);
612 }
613 x += glyph_info->geometry.width;
614 if (g & PANGO_GLYPH_UNKNOWN_FLAG) {
615 g &= ~PANGO_GLYPH_UNKNOWN_FLAG;
616 error(0, NULL, "char %04x not treated\n", g);
617 continue;
618 }
619
620 ret = FT_Load_Glyph(face,
621 g, // PangoGlyph = index
622 FT_LOAD_NO_SCALE);
623 if (ret != 0) {
624 fprintf(stdout, "%%%% freetype error %d\n", ret);
625 } else if (FT_HAS_GLYPH_NAMES(face)) {
626 if (FT_Get_Postscript_Name(face) != fontname) {
627 fontname = FT_Get_Postscript_Name(face);
628 if (glypharray)
629 a2b("]glypharray");
630 a2b("\n/%s %.1f selectfont[",
631 fontname,
632 (float) wi / PG_SCALE);
633 glypharray = 1;
634 }
635 FT_Get_Glyph_Name((FT_FaceRec *) face, g,
636 tmp, sizeof tmp);
637 if (job == T_JUSTIFY
638 && strcmp(tmp, "space") == 0) {
639 set_move = 1;
640 continue;
641 }
642 if (!glypharray) {
643 a2b("[");
644 glypharray = 1;
645 }
646 a2b("/%s", tmp);
647 } else {
648 a2b("%% glyph: %s %d\n",
649 FT_Get_Postscript_Name(face), g);
650 }
651 }
652 pango_fc_font_unlock_face(fc_font);
653 if (glypharray) {
654 a2b("]glypharray\n");
655 glypharray = 0;
656 }
657 }
658 if (glypharray) {
659 a2b("]glypharray\n");
660 glypharray = 0;
661 }
662 }
663 bskip(y);
664 pango_layout_set_attributes(layout, NULL);
665 pg_str = g_string_truncate(pg_str, 0);
666 }
667
668 /* output of filled / justified text */
pg_write_text(char * s,int job,float parskip)669 static void pg_write_text(char *s, int job, float parskip)
670 {
671 char *p;
672
673 curft = defft;
674 pango_layout_set_width(layout, strlw * PANGO_SCALE);
675 pango_layout_set_justify(layout, job == T_JUSTIFY);
676 attrs = pango_attr_list_new();
677
678 p = s;
679 while (*p != '\0') {
680 if (*p++ != '\n')
681 continue;
682 if (*p == '\n') { /* if empty line */
683 p[-1] = '\0';
684 tex_str(s);
685 str_set_font(tex_buf);
686 if (pg_str->len > 0)
687 pg_para_output(job);
688 bskip(parskip);
689 buffer_eob(0);
690 s = ++p;
691 continue;
692 }
693 //fixme: maybe not useful
694 p [-1] = ' ';
695 }
696 tex_str(s);
697 str_set_font(tex_buf);
698 if (pg_str->len)
699 pg_para_output(job);
700 pango_attr_list_unref(attrs);
701 }
702
703 /* check if pango is needed */
is_latin(unsigned char * p)704 static int is_latin(unsigned char *p)
705 {
706 while (*p != '\0') {
707 if (*p >= 0xc6) {
708 if (*p == 0xe2) {
709 if (p[1] != 0x99
710 || p[2] < 0xad || p[2] > 0xaf)
711 return 0;
712 p += 2;
713 } else if (*p == 0xf0) {
714 if (p[1] != 0x9d
715 || p[2] != 0x84
716 || p[3] < 0xaa || p[3] > 0xab)
717 return 0;
718 } else {
719 return 0;
720 }
721 }
722 p++;
723 }
724 return 1;
725 }
726 #endif /* HAVE_PANGO */
727
728 /* -- set the default font of a string -- */
str_font(int ft)729 void str_font(int ft)
730 {
731 curft = defft = ft;
732 }
733
734 /* -- get the current and default fonts -- */
get_str_font(int * cft,int * dft)735 void get_str_font(int *cft, int *dft)
736 {
737 *cft = curft;
738 *dft = defft;
739 }
740
741 /* -- set the current and default fonts -- */
set_str_font(int cft,int dft)742 void set_str_font(int cft, int dft)
743 {
744 curft = cft;
745 defft = dft;
746 }
747
748 static char *strop_tb[] = { /* index = action (A_xxxx) * 2 */
749 "show", "arrayshow", // left
750 "showc", "arrayshow", // center
751 "showr", "arrayshow", // right
752 "lyshow", "alyshow", // lyric
753 "gcshow", "agcshow", // gchord
754 "anshow", "aanshow", // annot
755 "gxshow", "arrayshow", // gchexp
756 "strop", "arrayshow", // (7 = justify)
757 };
758
759 /* close a string */
str_end(int end)760 static void str_end(int end)
761 {
762 if (strtx & TX_STR) {
763 a2b(")");
764 strtx &= ~TX_STR;
765 if (!(strtx & TX_ARR)) {
766 a2b("%s", strop_tb[stropx]);
767 return;
768 }
769 }
770 if (!end || !(strtx & TX_ARR))
771 return;
772 strtx &= ~TX_ARR;
773 a2b("]%s", strop_tb[stropx + 1]);
774 }
775
776 /* check if some non ASCII characters */
non_ascii_p(char * p)777 static int non_ascii_p(char *p)
778 {
779 while (*p != '\0') {
780 if ((signed char) *p++ < 0)
781 return 1;
782 }
783 return 0;
784 }
785
786 /* -- output one string -- */
str_ft_out1(char * p,int l)787 static void str_ft_out1(char *p, int l)
788 {
789 if (curft != outft) {
790 str_end(1);
791 a2b(" ");
792 set_font(curft);
793 }
794 if (!(strtx & TX_STR)) {
795 a2b("(");
796 strtx |= TX_STR;
797 }
798 a2b("%.*s", l, p);
799 }
800
801 /* -- output a string handling the font changes -- */
str_ft_out(char * p,int end)802 static void str_ft_out(char *p, int end)
803 {
804 int use_glyph;
805 char *q;
806
807 use_glyph = !svg && epsf <= 1 && /* not SVG */
808 get_font_encoding(curft) == 0; /* utf-8 font */
809 if (use_glyph && non_ascii_p(p)) {
810 if (curft != outft) {
811 str_end(1);
812 a2b(" ");
813 set_font(curft);
814 }
815 str_end(0);
816 if (!(strtx & TX_ARR)) {
817 a2b("[");
818 strtx |= TX_ARR;
819 }
820 }
821 q = p;
822 while (*p != '\0') {
823 if ((signed char) *p < 0
824 && use_glyph) {
825 if (p > q)
826 str_ft_out1(q, p - q);
827 str_end(0);
828 if (curft != outft) {
829 str_end(1);
830 a2b(" ");
831 set_font(curft);
832 }
833 if (!(strtx & TX_ARR)) {
834 a2b("[");
835 strtx |= TX_ARR;
836 }
837 q = p = glyph_out(p);
838 continue;
839 }
840 switch (*p) {
841 case '$':
842 if (isdigit((unsigned char) p[1])
843 && (unsigned) (p[1] - '0') < FONT_UMAX) {
844 if (p > q)
845 str_ft_out1(q, p - q);
846 if (curft != p[1] - '0') {
847 curft = p[1] - '0';
848 if (curft == 0)
849 curft = defft;
850 use_glyph = !svg && epsf <= 1 &&
851 get_font_encoding(curft) == 0;
852 }
853 p += 2;
854 q = p;
855 continue;
856 }
857 if (p[1] == '$') {
858 str_ft_out1(q, p - q);
859 q = ++p;
860 }
861 break;
862 case '(':
863 case ')':
864 case '\\':
865 if (p > q)
866 str_ft_out1(q, p - q);
867 str_ft_out1("\\", 1);
868 q = p;
869 break;
870 }
871 p++;
872 }
873 if (p > q)
874 str_ft_out1(q, p - q);
875 if (end && strtx)
876 str_end(1);
877 }
878
879 /* -- output a string, handling the font changes -- */
str_out(char * p,int action)880 void str_out(char *p, int action)
881 {
882 if (curft <= 0) /* first call */
883 curft = defft;
884
885 /* special case when font change at start of text */
886 if (*p == '$' && isdigit((unsigned char) p[1])
887 && (unsigned) (p[1] - '0') < FONT_UMAX) {
888 if (curft != p[1] - '0') {
889 curft = p[1] - '0';
890 if (curft == 0)
891 curft = defft;
892 }
893 p += 2;
894 }
895
896 #ifdef HAVE_PANGO
897 //fixme: pango KO if user modification of ly/gc/an/gxshow
898 /* use pango if some characters are out of the utf-array (in syms.c) */
899 if (cfmt.pango) {
900 if (cfmt.pango == 2 || !is_latin((unsigned char *) p)) {
901 str_pg_out(p, action); /* output the string */
902 return;
903 }
904 }
905 #endif
906
907 stropx = action * 2;
908
909 /* direct output if no font change and only ASCII characters */
910 if (!strchr(p, '$')
911 && !non_ascii_p(p)) {
912 str_ft_out(p, 1); /* output the string */
913 return;
914 }
915
916 /* if not left aligned, build a PS function */
917 switch (action) {
918 case A_CENTER:
919 case A_RIGHT:
920 if (!svg && epsf <= 1) {
921 a2b("/str{");
922 outft = -1;
923 stropx = 0;
924 }
925 /* fall thru */
926 // default:
927 // if (!svg && epsf <= 1) /* if not SVG */
928 // stropx++;
929 break;
930 }
931
932 str_ft_out(p, 1); /* output the string */
933
934 /* if not left aligned, call the PS function */
935 if (svg || epsf > 1) /* not for SVG */
936 return;
937 if (action == A_CENTER || action == A_RIGHT) {
938 a2b("}def\n"
939 "strw w");
940 if (action == A_CENTER)
941 a2b(" 0.5 mul");
942 a2b(" neg 0 RM str");
943 }
944 }
945
946 /* -- output a string with TeX translation -- */
put_str(char * str,int action)947 void put_str(char *str, int action)
948 {
949 tex_str(str);
950 str_out(tex_buf, action);
951 a2b("\n");
952 }
953
954 /* -- output a header information -- */
put_inf(struct SYMBOL * s)955 static void put_inf(struct SYMBOL *s)
956 {
957 char *p;
958
959 p = s->text;
960 if (p[1] == ':')
961 p += 2;
962 while (isspace((unsigned char) *p))
963 p++;
964 put_str(p, A_LEFT);
965 }
966
967 /* -- output a header format '111 (222)' -- */
put_inf2r(struct SYMBOL * s1,struct SYMBOL * s2,int action)968 static void put_inf2r(struct SYMBOL *s1,
969 struct SYMBOL *s2,
970 int action)
971 {
972 char buf[256], *p, *q;
973
974 if (!s1) {
975 s1 = s2;
976 s2 = NULL;
977 }
978 p = &s1->text[2];
979 if (s1->text[0] == 'T')
980 p = trim_title(p, s1);
981 if (s2) {
982 buf[sizeof buf - 1] = '\0';
983 strncpy(buf, p, sizeof buf - 1);
984 q = buf + strlen(buf);
985 if (q < buf + sizeof buf - 4) {
986 *q++ = ' ';
987 *q++ = '(';
988 p = &s2->text[2];
989 strncpy(q, p, buf + sizeof buf - 2 - q);
990 q += strlen(q);
991 *q++ = ')';
992 *q = '\0';
993 }
994 p = buf;
995 }
996 put_str(p, action);
997 }
998
999 /* -- write a text block (%%begintext / %%text / %%center) -- */
write_text(char * cmd,char * s,int job)1000 void write_text(char *cmd, char *s, int job)
1001 {
1002 int nw;
1003 #ifdef HAVE_PANGO
1004 int do_pango;
1005 #endif
1006 float lineskip, parskip, strw;
1007 char *p;
1008 struct FONTSPEC *f;
1009
1010 str_font(TEXTFONT);
1011 strlw = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
1012 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
1013
1014 f = &cfmt.font_tb[TEXTFONT];
1015 lineskip = f->size * cfmt.lineskipfac;
1016 parskip = f->size * cfmt.parskipfac;
1017
1018 /* follow lines */
1019 switch (job) {
1020 case T_LEFT:
1021 case T_CENTER:
1022 case T_RIGHT:
1023 switch (job) {
1024 case T_LEFT:
1025 #if T_LEFT != A_LEFT
1026 job = A_LEFT;
1027 #endif
1028 strlw = 0;
1029 break;
1030 case T_CENTER:
1031 #if T_CENTER != A_CENTER
1032 job = A_CENTER;
1033 #endif
1034 strlw /= 2;
1035 break;
1036 default:
1037 #if T_RIGHT != A_RIGHT
1038 job = A_RIGHT;
1039 #endif
1040 break;
1041 }
1042 p = s;
1043 while (*s != '\0') {
1044 while (*p != '\0' && *p != '\n')
1045 p++;
1046 if (*p != '\0')
1047 *p++ = '\0';
1048 if (*s == '\0') { // new paragraph
1049 bskip(parskip);
1050 buffer_eob(0);
1051 while (*p == '\n') {
1052 bskip(lineskip);
1053 p++;
1054 }
1055 if (*p == '\0')
1056 goto skip;
1057 } else {
1058 bskip(lineskip);
1059 a2b("%.1f 0 M", strlw);
1060 put_str(s, job);
1061 }
1062 s = p;
1063 }
1064 goto skip;
1065 }
1066
1067 /* fill or justify lines */
1068 #ifdef HAVE_PANGO
1069 do_pango = cfmt.pango;
1070 if (do_pango == 1)
1071 do_pango = !is_latin((unsigned char *) s);
1072 if (do_pango) {
1073 pg_write_text(s, job, parskip);
1074 goto skip;
1075 }
1076 #endif
1077 // curft = defft;
1078 nw = 0; /* number of words */
1079 strw = 0; /* have gcc happy */
1080 stropx = (job == T_FILL ? A_LEFT : 7) * 2;
1081 while (*s != '\0') {
1082 float lw;
1083
1084 if (*s == '\n') { /* empty line = new paragraph */
1085 if (strtx) {
1086 str_end(1);
1087 if (job == T_JUSTIFY)
1088 a2b("}def\n"
1089 "/strop/show load def str");
1090 a2b("\n");
1091 }
1092 // a2b("\n");
1093 bskip(parskip);
1094 buffer_eob(0);
1095 // while (isspace((unsigned char) *s))
1096 // s++;
1097 while (*s == '\n') {
1098 bskip(lineskip);
1099 s++;
1100 }
1101 if (*s == '\0')
1102 goto skip;
1103 nw = 0;
1104 // a2b("0 0 M");
1105 // if (job != T_FILL) {
1106 // a2b("/str{");
1107 // outft = -1;
1108 // }
1109 // continue;
1110 }
1111
1112 if (nw == 0) { /* if new paragraph */
1113 bskip(lineskip);
1114 a2b("0 0 M");
1115 if (job != T_FILL) {
1116 a2b("/str{");
1117 outft = -1;
1118 }
1119 strw = 0; /* current line width */
1120 }
1121
1122 /* get a word */
1123 p = s;
1124 while (*p != '\0' && !isspace((unsigned char) *p))
1125 p++;
1126 if (*p != '\0') {
1127 char *q;
1128
1129 q = p;
1130 if (*p != '\n') {
1131 do {
1132 p++;
1133 } while (*p != '\n' && isspace((unsigned char) *p));
1134 }
1135 if (*p == '\n')
1136 p++;
1137 *q = '\0';
1138 }
1139
1140 lw = tex_str(s);
1141 if (strw + lw > strlw) {
1142 str_end(1);
1143 if (job == T_JUSTIFY) {
1144 int n;
1145
1146 n = nw - 1;
1147 if (n <= 0)
1148 n = 1;
1149 if (svg || epsf > 1)
1150 a2b("}def\n"
1151 "%.1f jshow"
1152 "/strop/show load def str",
1153 strlw);
1154 else
1155 a2b("}def\n"
1156 "strw"
1157 "/w %.1f w sub %d div def"
1158 "/strop/jshow load def str",
1159 strlw, n);
1160 }
1161 a2b("\n");
1162 bskip(lineskip);
1163 a2b("0 0 M");
1164 if (job == T_JUSTIFY) {
1165 a2b("/str{");
1166 outft = -1;
1167 }
1168 nw = 0;
1169 strw = 0;
1170 }
1171
1172 if (nw != 0) {
1173 str_ft_out1(" ", 1);
1174 strw += cwid(' ') * cfmt.font_tb[curft].swfac;
1175 }
1176 str_ft_out(tex_buf, 0);
1177 strw += lw;
1178 nw++;
1179
1180 s = p;
1181 }
1182 if (strtx) {
1183 str_end(1);
1184 if (job == T_JUSTIFY)
1185 a2b("}def\n"
1186 "/strop/show load def str");
1187 }
1188 // if (mbf[-1] != '\n')
1189 a2b("\n");
1190 skip:
1191 bskip(parskip);
1192 buffer_eob(0);
1193 }
1194
1195 /* -- output a line of words after tune -- */
put_wline(char * p,float x,int right)1196 static int put_wline(char *p,
1197 float x,
1198 int right)
1199 {
1200 char *q, *r, sep;
1201
1202 while (isspace((unsigned char) *p))
1203 p++;
1204 if (*p == '$' && isdigit((unsigned char) p[1])
1205 && (unsigned) (p[1] - '0') < FONT_UMAX) {
1206 if (curft != p[1] - '0') {
1207 curft = p[1] - '0';
1208 if (curft == 0)
1209 curft = defft;
1210 }
1211 p += 2;
1212 }
1213 r = 0;
1214 q = p;
1215 if (isdigit((unsigned char) *p) || p[1] == '.') {
1216 while (*p != '\0') {
1217 p++;
1218 if (*p == ' '
1219 || p[-1] == ':'
1220 || p[-1] == '.')
1221 break;
1222 }
1223 r = p;
1224 while (*p == ' ')
1225 p++;
1226 }
1227
1228 if (r != 0) {
1229 sep = *r;
1230 *r = '\0';
1231 a2b("%.1f 0 M", x);
1232 put_str(q, A_RIGHT);
1233 *r = sep;
1234 }
1235 if (*p != '\0') {
1236 a2b("%.1f 0 M", x + 5);
1237 put_str(p, A_LEFT);
1238 }
1239 return *p == '\0' && r == 0;
1240 }
1241
1242 /* -- output the words after tune -- */
put_words(struct SYMBOL * words)1243 void put_words(struct SYMBOL *words)
1244 {
1245 struct SYMBOL *s, *s_end, *s2;
1246 char *p;
1247 int i, n, have_text, max2col;
1248 float middle;
1249
1250 buffer_eob(0);
1251 str_font(WORDSFONT);
1252
1253 /* see if we may have 2 columns */
1254 middle = 0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
1255 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
1256 max2col = (int) ((middle - 45.) / (cwid('a') * cfmt.font_tb[WORDSFONT].swfac));
1257 n = 0;
1258 have_text = 0;
1259 for (s = words; s != 0; s = s->next) {
1260 p = &s->text[2];
1261 /*fixme:utf8*/
1262 if ((int) strlen(p) > max2col) {
1263 n = 0;
1264 break;
1265 }
1266 if (*p == '\0') {
1267 if (have_text) {
1268 n++;
1269 have_text = 0;
1270 }
1271 } else {
1272 have_text = 1;
1273 }
1274 }
1275 if (n > 0) {
1276 n++;
1277 n /= 2;
1278 i = n;
1279 have_text = 0;
1280 s_end = words;
1281 for (;;) {
1282 p = &s_end->text[2];
1283 while (isspace((unsigned char) *p))
1284 p++;
1285 if (*p == '\0') {
1286 if (have_text && --i <= 0)
1287 break;
1288 have_text = 0;
1289 } else {
1290 have_text = 1;
1291 }
1292 s_end = s_end->next;
1293 }
1294 s2 = s_end->next;
1295 } else {
1296 s_end = NULL;
1297 s2 = NULL;
1298 }
1299
1300 /* output the text */
1301 bskip(cfmt.wordsspace);
1302 for (s = words; s || s2; ) {
1303 //fixme:should also permit page break on stanza start
1304 if (s && s->text[2] == '\0')
1305 buffer_eob(0);
1306 bskip(cfmt.lineskipfac * cfmt.font_tb[WORDSFONT].size);
1307 if (s) {
1308 put_wline(&s->text[2], 45., 0);
1309 s = s->next;
1310 if (s == s_end)
1311 s = NULL;
1312 }
1313 if (s2) {
1314 if (put_wline(&s2->text[2], 20. + middle, 1)) {
1315 if (--n == 0) {
1316 if (s) {
1317 n++;
1318 } else if (s2->next) {
1319
1320 /* center the last words */
1321 /*fixme: should compute the width average.. */
1322 middle *= 0.6;
1323 }
1324 }
1325 }
1326 s2 = s2->next;
1327 }
1328 }
1329 // buffer_eob(0);
1330 }
1331
1332 /* -- output history -- */
put_history(void)1333 void put_history(void)
1334 {
1335 struct SYMBOL *s, *s2;
1336 int font;
1337 unsigned u;
1338 float w, h;
1339 char tmp[265];
1340
1341 font = 0;
1342 for (s = info['I' - 'A']; s; s = s->next) {
1343 u = s->text[0] - 'A';
1344 if (!(cfmt.fields[0] & (1 << u))
1345 || (s2 = info[u]) == NULL)
1346 continue;
1347 if (!font) {
1348 bskip(cfmt.textspace);
1349 str_font(HISTORYFONT);
1350 font = 1;
1351 }
1352 get_str(tmp, &s->text[1], sizeof tmp);
1353 w = tex_str(tmp);
1354 h = cfmt.font_tb[HISTORYFONT].size * cfmt.lineskipfac;
1355 set_font(HISTORYFONT);
1356 // a2b("0 0 M(%s)show ", tex_buf);
1357 a2b("0 0 M");
1358 str_out(tex_buf, A_LEFT);
1359 for (;;) {
1360 put_inf(s2);
1361 if ((s2 = s2->next) == NULL)
1362 break;
1363 if (s2->text[0] == '+' && s2->text[1] == ':') {
1364 put_str(" ", A_LEFT);
1365 } else {
1366 bskip(h);
1367 a2b("%.2f 0 M ", w);
1368 }
1369 }
1370 bskip(h * 1.2);
1371 buffer_eob(0);
1372 }
1373 }
1374
1375 /* -- move trailing "The" to front, set to uppercase letters or add xref -- */
trim_title(char * p,struct SYMBOL * title)1376 char *trim_title(char *p, struct SYMBOL *title)
1377 {
1378 char *b, *q, *r;
1379 static char buf[STRL1];
1380
1381 q = NULL;
1382 if (cfmt.titletrim) {
1383 q = strrchr(p, ',');
1384 if (q) {
1385 if (q[1] != ' ' || !isupper((unsigned char) q[2])) {
1386 q = NULL;
1387 } else if (cfmt.titletrim == 1) { // (true)
1388 if (strlen(q) > 7 /* word no more than 5 characters */
1389 || strchr(q + 2, ' '))
1390 q = NULL;
1391 } else {
1392 if (strlen(q) > cfmt.titletrim - 2)
1393 q = NULL;
1394 }
1395 }
1396 }
1397 if (title != info['T' - 'A']
1398 || !(cfmt.fields[0] & (1 << ('X' - 'A'))))
1399 title = NULL;
1400 if (!q
1401 && !title
1402 && !cfmt.titlecaps)
1403 return p; /* keep the title as it is */
1404 b = buf;
1405 r = &info['X' - 'A']->text[2];
1406 if (title
1407 && *r != '\0') {
1408 if (strlen(p) + strlen(r) + 3 >= sizeof buf) {
1409 error(1, NULL, "Title or X: too long");
1410 return p;
1411 }
1412 b += sprintf(b, "%s. ", r);
1413 } else {
1414 if (strlen(p) >= sizeof buf) {
1415 error(1, NULL, "Title too long");
1416 return p;
1417 }
1418 }
1419 if (q)
1420 sprintf(b, "%s %.*s", q + 2, (int) (q - p), p);
1421 else
1422 strcpy(b, p);
1423 if (cfmt.titlecaps)
1424 cap_str(buf);
1425 return buf;
1426 }
1427
1428 /* -- write a title -- */
write_title(struct SYMBOL * s)1429 void write_title(struct SYMBOL *s)
1430 {
1431 char *p;
1432 float sz;
1433
1434 p = &s->text[2];
1435 if (*p == '\0')
1436 return;
1437 if (s == info['T' - 'A']) {
1438 sz = cfmt.font_tb[TITLEFONT].size;
1439 bskip(cfmt.titlespace + sz);
1440 str_font(TITLEFONT);
1441 a2b("%% --- title");
1442 } else {
1443 sz = cfmt.font_tb[SUBTITLEFONT].size;
1444 bskip(cfmt.subtitlespace + sz);
1445 str_font(SUBTITLEFONT);
1446 a2b("%% --- titlesub");
1447 }
1448 a2b(" %s\n", p);
1449 if (cfmt.titleleft)
1450 a2b("0");
1451 else
1452 a2b("%.1f",
1453 0.5 * ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
1454 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale);
1455 a2b(" %.1f M ", sz * 0.2);
1456 p = trim_title(p, s);
1457 put_str(p, cfmt.titleleft ? A_LEFT : A_CENTER);
1458 }
1459
1460 /* -- write heading with format -- */
write_headform(float lwidth)1461 static void write_headform(float lwidth)
1462 {
1463 char *p, *q;
1464 struct SYMBOL *s;
1465 struct FONTSPEC *f;
1466 int align, i;
1467 unsigned j;
1468 float x, y, xa[3], ya[3], sz, yb[3]; /* !! see action A_xxx */
1469 char inf_nb[26];
1470 INFO inf_s;
1471 char inf_ft[26];
1472 float inf_sz[26];
1473 char fmt[64];
1474
1475 memset(inf_nb, 0, sizeof inf_nb);
1476 memset(inf_ft, HISTORYFONT, sizeof inf_ft);
1477 inf_ft['A' - 'A'] = INFOFONT;
1478 inf_ft['C' - 'A'] = COMPOSERFONT;
1479 inf_ft['O' - 'A'] = COMPOSERFONT;
1480 inf_ft['P' - 'A'] = PARTSFONT;
1481 inf_ft['Q' - 'A'] = TEMPOFONT;
1482 inf_ft['R' - 'A'] = INFOFONT;
1483 inf_ft['T' - 'A'] = TITLEFONT;
1484 inf_ft['X' - 'A'] = TITLEFONT;
1485 memcpy(inf_s, info, sizeof inf_s);
1486 memset(inf_sz, 0, sizeof inf_sz);
1487 inf_sz['A' - 'A'] = cfmt.infospace;
1488 inf_sz['C' - 'A'] = cfmt.composerspace;
1489 inf_sz['O' - 'A'] = cfmt.composerspace;
1490 inf_sz['R' - 'A'] = cfmt.infospace;
1491 p = cfmt.titleformat;
1492 j = 0;
1493 for (;;) {
1494 while (isspace((unsigned char) *p))
1495 p++;
1496 if (*p == '\0')
1497 break;
1498 i = *p - 'A';
1499 if ((unsigned) i < 26) {
1500 inf_nb[i]++;
1501 switch (p[1]) {
1502 default:
1503 align = A_CENTER;
1504 break;
1505 case '1':
1506 align = A_RIGHT;
1507 p++;
1508 break;
1509 case '-':
1510 align = A_LEFT;
1511 p++;
1512 break;
1513 }
1514 if (j < sizeof fmt - 4) {
1515 fmt[j++] = i;
1516 fmt[j++] = align;
1517 }
1518 } else if (*p == ',') {
1519 if (j < sizeof fmt - 3)
1520 fmt[j++] = 126; /* next line */
1521 } else if (*p == '+') {
1522 if (j > 0 && fmt[j - 1] < 125
1523 && j < sizeof fmt - 4) {
1524 fmt[j++] = 125; /* concatenate */
1525 fmt[j++] = 0;
1526 }
1527 /*new fixme: add free text "..." ?*/
1528 }
1529 p++;
1530 }
1531 fmt[j++] = 126; /* newline */
1532 fmt[j] = 127; /* end of format */
1533
1534 ya[0] = ya[1] = ya[2] = cfmt.titlespace;
1535 xa[0] = 0;
1536 xa[1] = lwidth * 0.5;
1537 xa[2] = lwidth;
1538
1539 p = fmt;
1540 for (;;) {
1541 yb[0] = yb[1] = yb[2] = y = 0;
1542 q = p;
1543 for (;;) {
1544 i = *q++;
1545 if (i >= 126) /* if newline */
1546 break;
1547 align = *q++;
1548 if (yb[align] != 0
1549 || i == 125)
1550 continue;
1551 s = inf_s[i];
1552 if (s == 0 || inf_nb[i] == 0)
1553 continue;
1554 j = inf_ft[i];
1555 f = &cfmt.font_tb[j];
1556 sz = f->size * 1.1 + inf_sz[i];
1557 if (y < sz)
1558 y = sz;
1559 yb[align] = sz;
1560 /*fixme:should count the height of the concatenated field*/
1561 }
1562 for (i = 0; i < 3; i++)
1563 ya[i] += y - yb[i];
1564 for (;;) {
1565 i = *p++;
1566 if (i >= 126) /* if newline */
1567 break;
1568 align = *p++;
1569 if (i == 125)
1570 continue;
1571 s = inf_s[i];
1572 if (!s || inf_nb[i] == 0)
1573 continue;
1574 j = inf_ft[i];
1575 str_font(j);
1576 x = xa[align];
1577 f = &cfmt.font_tb[j];
1578 sz = f->size * 1.1 + inf_sz[i];
1579 y = ya[align] + sz;
1580 if (s->text[2] != '\0') {
1581 if (i == 'T' - 'A') {
1582 if (s == info['T' - 'A'])
1583 a2b("%% --- title");
1584 else
1585 a2b("%% --- titlesub");
1586 a2b(" %s\n", &s->text[2]);
1587 }
1588 a2b("%.1f %.1f M ", x, -y);
1589 }
1590 if (*p == 125) { /* concatenate */
1591 p += 2;
1592 /*fixme: do it work with different fields*/
1593 if (*p == i && p[1] == align
1594 && s->next) {
1595 char buf[256], *r;
1596
1597 q = s->text;
1598 if (q[1] == ':')
1599 q += 2;
1600 while (isspace((unsigned char) *q))
1601 q++;
1602 if (i == 'T' - 'A')
1603 q = trim_title(q, s);
1604 strncpy(buf, q, sizeof buf - 1);
1605 buf[sizeof buf - 1] = '\0';
1606 j = strlen(buf);
1607 if (j < sizeof buf - 1) {
1608 buf[j] = ' ';
1609 buf[j + 1] = '\0';
1610 }
1611 s = s->next;
1612 q = s->text;
1613 if (q[1] == ':')
1614 q += 2;
1615 while (isspace((unsigned char) *q))
1616 q++;
1617 if (s->text[0] == 'T'/* && s->text[1] == ':'*/)
1618 q = trim_title(q, s);
1619 r = buf + strlen(buf);
1620 strncpy(r, q, buf + sizeof buf - r - 1);
1621 tex_str(buf);
1622 str_out(tex_buf, align);
1623 a2b("\n");
1624 inf_nb[i]--;
1625 p += 2;
1626 } else {
1627 put_inf2r(s, NULL, align);
1628 }
1629 } else if (i == 'Q' - 'A') { /* special case for tempo */
1630 if (align != A_LEFT) {
1631 float w;
1632
1633 w = -tempo_width(s);
1634 if (align == A_CENTER)
1635 w *= 0.5;
1636 a2b("%.1f 0 RM ", w);
1637 }
1638 write_tempo(s, 0, 0.75);
1639 info['Q' - 'A'] = NULL; /* don't display in tune */
1640 } else {
1641 put_inf2r(s, NULL, align);
1642 }
1643 if (inf_s[i] == info['T' - 'A']) {
1644 inf_ft[i] = SUBTITLEFONT;
1645 str_font(SUBTITLEFONT);
1646 f = &cfmt.font_tb[SUBTITLEFONT];
1647 inf_sz[i] = cfmt.subtitlespace;
1648 sz = f->size * 1.1 + inf_sz[i];
1649 }
1650 s = s->next;
1651 if (inf_nb[i] == 1) {
1652 while (s) {
1653 y += sz;
1654 a2b("%.1f %.1f M ", x, -y);
1655 put_inf2r(s, 0, align);
1656 s = s->next;
1657 }
1658 }
1659 inf_s[i] = s;
1660 inf_nb[i]--;
1661 ya[align] = y;
1662 }
1663 if (ya[1] > ya[0])
1664 ya[0] = ya[1];
1665 if (ya[2] > ya[0])
1666 ya[0] = ya[2];
1667 if (*p == 127) {
1668 bskip(ya[0]);
1669 break;
1670 }
1671 ya[1] = ya[2] = ya[0];
1672 }
1673 }
1674
1675 /* -- output the tune heading -- */
write_heading(void)1676 void write_heading(void)
1677 {
1678 struct SYMBOL *s, *rhythm, *area, *author, *composer, *origin;
1679 float lwidth, down1, down2;
1680
1681 lwidth = ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth)
1682 - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale;
1683
1684 if (cfmt.titleformat && cfmt.titleformat[0] != '\0') {
1685 write_headform(lwidth);
1686 bskip(cfmt.musicspace);
1687 return;
1688 }
1689
1690 /* titles */
1691 if (cfmt.fields[0] & (1 << ('T' - 'A'))) {
1692 for (s = info['T' - 'A']; s; s = s->next)
1693 write_title(s);
1694 }
1695
1696 /* rhythm, composer, origin */
1697 down1 = cfmt.composerspace + cfmt.font_tb[COMPOSERFONT].size;
1698 rhythm = ((first_voice->key.instr == K_HP
1699 || first_voice->key.instr == K_Hp
1700 || pipeformat)
1701 && !cfmt.infoline
1702 && (cfmt.fields[0] & (1 << ('R' - 'A'))))
1703 ? info['R' - 'A'] : NULL;
1704 if (rhythm) {
1705 str_font(COMPOSERFONT);
1706 a2b("0 %.1f M ", -cfmt.composerspace);
1707 put_inf(rhythm);
1708 down1 = cfmt.composerspace;
1709 }
1710 area = author = NULL;
1711 if (parse.abc_vers != (2 << 16))
1712 area = info['A' - 'A'];
1713 else
1714 author = info['A' - 'A'];
1715 composer = (cfmt.fields[0] & (1 << ('C' - 'A'))) ? info['C' - 'A'] : NULL;
1716 origin = (cfmt.fields[0] & (1 << ('O' - 'A'))) ? info['O' - 'A'] : NULL;
1717 if (composer || origin || author || cfmt.infoline) {
1718 float xcomp;
1719 int align;
1720
1721 str_font(COMPOSERFONT);
1722 bskip(cfmt.composerspace);
1723 if (cfmt.aligncomposer < 0) {
1724 xcomp = 0;
1725 align = A_LEFT;
1726 } else if (cfmt.aligncomposer == 0) {
1727 xcomp = lwidth * 0.5;
1728 align = A_CENTER;
1729 } else {
1730 xcomp = lwidth;
1731 align = A_RIGHT;
1732 }
1733 down2 = down1;
1734 if (author) {
1735 for (;;) {
1736 bskip(cfmt.font_tb[COMPOSERFONT].size);
1737 down2 += cfmt.font_tb[COMPOSERFONT].size;
1738 a2b("0 0 M ");
1739 put_inf(author);
1740 if ((author = author->next) == NULL)
1741 break;
1742 }
1743 }
1744 if (composer || origin) {
1745 if (cfmt.aligncomposer >= 0
1746 && down1 != down2)
1747 bskip(down1 - down2);
1748 s = composer;
1749 for (;;) {
1750 bskip(cfmt.font_tb[COMPOSERFONT].size);
1751 a2b("%.1f 0 M ", xcomp);
1752 put_inf2r(s,
1753 (!s || !s->next) ? origin : NULL,
1754 align);
1755 if (!s)
1756 break;
1757 if ((s = s->next) == NULL)
1758 break;
1759 down1 += cfmt.font_tb[COMPOSERFONT].size;
1760 }
1761 if (down2 > down1)
1762 bskip(down2 - down1);
1763 }
1764
1765 rhythm = rhythm ? NULL : info['R' - 'A'];
1766 if ((rhythm || area) && cfmt.infoline) {
1767
1768 /* if only one of rhythm or area then do not use ()'s
1769 * otherwise output 'rhythm (area)' */
1770 str_font(INFOFONT);
1771 bskip(cfmt.font_tb[INFOFONT].size + cfmt.infospace);
1772 a2b("%.1f 0 M ", lwidth);
1773 put_inf2r(rhythm, area, A_RIGHT);
1774 down1 += cfmt.font_tb[INFOFONT].size + cfmt.infospace;
1775 }
1776 down2 = 0;
1777 } else {
1778 down2 = cfmt.composerspace;
1779 }
1780
1781 /* parts */
1782 if (info['P' - 'A']
1783 && (cfmt.fields[0] & (1 << ('P' - 'A')))) {
1784 down1 = cfmt.partsspace + cfmt.font_tb[PARTSFONT].size - down1;
1785 if (down1 > 0)
1786 down2 += down1;
1787 if (down2 > 0.01)
1788 bskip(down2);
1789 str_font(PARTSFONT);
1790 a2b("0 0 M ");
1791 put_inf(info['P' - 'A']);
1792 down2 = 0;
1793 }
1794 bskip(down2 + cfmt.musicspace);
1795 }
1796
1797 /* -- memorize a PS / SVG line -- */
1798 /* 'use' may be:
1799 * 'g': SVG code
1800 * 'p': PS code for PS output only
1801 * 's': PS code for SVG output only
1802 * 'b': PS code for PS or SVG output
1803 */
user_ps_add(char * s,char use)1804 void user_ps_add(char *s, char use)
1805 {
1806 struct u_ps *t, *r;
1807 int l;
1808
1809 if (*s == '\0' || *s == '%')
1810 return;
1811 l = strlen(s);
1812 if (use == 'g') {
1813 t = malloc(sizeof *user_ps - sizeof user_ps->text + l + 6);
1814 sprintf(t->text, "%%svg %s", s);
1815 } else {
1816 t = malloc(sizeof *user_ps - sizeof user_ps->text + l + 2);
1817 sprintf(t->text, "%c%s", use, s);
1818 }
1819 t->next = NULL;
1820 if ((r = user_ps) == NULL) {
1821 user_ps = t;
1822 } else {
1823 while (r->next)
1824 r = r->next;
1825 r->next = t;
1826 }
1827 }
1828
1829 /* -- output the user defined postscript sequences -- */
user_ps_write(void)1830 void user_ps_write(void)
1831 {
1832 struct u_ps *t;
1833 char *p;
1834
1835 for (t = user_ps; t; t = t->next) {
1836 p = t->text;
1837 switch (*p) {
1838 case '\001': { /* PS file */
1839 FILE *f;
1840 char line[BSIZE];
1841
1842 if ((f = fopen(p + 1, "r")) == NULL) {
1843 error(1, NULL, "Cannot open PS file '%s'",
1844 &t->text[1]);
1845 } else {
1846 while (fgets(line, sizeof line, f)) /* copy the file */
1847 fputs(line, fout);
1848 fclose(f);
1849 }
1850 continue;
1851 }
1852 case '%': /* "%svg " = SVG code */
1853 // if (svg || epsf > 1)
1854 // svg_write(t->text, strlen(t->text));
1855 fputs(p + 5, fout);
1856 fputc('\n', fout);
1857 continue;
1858 case 'p': /* PS code for PS output only */
1859 // if (secure || svg || epsf > 1)
1860 // continue;
1861 break;
1862 case 'b': /* PS code for both PS and SVG */
1863 if (svg || epsf > 1) {
1864 svg_write(p + 1, strlen(p + 1));
1865 continue;
1866 }
1867 // if (secure)
1868 // continue;
1869 break;
1870 case 's': /* PS code for SVG output only */
1871 // if (!svg && epsf <= 1)
1872 // continue;
1873 svg_write(p + 1, strlen(&t->text[1]));
1874 continue;
1875 }
1876 fputs(p + 1, fout);
1877 fputc('\n', fout);
1878 }
1879 }
1880