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(<ime);
161 #ifndef WIN32
162 strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(<ime));
163 #else
164 strftime(tex_buf, TEX_BUF_SZ, "%b %#d, %Y %H:%M", localtime(<ime));
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(<ime);
357 dput: cnv_date(<ime);
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