/* * Formatting functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2019 Jean-François Moine (http://moinejf.free.fr) * Adapted from abc2ps, Copyright (C) 1996-1998 Michael Methfessel * * 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 "abcm2ps.h" struct FORMAT cfmt; /* current format for output */ char *fontnames[MAXFONTS]; /* list of font names */ static char font_enc[MAXFONTS]; /* font encoding */ static char def_font_enc[MAXFONTS]; /* default font encoding */ static char used_font[MAXFONTS]; /* used fonts */ static float swfac_font[MAXFONTS]; /* width scale */ static int nfontnames; static float staffwidth; /* format table */ static struct format { char *name; void *v; char type; #define FORMAT_I 0 /* int */ #define FORMAT_R 1 /* float */ #define FORMAT_F 2 /* font spec */ #define FORMAT_U 3 /* float with unit */ #define FORMAT_B 4 /* boolean */ #define FORMAT_S 5 /* string */ char subtype; /* special cases - see code */ short lock; } format_tb[] = { {"abc2pscompat", &cfmt.abc2pscompat, FORMAT_B, 3}, {"alignbars", &cfmt.alignbars, FORMAT_I, 0}, {"aligncomposer", &cfmt.aligncomposer, FORMAT_I, 0}, {"annotationfont", &cfmt.font_tb[ANNOTATIONFONT], FORMAT_F, 0}, {"autoclef", &cfmt.autoclef, FORMAT_B, 0}, {"barsperstaff", &cfmt.barsperstaff, FORMAT_I, 0}, {"bgcolor", &cfmt.bgcolor, FORMAT_S, 0}, {"botmargin", &cfmt.botmargin, FORMAT_U, 1}, {"breaklimit", &cfmt.breaklimit, FORMAT_R, 3}, {"breakoneoln", &cfmt.breakoneoln, FORMAT_B, 0}, {"bstemdown", &cfmt.bstemdown, FORMAT_B, 0}, {"cancelkey", &cfmt.cancelkey, FORMAT_B, 0}, {"capo", &cfmt.capo, FORMAT_I, 0}, {"combinevoices", &cfmt.combinevoices, FORMAT_I, 0}, {"composerfont", &cfmt.font_tb[COMPOSERFONT], FORMAT_F, 0}, {"composerspace", &cfmt.composerspace, FORMAT_U, 0}, {"contbarnb", &cfmt.contbarnb, FORMAT_B, 0}, {"continueall", &cfmt.continueall, FORMAT_B, 0}, {"custos", &cfmt.custos, FORMAT_B, 0}, {"dateformat", &cfmt.dateformat, FORMAT_S, 0}, {"dblrepbar", &cfmt.dblrepbar, FORMAT_I, 2}, {"decoerr", &cfmt.decoerr, FORMAT_B, 0}, {"dynalign", &cfmt.dynalign, FORMAT_B, 0}, {"footer", &cfmt.footer, FORMAT_S, 0}, {"footerfont", &cfmt.font_tb[FOOTERFONT], FORMAT_F, 0}, {"flatbeams", &cfmt.flatbeams, FORMAT_B, 0}, {"flatbeamgracing", &cfmt.flatbeamgracing, FORMAT_B, 0}, {"gchordbox", &cfmt.gchordbox, FORMAT_B, 0}, {"gchordfont", &cfmt.font_tb[GCHORDFONT], FORMAT_F, 3}, {"graceslurs", &cfmt.graceslurs, FORMAT_B, 0}, {"graceword", &cfmt.graceword, FORMAT_B, 0}, {"gracespace", &cfmt.gracespace, FORMAT_I, 5}, {"gutter", &cfmt.gutter, FORMAT_U, 0}, {"header", &cfmt.header, FORMAT_S, 0}, {"headerfont", &cfmt.font_tb[HEADERFONT], FORMAT_F, 0}, {"historyfont", &cfmt.font_tb[HISTORYFONT], FORMAT_F, 0}, {"hyphencont", &cfmt.hyphencont, FORMAT_B, 0}, {"indent", &cfmt.indent, FORMAT_U, 0}, {"infofont", &cfmt.font_tb[INFOFONT], FORMAT_F, 0}, {"infoline", &cfmt.infoline, FORMAT_B, 0}, {"infospace", &cfmt.infospace, FORMAT_U, 0}, {"keywarn", &cfmt.keywarn, FORMAT_B, 0}, {"landscape", &cfmt.landscape, FORMAT_B, 0}, {"leftmargin", &cfmt.leftmargin, FORMAT_U, 1}, {"lineskipfac", &cfmt.lineskipfac, FORMAT_R, 0}, {"linewarn", &cfmt.linewarn, FORMAT_B, 0}, {"maxshrink", &cfmt.maxshrink, FORMAT_R, 2}, {"maxstaffsep", &cfmt.maxstaffsep, FORMAT_U, 0}, {"maxsysstaffsep", &cfmt.maxsysstaffsep, FORMAT_U, 0}, {"measurebox", &cfmt.measurebox, FORMAT_B, 0}, {"measurefirst", &cfmt.measurefirst, FORMAT_I, 0}, {"measurefont", &cfmt.font_tb[MEASUREFONT], FORMAT_F, 2}, {"measurenb", &cfmt.measurenb, FORMAT_I, 0}, {"musicfont", &cfmt.musicfont, FORMAT_S, 1}, {"musicspace", &cfmt.musicspace, FORMAT_U, 0}, {"notespacingfactor", &cfmt.notespacingfactor, FORMAT_R, 1}, {"oneperpage", &cfmt.oneperpage, FORMAT_B, 0}, {"pageheight", &cfmt.pageheight, FORMAT_U, 1}, {"pagewidth", &cfmt.pagewidth, FORMAT_U, 1}, {"pagescale", &cfmt.scale, FORMAT_R, 0}, #ifdef HAVE_PANGO {"pango", &cfmt.pango, FORMAT_B, 2}, #endif {"parskipfac", &cfmt.parskipfac, FORMAT_R, 0}, {"partsbox", &cfmt.partsbox, FORMAT_B, 0}, {"partsfont", &cfmt.font_tb[PARTSFONT], FORMAT_F, 1}, {"partsspace", &cfmt.partsspace, FORMAT_U, 0}, {"pdfmark", &cfmt.pdfmark, FORMAT_I, 0}, {"rbdbstop", &cfmt.rbdbstop, FORMAT_B, 0}, {"rbmax", &cfmt.rbmax, FORMAT_I, 0}, {"rbmin", &cfmt.rbmin, FORMAT_I, 0}, {"repeatfont", &cfmt.font_tb[REPEATFONT], FORMAT_F, 0}, {"rightmargin", &cfmt.rightmargin, FORMAT_U, 1}, // {"scale", &cfmt.scale, FORMAT_R, 0}, {"setdefl", &cfmt.setdefl, FORMAT_B, 0}, // {"shifthnote", &cfmt.shiftunison, FORMAT_B, 0}, /*to remove*/ {"shiftunison", &cfmt.shiftunison, FORMAT_I, 0}, {"slurheight", &cfmt.slurheight, FORMAT_R, 0}, {"splittune", &cfmt.splittune, FORMAT_I, 1}, {"squarebreve", &cfmt.squarebreve, FORMAT_B, 0}, {"staffnonote", &cfmt.staffnonote, FORMAT_I, 0}, {"staffsep", &cfmt.staffsep, FORMAT_U, 0}, {"staffwidth", &staffwidth, FORMAT_U, 2}, {"stemheight", &cfmt.stemheight, FORMAT_R, 0}, {"straightflags", &cfmt.straightflags, FORMAT_B, 0}, {"stretchlast", &cfmt.stretchlast, FORMAT_R, 2}, {"stretchstaff", &cfmt.stretchstaff, FORMAT_B, 0}, {"subtitlefont", &cfmt.font_tb[SUBTITLEFONT], FORMAT_F, 0}, {"subtitlespace", &cfmt.subtitlespace, FORMAT_U, 0}, {"sysstaffsep", &cfmt.sysstaffsep, FORMAT_U, 0}, {"tempofont", &cfmt.font_tb[TEMPOFONT], FORMAT_F, 0}, {"textfont", &cfmt.font_tb[TEXTFONT], FORMAT_F, 0}, {"textoption", &cfmt.textoption, FORMAT_I, 4}, {"textspace", &cfmt.textspace, FORMAT_U, 0}, {"tieheight", &cfmt.tieheight, FORMAT_R, 0}, {"titlecaps", &cfmt.titlecaps, FORMAT_B, 0}, {"titlefont", &cfmt.font_tb[TITLEFONT], FORMAT_F, 0}, {"titleformat", &cfmt.titleformat, FORMAT_S, 0}, {"titleleft", &cfmt.titleleft, FORMAT_B, 0}, {"titlespace", &cfmt.titlespace, FORMAT_U, 0}, {"titletrim", &cfmt.titletrim, FORMAT_I, 0}, {"timewarn", &cfmt.timewarn, FORMAT_B, 0}, {"topmargin", &cfmt.topmargin, FORMAT_U, 1}, {"topspace", &cfmt.topspace, FORMAT_U, 0}, {"tuplets", &cfmt.tuplets, FORMAT_I, 3}, {"vocalfont", &cfmt.font_tb[VOCALFONT], FORMAT_F, 0}, {"vocalspace", &cfmt.vocalspace, FORMAT_U, 0}, {"voicefont", &cfmt.font_tb[VOICEFONT], FORMAT_F, 0}, {"wordsfont", &cfmt.font_tb[WORDSFONT], FORMAT_F, 0}, {"wordsspace", &cfmt.wordsspace, FORMAT_U, 0}, {"writefields", &cfmt.fields, FORMAT_B, 1}, {0, 0, 0, 0} /* end of table */ }; static const char helvetica[] = "Helvetica"; static const char times[] = "Times-Roman"; static const char times_bold[] = "Times-Bold"; static const char times_italic[] = "Times-Italic"; static const char sans[] = "sans-serif"; static const char serif[] = "serif"; static const char serif_italic[] = "serif-Italic"; static const char serif_bold[] = "serif-Bold"; /* -- search a font and add it if not yet defined -- */ static int get_font(const char *fname, int encoding) { int fnum; /* get or set the default encoding */ for (fnum = nfontnames; --fnum >= 0; ) if (strcmp(fname, fontnames[fnum]) == 0) { if (encoding < 0) encoding = def_font_enc[fnum]; if (encoding == font_enc[fnum]) return fnum; /* font found */ break; } while (--fnum >= 0) { if (strcmp(fname, fontnames[fnum]) == 0 && encoding == font_enc[fnum]) return fnum; } /* add the font */ if (nfontnames >= MAXFONTS) { error(1, NULL, "Too many fonts"); return 0; } if (epsf <= 1 && !svg) { if (file_initialized > 0) error(1, NULL, "Cannot have a new font when the output file is opened"); if (strchr(fname, ' ') != NULL) { error(1, NULL, "PostScript fonts cannot have names with spaces"); return 0; } } fnum = nfontnames++; fontnames[fnum] = strdup(fname); if (encoding < 0) encoding = 0; font_enc[fnum] = encoding; return fnum; } /* -- set a dynamic font -- */ static int dfont_set(struct FONTSPEC *f) { int i; for (i = FONT_DYN; i < cfmt.ndfont; i++) { if (cfmt.font_tb[i].fnum == f->fnum && cfmt.font_tb[i].size == f->size) return i; } if (i >= FONT_MAX - 1) { error(1, NULL, "Too many dynamic fonts"); return FONT_MAX - 1; } memcpy(&cfmt.font_tb[i], f, sizeof cfmt.font_tb[0]); cfmt.ndfont = i + 1; return i; } /* -- define a font -- */ static void fontspec(struct FONTSPEC *f, const char *name, int encoding, float size) { if (name) f->fnum = get_font(name, encoding); else name = fontnames[f->fnum]; f->size = size; f->swfac = size; if (swfac_font[f->fnum] != 0) { f->swfac *= swfac_font[f->fnum]; } else if (strncmp(name, times, 5) == 0 || strncmp(name, serif, 5) == 0) { if (strcmp(name, times_bold) == 0 || strcmp(name, serif_bold) == 0) f->swfac *= 1.05; } else if (strcmp(name, "Helvetica-Bold") == 0) { f->swfac *= 1.15; } else if (strncmp(name, helvetica, 9) == 0 || strncmp(name, "Palatino", 8) == 0) { f->swfac *= 1.1; } else if (strncmp(name, "Courier", 7) == 0) { f->swfac *= 1.35; } else { f->swfac *= 1.1; /* unknown font */ } if (f == &cfmt.font_tb[GCHORDFONT]) cfmt.gcf = dfont_set(f); else if (f == &cfmt.font_tb[ANNOTATIONFONT]) cfmt.anf = dfont_set(f); else if (f == &cfmt.font_tb[VOCALFONT]) cfmt.vof = dfont_set(f); } /* -- output the font definitions with their encodings -- */ /* This output must occurs after user PostScript definitions because * these ones may change the default behaviour */ void define_fonts(void) { int i; static char *mkfont = "/mkfont{findfont dup length 1 add dict begin\n" " {1 index/FID ne{def}{pop pop}ifelse}forall\n" " CharStrings/double_sharp known not{\n" " /CharStrings CharStrings dup length dict copy def\n" " FontMatrix 0 get 1 eq{\n" " CharStrings/sharp{pop .46 0 setcharwidth .001 dup scale usharp ufill}bind put\n" " CharStrings/flat{pop .46 0 setcharwidth .001 dup scale uflat ufill}bind put\n" " CharStrings/natural{pop .40 0 setcharwidth .001 dup scale unat ufill}bind put\n" " CharStrings/double_sharp{pop .46 0 setcharwidth .001 dup scale udblesharp ufill}bind put\n" " CharStrings/double_flat{pop .50 0 setcharwidth .001 dup scale udbleflat ufill}bind put\n" " }{\n" " CharStrings/sharp{pop 460 0 setcharwidth usharp ufill}bind put\n" " CharStrings/flat{pop 460 0 setcharwidth uflat ufill}bind put\n" " CharStrings/natural{pop 400 0 setcharwidth unat ufill}bind put\n" " CharStrings/double_sharp{pop 460 0 setcharwidth udblesharp ufill}bind put\n" " CharStrings/double_flat{pop 500 0 setcharwidth udbleflat ufill}bind put\n" " }ifelse\n" " }if currentdict definefont pop end}!\n"; fputs(mkfont, fout); make_font_list(); for (i = 0; i < nfontnames; i++) { if (used_font[i]) define_font(fontnames[i], i, font_enc[i]); } } /* -- mark the used fonts -- */ void make_font_list(void) { struct FORMAT *f; int i; f = &cfmt; for (i = FONT_UMAX; i < FONT_DYN; i++) used_font[f->font_tb[i].fnum] = 1; } /* -- set the name of an information header type -- */ /* the argument is * [ ] * this information is kept in the 'I' information */ static void set_infoname(char *p) { struct SYMBOL *s, *prev; if (*p == 'I') return; s = info['I' - 'A']; prev = NULL; while (s) { if (s->text[0] == *p) break; prev = s; s = s->next; } if (p[1] == '\0') { /* if delete */ if (s) { if (!prev) info['I' - 'A'] = s->next; else if ((prev->next = s->next) != 0) prev->next->prev = prev; } return; } if (!s) { s = (struct SYMBOL *) getarena(sizeof *s); memset(s, 0, sizeof *s); if (!prev) info['I' - 'A'] = s; else { prev->next = s; s->prev = prev; } } s->text = (char *) getarena(strlen(p) + 1); strcpy(s->text, p); } /* -- set the default format -- */ /* this function is called only once, at abcm2ps startup time */ void set_format(void) { struct FORMAT *f; f = &cfmt; memset(f, 0, sizeof *f); f->pageheight = PAGEHEIGHT; f->pagewidth = PAGEWIDTH; f->leftmargin = MARGIN; f->rightmargin = MARGIN; f->topmargin = 1.0 CM; f->botmargin = 1.0 CM; f->topspace = 22.0 PT; f->titlespace = 6.0 PT; f->subtitlespace = 3.0 PT; f->composerspace = 6.0 PT; f->musicspace = 6.0 PT; f->partsspace = 8.0 PT; f->staffsep = 46.0 PT; f->sysstaffsep = 34.0 PT; f->maxstaffsep = 2000.0 PT; f->maxsysstaffsep = 2000.0 PT; f->vocalspace = 10 PT; f->textspace = 14 PT; f->scale = 1.0; f->slurheight = 1.0; f->tieheight = 1.0; f->maxshrink = 0.65; f->breaklimit = 0.7; f->stretchlast = 0.25; f->stretchstaff = 1; f->graceslurs = 1; f->hyphencont = 1; f->lineskipfac = 1.1; f->parskipfac = 0.4; f->measurenb = -1; f->measurefirst = 1; f->autoclef = 1; f->breakoneoln = 1; f->cancelkey = 1; f->dblrepbar = (B_COL << 12) + (B_CBRA << 8) + (B_OBRA << 4) + B_COL; f->decoerr = 1; f->dynalign = 1; f->keywarn = 1; f->linewarn = 1; #ifdef HAVE_PANGO if (!svg && epsf <= 1) f->pango = 1; else lock_fmt(&cfmt.pango); /* SVG output does not use pango */ #endif f->rbdbstop = 1; f->rbmax = 4; f->rbmin = 2; f->staffnonote = 1; f->titletrim = 1; f->aligncomposer = A_RIGHT; f->notespacingfactor = 1.414; f->stemheight = STEM; #ifndef WIN32 f->dateformat = strdup("%b %e, %Y %H:%M"); #else f->dateformat = strdup("%b %#d, %Y %H:%M"); #endif f->gracespace = (65 << 16) | (80 << 8) | 120; /* left-inside-right - unit 1/10 pt */ f->textoption = T_LEFT; f->ndfont = FONT_DYN; if (svg || epsf > 2) { // SVG output fontspec(&f->font_tb[ANNOTATIONFONT], sans, 0, 12.0); fontspec(&f->font_tb[COMPOSERFONT], serif_italic, 0, 14.0); fontspec(&f->font_tb[FOOTERFONT], serif, 0, 16.0); fontspec(&f->font_tb[GCHORDFONT], sans, 0, 12.0); fontspec(&f->font_tb[HEADERFONT], serif, 0, 16.0); fontspec(&f->font_tb[HISTORYFONT], serif, 0, 16.0); fontspec(&f->font_tb[INFOFONT], serif_italic, 0, 14.0); /* same as composer by default */ fontspec(&f->font_tb[MEASUREFONT], serif_italic, 0, 14.0); fontspec(&f->font_tb[PARTSFONT], serif, 0, 15.0); fontspec(&f->font_tb[REPEATFONT], serif, 0, 13.0); fontspec(&f->font_tb[SUBTITLEFONT], serif, 0, 16.0); fontspec(&f->font_tb[TEMPOFONT], serif_bold, 0, 15.0); fontspec(&f->font_tb[TEXTFONT], serif, 0, 16.0); fontspec(&f->font_tb[TITLEFONT], serif, 0, 20.0); fontspec(&f->font_tb[VOCALFONT], serif_bold, 0, 13.0); fontspec(&f->font_tb[VOICEFONT], serif_bold, 0, 13.0); fontspec(&f->font_tb[WORDSFONT], serif, 0, 16.0); } else { // PS output fontspec(&f->font_tb[ANNOTATIONFONT], helvetica, 0, 12.0); fontspec(&f->font_tb[COMPOSERFONT], times_italic, 0, 14.0); fontspec(&f->font_tb[FOOTERFONT], times, 0, 16.0); fontspec(&f->font_tb[GCHORDFONT], helvetica, 0, 12.0); fontspec(&f->font_tb[HEADERFONT], times, 0, 16.0); fontspec(&f->font_tb[HISTORYFONT], times, 0, 16.0); fontspec(&f->font_tb[INFOFONT], times_italic, 0, 14.0); /* same as composer by default */ fontspec(&f->font_tb[MEASUREFONT], times_italic, 0, 14.0); fontspec(&f->font_tb[PARTSFONT], times, 0, 15.0); fontspec(&f->font_tb[REPEATFONT], times, 0, 13.0); fontspec(&f->font_tb[SUBTITLEFONT], times, 0, 16.0); fontspec(&f->font_tb[TEMPOFONT], times_bold, 0, 15.0); fontspec(&f->font_tb[TEXTFONT], times, 0, 16.0); fontspec(&f->font_tb[TITLEFONT], times, 0, 20.0); fontspec(&f->font_tb[VOCALFONT], times_bold, 0, 13.0); fontspec(&f->font_tb[VOICEFONT], times_bold, 0, 13.0); fontspec(&f->font_tb[WORDSFONT], times, 0, 16.0); } f->fields[0] = (1 << ('C' - 'A')) | (1 << ('M' - 'A')) | (1 << ('O' - 'A')) | (1 << ('P' - 'A')) | (1 << ('Q' - 'A')) | (1 << ('T' - 'A')) | (1 << ('W' - 'A')); f->fields[1] = (1 << ('w' - 'a')); set_infoname("R \"Rhythm: \""); set_infoname("B \"Book: \""); set_infoname("S \"Source: \""); set_infoname("D \"Discography: \""); set_infoname("N \"Notes: \""); set_infoname("Z \"Transcription: \""); set_infoname("H \"History: \""); } /* -- print the current format -- */ void print_format(void) { struct format *fd; static char *yn[2] = {"no","yes"}; for (fd = format_tb; fd->name; fd++) { printf("%-15s ", fd->name); switch (fd->type) { case FORMAT_B: switch (fd->subtype) { #ifdef HAVE_PANGO case 2: /* pango = 0, 1 or 2 */ if (cfmt.pango == 2) { printf("2\n"); break; } /* fall thru */ #endif default: case 0: printf("%s\n", yn[*((int *) fd->v)]); break; case 1: { /* writefields */ int i; for (i = 0; i < 32; i++) { if (cfmt.fields[0] & (1 << i)) printf("%c", (char) ('A' + i)); if (cfmt.fields[1] & (1 << i)) printf("%c", (char) ('a' + i)); } printf("\n"); break; } } break; case FORMAT_I: switch (fd->subtype) { default: printf("%d\n", *((int *) fd->v)); break; case 2: { /* dblrepbar */ int v; char tmp[16], *p; p = &tmp[sizeof tmp - 1]; *p = '\0'; for (v = cfmt.dblrepbar; v != 0; v >>= 4) { switch (v & 0x0f) { case B_BAR: *--p = '|'; break; case B_OBRA: *--p = '['; break; case B_CBRA: *--p = ']'; break; default: // case B_COL: *--p = ':'; break; } } printf("%s\n", p); break; } case 3: /* tuplets */ printf("%d %d %d %d\n", cfmt.tuplets >> 12, (cfmt.tuplets >> 8) & 0x0f, (cfmt.tuplets >> 4) & 0x0f, cfmt.tuplets & 0x0f); break; // case 4: /* textoption */ // break; case 5: /* gracespace */ printf("%d.%d %d.%d %d.%d\n", (cfmt.gracespace >> 16) / 10, (cfmt.gracespace >> 16) % 10, ((cfmt.gracespace >> 8) & 0xff) / 10, ((cfmt.gracespace >> 8) & 0xff) % 10, (cfmt.gracespace & 0xff) / 10, (cfmt.gracespace & 0xff) % 10); break; } break; case FORMAT_R: printf("%.2f\n", *((float *) fd->v)); break; case FORMAT_F: { struct FONTSPEC *s; s = (struct FONTSPEC *) fd->v; printf("%s", fontnames[s->fnum]); printf(" %s", font_enc[s->fnum] ? "native" : "utf-8"); printf(" %.1f", s->size); if ((fd->subtype == 1 && cfmt.partsbox) || (fd->subtype == 2 && cfmt.measurebox) || (fd->subtype == 3 && cfmt.gchordbox)) printf(" box"); printf("\n"); break; } case FORMAT_U: if (fd->subtype == 0) printf("%.2f\n", *((float *) fd->v)); else if (fd->subtype == 1) printf("%.2fcm\n", *((float *) fd->v) / (1 CM)); else //if (fd->subtype == 2) printf("%.2fcm\n", (cfmt.pagewidth - cfmt.leftmargin - cfmt.rightmargin) / (1 CM)); break; case FORMAT_S: printf("\"%s\"\n", *((char **) fd->v) != 0 ? *((char **) fd->v) : ""); break; } } } /* -- get a number with a unit -- */ /* The type may be * = 0: internal space - convert 'cm' and 'in' to 72 PPI * != 0: page dimensions and margins */ float scan_u(char *str, int type) { float a; int nch; if (sscanf(str, "%f%n", &a, &nch) == 1) { str += nch; if (a == 0) return 0; if (*str == '\0' || *str == ' ') { if (type != 0) error(0, NULL, "No unit \"%s\"", str - nch); return a PT; } if (!strncasecmp(str, "pt", 2)) return a PT; if (!strncasecmp(str, "cm", 2)) return type ? a CM : a * 28.35; if (!strncasecmp(str, "in", 2)) return type ? a IN : a * 72; } error(1, NULL, "Unknown unit value \"%s\"", str); return 20 PT; } /* -- get an encoding -- */ static int parse_encoding(char *p) { return strncasecmp(p, "native", 6) == 0; } /* -- get a position -- */ static int get_posit(char *p) { if (strcmp(p, "up") == 0 || strcmp(p, "above") == 0) return SL_ABOVE; if (strcmp(p, "down") == 0 || strcmp(p, "below") == 0 || strcmp(p, "under") == 0) return SL_BELOW; if (strcmp(p, "hidden") == 0 || strcmp(p, "opposite") == 0) return SL_HIDDEN; if (strcmp(p, "auto") == 0) return 0; /* auto (!= SL_AUTO) */ return -1; } /* -- get the option for text -- */ int get_textopt(char *p) { if (strncmp(p, "align", 5) == 0 || strncmp(p, "justify", 7) == 0) return T_JUSTIFY; if (strncmp(p, "ragged", 6) == 0 || strncmp(p, "fill", 4) == 0) return T_FILL; if (strncmp(p, "center", 6) == 0) return T_CENTER; if (strncmp(p, "skip", 4) == 0) return T_SKIP; if (strncmp(p, "right", 5) == 0) return T_RIGHT; return T_LEFT; } /* -- get the double repeat bar -- */ static int get_dblrepbar(char *p) { int bar_type; bar_type = 0; for (;;) { switch (*p++) { case '|': bar_type <<= 4; bar_type |= B_BAR; continue; case '[': bar_type <<= 4; bar_type |= B_OBRA; continue; case ']': bar_type <<= 4; bar_type |= B_CBRA; continue; case ':': bar_type <<= 4; bar_type |= B_COL; continue; default: break; } break; } return bar_type; } /* -- get a boolean value -- */ int get_bool(char *p) { switch (*p) { case '\0': case '1': case 'y': case 'Y': case 't': case 'T': return 1; case '0': case 'n': case 'N': case 'f': case 'F': break; default: error(0, NULL, "Unknown logical '%s' - false assumed", p); break; } return 0; } /* -- get a font specifier -- */ static void g_fspc(char *p, struct FONTSPEC *f) { char fname[80]; int encoding; float fsize; p = get_str(fname, p, sizeof fname); if (isalpha((unsigned char) *p) || *p == '*') { if (*p == '*') encoding = font_enc[f->fnum]; else encoding = parse_encoding(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } else { encoding = -1; } fsize = f->size; if (*p != '\0' && *p != '*') { char *q; float v; v = strtod(p, &q); if (v <= 0 || (*q != '\0' && *q != ' ')) error(1, NULL, "Bad font size '%s'", p); else fsize = v; } fontspec(f, strcmp(fname, "*") != 0 ? fname : NULL, encoding, fsize); if (file_initialized <= 0) used_font[f->fnum] = 1; if (f - cfmt.font_tb == outft) outft = -1; #ifdef HAVE_PANGO pg_reset_font(); #endif } /* -- parse a 'tablature' definition -- */ /* %%tablature * [#] * [pitch= # | b)>] * [[] * ] * * * * [] */ struct tblt_s *tblt_parse(char *p) { struct tblt_s *tblt; int n; char *q; static const char notes_tb[] = "CDEFGABcdefgab"; static const char pitch_tb[14] = {60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83}; /* number */ if (*p == '#') { p++; n = *p++ - '0' - 1; if ((unsigned) n >= MAXTBLT || (*p != '\0' && *p != ' ')) { error(1, NULL, "Invalid number in %%%%tablature"); return 0; } if (*p == '\0') return tblts[n]; while (isspace((unsigned char) *p)) p++; } else { n = -1; } /* pitch */ tblt = malloc(sizeof *tblt); memset(tblt, 0, sizeof *tblt); if (strncmp(p, "pitch=", 6) == 0) { p += 6; if (*p == '^' || *p == '_') { if (*p == '^') { tblt->pitch++; tblt->instr[1] = '#'; } else { tblt->pitch--; tblt->instr[1] = 'b'; } p++; } if (*p == '\0' || (q = strchr(notes_tb, *p)) == NULL) { error(1, NULL, "Invalid pitch in %%%%tablature"); return 0; } tblt->pitch += pitch_tb[q - notes_tb]; tblt->instr[0] = toupper(*p++); while (*p == '\'' || *p == ',') { if (*p++ == '\'') tblt->pitch += 12; else tblt->pitch -= 12; } if (*p == '#' || *p == 'b') { if (*p == '#') tblt->pitch++; else tblt->pitch--; tblt->instr[1] = *p++; } while (*p == '\'' || *p == ',') { if (*p++ == '\'') tblt->pitch += 12; else tblt->pitch -= 12; } while (isspace((unsigned char) *p)) p++; } /* width and heights */ if (!isdigit(*p)) { error(1, NULL, "Invalid width/height in %%%%tablature"); return 0; } tblt->hu = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; if (isdigit(*p)) { tblt->ha = tblt->hu; tblt->hu = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; if (isdigit(*p)) { tblt->wh = tblt->ha; tblt->ha = tblt->hu; tblt->hu = scan_u(p, 0); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } } if (*p == '\0') goto err; /* PS functions */ p = strdup(p); tblt->head = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; if (*p == '\0') goto err; *p++ = '\0'; while (isspace((unsigned char) *p)) p++; tblt->note = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; if (*p != '\0') { *p++ = '\0'; while (isspace((unsigned char) *p)) p++; tblt->bar = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; if (*p != '\0') goto err; } /* memorize the definition */ if (n >= 0) tblts[n] = tblt; return tblt; err: error(1, NULL, "Wrong values in %%%%tablature"); return 0; } /* functions to set a voice parameter */ #define F_SET_PAR(param) \ static void set_ ## param(struct VOICE_S *p_voice, int val)\ {\ p_voice->posit.param = val;\ } F_SET_PAR(dyn) F_SET_PAR(gch) F_SET_PAR(orn) F_SET_PAR(voc) F_SET_PAR(vol) F_SET_PAR(std) F_SET_PAR(gsd) struct vpar { char *name; void (*f)(struct VOICE_S *p_voice, int val); }; static const struct vpar vpar_tb[] = { {"dynamic", set_dyn}, /* 0 */ {"gchord", set_gch}, /* 1 */ {"gstemdir", set_gsd}, /* 2 */ {"ornament", set_orn}, /* 3 */ {"stemdir", set_std}, /* 4 */ {"vocal", set_voc}, /* 5 */ {"volume", set_vol}, /* 6 */ #ifndef WIN32 {NULL, 0} #else {NULL, NULL, 0} #endif }; /* -- set a voice parameter -- */ void set_voice_param(struct VOICE_S *p_voice, /* current voice */ int state, /* tune state */ char *w, /* keyword */ char *p) /* argument */ { const struct vpar *vpar, *vpar2 = NULL; int i, l, val; l = strlen(w); for (vpar = vpar_tb; vpar->name; vpar++) { if (strncmp(w, vpar->name, l)) continue; // if (!isdigit(*p)) val = get_posit(p); // else // val = strtol(p, NULL, 10); // if ((unsigned) val > vpar->max) // goto err; break; } if (!vpar->name) { /* compatibility with previous versions */ val = -1; switch (*w) { case 'e': if (strcmp(w, "exprabove") == 0) { vpar = &vpar[0]; /* dyn */ vpar2 = &vpar[6]; /* vol */ if (get_bool(p)) val = SL_ABOVE; else val = SL_BELOW; break; } if (strcmp(w, "exprbelow") == 0) { vpar = &vpar[0]; /* dyn */ vpar2 = &vpar[6]; /* vol */ if (get_bool(p)) val = SL_BELOW; else val = SL_ABOVE; break; } break; case 'v': if (strcmp(w, "vocalabove") == 0) { /* compatibility */ vpar = &vpar[5]; /* voc */ if (get_bool(p)) val = SL_ABOVE; else val = SL_BELOW; break; } break; } if (val < 0) goto err; } if (state == ABC_S_TUNE) { vpar->f(p_voice, val); if (vpar2) vpar2->f(p_voice, val); return; } for (i = MAXVOICE, p_voice = voice_tb; /* global */ --i >= 0; p_voice++) { vpar->f(p_voice, val); if (vpar2) vpar2->f(p_voice, val); } cfmt.posit = voice_tb[0].posit; return; err: error(1, NULL, "Bad value %%%%%s %s", w, p); } /* -- parse a format line -- */ void interpret_fmt_line(char *w, /* keyword */ char *p, /* value */ int lock) { struct format *fd; int i; char *q; float f; switch (*w) { case 'b': if (strcmp(w, "barnumbers") == 0) /* compatibility */ w = "measurenb"; break; case 'c': if (strcmp(w, "comball") == 0) { /* compatibility */ cfmt.combinevoices = 2; return; } break; case 'f': if (strcmp(w, "font") == 0) { int fnum, encoding; float swfac; char fname[80]; if (file_initialized > 0 && !svg && epsf <= 1) { /* PS */ error(1, NULL, "Cannot define a font when the output file is opened"); return; } p = get_str(fname, p, sizeof fname); swfac = 0; /* defaults to 1.2 */ encoding = 0; if (*p != '\0') { if (isalpha((unsigned char) *p)) { encoding = parse_encoding(p); while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; } if (isdigit((unsigned char) *p)) { f = strtod(p, &q); if (f > 2 || (*q != '\0' && *q != '\0')) goto bad; swfac = f; } } fnum = get_font(fname, encoding); def_font_enc[fnum] = encoding; swfac_font[fnum] = swfac; used_font[fnum] = 1; for (i = FONT_UMAX; i < FONT_MAX; i++) { if (cfmt.font_tb[i].fnum == fnum) cfmt.font_tb[i].swfac = cfmt.font_tb[i].size * swfac; } return; } break; case 'i': if (strcmp(w, "infoname") == 0) { if (*p < 'A' || *p > 'Z') goto bad; set_infoname(p); return; } break; case 'm': if (strcmp(w, "musiconly") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[1] &= ~(1 << ('w' - 'a')); else cfmt.fields[1] |= (1 << ('w' - 'a')); return; } break; case 'p': if (strcmp(w, "printparts") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[0] |= (1 << ('P' - 'A')); else cfmt.fields[0] &= ~(1 << ('P' - 'A')); return; } if (strcmp(w, "printtempo") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[0] |= (1 << ('Q' - 'A')); else cfmt.fields[0] &= ~(1 << ('Q' - 'A')); return; } break; case 's': if (strncmp(w, "setfont-", 8) == 0) { i = w[8] - '0'; if (i < 0 || i >= FONT_UMAX) return; g_fspc(p, &cfmt.font_tb[i]); return; } if (strcmp(w, "scale") == 0) { for (fd = format_tb; fd->name; fd++) if (strcmp("pagescale", fd->name) == 0) break; if (fd->lock) return; fd->lock = lock; f = strtod(p, &q); if (*q != '\0' && *q != ' ') goto bad; cfmt.scale = f / 0.75; // old -> new scale return; } break; case 'w': if (strcmp(w, "withxrefs") == 0) { /* compatibility */ if (get_bool(p)) cfmt.fields[0] |= (1 << ('X' - 'A')); else cfmt.fields[0] &= ~(1 << ('X' - 'A')); return; } if (strcmp(w, "writehistory") == 0) { /* compatibility */ struct SYMBOL *s; int bool; unsigned u; bool = get_bool(p); for (s = info['I' - 'A']; s != 0; s = s->next) { u = s->text[0] - 'A'; if (bool) cfmt.fields[0] |= (1 << u); else cfmt.fields[0] &= ~(1 << u); } return; } break; } for (fd = format_tb; fd->name; fd++) if (strcmp(w, fd->name) == 0) break; if (!fd->name) return; i = strlen(p); if (strcmp(p + i - 5, " lock") == 0) { p[i - 5] = '\0'; lock = 1; } if (lock) fd->lock = 1; else if (fd->lock) return; switch (fd->type) { case FORMAT_B: switch (fd->subtype) { #ifdef HAVE_PANGO case 2: /* %%pango = 0, 1 or 2 */ if (svg || epsf > 1) // if SVG output break; if (*p == '2') { cfmt.pango = 2; break; } /* fall thru */ #endif default: case 0: case 3: /* %%abc2pscompat */ *((int *) fd->v) = get_bool(p); if (fd->subtype == 3) { if (cfmt.abc2pscompat) deco['M'] = "tenuto"; else deco['M'] = "lowermordent"; } break; case 1: { /* %%writefields */ int bool; unsigned u; q = p; while (*p != '\0' && !isspace((unsigned char) *p)) p++; while (isspace((unsigned char) *p)) p++; bool = get_bool(p); while (*q != '\0' && !isspace((unsigned char) *q)) { u = *q - 'A'; if (u < 26) { i = 0; } else { u = *q - 'a'; if (u < 26) i = 1; else break; /*fixme: error */ } if (bool) cfmt.fields[i] |= (1 << u); else cfmt.fields[i] &= ~(1 << u); q++; } break; } } break; case FORMAT_I: if (fd->subtype == 3) { /* tuplets */ unsigned i1, i2, i3, i4 = 0; if (sscanf(p, "%d %d %d %d", &i1, &i2, &i3, &i4) != 4 && sscanf(p, "%d %d %d", &i1, &i2, &i3) != 3) goto bad; if (i1 > 2 || i2 > 2 || i3 > 2 || i4 > 2) goto bad; cfmt.tuplets = (i1 << 12) | (i2 << 8) | (i3 << 4) | i4; break; } if (fd->subtype == 5) { /* gracespace */ unsigned i1, i2, i3; float f1, f2, f3; if (sscanf(p, "%f %f %f", &f1, &f2, &f3) != 3 || f1 > 25 || f2 > 25 || f3 > 25) goto bad; i1 = f1 * 10; i2 = f2 * 10; i3 = f3 * 10; cfmt.gracespace = (i1 << 16) | (i2 << 8) | i3; break; } if (fd->subtype == 1 /* splittune */ && (strcmp(p, "odd") == 0 || strcmp(p, "even") == 0)) cfmt.splittune = p[0] == 'e' ? 2 : 3; else if (fd->subtype == 2) /* dblrepbar */ cfmt.dblrepbar = get_dblrepbar(p); else if (fd->subtype == 4 && !isdigit(*p)) /* textoption */ cfmt.textoption = get_textopt(p); else if (isdigit(*p) || *p == '-' || *p == '+') sscanf(p, "%d", (int *) fd->v); else *((int *) fd->v) = get_bool(p); if (fd->subtype == 4) { /* textoption */ if (cfmt.textoption < 0) { cfmt.textoption = 0; goto bad; } } break; case FORMAT_R: f = strtod(p, &q); if (*q != '\0' && *q != ' ') goto bad; switch (fd->subtype) { default: if (f <= 0) goto bad; break; case 1: { /* note spacing factor */ float v2; if (f < 1 || f > 2) goto bad; i = C_XFLAGS; /* crotchet index */ v2 = space_tb[i]; for ( ; --i >= 0; ) { v2 /= f; space_tb[i] = v2; } i = C_XFLAGS; v2 = space_tb[i]; for ( ; ++i < NFLAGS_SZ; ) { v2 *= f; space_tb[i] = v2; } break; } case 2: /* maxshrink / stretchlast */ if (f < 0 || f > 1) goto bad; break; case 3: /* breaklimit */ if (f < 0.5 || f > 1) goto bad; break; } *((float *) fd->v) = f; break; case FORMAT_F: { int b; g_fspc(p, (struct FONTSPEC *) fd->v); b = strstr(p, "box") != NULL; switch (fd->subtype) { case 1: cfmt.partsbox = b; break; case 2: cfmt.measurebox = b; break; case 3: cfmt.gchordbox = b; break; } break; } case FORMAT_U: *((float *) fd->v) = scan_u(p, fd->subtype); switch (fd->subtype) { case 1: if (strcmp(fd->name, "rightmargin") != 0 && strcmp(fd->name, "leftmargin") != 0) break; staffwidth = cfmt.pagewidth - cfmt.leftmargin - cfmt.rightmargin; if (staffwidth > 100) break; error(1, NULL, "'staffwidth' too small\n"); staffwidth = 100; if (fd->name[0] == 'r') cfmt.rightmargin = cfmt.pagewidth - cfmt.leftmargin - staffwidth; else cfmt.leftmargin = cfmt.pagewidth - cfmt.rightmargin - staffwidth; break; case 2: /* staffwidth */ if (staffwidth < 100) { error(1, NULL, "'staffwidth' too small\n"); break; } f = (cfmt.landscape ? cfmt.pageheight : cfmt.pagewidth) - staffwidth - cfmt.leftmargin; if (f < 0) error(1, NULL, "'staffwidth' too big\n"); else cfmt.rightmargin = f; break; } break; case FORMAT_S: i = strlen(p) + 1; *((char **) fd->v) = getarena(i); if (*p == '"') get_str(*((char **) fd->v), p, i); else strcpy(*((char **) fd->v), p); if (fd->subtype == 1) { // musicfont // (no control) svg_font_switch(); } break; } return; bad: error(1, NULL, "Bad value '%s' for '%s' - ignored", p, w); } /* -- lock a format -- */ void lock_fmt(void *fmt) { struct format *fd; for (fd = format_tb; fd->name; fd++) if (fd->v == fmt) break; if (fd->name == 0) return; fd->lock = 1; } /* -- start a new font -- */ void set_font(int ft) { int fnum; struct FONTSPEC *f, *f2; if (ft == outft) return; f = &cfmt.font_tb[ft]; if (outft >= 0) { f2 = &cfmt.font_tb[outft]; outft = ft; fnum = f->fnum; if (fnum == f2->fnum && f->size == f2->size) return; } else { outft = ft; fnum = f->fnum; } if (!used_font[fnum] && epsf <= 1 && !svg) { /* (not usefull for svg output) */ if (file_initialized <= 0) { used_font[fnum] = 1; } else { error(1, NULL, "Font '%s' not predefined; using first in list", fontnames[fnum]); fnum = 0; } } if (f->size == 0) { error(0, NULL, "Font '%s' with a null size - set to 8", fontnames[fnum]); f->size = 8; } a2b("%.1f F%d ", f->size, fnum); } /* -- get the encoding of a font -- */ int get_font_encoding(int ft) { return font_enc[cfmt.font_tb[ft].fnum]; }