1 /*
2  * Postscript buffering functions.
3  *
4  * This file is part of abcm2ps.
5  *
6  * Copyright (C) 1998-2017 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 <stdarg.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <ctype.h>
19 
20 #include "abcm2ps.h"
21 
22 #define PPI_96_72 0.75		// convert page format to 72 PPI
23 #define BUFFLN	80		/* max number of lines in output buffer */
24 
25 static int ln_num;		/* number of lines in buffer */
26 static float ln_pos[BUFFLN];	/* vertical positions of buffered lines */
27 static char *ln_buf[BUFFLN];	/* buffer location of buffered lines */
28 static float ln_lmarg[BUFFLN];	/* left margin of buffered lines */
29 static float ln_scale[BUFFLN];	/* scale of buffered lines */
30 static signed char ln_font[BUFFLN];	/* font of buffered lines */
31 static float cur_lmarg = 0;	/* current left margin */
32 static float min_lmarg, max_rmarg;	/* margins for -E/-g */
33 static float cur_scale = 1.0;	/* current scale */
34 static float maxy;		/* usable vertical space in page */
35 static float remy;		/* remaining vertical space in page */
36 static float bposy;		/* current position in buffered data */
37 static int nepsf;		/* counter for -E/-g output files */
38 static int nbpages;		/* number of pages in the output file */
39 	int outbufsz;		/* size of outbuf */
40 static char outfnam[FILENAME_MAX]; /* internal file name for open/close */
41 static struct FORMAT *p_fmt;	/* current format while treating a new page */
42 
43 int (*output)(FILE *out, const char *fmt, ...);
44 
45 int in_page;			/* filling a PostScript page */
46 char *outbuf;			/* output buffer.. should hold one tune */
47 char *mbf;			/* where to a2b() */
48 int use_buffer;			/* 1 if lines are being accumulated */
49 int (*output)(FILE *out, const char *fmt, ...)
50 #ifdef __GNUC__
51 	__attribute__ ((format (printf, 2, 3)))
52 #endif
53 	;
54 
55 /* -- cut off extension on a file identifier -- */
cutext(char * fid)56 static void cutext(char *fid)
57 {
58 	char *p;
59 
60 	if ((p = strrchr(fid, DIRSEP)) == NULL)
61 		p = fid;
62 	if ((p = strrchr(p, '.')) != NULL)
63 		*p = '\0';
64 }
65 
66 /* -- open the output file -- */
open_fout(void)67 void open_fout(void)
68 {
69 	int i;
70 	char fnm[FILENAME_MAX];
71 
72 	strcpy(fnm, outfn);
73 	i = strlen(fnm) - 1;
74 	if (i < 0) {
75 		strcpy(fnm, svg || epsf > 1 ? "Out.xhtml" : OUTPUTFILE);
76 	} else if (i != 0 || fnm[0] != '-') {
77 		if (fnm[i] == '=' && in_fname) {
78 			char *p;
79 
80 			if ((p = strrchr(in_fname, DIRSEP)) == NULL)
81 				p = in_fname;
82 			else
83 				p++;
84 			strcpy(&fnm[i], p);
85 			strext(fnm, svg || epsf > 1 ? "xhtml" : "ps");
86 		} else if (fnm[i] == DIRSEP) {
87 			strcpy(&fnm[i + 1],
88 				svg || epsf > 1 ? "Out.xhtml" : OUTPUTFILE);
89 		}
90 #if 0
91 /*fixme: fnm may be a directory*/
92 		else	...
93 #endif
94 	}
95 	if (svg == 1
96 	 && (i != 0 || fnm[0] != '-')) {
97 		cutext(fnm);
98 		i = strlen(fnm) - 1;
99 		if (strncmp(fnm, outfnam, i) != 0)
100 			nepsf = 0;
101 		sprintf(&fnm[i + 1], "%03d.svg", ++nepsf);
102 	} else if (strcmp(fnm, outfnam) == 0) {
103 		return;				/* same output file */
104 	}
105 
106 	close_output_file();
107 	strcpy(outfnam, fnm);
108 	if (i != 0 || fnm[0] != '-') {
109 		if ((fout = fopen(fnm, "w")) == NULL) {
110 			error(1, NULL, "Cannot create output file %s - abort", fnm);
111 			exit(EXIT_FAILURE);
112 		}
113 	} else {
114 		fout = stdout;
115 	}
116 }
117 
118 /* -- convert a date -- */
cnv_date(time_t * ltime)119 static void cnv_date(time_t *ltime)
120 {
121 	char buf[TEX_BUF_SZ];
122 
123 	tex_str(cfmt.dateformat);
124 	strcpy(buf, tex_buf);
125 	strftime(tex_buf, TEX_BUF_SZ, buf, localtime(ltime));
126 }
127 
128 /* initialize the min/max margin values */
129 /* (used only with eps -E and svg -g) */
marg_init(void)130 void marg_init(void)
131 {
132 	min_lmarg = cfmt.pagewidth;
133 	max_rmarg = cfmt.pagewidth;
134 }
135 
136 /* -- initialize the postscript file (PS or EPS) -- */
init_ps(char * str)137 static void init_ps(char *str)
138 {
139 	time_t ltime;
140 	unsigned i;
141 	char version[] = "/creator [(abcm2ps) " VERSION "] def";
142 
143 	if (epsf) {
144 		cur_lmarg = min_lmarg - 10;
145 		fprintf(fout, "%%!PS-Adobe-2.0 EPSF-2.0\n"
146 			"%%%%BoundingBox: 0 0 %.0f %.0f\n",
147 			((p_fmt->landscape ? p_fmt->pageheight : p_fmt->pagewidth)
148 				- cur_lmarg - max_rmarg + 10) * PPI_96_72,
149 			-bposy * PPI_96_72);
150 		marg_init();
151 	} else {
152 		if (!fout)
153 			open_fout();
154 		fprintf(fout, "%%!PS-Adobe-2.0\n");
155 		fprintf(fout, "%%%%BoundingBox: 0 0 %.0f %.0f\n",
156 			p_fmt->pagewidth * PPI_96_72,
157 			p_fmt->pageheight * PPI_96_72);
158 	}
159 	fprintf(fout, "%%%%Title: %s\n", str);
160 	time(&ltime);
161 #ifndef WIN32
162 	strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(&ltime));
163 #else
164 	strftime(tex_buf, TEX_BUF_SZ, "%b %#d, %Y %H:%M", localtime(&ltime));
165 #endif
166 	fprintf(fout, "%%%%Creator: abcm2ps-" VERSION "\n"
167 		"%%%%CreationDate: %s\n", tex_buf);
168 	if (!epsf)
169 		fprintf(fout, "%%%%Pages: (atend)\n");
170 	fprintf(fout, "%%%%LanguageLevel: 3\n"
171 		"%%%%EndComments\n"
172 		"%%CommandLine:");
173 	for (i = 1; i < (unsigned) s_argc; i++) {
174 		char *p, *q;
175 		int space;
176 
177 		p = s_argv[i];
178 		space = strchr(p, ' ') != NULL || strchr(p, '\n') != NULL;
179 		fputc(' ', fout);
180 		if (space)
181 			fputc('\'', fout);
182 		for (;;) {
183 			q = strchr(p, '\n');
184 			if (!q)
185 				break;
186 			fprintf(fout, " %.*s\n%%", (int) (q - p), p);
187 			p = q + 1;
188 		}
189 		fprintf(fout, "%s", p);
190 		if (space)
191 			fputc('\'', fout);
192 	}
193 	fprintf(fout, "\n\n");
194 	if (epsf)
195 		fprintf(fout, "save\n");
196 	for (i = 0; i < strlen(version); i++) {
197 		if (version[i] == '.')
198 			version[i] = ' ';
199 	}
200 	fprintf(fout, "%%%%BeginSetup\n"
201 		"/!{bind def}bind def\n"
202 		"/bdef{bind def}!\n"		/* for compatibility */
203 		"/T/translate load def\n"
204 		"/M/moveto load def\n"
205 		"/RM/rmoveto load def\n"
206 		"/L/lineto load def\n"
207 		"/RL/rlineto load def\n"
208 		"/C/curveto load def\n"
209 		"/RC/rcurveto load def\n"
210 		"/SLW/setlinewidth load def\n"
211 		"/defl 0 def\n"	/* decoration flags - see deco.c for values */
212 		"/dlw{0.7 SLW}!\n"
213 		"%s\n", version);
214 	define_symbols();
215 	output = fprintf;
216 	user_ps_write();
217 	define_fonts();
218 	if (!epsf) {
219 		fprintf(fout, "/setpagedevice where{pop\n"
220 			"	<</PageSize[%.0f %.0f]",
221 				p_fmt->pagewidth * PPI_96_72,
222 				p_fmt->pageheight * PPI_96_72);
223 		if (cfmt.gutter)
224 			fprintf(fout,
225 			 "\n	/BeginPage{1 and 0 eq{%.1f 0 T}{-%.1f 0 T}ifelse}bind\n	",
226 				cfmt.gutter, cfmt.gutter);
227 		fprintf(fout, ">>setpagedevice}if\n");
228 	}
229 	fprintf(fout, "%%%%EndSetup\n");
230 	file_initialized = 1;
231 }
232 
233 /* -- initialize the svg file (option '-g') -- */
init_svg(char * str)234 static void init_svg(char *str)
235 {
236 	cur_lmarg = min_lmarg - 10;
237 	output = svg_output;
238 #if 1 //fixme:test
239 	if (file_initialized > 0)
240 		fprintf(stderr, "??? init_svg: file_initialized\n");
241 #endif
242 	define_svg_symbols(str, nepsf,
243 		(p_fmt->landscape ? p_fmt->pageheight : p_fmt->pagewidth)
244 				- cur_lmarg - max_rmarg + 10,
245 		-bposy);
246 	file_initialized = 1;
247 	user_ps_write();
248 }
249 
close_fout(void)250 static void close_fout(void)
251 {
252 	long m;
253 
254 	if (fout == stdout)
255 		goto out2;
256 	if (quiet)
257 		goto out1;
258 	m = ftell(fout);
259 	if (epsf || svg == 1)
260 		printf("Output written on %s (%ld bytes)\n",
261 			outfnam, m);
262 	else
263 		printf("Output written on %s (%d page%s, %d title%s, %ld bytes)\n",
264 			outfnam,
265 			nbpages, nbpages == 1 ? "" : "s",
266 			tunenum, tunenum == 1 ? "" : "s",
267 			m);
268 out1:
269 	fclose(fout);
270 out2:
271 	fout = NULL;
272 	file_initialized = 0;
273 }
274 
275 /* -- close the output file -- */
276 /* epsf is always null */
close_output_file(void)277 void close_output_file(void)
278 {
279 	if (!fout)
280 		return;
281 	if (multicol_start != 0) {	/* if no '%%multicol end' */
282 		error(1, NULL, "No \"%%%%multicol end\"");
283 		multicol_start = 0;
284 		write_buffer();
285 	}
286 	if (tunenum == 0)
287 		error(0, NULL, "No tunes written to output file");
288 	close_page();
289 	switch (svg) {
290 	case 0:				/* PS */
291 		if (epsf == 0)
292 			fprintf(fout, "%%%%Trailer\n"
293 				"%%%%Pages: %d\n"
294 				"%%EOF\n", nbpages);
295 		close_fout();
296 		break;
297 	case 2:				/* -X */
298 		fputs("</body>\n"
299 			"</html>\n", fout);
300 	case 3:				/* -z */
301 		close_fout();
302 		break;
303 //	default:
304 //	case 1:				/* -v */
305 //		'fout' is closed in close_page
306 	}
307 
308 	nbpages = tunenum = 0;
309 	defl = 0;
310 }
311 
312 /* -- close the PS / SVG page -- */
close_page(void)313 void close_page(void)
314 {
315 	if (!in_page)
316 		return;
317 	in_page = 0;
318 	if (svg) {
319 		svg_close();
320 		if (svg == 1 && fout != stdout)
321 			close_fout();
322 //		else
323 //			fputs("</p>\n", fout);
324 	} else {
325 		fprintf(fout, "grestore\n"
326 				"showpage\n"
327 				"%%%%EndPage: %d %d\n",
328 				nbpages, nbpages);
329 	}
330 	cur_lmarg = 0;
331 	cur_scale = 1.0;
332 	outft = -1;
333 	use_buffer = 0;
334 }
335 
336 /* -- output a header/footer element -- */
format_hf(char * d,char * p)337 static void format_hf(char *d, char *p)
338 {
339 	char *q;
340 	time_t ltime;
341 
342 	for (;;) {
343 		if (*p == '\0')
344 			break;
345 		if ((q = strchr(p, '$')) != NULL)
346 			*q = '\0';
347 		d += sprintf(d, "%s", p);
348 		if (!q)
349 			break;
350 		p = q + 1;
351 		switch (*p) {
352 		case 'd':
353 			ltime = mtime;
354 			goto dput;
355 		case 'D':
356 			time(&ltime);
357 		dput:	cnv_date(&ltime);
358 			d += sprintf(d, "%s", tex_buf);
359 			break;
360 		case 'F':		/* ABC file name */
361 #if DIRSEP!='\\'
362 			d += sprintf(d, "%s", in_fname);
363 #else
364 			{
365 				int i;
366 
367 				q = in_fname;
368 				i = TEX_BUF_SZ;
369 				for (;;) {
370 					if (--i <= 0 || *q == '\0')
371 						break;
372 					if ((*d++ = *q++) == '\\') {
373 						i--;
374 						*d++ = '\\';
375 					}
376 				}
377 				*d = '\0';
378 			}
379 #endif
380 			break;
381 		case 'I':		/* information field */
382 			p++;
383 			if (*p < 'A' || *p > 'Z' || !info[*p - 'A'])
384 				break;
385 			d += sprintf(d, "%s", &info[*p - 'A']->text[2]);
386 			break;
387 		case 'P':		/* page number */
388 			if (p[1] == '0') {
389 				p++;
390 				if (pagenum & 1)
391 					break;
392 			} else if (p[1] == '1') {
393 				p++;
394 				if ((pagenum & 1) == 0)
395 					break;
396 			}
397 			d += sprintf(d, "%d", pagenum);
398 			break;
399 		case 'T':		/* tune title */
400 			q = &info['T' - 'A']->text[2];
401 			tex_str(q);
402 			d += sprintf(d, "%s", tex_buf);
403 			break;
404 		case 'V':
405 			d += sprintf(d,"abcm2ps-"  VERSION);
406 			break;
407 		default:
408 			continue;
409 		}
410 		p++;
411 	}
412 	*d = '\0';		/* in case of empty string */
413 }
414 
415 /* -- output the header or footer -- */
headfooter(int header,float pwidth,float pheight)416 static float headfooter(int header,
417 			float pwidth,
418 			float pheight)
419 {
420 	char tmp[2048], str[TEX_BUF_SZ + 512];
421 	char *p, *q, *r, *outbuf_sav, *mbf_sav;
422 	float size, y, wsize;
423 	struct FONTSPEC *f, f_sav;
424 
425 	int cft_sav, dft_sav, outbufsz_sav;
426 
427 	if (header) {
428 		p = cfmt.header;
429 		f = &cfmt.font_tb[HEADERFONT];
430 		size = f->size;
431 		y = -size;
432 	} else {
433 		p = cfmt.footer;
434 		f = &cfmt.font_tb[FOOTERFONT];
435 		size = f->size;
436 		y = - (pheight - cfmt.topmargin - cfmt.botmargin)
437 			+ size;
438 	}
439 	if (*p == '-') {
440 		if (pagenum == 1)
441 			return 0;
442 		p++;
443 	}
444 	get_str_font(&cft_sav, &dft_sav);
445 	memcpy(&f_sav, &cfmt.font_tb[0], sizeof f_sav);
446 	str_font(f - cfmt.font_tb);
447 	output(fout, "%.1f F%d ", size, f->fnum);
448 	outft = f - cfmt.font_tb;
449 
450 	/* may have 2 lines */
451 	wsize = size;
452 	if ((r = strstr(p, "\\n")) != NULL) {
453 		if (!header)
454 			y += size;
455 		wsize += size;
456 		*r = '\0';
457 	}
458 	mbf_sav = mbf;
459 	outbuf_sav = outbuf;
460 	outbufsz_sav = outbufsz;
461 	outbuf = tmp;
462 	outbufsz = sizeof tmp;
463 	for (;;) {
464 		tex_str(p);
465 		strcpy(tmp, tex_buf);
466 		format_hf(str, tmp);
467 
468 		/* left side */
469 		p = str;
470 		if ((q = strchr(p, '\t')) != NULL) {
471 			if (q != p) {
472 				*q = '\0';
473 				output(fout, "%.1f %.1f M ",
474 					p_fmt->leftmargin, y);
475 				mbf = tmp;
476 				str_out(p, A_LEFT);
477 				a2b("\n");
478 				if (svg)
479 					svg_write(tmp, strlen(tmp));
480 				else
481 					fputs(tmp, fout);
482 			}
483 			p = q + 1;
484 		}
485 		if ((q = strchr(p, '\t')) != NULL)
486 			*q = '\0';
487 
488 		/* center */
489 		if (q != p) {
490 			output(fout, "%.1f %.1f M ",
491 				pwidth * 0.5, y);
492 			mbf = tmp;
493 			str_out(p, A_CENTER);
494 			a2b("\n");
495 			if (svg)
496 				svg_write(tmp, strlen(tmp));
497 			else
498 				fputs(tmp, fout);
499 		}
500 
501 		/* right side */
502 		if (q) {
503 			p = q + 1;
504 			if (*p != '\0') {
505 				output(fout, "%.1f %.1f M ",
506 					pwidth - p_fmt->rightmargin, y);
507 				mbf = tmp;
508 				str_out(p, A_RIGHT);
509 				a2b("\n");
510 				if (svg)
511 					svg_write(tmp, strlen(tmp));
512 				else
513 					fputs(tmp, fout);
514 			}
515 		}
516 		if (!r)
517 			break;
518 		*r = '\\';
519 		p = r + 2;
520 		r = NULL;
521 		y -= size;
522 	}
523 
524 	/* restore the buffer and fonts */
525 	outbuf = outbuf_sav;
526 	outbufsz = outbufsz_sav;
527 	mbf = mbf_sav;
528 	memcpy(&cfmt.font_tb[0], &f_sav, sizeof cfmt.font_tb[0]);
529 	set_str_font(cft_sav, dft_sav);
530 	return wsize;
531 }
532 
533 /* -- initialize the first page or a new page for svg -- */
534 /* the flag 'in_page' is always false and epsf is always null */
init_page(void)535 static void init_page(void)
536 {
537 	float pheight, pwidth;
538 
539 	p_fmt = !info['X' - 'A'] ? &cfmt : &dfmt;	/* global format */
540 
541 	nbpages++;
542 	if (svg) {
543 		if (file_initialized <= 0) {
544 			if (!fout)
545 				open_fout();
546 			define_svg_symbols(in_fname, nbpages,
547 				cfmt.landscape ? p_fmt->pageheight : p_fmt->pagewidth,
548 				cfmt.landscape ? p_fmt->pagewidth : p_fmt->pageheight);
549 			user_ps_write();
550 			file_initialized = 1;
551 			output = svg_output;
552 		} else {
553 			define_svg_symbols(in_fname, nbpages,
554 				cfmt.landscape ? p_fmt->pageheight : p_fmt->pagewidth,
555 				cfmt.landscape ? p_fmt->pagewidth : p_fmt->pageheight);
556 		}
557 	} else if (file_initialized <= 0) {
558 		init_ps(in_fname);
559 	}
560 	in_page = 1;
561 	outft = -1;
562 
563 	if (!svg)
564 		fprintf(fout, "%%%%Page: %d %d\n",
565 			nbpages, nbpages);
566 	if (cfmt.landscape) {
567 		pheight = p_fmt->pagewidth;
568 		pwidth = cfmt.pageheight;
569 		if (!svg)
570 			fprintf(fout, "%%%%PageOrientation: Landscape\n"
571 				"gsave 0.75 dup scale 90 rotate 0 %.1f T\n",
572 				-cfmt.topmargin);
573 	} else {
574 		pheight = cfmt.pageheight;
575 		pwidth = p_fmt->pagewidth;
576 		if (!svg)
577 			fprintf(fout, "gsave 0.75 dup scale 0 %.1f T\n",
578 				pheight - cfmt.topmargin);
579 	}
580 	if (svg)
581 		output(fout, "0 %.1f T\n", -cfmt.topmargin);
582 	else
583 		output(fout,
584 			"%% --- width %.1f\n",		/* for index */
585 			(pwidth - cfmt.leftmargin - cfmt.rightmargin) /
586 					cfmt.scale);
587 
588 	remy = maxy = pheight - cfmt.topmargin - cfmt.botmargin;
589 
590 	/* output the header and footer */
591 	if (!cfmt.header) {
592 		char *p = NULL;
593 
594 		switch (pagenumbers) {
595 		case 1: p = "$P\t"; break;
596 		case 2: p = "\t\t$P"; break;
597 		case 3: p = "$P0\t\t$P1"; break;
598 		case 4: p = "$P1\t\t$P0"; break;
599 		}
600 		if (p)
601 			cfmt.header = strdup(p);
602 	}
603 	if (cfmt.header) {
604 		float dy;
605 
606 		dy = headfooter(1, pwidth, pheight);
607 		if (dy != 0) {
608 			output(fout, "0 %.1f T\n", -dy);
609 			remy -= dy;
610 		}
611 	}
612 	if (cfmt.footer)
613 		remy -= headfooter(0, pwidth, pheight);
614 	pagenum++;
615 	outft = -1;
616 }
617 
618 /* -- adjust the tune title part of the output file name -- */
epsf_fn_adj(char * p)619 static void epsf_fn_adj(char *p)
620 {
621 	char c;
622 
623 	while ((c = *p) != '\0') {
624 		if (c == ' ')
625 			*p = '_';
626 		else if (c == DIRSEP || (unsigned) c >= 127)
627 			*p = '.';
628 		p++;
629 	}
630 }
631 
632 /* -- build the title of the eps/svg file and check if correct utf-8 -- */
epsf_title(char * p,int sz)633 static void epsf_title(char *p, int sz)
634 {
635 	unsigned char c;
636 
637 	snprintf(p, sz, "%.72s (%.4s)", in_fname, &info['X' - 'A']->text[2]);
638 	while ((c = (unsigned char) *p) != '\0') {
639 		if (c >= 0x80) {
640 			if ((c & 0xf8) == 0xf0) {
641 				if ((p[1] & 0xc0) != 0x80
642 				 && (p[2] & 0xc0) != 0x80
643 				 && (p[3] & 0xc0) != 0x80)
644 					*p = ' ';
645 			} else if ((c & 0xf0) == 0xe0) {
646 				if ((p[1] & 0xc0) != 0x80
647 				 && (p[2] & 0xc0) != 0x80)
648 					*p = ' ';
649 			} else if ((c & 0xe0) == 0xc0) {
650 				if ((p[1] & 0xc0) != 0x80)
651 					*p = ' ';
652 			} else {
653 				*p = ' ';
654 			}
655 		}
656 		p++;
657 	}
658 }
659 
660 /* -- output a EPS (-E) or SVG (-g) file -- */
write_eps(void)661 void write_eps(void)
662 {
663 	unsigned i;
664 	char *p, title[80];
665 
666 	if (mbf == outbuf
667 	 || !info['X' - 'A'])
668 		return;
669 
670 	p_fmt = &cfmt;				/* tune format */
671 
672 	if (epsf != 3) {			/* if not -z */
673 		strcpy(outfnam, outfn);
674 		if (outfnam[0] == '\0')
675 			strcpy(outfnam, OUTPUTFILE);
676 		cutext(outfnam);
677 		i = strlen(outfnam) - 1;
678 		if (i == 0 && outfnam[0] == '-') {
679 			if (epsf == 1) {
680 				error(1, NULL, "Cannot use stdout with '-E' - abort");
681 				exit(EXIT_FAILURE);
682 			}
683 			fout = stdout;
684 		} else {
685 			if (outfnam[i] == '=') {
686 				p = &info['T' - 'A']->text[2];
687 				while (isspace((unsigned char) *p))
688 					p++;
689 				strncpy(&outfnam[i], p, sizeof outfnam - i - 4);
690 				outfnam[sizeof outfnam - 5] = '\0';
691 				epsf_fn_adj(&outfnam[i]);
692 			} else {
693 				if (i >= sizeof outfnam - 4 - 3)
694 					i = sizeof outfnam - 4 - 3;
695 				sprintf(&outfnam[i + 1], "%03d", ++nepsf);
696 			}
697 			strcat(outfnam, epsf == 1 ? ".eps" : ".svg");
698 			if ((fout = fopen(outfnam, "w")) == NULL) {
699 				error(1, NULL, "Cannot open output file %s - abort",
700 						outfnam);
701 				exit(EXIT_FAILURE);
702 			}
703 		}
704 	}
705 	epsf_title(title, sizeof title);
706 	if (epsf == 1) {
707 		init_ps(title);
708 		fprintf(fout, "0.75 dup scale 0 %.1f T\n", -bposy);
709 		write_buffer();
710 		fprintf(fout, "showpage\nrestore\n");
711 	} else {
712 		if (epsf == 3 && file_initialized == 0)
713 			fputs("<br/>\n", fout);	/* new image in the same flow */
714 		init_svg(title);
715 		write_buffer();
716 		svg_close();
717 	}
718 	if (epsf != 3)
719 		close_fout();
720 	else
721 		file_initialized = 0;
722 	cur_lmarg = 0;
723 	cur_scale = 1.0;
724 }
725 
726 /*  subroutines to handle output buffer  */
727 
728 /* -- update the output buffer pointer -- */
a2b(char * fmt,...)729 void a2b(char *fmt, ...)
730 {
731 	va_list args;
732 
733 	if (mbf + BSIZE > outbuf + outbufsz) {
734 		if (epsf) {
735 			error(1, NULL, "Output buffer overflow - increase outbufsz");
736 			fprintf(stderr, "*** abort\n");
737 			exit(EXIT_FAILURE);
738 		}
739 		error(0, NULL, "Possible buffer overflow");
740 		write_buffer();
741 //		use_buffer = 0;
742 	}
743 	va_start(args, fmt);
744 	mbf += vsnprintf(mbf, outbuf + outbufsz - mbf, fmt, args);
745 	va_end(args);
746 }
747 
748 /* -- translate down by 'h' scaled points in output buffer -- */
bskip(float h)749 void bskip(float h)
750 {
751 	if (h == 0)
752 		return;
753 	bposy -= h * cfmt.scale;
754 	a2b("0 %.2f T\n", -h);
755 }
756 
757 /* -- initialize the output buffer -- */
init_outbuf(int kbsz)758 void init_outbuf(int kbsz)
759 {
760 	if (outbuf)
761 		free(outbuf);
762 	outbufsz = kbsz * 1024;
763 //	if (outbufsz < 0x10000)
764 //		outbufsz = 0x10000;
765 	outbuf = malloc(outbufsz);
766 	if (!outbuf) {
767 		error(1, NULL, "Out of memory for outbuf - abort");
768 		exit(EXIT_FAILURE);
769 	}
770 	bposy = 0;
771 	ln_num = 0;
772 	mbf = outbuf;
773 }
774 
775 /* -- write buffer contents, break at full pages -- */
write_buffer(void)776 void write_buffer(void)
777 {
778 	char *p_buf;
779 	int l, np;
780 	float p1, dp;
781 	int outft_sav;
782 
783 	if (mbf == outbuf || multicol_start != 0)
784 		return;
785 	if (!in_page && !epsf)
786 		init_page();
787 	outft_sav = outft;
788 	p1 = 0;
789 	p_buf = outbuf;
790 	for (l = 0; l < ln_num; l++) {
791 		if (ln_pos[l] > 0) {		/* if in multicol */
792 			int ll;
793 			float pos;
794 
795 			for (ll = l + 1; ll < ln_num; ll++) {
796 				if (ln_pos[ll] <= 0) {
797 					pos = ln_pos[ll];
798 					while (--ll >= l)
799 						ln_pos[ll] = pos;
800 					break;
801 				}
802 			}
803 		}
804 		dp = ln_pos[l] - p1;
805 		np = remy + dp < 0 && !epsf;
806 		if (np) {
807 			close_page();
808 			init_page();
809 			if (ln_font[l] >= 0) {
810 				struct FONTSPEC *f;
811 
812 				f = &cfmt.font_tb[ln_font[l]];
813 				output(fout, "%.1f F%d\n",
814 					f->size, f->fnum);
815 			}
816 		}
817 		if (ln_scale[l] != cur_scale) {
818 			output(fout, "%.3f dup scale\n",
819 				ln_scale[l] / cur_scale);
820 			cur_scale = ln_scale[l];
821 		}
822 		if (ln_lmarg[l] != cur_lmarg) {
823 			output(fout, "%.2f 0 T\n",
824 				(ln_lmarg[l] - cur_lmarg) / cur_scale);
825 			cur_lmarg = ln_lmarg[l];
826 		}
827 		if (np) {
828 			output(fout, "0 %.2f T\n", -cfmt.topspace);
829 			remy -= cfmt.topspace * cfmt.scale;
830 		}
831 		if (*p_buf != '\001') {
832 			if (epsf > 1 || svg)
833 				svg_write(p_buf, ln_buf[l] - p_buf);
834 			else
835 				fwrite(p_buf, 1, ln_buf[l] - p_buf, fout);
836 		} else {			/* %%EPS - see parse.c */
837 			FILE *f;
838 			char line[BSIZE], *p, *q;
839 
840 			p = strchr(p_buf + 1, '\n');
841 			fwrite(p_buf + 1, 1, p - p_buf, fout);
842 			p_buf = p + 1;
843 			p = strchr(p_buf, '%');
844 			*p++ = '\0';
845 			q = strchr(p, '\n');
846 			*q = '\0';
847 			if ((f = fopen(p, "r")) == NULL) {
848 				error(1, NULL, "Cannot open EPS file '%s'", p);
849 			} else {
850 				if (epsf > 1 || svg) {
851 					fprintf(fout, "<!--Begin document %s-->\n",
852 							p);
853 					svg_output(fout, "gsave\n"
854 							"%s T\n",
855 							p_buf);
856 					while (fgets(line, sizeof line, f))	/* copy the file */
857 						svg_write(line, strlen(line));
858 					svg_output(fout, "grestore\n"
859 							"%s T\n",
860 							p_buf);
861 					fprintf(fout, "<!--End document %s-->\n",
862 							p);
863 				} else {
864 					fprintf(fout,
865 						"save\n"
866 						"/showpage{}def/setpagedevice{pop}def\n"
867 						"%s T\n"
868 						"%%%%BeginDocument: %s\n",
869 						p_buf, p);
870 					while (fgets(line, sizeof line, f))	/* copy the file */
871 						fwrite(line, 1, strlen(line), fout);
872 					fprintf(fout, "%%%%EndDocument\n"
873 							"restore\n");
874 				}
875 				fclose(f);
876 			}
877 		}
878 		p_buf = ln_buf[l];
879 		remy += dp;
880 		p1 = ln_pos[l];
881 	}
882 #if 1 //fixme:test
883 	if (*p_buf != '\0') {
884 //		fprintf(stderr, "??? bug - buffer not empty:\n%s\n", p_buf);
885 		memcpy(outbuf, p_buf, strlen(p_buf) + 1);
886 		mbf = &outbuf[strlen(outbuf)];
887 	} else {
888 		mbf = outbuf;
889 	}
890 #endif
891 	outft = outft_sav;
892 	bposy = 0;
893 	ln_num = 0;
894 	if (epsf != 3)
895 		use_buffer = 0;
896 }
897 
898 /* -- add a block of commmon margins / scale in the output buffer -- */
block_put(void)899 void block_put(void)
900 {
901 	if (mbf == outbuf)
902 		return;
903 //fixme: should be done sooner and should be adjusted when cfmt change...
904 	if (remy == 0)
905 		remy = maxy = (cfmt.landscape ? cfmt.pagewidth : cfmt.pageheight)
906 			- cfmt.topmargin - cfmt.botmargin;
907 	if (ln_num > 0 && mbf == ln_buf[ln_num - 1])
908 		return;				/* no data */
909 	if (ln_num >= BUFFLN) {
910 		char c, *p;
911 
912 		error(1, NULL, "max number of buffer lines exceeded"
913 				" -- check BUFFLN");
914 		multicol_start = 0;
915 		p = ln_buf[ln_num - 1];
916 		c = *p;				/* (avoid "buffer not empty") */
917 		*p = '\0';
918 		write_buffer();
919 		multicol_start = remy + bposy;
920 		*p = c;
921 		strcpy(outbuf, p);
922 //		use_buffer = 0;
923 	}
924 	ln_buf[ln_num] = mbf;
925 	ln_pos[ln_num] = multicol_start == 0 ? bposy : 1;
926 	ln_lmarg[ln_num] = cfmt.leftmargin;
927 	if (epsf) {
928 		if (cfmt.leftmargin < min_lmarg)
929 			min_lmarg = cfmt.leftmargin;
930 		if (cfmt.rightmargin < max_rmarg)
931 			max_rmarg = cfmt.rightmargin;
932 	}
933 	ln_scale[ln_num] = cfmt.scale;
934 	ln_font[ln_num] = outft;
935 	ln_num++;
936 
937 	if (!use_buffer)
938 		write_buffer();
939 }
940 
941 /* -- handle completed block in buffer -- */
942 /* if the added stuff does not fit on current page, write it out
943    after page break and change buffer handling mode to pass though */
buffer_eob(int eot)944 void buffer_eob(int eot)
945 {
946 	block_put();
947 	if (epsf) {
948 		if (epsf == 3)
949 			write_eps();		/* close the image */
950 		return;
951 	}
952 	if (remy + bposy >= 0
953 	 || multicol_start != 0)
954 		return;
955 
956 	// page full
957 	if (!in_page) {
958 		if ((cfmt.splittune == 2 && !(nbpages & 1))
959 		 || (cfmt.splittune == 3 && (nbpages & 1))) { // wrong odd/even page
960 			if (remy + maxy + bposy < 0) {	// 2nd overflow
961 				init_page();
962 				close_page();
963 				write_buffer();
964 				return;
965 			}
966 			if (eot)
967 				write_buffer();
968 			return;
969 		}
970 	} else {
971 		close_page();
972 	}
973 	if ((cfmt.splittune == 2 && !(nbpages & 1))
974 	 || (cfmt.splittune == 3 && (nbpages & 1)))
975 		use_buffer = 1;			// if wrong odd/even page
976 	else
977 		write_buffer();
978 #if 0
979 --- old
980 		if (use_buffer
981 		 && !eot
982 		 && ((cfmt.splittune == 2 && !(nbpages & 1))
983 		   || (cfmt.splittune == 3 && (nbpages & 1)))) {
984 			init_page();
985 // 8.7.4
986 			use_buffer = 1;
987 		} else {
988 // 8.7.5 - required to avoid buffer overflow
989 			write_buffer();
990 		}
991 //8.7.0
992 //		write_buffer();
993 //8.6.2
994 //		use_buffer = 0;
995 #endif
996 }
997 
998 /* -- dump buffer if not enough place for a music line -- */
check_buffer(void)999 void check_buffer(void)
1000 {
1001 	if (mbf + 5000 > outbuf + outbufsz) { /* assume music line < 5000 bytes */
1002 		error(0, NULL,
1003 		      "Possibly bad page breaks, outbufsz exceeded");
1004 		write_buffer();
1005 //		use_buffer = 0;
1006 	}
1007 }
1008 
1009 /* -- return the current vertical offset in the page -- */
get_bposy(void)1010 float get_bposy(void)
1011 {
1012 	return remy + bposy;
1013 }
1014