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