/* * SVG definitions. * * This file is part of abcm2ps. * * Copyright (C) 2011-2019 Jean-François Moine * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. */ #include #include #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef NaN #define NaN ((float) (2 << 22)) #endif #include "abcm2ps.h" enum elt_t { /* element types */ VAL, STR, SEQ, /* {..} */ BRK, /* [..] */ }; struct elt_s { struct elt_s *next; char type; union { float v; char *s; struct elt_s *e; } u; }; struct ps_sym_s { char *n; /* name */ struct elt_s *e; /* value */ int exec; /* current number of execution */ }; /* -- PostScript tiny interpreter -- */ #define NELTS 2048 /* number of elements per block */ #define NSYMS 512 /* max number of symbols */ static struct elt_s *elts; static struct elt_s *stack, *free_elt; static struct ps_sym_s ps_sym[NSYMS]; static int n_sym; static int ps_error; static int in_cnt; /* in [..] or {..} */ static char *path; static char path_buf[256]; /* graphical context */ static struct gc { float cx, cy; // current point (volatile) float xscale, yscale; // scale float xoffs, yoffs; // translate float rotate, sin, cos; // rotate char *font_n; // current font float font_s; char *font_n_old; float linewidth; int rgb; char dash[64]; } gcur, gold, gsave[8]; static int nsave; static float x_rot, y_rot; /* save x and y offset when rotate != 0 */ static int g; /* current container */ static int boxend; static char *defs; // SVG ID's from %%beginsvg static int defssz; /* abcm2ps definitions */ static struct { char *def; char use; char defined; } def_tb[] = { #define D_brace 0 { "\n"}, #define D_utclef 1 { "\n"}, #define D_tclef 2 { "\n", D_utclef}, #define D_stclef 3 { "\n", D_utclef}, #define D_ubclef 4 { "\n"}, #define D_bclef 5 { "\n", D_ubclef}, #define D_sbclef 6 { "\n", D_ubclef}, #define D_ucclef 7 { "\n"}, #define D_cclef 8 { "\n", D_ucclef}, #define D_scclef 9 { "\n", D_ucclef}, #define D_pclef 10 { "\n"}, #define D_hd 11 { "\n"}, #define D_Hd 12 { "\n"}, #define D_HD 13 { "\n"}, #define D_HDD 14 { "\n" " \n" " \n" "\n", D_HD}, #define D_breve 15 { "\n" " \n" " \n" "\n"}, #define D_longa 16 { "\n" " \n" " \n" "\n"}, #define D_ghd 17 { "\n"}, #define D_r00 18 { "\n"}, #define D_r0 19 { "\n"}, #define D_r1 20 { "\n"}, #define D_r2 21 { "\n"}, #define D_r4 22 { "\n"}, #define D_r8e 23 { "\n"}, #define D_r8 24 { "\n" " \n" " \n" "\n", D_r8e}, #define D_r16 25 { "\n" " \n" " \n" " \n" "\n", D_r8e}, #define D_r32 26 { "\n" " \n" " \n" " \n" " \n" "\n", D_r8e}, #define D_r64 27 { "\n" " \n" " \n" " \n" " \n" " \n" "\n", D_r8e}, #define D_r128 28 { "\n" " \n" " \n" " \n" " \n" " \n" " \n" "\n", D_r8e}, #define D_mrest 29 { "\n" " \n" " \n" "\n"}, #define D_usharp 30 { "\n"}, #define D_uflat 31 { "\n"}, #define D_unat 32 { "\n"}, #define D_udblesharp 33 { "\n"}, #define D_udbleflat 34 { "\n"}, #define D_sh0 35 { "\n", D_usharp}, #define D_ft0 36 { "\n", D_uflat}, #define D_nt0 37 { "\n", D_unat}, #define D_dsh0 38 { "\n", D_udblesharp}, #define D_dft0 39 { "\n", D_udbleflat}, #define D_sh1 40 { "\n" " \n" " \n" "\n"}, #define D_sh513 41 { "\n" " \n" " \n" "\n"}, #define D_ft1 42 { "\n" " \n" "\n", D_ft0}, #define D_ft513 43 { "\n" " \n" " \n" "\n"}, #define D_pshhd 44 { "\n" " \n" "\n", D_dsh0}, #define D_pfthd 45 { "\n" " \n" " \n" "\n", D_dsh0}, #define D_csig 46 { "\n"}, #define D_ctsig 47 { "\n" " \n" " \n" "\n", D_csig}, #define D_pmsig 48 { "\n"}, #define D_pMsig 49 { "\n" " \n" " \n" "\n", D_pmsig}, #define D_imsig 50 { "\n"}, #define D_iMsig 51 { "\n" " \n" " \n" "\n", D_imsig}, #define D_hl 52 { "\n"}, #define D_hl1 53 { "\n"}, #define D_hl2 54 { "\n"}, #define D_ghl 55 { "\n"}, #define D_rdots 56 { "\n" " \n" " \n" "\n"}, #define D_srep 57 { "\n"}, #define D_mrep 58 { "\n"}, #define D_mrep2 59 { "\n" " \n" " \n" "\n"}, #define D_accent 60 { "\n" " \n" "\n"}, #define D_umrd 61 { "\n"}, #define D_lmrd 62 { "\n" " \n" " \n" "\n", D_umrd}, #define D_grm 63 { "\n"}, #define D_stc 64 { "\n"}, #define D_sld 65 { "\n"}, #define D_emb 66 { "\n"}, #define D_hld 67 { "\n" " \n" " \n" "\n"}, #define D_cpu 68 { "\n"}, #define D_upb 69 { "\n"}, #define D_dnb 70 { "\n" " \n" " \n" "\n"}, #define D_sgno 71 { "\n" " \n" " \n" " \n" " \n" "\n"}, #define D_coda 72 { "\n" " \n" " \n" "\n"}, #define D_dplus 73 { "\n"}, #define D_lphr 74 { "\n"}, #define D_mphr 75 { "\n"}, #define D_sphr 76 { "\n"}, #define D_opend 77 { "\n"}, #define D_snap 78 { "\n"}, #define D_thumb 79 { "\n"}, #define D_turn 80 { "\n"}, #define D_turnx 81 { "\n" " \n" " \n" "\n", D_turn}, #define D_wedge 82 { "\n"}, #define D_ltr 83 { "\n"}, #define D_custos 84 { "\n" " \n" " \n" "\n"}, #define D_showerror 85 { "\n"}, #define D_sfz 86 { "sfz\n"}, #define D_trl 87 { "tr\n"}, #define D_marcato 88 { "\n"}, #define D_ped 89 { "Ped\n"}, #define D_pedoff 90 { "*\n"}, }; static struct { int index; char *def; } font_gl[] = { {D_brace, "\n"}, {D_sgno, "\n"}, {D_coda, "\n"}, {D_tclef, "\n"}, {D_cclef, "\n"}, {D_bclef, "\n"}, {D_pclef, "\n"}, {D_stclef, "\n"}, {D_scclef, "\n"}, {D_sbclef, "\n"}, {D_csig, "\n"}, {D_ctsig, "\n"}, {D_HDD, "\n"}, {D_breve, "\n"}, {D_HD, "\n"}, {D_Hd, "\n"}, {D_hd, "\n"}, {D_ft0, "\n"}, {D_nt0, "\n"}, {D_sh0, "\n"}, {D_dsh0, "\n"}, {D_pshhd, "\n"}, {D_dft0, "\n"}, {D_accent, "\n"}, {D_marcato, "\n"}, {D_hld, "\n"}, {D_r00, "\n"}, {D_r0, "\n"}, {D_r1, "\n"}, {D_r2, "\n"}, {D_r4, "\n"}, {D_r8, "\n"}, {D_r16, "\n"}, {D_r32, "\n"}, {D_r64, "\n"}, {D_r128, "\n"}, {D_mrest, "\n"}, {D_mrep, "\n"}, {D_mrep2, "\n"}, {D_turn, "\n"}, {D_umrd, "\n"}, {D_lmrd, "\n"}, {D_ped, "\n"}, {D_pedoff, "\n"}, {D_longa, "\n"}, }; // switch to a music font void svg_font_switch(void) { int i, j; for (i = 0; i < sizeof font_gl / sizeof font_gl[0]; i++) { j = font_gl[i].index; def_tb[j].def = font_gl[i].def; def_tb[j].use = 0; } } /* PS functions */ static void ps_exec(char *op); static void elts_link(struct elt_s *e) { int i; /* set the linkages - the first element is the link to the next block */ for (i = 1; i < NELTS - 1; i++) { e[i].next = &e[i + 1]; if (e[i].type == STR) free(e[i].u.s); e[i].type = VAL; } e[NELTS - 1].next = NULL; } /* (re)initialize all PS elements */ static void elts_reset(void) { struct elt_s *e; if (!elts) elts = calloc(sizeof *elts, NELTS); elts_link(elts); free_elt = elts + 1; /* link all blocks */ for (e = elts; e->u.e; e = e->u.e) { elts_link(e->u.e); e[NELTS - 1].next = e->u.e; } } static struct elt_s *elt_new(void) { struct elt_s *e; e = free_elt; if (!e) { e = calloc(sizeof *e, NELTS); if (!e) { fprintf(stderr, "svg: elt_new out of memory\n"); ps_error = 1; return e; } elts_link(e); e->u.e = elts; elts = e; e++; } free_elt = e->next; e->next = NULL; e->type = VAL; return e; } static void elt_free(struct elt_s *e) { struct elt_s *e2; e->next = free_elt; free_elt = e; switch (e->type) { case STR: free(e->u.s); e->type = VAL; e->u.v = 0; break; case SEQ: case BRK: e2 = e->u.e; e->type = VAL; e->u.v = 0; while (e2) { e = e2->next; elt_free(e2); e2 = e; } break; } } static struct elt_s *elt_dup(struct elt_s *e) { struct elt_s *e2, *e3, *e4; e2 = elt_new(); if (!e2) return e2; e2->type = e->type; switch (e->type) { case VAL: e2->u.v = e->u.v; break; case STR: e2->u.s = strdup(e->u.s); break; case SEQ: case BRK: e = e->u.e; if (!e) { e2->u.e = NULL; break; } e3 = e2->u.e = elt_dup(e); if (!e3) break; for (;;) { e = e->next; if (!e) break; e4 = elt_dup(e); if (!e4) break; e3->next = e4; e3 = e4; } e3->next = NULL; break; } return e2; } static void elt_dump(struct elt_s *e) { int type; type = e->type; switch (type) { case VAL: fprintf(stderr, " %.2f", e->u.v); break; case STR: fprintf(stderr, " %s", e->u.s); if (e->u.s[0] == '(') fprintf(stderr, ")"); break; case SEQ: case BRK: fprintf(stderr, type == SEQ ? " {" : " ["); e = e->u.e; while (e) { elt_dump(e); e = e->next; } fprintf(stderr, type == SEQ ? " }" : " ]"); } } static void elt_lst_dump(struct elt_s *e) { do { elt_dump(e); e = e->next; } while (e); } static struct ps_sym_s *ps_sym_lookup(char *name) { struct ps_sym_s *ps; if (n_sym == 0) return NULL; ps = &ps_sym[n_sym]; for (;;) { ps--; if (strcmp(ps->n, name) == 0) break; if (ps == ps_sym) return NULL; } return ps; } static struct ps_sym_s *ps_sym_def(char *name, struct elt_s *e) { struct ps_sym_s *ps; ps = ps_sym_lookup(name); if (ps) { elt_free(ps->e); } else { if (n_sym >= NSYMS) { fprintf(stderr, "svg: Too many PS symbols\n"); ps_error = 1; return NULL; } ps = &ps_sym[n_sym++]; ps->n = strdup(name); } ps->e = e; ps->exec = 0; return ps; } static void push(struct elt_s *e) { e->next = stack; stack = e; } static void stack_dump(void) { fprintf(stderr, "stack:"); if (stack) elt_lst_dump(stack); else fprintf(stderr, "(empty)"); fprintf(stderr, "\n"); } static struct elt_s *pop(int type) { struct elt_s *e; e = stack; if (!e) { fprintf(stderr, "svg pop: Stack empty\n"); ps_error = 1; return NULL; } if (e->type != type) { fprintf(stderr, "svg pop: Bad element type %d != %d\n", e->type, type); stack_dump(); ps_error = 1; return NULL; } stack = e->next; return e; } static float pop_free_val(void) { struct elt_s *e; e = pop(VAL); if (!e) return 0; e->next = free_elt; free_elt = e; return e->u.v; } static char *pop_free_str(void) { struct elt_s *e; char *s; e = pop(STR); if (!e) return NULL; s = e->u.s; e->type = VAL; e->next = free_elt; free_elt = e; return s; } /* PS condition code */ #define C_EQ 0 #define C_NE 1 #define C_GT 2 #define C_GE 3 #define C_LT 4 #define C_LE 5 static void cond(int type) { float v; char *s, *s2; if (!stack || !stack->next) { fprintf(stderr, "svg: Stack underflow in condition\n"); ps_error = 1; return; } /* string compare */ if (stack->type == STR && stack->next->type == STR) { s = pop_free_str(); s2 = stack->u.s; switch (type) { case C_EQ: stack->u.v = strcmp(s2, s) == 0; break; case C_NE: stack->u.v = strcmp(s2, s) != 0; break; default: fprintf(stderr, "svg: String condition not treated\n"); break; } free(s); free(s2); stack->type = VAL; return; } /* special case when 1 character strings */ if (stack->type == STR) { s = stack->u.s; stack->u.v = s[1]; free(s); stack->type = VAL; } if (stack->next->type == STR) { s = stack->next->u.s; stack->next->u.v = s[1]; free(s); stack->next->type = VAL; } v = pop_free_val(); if (stack->type != VAL) { fprintf(stderr, "svg: Bad type for condition\n"); ps_error = 1; return; } switch (type) { case C_EQ: stack->u.v = stack->u.v == v; break; case C_NE: stack->u.v = stack->u.v != v; break; case C_GT: stack->u.v = stack->u.v > v; break; case C_GE: stack->u.v = stack->u.v >= v; break; case C_LT: stack->u.v = stack->u.v < v; break; case C_LE: stack->u.v = stack->u.v <= v; break; } } /* output a xml string */ static void xml_str_out(char *p) { char *q, *r; for (q = p; *p != '\0';) { switch (*p++) { case '<': r = "<"; break; case '>': r = ">"; break; case '\'': r = "'"; break; case '"': r = """; break; case '&': if (*p == '#' || strncmp(p, "lt;", 3) == 0 || strncmp(p, "gt;", 3) == 0 || strncmp(p, "amp;", 4) == 0 || strncmp(p, "apos;", 5) == 0 || strncmp(p, "quot;", 5) == 0) continue; r = "&"; break; default: continue; } if (p - 1 != q) fwrite(q, 1, p - 1 - q, fout); q = p; fputs(r, fout); } if (p != q) fputs(q, fout); } /* -- output information about the generation in the XHTML/SVG headers -- */ static void gen_info(void) { unsigned i; time_t ltime; time(<ime); #ifndef WIN32 strftime(tex_buf, TEX_BUF_SZ, "%b %e, %Y %H:%M", localtime(<ime)); #else strftime(tex_buf, TEX_BUF_SZ, "%b %#d, %Y %H:%M", localtime(<ime)); #endif fprintf(fout, "\n" "\n", fout); } static void define_head(float w, float h) { static const char svg_head1[] = "\n" "\n" ""; fprintf(fout, svg_head1, w, h); if (cfmt.musicfont) { if (strchr(cfmt.musicfont, '(')) fprintf(fout, svg_font_style_url, cfmt.musicfont); else fprintf(fout, svg_font_style, cfmt.musicfont); } fputs(svg_head2, fout); } /* -- output the symbol definitions -- */ void define_svg_symbols(char *title, int num, float w, float h) { char *s; unsigned i; static const char svg_head3[] = " %s %d\n"; if (svg == 2) { /* if XHTML */ if (file_initialized <= 0) { if ((s = strrchr(in_fname, DIRSEP)) == NULL) s = in_fname; else s++; fputs("\n" "\n" "\n" "\n" "\n", fout); gen_info(); fprintf(fout, "\n" "%s\n" "\n" "\n", s); } else { fputs("
\n", fout); } define_head(w, h); xml_str_out(title); fprintf(fout, svg_head3, "page", num); // if (cfmt.bgcolor && cfmt.bgcolor[0] != '\0') // fprintf(fout, // "\n", // cfmt.bgcolor); } else { /* -g, -v or -z */ if (epsf != 3) { if (fout != stdout) fputs("\n" "\n", fout); // else if (svg) // fputs("

\n", fout); } define_head(w, h); xml_str_out(title); fprintf(fout, svg_head3, epsf ? "tune" : "page", num); fputs("\n", fout); gen_info(); if (cfmt.bgcolor && cfmt.bgcolor[0] != '\0') fprintf(fout, "\n", cfmt.bgcolor); } // reset the interpreter memset(&gcur, 0, sizeof gcur); gcur.xscale = gcur.yscale = 1; gcur.linewidth = 0.7; // default line width gcur.cos = 1; gcur.font_n = strdup(""); gcur.font_n_old = strdup(""); memcpy(&gold, &gcur, sizeof gold); x_rot = y_rot = 0; nsave = 0; for (i = 0; i < sizeof def_tb / sizeof def_tb[0]; i++) { if (def_tb[i].defined == 1) def_tb[i].defined = 0; } /* if new page, done */ if (file_initialized > 0) return; elts_reset(); n_sym = 0; in_cnt = 0; path = NULL; ps_error = 0; s = strdup("/defl 0 def\n" "/svg 1 def\n" "/dlw{0.7 SLW}def\n" "/gsc{gsave y T .8 dup scale 0 0}def\n"); svg_write(s, strlen(s)); free(s); } static void output_font(int span) { char *p, *fn; int i, imin; if (gcur.font_n[0] == '\0' && (span || !gcur.rgb)) return; fprintf(fout, " style=\""); if (!span && gcur.rgb) { fprintf(fout, "color:#%06x;", gcur.rgb); if (gcur.font_n[0] == '\0') { fprintf(fout, "\""); return; } } fprintf(fout, "font:"); fn = gcur.font_n; if (fn[0] == '/') fn++; imin = 255; p = strchr(fn, '-'); if (p) imin = p - fn; p = strstr(fn, "old"); if (p && (p[-1] == 'B' || p[-1] == 'b')) { fprintf(fout, "bold "); i = p - fn - 1; if (imin > i) imin = i; } p = strstr(fn, "talic"); if (p && (p[-1] == 'I' || p[-1] == 'i')) { fprintf(fout, "italic "); i = p - fn - 1; if (imin > i) imin = i; } p = strstr(fn, "blique"); if (p && (p[-1] == 'O' || p[-1] == 'o')) { fprintf(fout, "oblique "); i = p - fn - 1; if (imin > i) imin = i; } fprintf(fout, "%.2fpx %.*s\"", gcur.font_s, imin, fn); } static float strw(char *s) { unsigned char c; float w; w = 0; for (;;) { c = (unsigned char) *s++; if (c == '\0') break; w += cwid(c) * 1.1; } return w * gcur.font_s; } /* define the global container */ static void setg(int newg); static void defg1(void) { setg(0); fprintf(fout, "\n", gcur.dash); fprintf(fout, ">\n"); g = 1; memcpy(&gold, &gcur, sizeof gold); } /* * set the state of the containers * state: * 0: no container * 1: graphical container * 2: graphical container and text * newg: * 0: close both the text and the graphical container * 1: close only the text and reset the graphical container */ static void setg(int newg) { if (g == 2) { fputs("\n", fout); g = 1; } if (newg == 0) { if (g != 0) { fputs("\n", fout); if (gcur.rotate != 0) { gcur.xoffs = x_rot; gcur.yoffs = y_rot; x_rot = 0; y_rot = 0; } g = 0; } } else { gold.cx = gcur.cx; gold.cy = gcur.cy; if (memcmp(&gcur, &gold, sizeof gcur) != 0) defg1(); } } /* graphic path */ static void path_print(char *fmt, ...) { va_list args; char *p; va_start(args, fmt); vsnprintf(path_buf, sizeof path_buf, fmt, args); va_end(args); if (!path) { path = malloc(strlen(path_buf) + 1); p = path; } else { path = realloc(path, strlen(path) + strlen(path_buf) + 1); p = path + strlen(path); } if (!path) { fprintf(stderr, "Out of memory.\n"); exit(EXIT_FAILURE); } strcpy(p, path_buf); } static void path_def(void) { if (path) return; setg(1); path_print("\n", fout); // g = 1; // } else { setg(1); // } if (def_tb[def].defined) return; def_tb[def].defined = 1; fputs("\n", fout); i = def_tb[def].use; while (i != 0 && !def_tb[i].defined) { def_tb[i].defined = 1; fputs(def_tb[i].def, fout); i = def_tb[i].use; } fputs(def_tb[def].def, fout); fputs("\n", fout); } // SVG definition found in %%beginsvg // mark the id as defined if standard glyph // or create a PS symbol void svg_def_id(char *id, int idsz) { char *p; int i; for (i = 0; i < sizeof def_tb / sizeof def_tb[0]; i++) { p = strstr(def_tb[i].def, "id="); // (cannot be NULL) if (strncmp(p, id, idsz) == 0) { def_tb[i].defined = 2; // (don't erase) return; } } if (!defs) { defssz = 8192; defs = malloc(defssz); *defs = '\0'; } i = strlen(defs); if (idsz + i + 1 >= defssz) { defssz += 8192; defs = realloc(defs, defssz); } strncpy(defs + i, id, idsz); defs[i + idsz] = '\0'; } static void xysym(char *op, int use) { float x, y; if (use >= 0) def_use(use); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "\n", x, y, op); } static void setxory(char *s, float v) { struct elt_s *e; struct ps_sym_s *sym; sym = ps_sym_lookup(s); if (!sym || sym->e->type != VAL) { e = elt_new(); if (!e) return; e->type = VAL; sym = ps_sym_def(s, e); if (!sym) return; } sym->e->u.v = v; } static void setxysym(char *op, int use) { float x, y; y = pop_free_val(); x = pop_free_val(); setxory("x", x); setxory("y", y); def_use(use); fprintf(fout, "\n", gcur.xoffs + x, gcur.yoffs - y, op); } /* gua gda (acciaccatura) */ static void acciac(char *op) { struct ps_sym_s *sym; float x, y, dx, dy; setg(1); dy = pop_free_val(); dx = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; if (op[1] == 'u') { x -= 1; y -= 4; } else { x -= 5; y += 4; } fprintf(fout, "\n", x, y, dx, -dy); } /* arp - ltr */ static void arp_ltr(char type) { float x, y, t; int n; def_use(D_ltr); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); n = (pop_free_val() + 5) / 6; if (type == 'a') { fprintf(fout, "\n"); t = x; x = -y; y = t; } y -= 4; while (--n >= 0) { fprintf(fout, "\n", x, y); x += 6; } if (type == 'a') fprintf(fout, "\n"); } // glissando static void gliss(int squiggle) { float x1, y1, x2, y2, ar, a, len; int n; if (squiggle) def_use(D_ltr); y1 = gcur.yoffs - pop_free_val(); x1 = gcur.xoffs + pop_free_val(); y2 = gcur.yoffs - pop_free_val(); x2 = gcur.xoffs + pop_free_val(); ar = atan((y2 - y1) / (x2 - x1)); a = ar / M_PI * 180; len = (x2 - x1 - 14) / cos(ar); fprintf(fout, "\n", x1, y1, a); if (squiggle) { n = (len + 2) / 6; x1 = 8; while (--n >= 0) { fprintf(fout, "\n", x1); x1 += 6; } } else { fprintf(fout, "\n", len); } fprintf(fout, "\n"); } /* sd su gd gu */ static void stem(char *op) { struct ps_sym_s *sym; float x, y, dx, h; ps_exec("dlw"); setg(1); h = pop_free_val(); if (op[0] == 's') dx = 3.5; else dx = GSTEM_XOFF; if (op[1] == 'd') dx = -dx; sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + dx; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n", x, y, -h); } /* * types: * s show / c showc / r showr / j jshow / b showb /x gxshow */ static void show(char type) { float x, y, w; char tmp[4], *s, *p, *q; int span; span = 0; gold.cx = gcur.cx; gold.cy = gcur.cy; if (memcmp(&gcur, &gold, sizeof gcur) != 0) { if (g == 2) span = 1; else defg1(); } x = gcur.cx; y = gcur.cy; switch (type) { case 'j': w = pop_free_val(); p = tmp; tmp[0] = '\0'; s = NULL; break; default: if (!stack) { fprintf(stderr, "svg top: Stack empty\n"); ps_error = 1; return; } if (stack->type == STR) { s = pop_free_str(); if (!s || s[0] != '(') { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } p = s + 1; /* remove '(' */ } else { p = tmp; tmp[0] = pop_free_val(); tmp[1] = '\0'; s = NULL; } w = strw(p); if (type == 'x') { /* gxshow */ w = pop_free_val(); /* inter TAB width */ q = strchr(p, '\t'); *q = '\0'; /* string after the 1st one */ } break; } if (span) { fprintf(fout, ""); } else if (g != 2) { fprintf(fout, "", fout); g = 2; } back: xml_str_out(p); if (span) fprintf(fout, ""); if (type == 'x') { p = p + strlen(p) + 1; /* next string of gxshow */ q = strchr(p, '\t'); if (q) { *q = '\0'; } else { /* restore the string width (!! tied to elt_free() !!) */ w = free_elt->u.v; type = 's'; } fprintf(fout, "", w); span = 1; goto back; } if (type == 'b') { setg(1); fprintf(fout, "\n", gcur.xoffs + gcur.cx - 2, gcur.yoffs - y - gcur.font_s + 2, w + 4, gcur.font_s + 1); } gcur.cx = x + w; if (s) free(s); } /* execute a sequence * returns 1 on 'exit' or error */ static int seq_exec(struct elt_s *e) { struct elt_s *e2; switch (e->type) { case STR: if (e->u.s[0] != '/' && e->u.s[0] != '(') { if (strcmp(e->u.s, "exit") == 0) return 1; ps_exec(e->u.s); return 0; } /* fall thru */ case VAL: case BRK: e = elt_dup(e); if (!e) return 1; push(e); return 0; } /* (e->type == SEQ) */ e = e->u.e; while (e) { switch (e->type) { case STR: if (strcmp(e->u.s, "exit") == 0) return 1; if (e->u.s[0] != '(' && e->u.s[0] != '/') { ps_exec(e->u.s); break; } /* fall thru */ default: e2 = elt_dup(e); if (!e2) return 1; push(e2); break; } e = e->next; } return 0; } /* execute a command */ /* (in case of error, a string may be not freed, but this is not important!) */ static void ps_exec(char *op) { struct ps_sym_s *sym; struct elt_s *e, *e2; float x, y, w, h; int n; char *s; if (ps_error) return; #if 0 fprintf(stderr, "%s ", op); stack_dump(); #endif sym = ps_sym_lookup(op); if (sym) { if (++sym->exec > 2) { fprintf(stderr, "svg: Too many recursions of '%s'\n", op); ps_error = 1; return; } seq_exec(sym->e); sym->exec--; return; } if (*op == ' ') /* load */ op++; switch (*op) { case '!': /* def */ if (op[1] == '\0') { if (!stack) { fprintf(stderr, "svg def: Stack empty\n"); ps_error = 1; return; } e = pop(stack->type); /* value */ s = pop_free_str(); /* symbol */ if (!s || *s != '/') { fprintf(stderr, "svg def: No / bad symbol\n"); if (s) free(s); ps_error = 1; return; } ps_sym_def(&s[1], e); free(s); return; } break; case 'a': if (strcmp(op, "accent") == 0) { xysym(op, D_accent); return; } if (strcmp(op, "abs") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg abs: Bad value\n"); ps_error = 1; return; } if (stack->u.v < 0) stack->u.v = -stack->u.v; return; } if (strcmp(op, "add") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg add: Bad value\n"); ps_error = 1; return; } stack->u.v += x; return; } if (strcmp(op, "and") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg and: Bad value\n"); ps_error = 1; return; } stack->u.v = (int) x & (int) stack->u.v; return; } if (strcmp(op, "anshow") == 0) { show('s'); return; } if (strcmp(op, "arc") == 0 || strcmp(op, "arcn") == 0) { float r, a1, a2, x1, y1, x2, y2; a2 = pop_free_val(); a1 = pop_free_val(); r = pop_free_val(); if (r < 0) { fprintf(stderr, "svg arc: Bad value\n"); ps_error = 1; return; } if (a1 >= 360) a1 -= 360; if (a2 >= 360) a2 -= 360; y = pop_free_val(); x = pop_free_val(); x1 = x + r * cos(a1 * M_PI / 180); y1 = y + r * sinf(a1 * M_PI / 180); if (gcur.cx != NaN) { // if no newpath if (path) { path_print("\n\t%c%.2f %.2f", x1 != gcur.cx || y1 != gcur.cy ? 'l' : 'm', x1 - gcur.cx, -(y1 - gcur.cy)); } else { gcur.cx = x1; gcur.cy = y1; path_def(); } } else { gcur.cx = x1; gcur.cy = y1; path_def(); } if (a1 == a2) { /* circle */ a2 = 180 - a1; x2 = x + r * cosf(a2 * M_PI / 180); y2 = y + r * sinf(a2 * M_PI / 180); path_print("\n\ta%.2f %.2f 0 0 %d %.2f %.2f " "%.2f %.2f 0 0 %d %.2f %.2f\n", r, r, op[3] == 'n', x2 - x1, -(y2 - y1), r, r, op[3] == 'n', x1 - x2, -(y1 - y2)); gcur.cx = x1; gcur.cy = y1; } else { x2 = x + r * cosf(a2 * M_PI / 180); y2 = y + r * sinf(a2 * M_PI / 180); path_print("\n\ta%.2f %.2f 0 0 %d %.2f %.2f\n", r, r, op[3] == 'n', x2 - x1, -(y2 - y1)); gcur.cx = x2; gcur.cy = y2; } return; } if (strcmp(op, "arp") == 0) { arp_ltr('a'); return; } if (strcmp(op, "atan") == 0) { x = pop_free_val(); /* den */ if (!stack || stack->type != VAL || x == 0) { fprintf(stderr, "svg atan: Bad value\n"); ps_error = 1; return; } y = stack->u.v; /* num */ stack->u.v = atan(y / x) / M_PI * 180; return; } break; case 'b': if (strcmp(op, "bar") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); h = pop_free_val(); fprintf(fout, "\n", x, y, -h); return; } if (strcmp(op, "bclef") == 0) { xysym(op, D_bclef); return; } if (strcmp(op, "bdef") == 0) { ps_exec("!"); return; } if (strcmp(op, "bind") == 0) { return; } if (strcmp(op, "bitshift") == 0) { int shift; shift = pop_free_val(); if (!stack || stack->type != VAL || shift >= 32 || shift < -32) { fprintf(stderr, "svg: Bad value for bitshift\n"); ps_error = 1; return; } if (shift > 0) n = (int) stack->u.v << shift; else n = (int) stack->u.v >> -shift; stack->u.v = n; return; } if (strcmp(op, "bm") == 0) { float dx, dy; setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); dy = pop_free_val(); dx = pop_free_val(); h = pop_free_val(); fprintf(fout, "\n", x, y, dx, -dy, h,-dx, dy); return; } if (strcmp(op, "bnum") == 0 || strcmp(op, "bnumb") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } if (op[4] == 'b') { w = 7 * strlen(s); fprintf(fout, "\n", x - w / 2, y - 10, w); } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "box") == 0) { setg(1); h = pop_free_val(); w = pop_free_val(); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "\n", x, y - h, w, h); return; } if (strcmp(op, "boxdraw") == 0) { setg(1); h = pop_free_val(); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "\n", x, y - h, boxend - (x - gcur.xoffs) + 2, h); return; } if (strcmp(op, "boxmark") == 0) { if (gcur.cx > boxend) boxend = gcur.cx; return; } if (strcmp(op, "boxend") == 0) { boxend = gcur.cx; return; } if (strcmp(op, "brace") == 0) { def_use(D_brace); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); h = pop_free_val() * 0.01; fprintf(fout, "\n" " \n" "\n", x, y, h); return; } if (strcmp(op, "bracket") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 3; x = gcur.xoffs + pop_free_val() - 5; h = pop_free_val() + 2; fprintf(fout, "\n", x, y, h); return; } if (strcmp(op, "breve") == 0) { setxysym(op, D_breve); return; } if (strcmp(op, "brth") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 6; x = gcur.xoffs + pop_free_val(); fprintf(fout, "" ",\n", x, y); return; } break; case 'C': if (strcmp(op, "C") == 0) { float c1, c2, c3, c4; curveto: path_def(); y = pop_free_val(); x = pop_free_val(); c4 = gcur.yoffs - pop_free_val(); c3 = gcur.xoffs + pop_free_val(); c2 = gcur.yoffs - pop_free_val(); c1 = gcur.xoffs + pop_free_val(); path_print("\tC%.2f %.2f %.2f %.2f %.2f %.2f\n", c1, c2, c3, c4, gcur.xoffs + x, gcur.yoffs - y); gcur.cx = x; gcur.cy = y; return; } break; case 'c': if (strcmp(op, "cclef") == 0) { xysym(op, D_cclef); return; } if (strcmp(op, "csig") == 0) { xysym(op, D_csig); return; } if (strcmp(op, "ctsig") == 0) { xysym(op, D_ctsig); return; } if (strcmp(op, "coda") == 0) { xysym(op, D_coda); return; } if (strcmp(op, "closepath") == 0) { if (path) { // path_def(); path_print("\tz"); } return; } if (strcmp(op, "composefont") == 0) { pop(BRK); pop(STR); return; } if (strcmp(op, "copy") == 0) { struct elt_s *e3; n = pop_free_val(); if ((unsigned) n > 10) { fprintf(stderr, "svg copy: Too wide\n"); ps_error = 1; return; } e = stack; e2 = NULL; while (--n >= 0) { if (!e) break; e3 = elt_dup(e); if (!e3) return; e3->next = e2; e2 = e3; e = e->next; } if (n >= 0) { fprintf(stderr, "svg copy: Stack empty\n"); ps_error = 1; return; } while (e2) { e3 = e2->next; push(e2); e2 = e3; } return; } if (strcmp(op, "cos") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg cos: Bad value\n"); ps_error = 1; return; } stack->u.v = cos(stack->u.v * M_PI / 180); return; } if (strcmp(op, "cpu") == 0) { xysym(op, D_cpu); return; } if (strcmp(op, "crdc") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg crdc: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "cresc") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); x += w; if ((int) sym->e->u.v & 1) fprintf(fout, "\n", x, y, -w, w); else fprintf(fout, "\n", x, y, -w, w); return; } if (strcmp(op, "custos") == 0) { xysym(op, D_custos); return; } if (strcmp(op, "currentgray") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = (float) gcur.rgb / 0xffffff; push(e); return; } if (strcmp(op, "currentpoint") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = gcur.cx; push(e); e = elt_new(); if (!e) return; e->type = VAL; e->u.v = gcur.cy; push(e); return; } if (strcmp(op, "curveto") == 0) goto curveto; if (strcmp(op, "cvi") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg cvi: Bad value\n"); ps_error = 1; return; } n = stack->u.v; stack->u.v = n; return; } if (strcmp(op, "cvx") == 0) { s = pop_free_str(); if (!s || ((*s != '/') && (*s != '('))) { fprintf(stderr, "svg cvx: No / bad string\n"); if (s) free(s); ps_error = 1; return; } *s = '{'; svg_write(s, strlen(s)); svg_write("}", 1); free(s); return; } break; case 'd': if (strcmp(op, "dacs") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 3; x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg dacs: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "dacoda") == 0) { setg(1); e = elt_dup(stack); y = gcur.yoffs - pop_free_val() - 7; e2 = elt_dup(stack); e2->u.v += 10; x = gcur.xoffs + pop_free_val() - 10; fprintf(fout, "Da\n", x, y); push(e2); push(e); xysym("coda", D_coda); return; } if (strcmp(op, "def") == 0) { ps_exec("!"); return; } if (strcmp(op, "dim") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); if ((int) sym->e->u.v & 2) fprintf(fout, "\n", x, y, w, -w); else fprintf(fout, "\n", x, y, w, -w); return; } if (strcmp(op, "div") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL || x == 0) { fprintf(stderr, "svg: Bad value for div\n"); ps_error = 1; return; } stack->u.v /= x; return; } if (strcmp(op, "dnb") == 0) { xysym(op, D_dnb); return; } if (strcmp(op, "dplus") == 0) { xysym(op, D_dplus); return; } if (strcmp(op, "dSL") == 0) { float a1, a2, a3, a4, a5, a6, m1, m2; setg(1); m2 = gcur.yoffs - pop_free_val(); m1 = gcur.xoffs + pop_free_val(); a6 = pop_free_val(); a5 = pop_free_val(); a4 = pop_free_val(); a3 = pop_free_val(); a2 = pop_free_val(); a1 = pop_free_val(); fprintf(fout, "\n", m1, m2, a1, -a2, a3, -a4, a5, -a6); return; } if (strcmp(op, "dt") == 0) { setg(1); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; y -= pop_free_val(); x += pop_free_val(); fprintf(fout, "\n", x, y); return; } if (strcmp(op, "dotbar") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); h = pop_free_val(); fprintf(fout, "\n", x, y, -h); return; } if (strcmp(op, "dup") == 0) { if (!stack) { fprintf(stderr, "svg dup: Stack empty\n"); ps_error = 1; return; } e = elt_dup(stack); if (e) push(e); return; } if (strcmp(op, "dft0") == 0) { xysym(op, D_dft0); return; } if (strcmp(op, "dsh0") == 0) { xysym(op, D_dsh0); return; } break; case 'e': if (strcmp(op, "emb") == 0) { xysym(op, D_emb); return; } if (strcmp(op, "eofill") == 0) { if (!path) { fprintf(stderr, "svg eofill: No path\n"); ps_error = 1; return; } path_end(); fprintf(fout, "\t\" fill-rule=\"evenodd\" class=\"fill\"/>\n"); return; } if (strcmp(op, "eq") == 0) { cond(C_EQ); return; } if (strcmp(op, "exch") == 0) { if (!stack || !stack->next) { fprintf(stderr, "svg exch: Stack empty\n"); ps_error = 1; return; } e = stack->next; stack->next = e->next; e->next = stack; stack = e; return; } if (strcmp(op, "exec") == 0) { e = pop(SEQ); if (!e) return; seq_exec(e); elt_free(e); return; } break; case 'F': if (sscanf(op, "F%d", &n) == 1) { h = pop_free_val(); if (!fontnames[n]) break; if (gcur.font_s != h || strcmp(fontnames[n], gcur.font_n) != 0) { free(gcur.font_n_old); gcur.font_n_old = gcur.font_n; gcur.font_n = strdup(fontnames[n]); gcur.font_s = h; gold.font_n = NULL; } return; } break; case 'f': if (strcmp(op, "false") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 0; push(e); return; } if (strcmp(op, "fill") == 0) { if (!path) { fprintf(stderr, "svg fill: No path\n"); // ps_error = 1; return; } path_end(); fprintf(fout, "\t\" class=\"fill\"/>\n"); return; } if (strcmp(op, "findfont") == 0) { s = pop_free_str(); if (!s || *s != '/') { fprintf(stderr, "svg findfont: No / bad font\n"); if (s) free(s); ps_error = 1; return; } if (strcmp(s, gcur.font_n) != 0) { free(gcur.font_n_old); gcur.font_n_old = gcur.font_n; gcur.font_n = s; gold.font_n = NULL; } else { free(s); } return; } if (strcmp(op, "fng") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 1; x = gcur.xoffs + pop_free_val() - 3; s = pop_free_str(); if (!s) { fprintf(stderr, "svg fng: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "for") == 0) { float init, incr, limit; e = pop(SEQ); /* proc */ if (!e) return; limit = pop_free_val(); incr = pop_free_val(); init = pop_free_val(); if (incr == 0 || (limit - init) / incr > 100) { fprintf(stderr, "svg for: Bad values\n"); ps_error = 1; return; } if (incr > 0) { while (init <= limit) { e2 = elt_new(); if (!e2) break; e2->type = VAL; e2->u.v = init; push(e2); if (seq_exec(e) != 0) break; init += incr; } } else { while (init >= limit) { e2 = elt_new(); if (!e2) break; e2->type = VAL; e2->u.v = init; push(e2); if (seq_exec(e) != 0) break; init += incr; } } elt_free(e); return; } if (strcmp(op, "forall") == 0) { struct elt_s *e3; unsigned char *p; e = pop(SEQ); /* proc */ if (!e) return; e2 = stack; /* array/string */ if (!e2) { fprintf(stderr, "svg forall: Stack empty\n"); ps_error = 1; return; } stack = e2->next; switch (e2->type) { case STR: p = (unsigned char *) &e2->u.s[1]; while (*p != '\0') { e3 = elt_new(); if (!e3) return; e3->u.v = *p++; push(e3); if (seq_exec(e) != 0) break; } break; case BRK: for (e3 = e2->u.e; e3; e3 = e3->next) { struct elt_s *e4; e4 = elt_dup(e3); push(e4); if (seq_exec(e) != 0) break; } break; default: fprintf(stderr, "svg forall: Bad any\n"); ps_error = 1; return; } elt_free(e); elt_free(e2); return; } if (strcmp(op, "ft0") == 0) { xysym(op, D_ft0); return; } if (strcmp(op, "ft1") == 0) { xysym(op, D_ft1); return; } if (strcmp(op, "ft4") == 0) { n = pop_free_val(); switch (n) { case 1: xysym("ft1", D_ft1); break; case 2: xysym("ft0", D_ft0); break; case 3: xysym("ft513", D_ft513); break; default: xysym("dft0", D_dft0); break; } return; } if (strcmp(op, "ft513") == 0) { xysym(op, D_ft513); return; } break; case 'g': if (strcmp(op, "gcshow") == 0) { show('s'); return; } if (strcmp(op, "ge") == 0) { cond(C_GE); return; } if (strcmp(op, "get") == 0) { n = pop_free_val(); if (!stack) { fprintf(stderr, "svg get: Stack empty\n"); ps_error = 1; return; } switch (stack->type) { case VAL: if (n != 0) { fprintf(stderr, "svg get: Out of bounds\n"); ps_error = 1; return; } return; case STR: s = stack->u.s; if (!s || *s != '(') { fprintf(stderr, "svg get: Not a string\n"); if (s) free(s); ps_error = 1; return; } if ((unsigned) n >= strlen(s) - 1) { fprintf(stderr, "svg get: Out of bounds\n"); ps_error = 1; return; } stack->type = VAL; stack->u.v = s[n + 1]; free(s); return; } e = stack->u.e; e2 = NULL; while (--n >= 0) { if (!e) break; e2 = e; e = e->next; } if (!e) { fprintf(stderr, "svg get: Out of bounds\n"); ps_error = 1; return; } if (!e2) stack->u.e = e->next; else e2->next = e->next; e->next = stack->next; elt_free(stack); stack = e; return; } if (strcmp(op, "getinterval") == 0) { int count; count = pop_free_val(); n = pop_free_val(); s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg getinterval: No string\n"); if (s) free(s); ps_error = 1; return; } if ((unsigned) n >= strlen(s) || (unsigned) count >= strlen(s) - n) { fprintf(stderr, "svg getinterval: Out of bounds\n"); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = STR; e->u.s = malloc(count + 2); e->u.s[0] = '('; memcpy(&e->u.s[1], &s[n + 1], count); e->u.s[count + 1] = '\0'; push(e); free(s); return; } if (strcmp(op, "ghd") == 0) { setxysym(op, D_ghd); return; } if (strcmp(op, "ghl") == 0) { xysym(op, D_ghl); return; } if (strcmp(op, "glisq") == 0 || strcmp(op, "gliss") == 0) { gliss(op[4] == 'q'); return; } if (strcmp(op, "gt") == 0) { cond(C_GT); return; } if (strcmp(op, "gu") == 0 || strcmp(op, "gd") == 0) { stem(op); return; } if (strcmp(op, "gua") == 0 || strcmp(op, "gda") == 0) { acciac(op); return; } if (strcmp(op, "grestore") == 0) { if (nsave <= 0) { fprintf(stderr, "svg grestore: No gsave\n"); ps_error = 1; return; } setg(1); free(gcur.font_n); free(gcur.font_n_old); memcpy(&gcur, &gsave[--nsave], sizeof gcur); return; } if (strcmp(op, "grm") == 0) { xysym(op, D_grm); return; } if (strcmp(op, "gsave") == 0) { if (nsave >= (int) (sizeof gsave / sizeof gsave[0])) { fprintf(stderr, "svg grestore: Too many gsave's\n"); ps_error = 1; return; } // setg(1); memcpy(&gsave[nsave++], &gcur, sizeof gsave[0]); gcur.font_n = strdup(gcur.font_n); gcur.font_n_old = strdup(gcur.font_n_old); return; } if (strcmp(op, "gsl") == 0) { float a1, a2, a3, a4, a5, a6, m1, m2; setg(1); m2 = gcur.yoffs - pop_free_val(); m1 = gcur.xoffs + pop_free_val(); a6 = pop_free_val(); a5 = pop_free_val(); a4 = pop_free_val(); a3 = pop_free_val(); a2 = pop_free_val(); a1 = pop_free_val(); fprintf(fout, "\n", m1, m2, a1, -a2, a3, -a4, a5, -a6); return; } if (strcmp(op, "gxshow") == 0) { show('x'); return; } break; case 'H': if (strcmp(op, "Hd") == 0) { setxysym(op, D_Hd); return; } if (strcmp(op, "HD") == 0) { setxysym(op, D_HD); return; } if (strcmp(op, "HDD") == 0) { setxysym(op, D_HDD); return; } break; case 'h': if (strcmp(op, "hd") == 0) { setxysym(op, D_hd); return; } if (strcmp(op, "hl") == 0) { xysym(op, D_hl); return; } if (strcmp(op, "hl1") == 0) { xysym(op, D_hl1); return; } if (strcmp(op, "hl2") == 0) { xysym(op, D_hl2); return; } if (strcmp(op, "hld") == 0) { xysym(op, D_hld); return; } if (strcmp(op, "hyph") == 0) { int d; setg(1); y = pop_free_val(); x = pop_free_val(); w = pop_free_val(); d = 25 + (int) w / 20 * 3; n = (w - 15.) / d; x += (w - d * n - 5) / 2; fprintf(fout, "\n", d - 5, gcur.xoffs + x, gcur.yoffs - y - gcur.font_s * 0.3, d * n + 5); return; } break; case 'i': if (strcmp(op, "idiv") == 0) { n = pop_free_val(); if (!stack || stack->type != VAL || n == 0) { fprintf(stderr, "svg idiv: Bad value\n"); ps_error = 1; return; } n = (int) stack->u.v / n; stack->u.v = n; return; } if (strcmp(op, "if") == 0) { e = pop(SEQ); /* sequence */ if (!e) return; n = pop_free_val(); /* condition */ if (n != 0) seq_exec(e); elt_free(e); return; } if (strcmp(op, "ifelse") == 0) { e2 = pop(SEQ); /* sequence 2 */ e = pop(SEQ); /* sequence 1 */ if (!e || !e2) return; n = pop_free_val(); /* condition */ if (n != 0) seq_exec(e); else seq_exec(e2); elt_free(e); elt_free(e2); return; } if (strcmp(op, "imsig") == 0) { xysym(op, D_imsig); return; } if (strcmp(op, "iMsig") == 0) { xysym(op, D_iMsig); return; } if (strcmp(op, "index") == 0) { n = pop_free_val(); e = stack; while (--n >= 0) { if (!e) break; e = e->next; } if (!e) { fprintf(stderr, "svg index: Stack empty\n"); ps_error = 1; return; } e = elt_dup(e); if (!e) return; push(e); return; } break; case 'j': if (strcmp(op, "jshow") == 0) { show('j'); return; } break; case 'L': if (strcmp(op, "L") == 0) { lineto: path_def(); y = pop_free_val(); x = pop_free_val(); if (x == gcur.cx) path_print("\tv%.2f\n", gcur.cy - y); else if (y == gcur.cy) path_print("\th%.2f\n", x - gcur.cx); else path_print("\tl%.2f %.2f\n", x - gcur.cx, gcur.cy - y); gcur.cx = x; gcur.cy = y; return; } case 'l': if (strcmp(op, "le") == 0) { cond(C_LE); return; } if (strcmp(op, "lt") == 0) { cond(C_LT); return; } if (strcmp(op, "length") == 0) { s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg length: No string\n"); if (s) free(s); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = VAL; e->u.v = strlen(s + 1); push(e); free(s); return; } if (strcmp(op, "lineto") == 0) goto lineto; if (strcmp(op, "lmrd") == 0) { xysym(op, D_lmrd); return; } if (strcmp(op, "load") == 0) { s = pop_free_str(); if (!s || *s != '/') { fprintf(stderr, "svg load: No / bad symbol\n"); if (s) free(s); ps_error = 1; return; } sym = ps_sym_lookup(s + 1); if (!sym) { e = elt_new(); if (!e) return; e->type = STR; e->u.s = strdup(s); e->u.s[0] = ' '; /* internal */ } else { e = elt_dup(sym->e); if (!e) return; } free(s); push(e); return; } if (strcmp(op, "longa") == 0) { setxysym(op, D_longa); return; } if (strcmp(op, "lphr") == 0) { xysym(op, D_lphr); return; } if (strcmp(op, "ltr") == 0) { arp_ltr('l'); return; } if (strcmp(op, "lyshow") == 0) { show('s'); return; } break; case 'M': if (strcmp(op, "M") == 0) { moveto: gcur.cy = pop_free_val(); gcur.cx = pop_free_val(); if (path) { path_print("\tM%.2f %.2f\n", gcur.xoffs + gcur.cx, gcur.yoffs - gcur.cy); } else if (g == 2) { fputs("\n", fout); g = 1; } return; } break; case 'm': if (strcmp(op, "marcato") == 0) { xysym(op, D_marcato); return; } if (strcmp(op, "moveto") == 0) goto moveto; if (strcmp(op, "mphr") == 0) { xysym(op, D_mphr); return; } if (strcmp(op, "mod") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL || x == 0) { fprintf(stderr, "svg: Bad value for mod\n"); ps_error = 1; return; } n = (int) stack->u.v % (int) x; stack->u.v = n; return; } if (strcmp(op, "mrep") == 0) { xysym(op, D_mrep); return; } if (strcmp(op, "mrep2") == 0) { xysym(op, D_mrep2); return; } if (strcmp(op, "mrest") == 0) { #if 1 xysym(op, D_mrest); return; #else def_use(D_mrest); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } fprintf(fout, "\n" "%s\n", x, y, x, y - 28, s + 1); free(s); #endif return; } if (strcmp(op, "mul") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg: Bad value for mul\n"); ps_error = 1; return; } stack->u.v *= x; return; } break; case 'n': if (strcmp(op, "ne") == 0) { cond(C_NE); return; } if (strcmp(op, "neg") == 0) { if (!stack || stack->type != VAL) { fprintf(stderr, "svg: Bad value for neg\n"); ps_error = 1; return; } stack->u.v = -stack->u.v; return; } if (strcmp(op, "newpath") == 0) { // path_def(); gcur.cx = NaN; return; } if (strcmp(op, "nt0") == 0) { xysym(op, D_nt0); return; } break; case 'o': if (strcmp(op, "o8va") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); if (!((int) sym->e->u.v & 1)) { fprintf(fout, "8" "va\n", x - 5, y); x += 14; w -= 14; } else { w -= 5; } y -= 6; fprintf(fout, "\n", x, y, w); if (!((int) sym->e->u.v & 2)) fprintf(fout, "\n", x + w, y); return; } if (strcmp(op, "o8vb") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); w = pop_free_val(); sym = ps_sym_lookup("defl"); if (!((int) sym->e->u.v & 1)) { fprintf(fout, "8" "vb\n", x - 5, y); x += 8; w -= 8; } else { w -= 5; } fprintf(fout, "\n", x, y, w); if (!((int) sym->e->u.v & 2)) fprintf(fout, "\n", x + w, y); return; } if (strcmp(op, "oct") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); fprintf(fout, "8\n", x, y); return; } if (strcmp(op, "opend") == 0) { xysym(op, D_opend); return; } if (strcmp(op, "or") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg or: Bad value\n"); ps_error = 1; return; } stack->u.v = (int) x & (int) stack->u.v; return; } break; case 'p': if (strcmp(op, "pclef") == 0) { xysym(op, D_pclef); return; } if (strcmp(op, "ped") == 0) { xysym(op, D_ped); return; } if (strcmp(op, "pedoff") == 0) { xysym(op, D_pedoff); return; } if (strcmp(op, "pf") == 0) { setg(1); y = gcur.yoffs - pop_free_val() - 5; x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg pf: No string\n"); ps_error = 1; return; } fprintf(fout, "%s\n", x, y, s + 1); free(s); return; } if (strcmp(op, "pmsig") == 0) { xysym(op, D_pmsig); return; } if (strcmp(op, "pMsig") == 0) { xysym(op, D_pMsig); return; } if (strcmp(op, "pop") == 0) { if (!stack) { fprintf(stderr, "svg pop: Stack empty\n"); ps_error = 1; return; } e = pop(stack->type); elt_free(e); return; } if (strcmp(op, "pshhd") == 0) { setxysym(op, D_pshhd); return; } if (strcmp(op, "pdshhd") == 0) { setxysym("pshhd", D_pshhd); return; } if (strcmp(op, "pfthd") == 0) { setxysym(op, D_pfthd); return; } if (strcmp(op, "pdfthd") == 0) { setxysym("pfthd", D_pfthd); return; } #if 0 //fixme: cannot work because duplication... if (strcmp(op, "put") == 0) { int v; v = pop_free_val(); n = pop_free_val(); if (!stack) { fprintf(stderr, "svg put: Stack empty\n"); ps_error = 1; return; } s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg put: No string\n"); if (s) free(s); ps_error = 1; return; } if ((unsigned) n >= strlen(s) - 1) { fprintf(stderr, "svg put: Out of bounds\n"); if (s) free(s); ps_error = 1; return; } //fixme: should keep the original string... s[n + 1] = v; free(s); return; } #endif break; case 'R': if (strcmp(op, "RC") == 0) { float c1, c2, c3, c4; rcurveto: path_def(); y = pop_free_val(); x = pop_free_val(); c4 = pop_free_val(); c3 = pop_free_val(); c2 = pop_free_val(); c1 = pop_free_val(); path_print("\tc%.2f %.2f %.2f %.2f %.2f %.2f\n", c1, -c2, c3, -c4, x, -y); gcur.cx += x; gcur.cy += y; return; } if (strcmp(op, "RL") == 0) { rlineto: path_def(); y = pop_free_val(); x = pop_free_val(); if (x == 0) path_print("\tv%.2f\n", -y); else if (y == 0) path_print("\th%.2f\n", x); else path_print("\tl%.2f %.2f\n", x, -y); gcur.cx += x; gcur.cy += y; return; } if (strcmp(op, "RM") == 0) { rmoveto: y = pop_free_val(); x = pop_free_val(); if (path) { path_print("\tm%.2f %.2f\n", x, -y); } else if (g == 2) { fputs("\n", fout); g = 1; } gcur.cx += x; gcur.cy += y; return; } break; case 'r': if (strcmp(op, "r00") == 0) { setxysym(op, D_r00); return; } if (strcmp(op, "r0") == 0) { setxysym(op, D_r0); return; } if (strcmp(op, "r1") == 0) { setxysym(op, D_r1); return; } if (strcmp(op, "r2") == 0) { setxysym(op, D_r2); return; } if (strcmp(op, "r4") == 0) { setxysym(op, D_r4); return; } if (strcmp(op, "r8") == 0) { setxysym(op, D_r8); return; } if (strcmp(op, "r16") == 0) { setxysym(op, D_r16); return; } if (strcmp(op, "r32") == 0) { setxysym(op, D_r32); return; } if (strcmp(op, "r64") == 0) { setxysym(op, D_r64); return; } if (strcmp(op, "r128") == 0) { setxysym(op, D_r128); return; } if (strcmp(op, "rdots") == 0) { xysym(op, D_rdots); return; } if (strcmp(op, "rcurveto") == 0) goto rcurveto; if (strcmp(op, "rlineto") == 0) goto rlineto; if (strcmp(op, "rmoveto") == 0) goto rmoveto; if (strcmp(op, "roll") == 0) { int i, j; j = pop_free_val(); n = pop_free_val(); if (n <= 0) { fprintf(stderr, "svg roll: Invalid value\n"); ps_error = 1; return; } if (j > 0) { j = j % n; if (j > n / 2) j -= n; } else if (j < 0) { j = -(-j % n); if (j < -n / 2) j += n; } if (j == 0) return; e2 = stack; /* check the stack */ i = n; for (;;) { if (!e2) { fprintf(stderr, "svg roll: Stack empty\n"); ps_error = 1; return; } if (--i <= 0) break; e2 = e2->next; } if (j > 0) { while (j-- > 0) { e = stack; stack = e->next; e->next = e2->next; e2->next = e; e2 = e; } return; } while (j++ < 0) { e = stack; for (i = 0; i < n - 2; i++) e = e->next; e2 = e->next; e->next = e2->next; e2->next = stack; stack = e2; } return; } if (strcmp(op, "repbra") == 0) { int i; setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); w = pop_free_val(); i = pop_free_val(); h = pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg repbra: No string\n"); ps_error = 1; return; } fprintf(fout, "", x + 4, y - h); xml_str_out(s + 1); fprintf(fout, "\n" "\n"); free(s); return; } if (strcmp(op, "repeat") == 0) { e = pop(SEQ); /* sequence */ if (!e) return; n = pop_free_val(); /* n times */ if ((unsigned) n >= 100) { fprintf(stderr, "svg repeat: Too high value\n"); ps_error = 1; } while (--n >= 0) { if (seq_exec(e)) break; /* exit */ if (ps_error) break; } elt_free(e); return; } if (strcmp(op, "rotate") == 0) { float x, y, _sin, _cos; setg(0); // convert orig and currentpoint coord to absolute coord x = gcur.xoffs; y = -gcur.yoffs; _sin = gcur.sin; _cos = gcur.cos; gcur.xoffs = x * _cos + y * _sin; gcur.yoffs = -x * _sin + y * _cos; // PS orientation x = gcur.cx * _cos + gcur.cy * _sin; y = -gcur.cx * _sin + gcur.cy * _cos; // rotate gcur.rotate -= pop_free_val(); if (gcur.rotate > 180) gcur.rotate -= 360; else if (gcur.rotate <= -180) gcur.rotate += 360; h = gcur.rotate * M_PI / 180; gcur.sin = _sin = sin(h); gcur.cos = _cos = cos(h); gcur.cx = x * _cos - y * _sin; gcur.cy = x * _sin + y * _cos; x = gcur.xoffs; y = gcur.yoffs; gcur.xoffs = x * _cos - y * _sin; gcur.yoffs = -(x * _sin + y * _cos); // SVG orientation return; } break; case 'S': if (strcmp(op, "SL") == 0) { float c1, c2, c3, c4, c5, c6, l2; float a1, a2, a3, a4, a5, a6, m1, m2; setg(1); m2 = gcur.yoffs - pop_free_val(); m1 = gcur.xoffs + pop_free_val(); a6 = pop_free_val(); a5 = pop_free_val(); a4 = pop_free_val(); a3 = pop_free_val(); a2 = pop_free_val(); a1 = pop_free_val(); l2 = pop_free_val(); pop_free_val(); // always '0' c6 = pop_free_val(); c5 = pop_free_val(); c4 = pop_free_val(); c3 = pop_free_val(); c2 = pop_free_val(); c1 = pop_free_val(); fprintf(fout, "\n", m1, m2, a1, -a2, a3, -a4, a5, -a6, -l2, c1, -c2, c3, -c4, c5, -c6); return; } if (strcmp(op, "SLW") == 0) { gcur.linewidth = pop_free_val(); return; } break; case 's': if (strcmp(op, "scale") == 0) { y = pop_free_val(); x = pop_free_val(); gcur.xoffs /= x; gcur.yoffs /= y; gcur.cx /= x; gcur.cy /= y; gcur.xscale *= x; gcur.yscale *= y; return; } if (strcmp(op, "scalefont") == 0) { gcur.font_s = pop_free_val(); return; } if (strcmp(op, "search") == 0) { char *p; e = pop(STR); /* seek */ e2 = pop(STR); /* string */ if (!e || !e2 || e->u.s[0] != '(' || e2->u.s[0] != '(') { fprintf(stderr, "svg search: No string\n"); ps_error = 1; return; } p = strstr(&e2->u.s[1], &e->u.s[1]); if (p) { struct elt_s *e3; int l1, l2, l3; l1 = p - e2->u.s; l2 = strlen(e->u.s); l3 = strlen(e2->u.s) - l2 - l1 + 2; e3 = elt_new(); if (!e3) return; e3->type = STR; e3->u.s = malloc(l3); e3->u.s[0] = '('; memcpy(&e3->u.s[1], &e2->u.s[l1 + l2 - 2], l3 - 1); e3->u.s[l1 + l2 - 1] = '\0'; push(e3); push(e); e2->u.s[l1] = '\0'; push (e2); e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 1; } else { push(e2); free(e->u.s); e->type = VAL; e->u.v = 0; } push(e); return; } if (strcmp(op, "selectfont") == 0) { h = pop_free_val(); s = pop_free_str(); if (!s || *s != '/') { fprintf(stderr, "svg selectfont: No / bad font\n"); if (s) free(s); ps_error = 1; return; } if (gcur.font_s != h || strcmp(s, gcur.font_n) != 0) { free(gcur.font_n_old); gcur.font_n_old = gcur.font_n; gcur.font_n = strdup(s); gcur.font_s = h; gold.font_n = NULL; } else { free(s); } return; } if (strcmp(op, "sep0") == 0) { x = pop_free_val(); w = pop_free_val(); fprintf(fout, "\n", gcur.xoffs + x, gcur.yoffs, w); return; } if (strcmp(op, "setdash") == 0) { char *p; n = pop_free_val(); e = pop(BRK); if (!e) { fprintf(stderr, "svg setdash: Bad pattern\n"); ps_error = 1; return; } e = e->u.e; if (!e) { gcur.dash[0] = '\0'; return; } p = gcur.dash; if (n != 0) p += sprintf(p, " stroke-dashoffset=\"%d\"", n); p += sprintf(p, " stroke-dasharray=\""); do { if (e->type != VAL) { fprintf(stderr, "svg setdash: Bad pattern type\n"); ps_error = 1; return; } if (p >= &gcur.dash[sizeof gcur.dash] - 10) { fprintf(stderr, "svg setdash: Pattern too wide\n"); ps_error = 1; return; } p += sprintf(p, "%d,", (int) e->u.v); e = e->next; } while (e); p--; sprintf(p, "\""); return; } if (strcmp(op, "setfont") == 0) { return; } if (strcmp(op, "setgray") == 0) { n = pop_free_val() * 255; gcur.rgb = (n << 16) | (n << 8) | n; return; } if (strcmp(op, "setlinewidth") == 0) { gcur.linewidth = pop_free_val(); return; } //fixme: use 'use' for flags if (strcmp(op, "sfu") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + 3.5; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc0.9 3.7 9.1 6.4 6 12.4\n" " 1 -5.4 -4.2 -8.4 -6 -8.4\n", x, y); y += 5.4; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sfd") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v - 3.5; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc0.9 -3.7 9.1 -6.4 6 -12.4\n" " 1 5.4 -4.2 8.4 -6 8.4\n", x, y); y -= 5.4; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sfs") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v - 1; if (h > 0) { x += 3.5; y -= 1; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fl7 3.2 0 3.2 -7 -3.2z\n", x, y); y += 5.4; } } else { x -= 3.5; y += 1; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fl7 -3.2 0 -3.2 -7 3.2z\n", x, y); y -= 5.4; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sgu") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + GSTEM_XOFF; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc1 3.2 5.6 2.8 3.2 8\n" " 1.4 -4.8 -2.4 -5.4 -3.2 -5.2\n", x, y); y += 3.5; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sgd") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v - GSTEM_XOFF; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fc1 -3.2 5.6 -2.8 3.2 -8\n" " 1.4 4.8 -2.4 5.4 -3.2 5.2\n", x, y); y -= 3.5; } } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sgs") == 0) { setg(1); h = pop_free_val(); n = pop_free_val(); sym = ps_sym_lookup("x"); x = gcur.xoffs + sym->e->u.v + GSTEM_XOFF; sym = ps_sym_lookup("y"); y = gcur.yoffs - sym->e->u.v; fprintf(fout, "\n" "= 0) { fprintf(fout, "M%.2f %.2fl3 1.5 0 2 -3 -1.5z\n", x, y); y += 3; } fprintf(fout, "\"/>\n"); return; } if (strcmp(op, "sfz") == 0) { xysym(op, D_sfz); s = pop_free_str(); if (s) free(s); return; } if (strcmp(op, "sgno") == 0) { xysym(op, D_sgno); return; } if (strcmp(op, "show") == 0) { show('s'); return; } if (strcmp(op, "showb") == 0) { show('b'); return; } if (strcmp(op, "showc") == 0) { show('c'); return; } if (strcmp(op, "showr") == 0) { show('r'); return; } if (strcmp(op, "showerror") == 0) { xysym(op, D_showerror); return; } if (strcmp(op, "sld") == 0) { xysym(op, D_sld); return; } if (strcmp(op, "snap") == 0) { xysym(op, D_snap); return; } if (strcmp(op, "sphr") == 0) { xysym(op, D_sphr); return; } if (strcmp(op, "spclef") == 0) { xysym(op + 1, D_pclef); // same as 'pclef' return; } if (strcmp(op, "setrgbcolor") == 0) { int rgb; rgb = pop_free_val() * 255; rgb += (int) (pop_free_val() * 255) << 8; rgb += (int) (pop_free_val() * 255) << 16; gcur.rgb = rgb; return; } if (strcmp(op, "stc") == 0) { xysym(op, D_stc); return; } if (strcmp(op, "stroke") == 0) { if (!path) { fprintf(stderr, "svg: 'stroke' with no path\n"); // ps_error = 1; return; } path_end(); fprintf(fout, "\t\" class=\"stroke\"%s/>\n", gcur.dash); return; } if (strcmp(op, "su") == 0 || strcmp(op, "sd") == 0) { stem(op); return; } if (strcmp(op, "stsig") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); s = pop_free_str(); if (!s) { fprintf(stderr, "svg: No string\n"); ps_error = 1; return; } fprintf(fout, "\n" " %s\n" "\n", x, y, s + 1); free(s); return; } if (strcmp(op, "sub") == 0) { x = pop_free_val(); if (!stack || stack->type != VAL) { fprintf(stderr, "svg: Bad value for sub\n"); ps_error = 1; return; } stack->u.v -= x; return; } if (strcmp(op, "sbclef") == 0) { xysym(op, D_sbclef); return; } if (strcmp(op, "scclef") == 0) { xysym(op, D_scclef); return; } if (strcmp(op, "sh0") == 0) { xysym(op, D_sh0); return; } if (strcmp(op, "sh1") == 0) { xysym(op, D_sh1); return; } if (strcmp(op, "sh4") == 0) { n = pop_free_val(); switch (n) { case 1: xysym("sh1", D_sh1); break; case 2: xysym("sh0", D_sh0); break; case 3: xysym("sh513", D_sh513); break; default: xysym("dsh0", D_dsh0); break; } return; } if (strcmp(op, "sh513") == 0) { xysym(op, D_sh513); return; } if (strcmp(op, "srep") == 0) { xysym(op, D_srep); return; } if (strcmp(op, "stclef") == 0) { xysym(op, D_stclef); return; } if (strcmp(op, "stringwidth") == 0) { s = pop_free_str(); if (!s || *s != '(') { fprintf(stderr, "svg stringwidth: No string\n"); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = VAL; e->u.v = strw(s + 1); push(e); e = elt_new(); if (!e) return; e->type = VAL; e->u.v = gcur.font_s; push(e); return; } if (strcmp(op, "svg") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 1; push(e); return; } break; case 'T': if (strcmp(op, "T") == 0) { translate: //fixme:test // setg(1); y = pop_free_val(); x = pop_free_val(); gcur.xoffs += x; gcur.yoffs -= y; gcur.cx -= x; gcur.cy -= y; return; } break; case 't': if (strcmp(op, "tclef") == 0) { xysym(op, D_tclef); return; } if (strcmp(op, "thbar") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val() + 1.5; h = pop_free_val(); fprintf(fout, "\n", x, y, -h); return; } if (strcmp(op, "thumb") == 0) { xysym(op, D_thumb); return; } if (strcmp(op, "translate") == 0) goto translate; if (strcmp(op, "trem") == 0) { setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val() - 4.5; n = pop_free_val(); fprintf(fout, "", fout); return; } if (strcmp(op, "trl") == 0) { xysym(op, D_trl); return; } if (strcmp(op, "true") == 0) { e = elt_new(); if (!e) return; e->type = VAL; e->u.v = 1; push(e); return; } if (strcmp(op, "tsig") == 0) { char *d; setg(1); y = gcur.yoffs - pop_free_val() - 0.5; x = gcur.xoffs + pop_free_val(); d = pop_free_str(); s = pop_free_str(); if (!d || !s) { fprintf(stderr, "svg: No string\n"); if (d) free(d); if (s) free(s); ps_error = 1; return; } fprintf(fout, "\n" " %s\n" " %s\n" "\n", x, y, d + 1, s + 1); free(d); free(s); return; } if (strcmp(op, "tubr") == 0 || strcmp(op, "tubrl") == 0) { float dx, dy; int h; setg(1); y = gcur.yoffs - pop_free_val(); x = gcur.xoffs + pop_free_val(); dy = pop_free_val(); dx = pop_free_val(); if (op[4] == 'l') { h = 3; y -= 3; } else { h = -3; y += 3; } fprintf(fout, "\n", x, y, h, dx, -dy, -h); return; } if (strcmp(op, "turn") == 0) { xysym(op, D_turn); return; } if (strcmp(op, "turnx") == 0) { xysym(op, D_turnx); return; } break; case 'u': if (strcmp(op, "upb") == 0) { xysym(op, D_upb); return; } if (strcmp(op, "umrd") == 0) { xysym(op, D_umrd); return; } break; case 'w': if (strcmp(op, "wedge") == 0) { xysym(op, D_wedge); return; } if (strcmp(op, "wln") == 0) { setg(1); y = pop_free_val(); x = pop_free_val(); w = pop_free_val(); fprintf(fout, "\n", gcur.xoffs + x, gcur.yoffs - y, w); return; } if (strcmp(op, "where") == 0) { s = pop_free_str(); /* symbol */ if (!s || *s != '/') { fprintf(stderr, "svg where: No / bad symbol\n"); if (s) free(s); ps_error = 1; return; } e = elt_new(); if (!e) return; e->type = VAL; sym = ps_sym_lookup(&s[1]); if (!sym) { e->u.v = 0; } else { e->u.v = 1; e2 = elt_new(); /* dictionnary */ if (!e2) return; e2->type = VAL; e2->u.v = 0; push(e2); } free(s); push(e); return; } break; case 'x': if (strcmp(op, "xydef") == 0) { y = pop_free_val(); x = pop_free_val(); setxory("x", x); setxory("y", y); return; } if (strcmp(op, "xymove") == 0) { gcur.cy = pop_free_val(); gcur.cx = pop_free_val(); setxory("x", gcur.cx); setxory("y", gcur.cy); return; } break; } // check if already a SVG definition from %%beginsvg if (defs) { s = strstr(defs, op); if (s && s[-1] == '"' && s[strlen(op)] == '"') { xysym(op, -1); return; } } fprintf(stderr, "svg: Symbol '%s' not defined\n", op); ps_error = 1; } void svg_write(char *buf, int len) { int l; struct elt_s *e, *e2; unsigned char c, *p, *q, *r; if (ps_error) return; p = (unsigned char *) buf; #if 0 if (strncmp((char *) p, "%svg ", 5) == 0) { /* %%beginsvg */ fwrite(p + 5, 1, len - 5, fout); fputs("\n", fout); return; } #endif /* scan the string */ while (--len >= 0) { c = *p++; switch (c) { case ' ': case '\t': case '\n': continue; case '{': case '[': /* treat '[' as '{' */ e = elt_new(); if (!e) return; in_cnt++; e->type = STR; e->u.s = strdup(c == '{' ? "{" : "["); push(e); break; case '}': case ']': in_cnt--; if (in_cnt < 0) { if (c == '}') fprintf(stderr, "svg: '}' without '{'\n"); else fprintf(stderr, "svg: ']' without '['\n"); ps_error = 1; return; } e = elt_new(); if (!e) return; /* create a container with elements in direct order */ e->u.e = NULL; if (c == '}') { e->type = SEQ; c = '{'; } else { e->type = BRK; c = '['; } for (;;) { e2 = stack; stack = stack->next; if (e2->type == STR && (e2->u.s[0] == '[' || e2->u.s[0] == '{')) break; e2->next = e->u.e; e->u.e = e2; } if (e2->u.s[0] != c) { fprintf(stderr, "svg: '%c' found before '%c'\n", e2->u.s[0], c); ps_error = 1; return; } elt_free(e2); push(e); break; case '%': q = p; while (--len >= 0) { c = *p++; if (c == '\n') break; } if ((char *) q != &buf[1] && q[-2] != '\n') break; if (strncmp((char *) q, "A ", 2) == 0) { /* annotation */ char type; int row , col, h; float x, y, w; q += 2; type = *q++; if (type != 'b' && type != 'e') { /* if not beam */ sscanf((char *) q + 1, "%d %d %f %f %f %d", &row, &col, &x, &y, &w, &h); } else { sscanf((char *) q + 1, "%d %d %f %f", &row, &col, &x, &y); w = h = 6; } fprintf(fout, "\n", type, row, col, gcur.xoffs + x, gcur.yoffs - y - h, w, h); break; } if (strncmp((char *) q, " --- title", 10) == 0) { /* title info */ if (strstr((char *) q + 10, "--") < (char *) p) break; // cannmot have '--' in comments setg(1); if (q[10] == 's') { /* subtitle */ q += 14; fprintf(fout, "\n", (int) (p - q - 1), q); break; } q += 11; fprintf(fout, "\n", (int) (p - q -1), q); break; } break; case '(': q = p - 1; l = 1; for (;;) { switch (*p++) { case '\\': p++; l--; continue; default: continue; case ')': break; } break; } len -= p - q - 1; l += p - q - 1; p = q; e = elt_new(); if (!e) return; e->type = STR; r = malloc(l); e->u.s = (char *) r; for (;;) { c = *p++; switch (c) { case '\\': *r++ = *p++; continue; default: *r++ = c; continue; case ')': break; } break; } *r = '\0'; push(e); break; default: q = p - 1; while (--len >= 0) { c = *p++; switch (c) { case '(': case ' ': case '\t': case '\n': case '{': case '}': case '[': case ']': case '%': case '/': break; default: continue; } break; } if (len >= 0) { p--; len++; } if (isdigit((unsigned) *q) || *q == '-' || *q == '.') { int i; float v; e = elt_new(); if (!e) return; e->type = VAL; c = *p; *p = '\0'; if (q[1] == '#') { i = strtol((char *) q + 2, 0, 8); e->u.v = i; } else if (q[2] == '#') { i = strtol((char *) q + 3, 0, 16); e->u.v = i; } else { if (sscanf((char *) q, "%f", &v) != 1) { fprintf(stderr, "svg: Bad numeric value in '%s'\n", buf); v = 0; } e->u.v = v; } *p = c; } else { if (!in_cnt) { if (*q != '/') { /* operator */ c = *p; *p = '\0'; ps_exec((char *) q); if (ps_error) return; *p = c; break; } } else if (strncmp((char *) q, "pdfmark", 7) == 0) { in_cnt--; for (;;) { e = pop(stack->type); if (e->type == STR && (e->u.s[0] == '[' || e->u.s[0] == '{')) break; elt_free(e); } elt_free(e); break; } l = p - q; r = malloc(l + 1); memcpy(r, q, l); r[l] = '\0'; e = elt_new(); if (!e) { free(r); return; } e->type = STR; e->u.s = (char *) r; } push(e); break; } } } int svg_output(FILE *out, const char *fmt, ...) { va_list args; char tmp[128]; va_start(args, fmt); vsnprintf(tmp, sizeof tmp, fmt, args); va_end(args); svg_write(tmp, strlen(tmp)); return 0; } void svg_close(void) { struct elt_s *e, *e2; setg(0); fputs("\n", fout); e = stack; if (e) { stack = NULL; fprintf(stderr, "svg close: stack not empty "); elt_lst_dump(e); fprintf(stderr, "\n"); do { e2 = e->next; elt_free(e); e = e2; } while (e); } }