/* * Postscript buffering functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2007 Jean-François Moine * Adapted from abc2ps, Copyright (C) 1996,1997 Michael Methfessel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "abcparse.h" #include "abc2ps.h" #define BUFFLN 80 /* max number of lines in output buffer */ static int ln_num; /* number of lines in buffer */ static float ln_pos[BUFFLN]; /* vertical positions of buffered lines */ static int ln_buf[BUFFLN]; /* buffer location of buffered lines */ static float ln_lmarg[BUFFLN]; /* left margin of buffered lines */ static float ln_scale[BUFFLN]; /* scale of buffered lines */ static signed char ln_font[BUFFLN]; /* font of buffered lines */ static char buf[BUFFSZ]; /* output buffer.. should hold one tune */ static float cur_lmarg = 0; /* current left margin */ static float cur_scale = 1.0; /* current scale */ static float posy; /* vertical position on page */ static float bposy; /* current position in buffered data */ static int nepsf; /* counter for epsf output files */ static int nbpages; /* number of pages in the output file */ static char outfnam[STRL1]; /* internal file name for open/close */ static struct FORMAT *p_fmt; /* current format while treating a new page */ char *mbf; /* where to PUTx() */ int nbuf; /* number of bytes buffered */ int use_buffer; /* 1 if lines are being accumulated */ /* -- convert a date -- */ static void cnv_date(time_t *ltime) { char buf[TEX_BUF_SZ]; tex_str(cfmt.dateformat); strcpy(buf, tex_buf); strftime(tex_buf, TEX_BUF_SZ, buf, localtime(ltime)); } /* -- initialize the postscript file -- */ static void init_ps(char *str, int is_epsf) { time_t ltime; int i; char version[32]; if (is_epsf) { /*fixme: no landscape for EPS?*/ fprintf(fout, "%%!PS-Adobe-3.0 EPSF-3.0\n" "%%%%BoundingBox: 0 0 %.0f %.0f\n", p_fmt->pagewidth - p_fmt->leftmargin - p_fmt->rightmargin + 20, -bposy); cur_lmarg = p_fmt->leftmargin - 10; } else fprintf(fout, "%%!PS-Adobe-2.0\n"); fprintf(fout, "%%%%Title: %s\n", str); time(<ime); strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(<ime)); fprintf(fout, "%%%%Creator: abcm2ps-" VERSION "\n" "%%%%CreationDate: %s\n", tex_buf); if (!is_epsf) fprintf(fout, "%%%%Pages: (atend)\n"); fprintf(fout, "%%%%LanguageLevel: 2\n" "%%%%EndComments\n" "%%CommandLine:"); for (i = 1; i < s_argc; i++) { fprintf(fout, strchr(s_argv[i], ' ') != 0 ? " \'%s\'" : " %s", s_argv[i]); } fprintf(fout, "\n\n"); if (is_epsf) fprintf(fout, "gsave /origstate save def mark\n" "100 dict begin\n"); strcpy(version, "/creator [(abcm2ps) " VERSION "] def"); for (i = 0; i < strlen(version); i++) { if (version[i] == '.') version[i] = ' '; } fprintf(fout, "%%%%BeginSetup\n" "/!{bind def}bind def\n" "/bdef{bind def}!\n" /* for compatibility */ "/T/translate load def\n" "/M/moveto load def\n" "/RM/rmoveto load def\n" "/RL/rlineto load def\n" "/RC/rcurveto load def\n" "/SLW/setlinewidth load def\n" "/defl 0 def\n" /* decoration flags - see deco.c for values */ "/dlw{0.7 SLW}!\n" /* (simple!) level2 emulation */ "/languagelevel where{pop}{/languagelevel 1 def}ifelse\n" "languagelevel 1 eq{\n" " /rectstroke{\n" " 4 2 roll M 1 index 0 RL 0 exch RL neg 0 RL closepath\n" " stroke}!\n" " /rectfill{\n" " 4 2 roll M 1 index 0 RL 0 exch RL neg 0 RL closepath\n" " fill}!\n" " /selectfont{exch findfont exch dup\n" " type/arraytype eq{makefont}{scalefont}ifelse setfont}!\n" " /product where{pop}{/product()def}ifelse\n" " }if\n" "%s\n", version); define_symbols(); define_encodings(); user_ps_write(); define_fonts(); fprintf(fout, "%%%%EndSetup\n"); file_initialized = 1; } /* -- close_output_file -- */ void close_output_file(void) { long m; if (fout == 0) return; if (multicol_start != 0) { /* if no '%%multocol end' or filtering */ multicol_start = 0; write_buffer(); } if (tunenum == 0) error(0, 0, "No tunes written to output file"); close_page(); fprintf(fout, "%%%%Trailer\n" "%%%%Pages: %d\n" "%%EOF\n", nbpages); if (fout != stdout) { m = ftell(fout); fclose(fout); fprintf(stderr, "Output written on %s (%d page%s, %d title%s, %ld bytes)\n", outfnam, nbpages, nbpages == 1 ? "" : "s", tunenum, tunenum == 1 ? "" : "s", m); } fout = 0; file_initialized = 0; nbpages = tunenum = 0; defl = 0; } /* -- close the PS page -- */ void close_page(void) { if (!in_page) return; in_page = 0; fprintf(fout, "%%%%PageTrailer\n" "grestore\n" "showpage\n"); cur_lmarg = 0; cur_scale = 1.0; outft = -1; } /* -- output a header/footer element -- */ static void format_hf(char *p) { char *q; time_t ltime; for (;;) { if (*p == '\0') break; if ((q = strchr(p, '$')) != 0) *q = '\0'; fprintf(fout, "%s", p); if (q == 0) break; p = q + 1; switch (*p) { case 'd': ltime = mtime; goto dput; case 'D': time(<ime); dput: cnv_date(<ime); fprintf(fout, "%s", tex_buf); break; case 'F': /* ABC file name */ #if DIRSEP!='\\' fprintf(fout, "%s", in_fname); #else { int i; char *r; q = in_fname; r = tex_buf; i = TEX_BUF_SZ; for (;;) { if (--i <= 0 || *q == '\0') break; if ((*r++ = *q++) == '\\') { i--; *r++ = '\\'; } } *r = '\0'; } fprintf(fout, "%s", tex_buf); #endif break; case 'I': /* information field */ p++; if (*p < 'A' || *p > 'Z' || info[*p - 'A'] == 0) break; fprintf(fout, "%s", &info[*p - 'A']->as.text[2]); break; case 'P': /* page number */ if (p[1] == '0') { p++; if (pagenum & 1) break; } else if (p[1] == '1') { p++; if ((pagenum & 1) == 0) break; } fprintf(fout, "%d", pagenum); break; case 'T': /* tune title */ q = &info['T' - 'A']->as.text[2]; while (isspace((unsigned char) *q)) q++; tex_str(q); fprintf(fout, "%s", tex_buf); break; case 'V': fprintf(fout,"abcm2ps-" VERSION); break; default: continue; } p++; } } /* -- output the header or footer -- */ static float headfooter(int header, float pwidth, float pheight) { char str[512]; char *p, *q, *r; float size, y, wsize; struct FONTSPEC *f, f_sav; int defft_sav; if (header) { p = cfmt.header; f = &cfmt.font_tb[HEADERFONT]; size = f->size; y = 2; } else { p = cfmt.footer; f = &cfmt.font_tb[FOOTERFONT]; size = f->size; y = - (pheight - cfmt.topmargin - cfmt.botmargin) - size + 2; } if (*p == '-') { if (pagenum == 1) return 0; p++; } defft_sav = get_str_font(); memcpy(&f_sav, &cfmt.font_tb[0], sizeof f_sav); wsize = 0; str_font(f - cfmt.font_tb); fprintf(fout, "%.1f F%d ", size, f->fnum); outft = f - cfmt.font_tb; /* may have 2 lines */ if ((r = strstr(p, "\\n")) != 0) { if (!header) y += size; wsize += size; *r = '\0'; } for (;;) { tex_str(p); strcpy(str, tex_buf); /* left side */ p = str; if ((q = strchr(p, '\t')) != 0) { if (q != p) { *q = '\0'; fprintf(fout, "%.1f %.1f M(", p_fmt->leftmargin, y); format_hf(p); fprintf(fout, ")show\n"); } p = q + 1; } if ((q = strchr(p, '\t')) != 0) *q = '\0'; /* center */ if (q != p) { fprintf(fout, "%.1f %.1f M(", pwidth * 0.5, y); format_hf(p); fprintf(fout, ")showc\n"); } /* right side */ if (q != 0) { p = q + 1; if (*p != '\0') { fprintf(fout, "%.1f %.1f M(", pwidth - p_fmt->rightmargin, y); format_hf(p); fprintf(fout, ")showr\n"); } } if (r == 0) break; *r = '\\'; p = r + 2; r = 0; y -= size; } memcpy(&cfmt.font_tb[0], &f_sav, sizeof cfmt.font_tb[0]); str_font(defft_sav); return wsize; } /* -- initialize postscript page -- */ static void init_page(void) { float pheight, pwidth; if (in_page) return; p_fmt = info['X' - 'A'] == 0 ? &cfmt : &dfmt; /* global format */ if (!file_initialized) init_ps(in_fname, 0); in_page = 1; nbpages++; outft = -1; fprintf(fout, "%%%%Page: %d %d\n", nbpages, nbpages); if (cfmt.landscape) { pheight = p_fmt->pagewidth; pwidth = cfmt.pageheight; fprintf(fout, "%%%%PageOrientation: Landscape\n" "gsave 90 rotate 0 %.1f T\n", -cfmt.topmargin); } else { pheight = cfmt.pageheight; pwidth = p_fmt->pagewidth; fprintf(fout, "gsave 0 %.1f T\n", pheight - cfmt.topmargin); } fprintf(fout, "%% --- width %.1f\n", /* for index */ ((cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - cfmt.leftmargin - cfmt.rightmargin) / cfmt.scale); posy = pheight - cfmt.topmargin - cfmt.botmargin; /* output the header and footer */ if (cfmt.header == 0) { char *p = 0; switch (pagenumbers) { case 1: p = "$P\t"; break; case 2: p = "\t\t$P"; break; case 3: p = "$P0\t\t$P1"; break; case 4: p = "$P1\t\t$P0"; break; } if (p != 0) cfmt.header = strdup(p); } if (cfmt.header != 0) { float dy; dy = headfooter(1, pwidth, pheight); if (dy != 0) { fprintf(fout, "0 %.1f T\n", -dy); posy -= dy; } } if (cfmt.footer != 0) posy -= headfooter(0, pwidth, pheight); pagenum++; outft = -1; } /* -- open the output file -- */ void open_output_file(void) { int i; char fnm[STRL1]; strcpy(fnm, outfn); i = strlen(fnm) - 1; if (i < 0) strcpy(fnm, OUTPUTFILE); else if (i == 0 && fnm[0] == '-') ; else { if (fnm[i] == '=') { char *p; if ((p = strrchr(in_fname, DIRSEP)) == 0) p = in_fname; else p++; /*fixme: should check if there is a DIRSEP at the end of fnm*/ strcpy(&fnm[i], p); strext(fnm, "ps"); } else if (fnm[i] == DIRSEP) strcpy(&fnm[i + 1], OUTPUTFILE); #if 0 /*fixme: fnm may be a directory*/ else ... #endif } if (strcmp(fnm, outfnam) == 0) return; close_output_file(); strcpy(outfnam, fnm); if (i != 0 || fnm[0] != '-') { if ((fout = fopen(fnm, "w")) == 0) { fprintf(stderr, "Cannot create output file %s\n", fnm); exit(2); } } else fout = stdout; } /* -- cut off extension on a file identifier -- */ static void cutext(char *fid) { char *p; if ((p = strrchr(fid, DIRSEP)) == 0) p = fid; if ((p = strrchr(p, '.')) != 0) *p = '\0'; } /* -- epsf_title -- */ static void epsf_title(char *p) { char c; while ((c = *p) != '\0') { if (c == ' ') *p = '_'; else if (c == DIRSEP || (unsigned) c >= 127) *p = '.'; p++; } } /* -- output the EPS file -- */ void write_eps(void) { int i; long m; char *p, fnm[STRL1], finf[STRL1]; p_fmt = info['X' - 'A'] == 0 ? &cfmt : &dfmt; /* global format */ close_output_file(); strcpy(fnm, outfn); if (fnm[0] == '\0') strcpy(fnm, OUTPUTFILE); cutext(fnm); i = strlen(fnm) - 1; if (fnm[i] == '=') { p = &info['T' - 'A']->as.text[2]; while (isspace((unsigned char) *p)) p++; strncpy(&fnm[i], p, sizeof fnm - i - 4); fnm[sizeof fnm - 5] = '\0'; epsf_title(&fnm[i]); } else sprintf(&fnm[i + 1], "%03d", ++nepsf); strcat(fnm, ".eps"); if ((fout = fopen(fnm, "w")) == 0) { fprintf(stderr, "Cannot open output file %s\n", fnm); exit(2); } sprintf(finf, "%.72s (%.4s)", in_fname, &info['X' - 'A']->as.text[2]); init_ps(finf, 1); fprintf(fout, "0 %.1f T\n", -bposy); write_buffer(); fprintf(fout, "showpage\nend\n" "cleartomark origstate restore grestore\n"); m = ftell(fout); fclose(fout); fout = 0; fprintf(stderr, "Output written on %s (%ld bytes)\n", fnm, m); cur_lmarg = 0; cur_scale = 1.0; } /* -- start a new page -- */ static void newpage(void) { close_page(); init_page(); } /* subroutines to handle output buffer */ /* -- update the output buffer pointer -- */ /* called from the PUTx() macros */ void a2b(void) { int l; if (!in_page && !epsf) init_page(); l = strlen(mbf); nbuf += l; if (nbuf >= BUFFSZ - 500) { /* must have place for 1 more line */ error(1, 0, "a2b: buffer full, BUFFSZ=%d", BUFFSZ); exit(3); } mbf += l; } /* -- translate down by 'h' absolute points in output buffer -- */ void abskip(float h) { PUT1("0 %.2f T\n", -h / cfmt.scale); bposy -= h; } /* -- translate down by 'h' scaled points in output buffer -- */ void bskip(float h) { bposy -= h * cfmt.scale; PUT1("0 %.2f T\n", -h); } /* -- clear_buffer -- */ void clear_buffer(void) { nbuf = 0; bposy = 0; ln_num = 0; mbf = buf; } /* -- write buffer contents, break at full pages -- */ void write_buffer(void) { int i, l, b2, np; float p1, dp; int outft_sav; if (nbuf == 0 || multicol_start != 0) return; outft_sav = outft; i = 0; p1 = 0; for (l = 0; l < ln_num; l++) { b2 = ln_buf[l]; dp = ln_pos[l] - p1; np = posy + dp < 0 && !epsf; if (np) { newpage(); if (ln_font[l] >= 0) { struct FONTSPEC *f; f = &cfmt.font_tb[ln_font[l]]; fprintf(fout, "%.1f F%d\n", f->size, f->fnum); } } if (ln_scale[l] != cur_scale) { fprintf(fout, "%.2f dup scale\n", ln_scale[l] / cur_scale); cur_scale = ln_scale[l]; } if (ln_lmarg[l] != cur_lmarg) { fprintf(fout, "%.1f 0 T\n", (ln_lmarg[l] - cur_lmarg) / cur_scale); cur_lmarg = ln_lmarg[l]; } if (np) { fprintf(fout, "0 %.1f T\n", -cfmt.topspace); posy -= cfmt.topspace * cfmt.scale; } if (buf[i] != '\001') fwrite(&buf[i], 1, b2 - i, fout); else { /* %%EPS - see parse.c */ FILE *f; char line[BSIZE], *p, *q; i++; p = strchr(&buf[i], '\n'); fwrite(&buf[i], 1, p - &buf[i] + 1, fout); i = p - buf + 1; p = strchr(&buf[i], '%'); *p++ = '\0'; q = strchr(p, '\n'); *q = '\0'; if ((f = fopen(p, "r")) == 0) { error(1, 0, "Cannot open EPS file '%s'", p); } else { fprintf(fout, "save %% EPS file '%s'\n" "/showpage{}def/setpagedevice{pop}def\n" "%s T\n", p, &buf[i]); while (fgets(line, sizeof line, f)) /* copy the file */ fwrite(line, 1, strlen(line), fout); fclose(f); strcpy(line, "restore % end EPS\n"); fwrite(line, 1, strlen(line), fout); } } i = b2; posy += dp; p1 = ln_pos[l]; } fwrite(&buf[i], 1, nbuf - i, fout); clear_buffer(); outft = outft_sav; } /* -- handle completed block in buffer -- */ /* if the added stuff does not fit on current page, write it out after page break and change buffer handling mode to pass though */ void buffer_eob(void) { if (bposy == 0 && nbuf == 0) return; if (ln_num > 0 && ln_buf[ln_num - 1] == nbuf) return; if (ln_num >= BUFFLN) { error(1, 0, "max number of buffer lines exceeded" " -- check BUFFLN"); write_buffer(); use_buffer = 0; } ln_buf[ln_num] = nbuf; ln_pos[ln_num] = bposy; ln_lmarg[ln_num] = cfmt.leftmargin; ln_scale[ln_num] = cfmt.scale; ln_font[ln_num] = outft; ln_num++; if (!use_buffer) { write_buffer(); return; } if (posy + bposy < 0 && !epsf && multicol_start == 0) { if (tunenum > 1) newpage(); write_buffer(); use_buffer = 0; } } /* -- dump buffer if not enough place for a music line -- */ void check_buffer(void) { if (nbuf > BUFFSZ - 5000) { /* assume music line < 5000 bytes */ error(0, 0, "Possibly bad page breaks, BUFFSZ exceeded"); write_buffer(); use_buffer = 0; } } /* -- return the current vertical offset in the page -- */ float get_bposy(void) { return posy + bposy; }