lorid

convert chordpro to pdf
git clone git://git.relim.de/lorid.git
Log | Files | Refs | README | LICENSE

commit cc98b2e8c9fea3b9f0d62232b80a8fd558e1d3f7
parent 77f95b05cf452789229f9bd82b23a516c9b6dfdc
Author: nibo <nibo@relim.de>
Date:   Sun, 26 Jan 2025 12:59:53 +0100

Support markup in directive values/labels

e.g. {sov: <span foreground=blue>Verse 1</span>}

Diffstat:
Mchordpro.c | 252+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mchordpro.h | 25+++++++++++++++++++++++++
Mconfig.h | 24------------------------
Mout_pdf.c | 8++++++++
4 files changed, 260 insertions(+), 49 deletions(-)

diff --git a/chordpro.c b/chordpro.c @@ -13,6 +13,13 @@ #include "util.h" #include "chord_diagram.h" +static const char *text_types[] = { + "chord", "annotation", "chorus", + "footer", "grid", "tab", "toc", "toc_title", "text", + "title", "subtitle", "label", "comment", + "comment_italic", "comment_box" +}; + static const char *font_families[] = { "normal", "sans", "serif", "monospace" }; static const char *font_styles[] = { "normal", "oblique", "italic" }; static const char *font_weights[] = { "normal", "bold" }; @@ -249,6 +256,30 @@ cho_debug_chord_print(struct ChoChord *chord) printf("---- END CHORD ------\n"); } +static void +presence_print(struct ChoStylePresence *presence) +{ + printf("---- BEGIN PRESENCE ----\n"); + printf("font.name %d\n", presence->font.name); + printf("font.family %d\n", presence->font.family); + printf("font.style %d\n", presence->font.style); + printf("font.weight %d\n", presence->font.weight); + printf("font.size %d\n", presence->font.size); + printf("foreground_color %d\n", presence->foreground_color); + printf("background_color %d\n", presence->background_color); + printf("underline_style %d\n", presence->underline_style); + printf("underline_color %d\n", presence->underline_color); + printf("overline_style %d\n", presence->overline_style); + printf("overline_color %d\n", presence->overline_color); + printf("strikethrough %d\n", presence->strikethrough); + printf("strikethrough_color %d\n", presence->strikethrough_color); + printf("boxed %d\n", presence->boxed); + printf("boxed_color %d\n", presence->boxed_color); + printf("rise %d\n", presence->rise); + printf("href %d\n", presence->href); + printf("---- END PRESENCE ------\n"); +} + #endif /* DEBUG */ void @@ -873,6 +904,74 @@ cho_style_copy(struct ChoStyle *style) return copy; } +static void +cho_style_complement( + struct ChoStyle *old, + struct ChoStyle *new, + struct ChoStylePresence *presence +) +{ + if (presence->font.name) { + free(old->font->name); + old->font->name = strdup(new->font->name); + } + if (presence->font.family) { + old->font->family = new->font->family; + } + if (presence->font.style) { + old->font->style = new->font->style; + } + if (presence->font.weight) { + old->font->weight = new->font->weight; + } + if (presence->font.size) { + old->font->size = new->font->size; + } + if (presence->foreground_color) { + free(old->foreground_color); + old->foreground_color = cho_rgbcolor_copy(new->foreground_color); + } + if (presence->background_color) { + free(old->background_color); + old->background_color = cho_rgbcolor_copy(new->background_color); + } + if (presence->underline_style) { + old->underline_style = new->underline_style; + } + if (presence->underline_color) { + free(old->underline_color); + old->underline_color = cho_rgbcolor_copy(new->underline_color); + } + if (presence->overline_style) { + old->overline_style = new->overline_style; + } + if (presence->overline_color) { + free(old->overline_color); + old->overline_color = cho_rgbcolor_copy(new->overline_color); + } + if (presence->strikethrough) { + old->strikethrough = new->strikethrough; + } + if (presence->strikethrough_color) { + free(old->strikethrough_color); + old->strikethrough_color = cho_rgbcolor_copy(new->strikethrough_color); + } + if (presence->boxed) { + old->boxed = new->boxed; + } + if (presence->boxed_color) { + free(old->boxed_color); + old->boxed_color = cho_rgbcolor_copy(new->boxed_color); + } + if (presence->rise) { + old->rise = new->rise; + } + if (presence->href) { + free(old->href); + old->href = strdup(new->href); + } +} + static struct ChoStyle * cho_style_new_from_config(enum TextType ttype) { @@ -909,7 +1008,7 @@ cho_style_free(struct ChoStyle *style) } static struct Font * -cho_style_font_desc_parse(const char *str) +cho_style_font_desc_parse(const char *str, struct ChoStylePresence *presence) { if (strlen(str) == 0) { return NULL; @@ -948,28 +1047,33 @@ cho_style_font_desc_parse(const char *str) for (w = 0; words[w]; w++) { if (strcasecmp(words[w], "italic") == 0) { font->style = FS_ITALIC; + presence->font.style = true; if (stop_at == EMPTY_INT) { stop_at = w; } } else if (strcasecmp(words[w], "bold") == 0) { font->weight = FW_BOLD; + presence->font.weight = true; if (stop_at == EMPTY_INT) { stop_at = w; } } else if (strcasecmp(words[w], "oblique") == 0) { font->style = FS_OBLIQUE; + presence->font.style = true; if (stop_at == EMPTY_INT) { stop_at = w; } // TODO: Is that smart? } else if (strcasecmp(words[w], "regular") == 0) { font->weight = FW_REGULAR; + presence->font.weight = true; if (stop_at == EMPTY_INT) { stop_at = w; } // TODO: Is that smart? } else if (strcasecmp(words[w], "normal") == 0) { font->style = FS_ROMAN; + presence->font.style = true; if (stop_at == EMPTY_INT) { stop_at = w; } @@ -984,6 +1088,7 @@ cho_style_font_desc_parse(const char *str) stop_at = w; */ } else if (strcasecmp(words[w], "monospace") == 0) { font->family = FF_MONOSPACE; + presence->font.family = true; if (stop_at == EMPTY_INT) { stop_at = w; } @@ -993,6 +1098,7 @@ cho_style_font_desc_parse(const char *str) continue; } font->size = size; + presence->font.size = true; if (stop_at == EMPTY_INT) { stop_at = w; } @@ -1001,18 +1107,21 @@ cho_style_font_desc_parse(const char *str) if (stop_at == EMPTY_INT) { stop_at = w; } - int n = 0; - for (i = 0; i<stop_at; i++) { - for (k = 0; words[i][k]; n++, k++) { + if (stop_at > 0) { + int n = 0; + for (i = 0; i<stop_at; i++) { + for (k = 0; words[i][k]; n++, k++) { + font->name = erealloc(font->name, (n+1) * sizeof(char)); + font->name[n] = words[i][k]; + } font->name = erealloc(font->name, (n+1) * sizeof(char)); - font->name[n] = words[i][k]; + font->name[n] = ' '; + n++; } - font->name = erealloc(font->name, (n+1) * sizeof(char)); - font->name[n] = ' '; - n++; + n--; + font->name[n] = 0; + presence->font.name = true; } - n--; - font->name[n] = 0; for (w = 0; words[w]; w++) { free(words[w]); } @@ -1021,22 +1130,31 @@ cho_style_font_desc_parse(const char *str) } static struct ChoStyle * -cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inherited_style) +cho_style_parse( + const char *tag_name, + struct Attr **attrs, + struct ChoStyle *inherited_style, + struct ChoStylePresence *presence +) { size_t value_len, last_char; struct RGBColor *rgb_color; struct ChoStyle *style; struct Font *font; - if (inherited_style) + if (inherited_style) { style = cho_style_copy(inherited_style); - else + } else { style = cho_style_new_default(); + } if (!strcmp(tag_name, "span")) { int a; for (a = 0; attrs[a]; a++) { if (!strcmp(attrs[a]->name, "font_desc")) { - font = cho_style_font_desc_parse(attrs[a]->value); + font = cho_style_font_desc_parse(attrs[a]->value, presence); if (font) { + if (!font->name) { + font->name = strdup(style->font->name); + } cho_font_free(style->font); style->font = font; } @@ -1046,12 +1164,16 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe ) { if (!strcmp(attrs[a]->value, "normal")) { style->font->family = FF_NORMAL; + presence->font.family = true; } else if (!strcmp(attrs[a]->value, "sans")) { style->font->family = FF_SANS; + presence->font.family = true; } else if (!strcmp(attrs[a]->value, "serif")) { style->font->family = FF_SERIF; + presence->font.family = true; } else if (!strcmp(attrs[a]->value, "monospace")) { style->font->family = FF_MONOSPACE; + presence->font.family = true; } else { cho_log(LOG_ERR, "Invalid value in attribute 'font_family/face'."); return NULL; @@ -1065,6 +1187,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe int percentage = atoi(attrs[a]->value); if (percentage != 0 && percentage <= 100) { style->font->size *= percentage / 100.0; + presence->font.size = true; } else { cho_log(LOG_ERR, "Invalid percentage in attribute 'size'."); return NULL; @@ -1077,6 +1200,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe double size = strtod(attrs[a]->value, NULL); if (size != 0.0) { style->font->size = size; + presence->font.size = true; } else { cho_log(LOG_ERR, "Invalid number in attribute 'size'."); return NULL; @@ -1085,25 +1209,33 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe style->font->size *= 0.8; style->font->size *= 0.8; style->font->size *= 0.8; + presence->font.size = true; } else if (!strcmp(attrs[a]->value, "x-small")) { style->font->size *= 0.8; style->font->size *= 0.8; + presence->font.size = true; } else if (!strcmp(attrs[a]->value, "small")) { style->font->size *= 0.8; + presence->font.size = true; // } else if (!strcmp(attrs[a]->value, "medium")) { } else if (!strcmp(attrs[a]->value, "large")) { style->font->size *= 1.8; + presence->font.size = true; } else if (!strcmp(attrs[a]->value, "x-large")) { style->font->size *= 1.8; style->font->size *= 1.8; + presence->font.size = true; } else if (!strcmp(attrs[a]->value, "xx-large")) { style->font->size *= 1.8; style->font->size *= 1.8; style->font->size *= 1.8; + presence->font.size = true; } else if (!strcmp(attrs[a]->value, "larger")) { style->font->size *= 1.8; + presence->font.size = true; } else if (!strcmp(attrs[a]->value, "smaller")) { style->font->size *= 0.8; + presence->font.size = true; } else { cho_log(LOG_ERR, "Invalid value '%s' for the attribute 'size'.", attrs[a]->value); return NULL; @@ -1111,10 +1243,13 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else if (!strcmp(attrs[a]->name, "style")) { if (!strcmp(attrs[a]->value, "normal")) { style->font->style = FS_ROMAN; + presence->font.style = true; } else if (!strcmp(attrs[a]->value, "oblique")) { style->font->style = FS_OBLIQUE; + presence->font.style = true; } else if (!strcmp(attrs[a]->value, "italic")) { style->font->style = FS_ITALIC; + presence->font.style = true; } else { cho_log(LOG_ERR, "Invalid value in attribute 'style'."); return NULL; @@ -1122,8 +1257,10 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else if (!strcmp(attrs[a]->name, "weight")) { if (!strcmp(attrs[a]->value, "normal")) { style->font->weight = FW_REGULAR; + presence->font.weight = true; } else if (!strcmp(attrs[a]->value, "bold")) { style->font->weight = FW_BOLD; + presence->font.weight = true; } else { cho_log(LOG_ERR, "Invalid value in attribute 'weight'."); return NULL; @@ -1136,6 +1273,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else { free(style->foreground_color); style->foreground_color = rgb_color; + presence->foreground_color = true; } } else if (!strcmp(attrs[a]->name, "background")) { rgb_color = cho_color_parse(attrs[a]->value); @@ -1145,14 +1283,18 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else { free(style->background_color); style->background_color = rgb_color; + presence->background_color = true; } } else if (!strcmp(attrs[a]->name, "underline")) { if (!strcmp(attrs[a]->value, "single")) { style->underline_style = LS_SINGLE; + presence->underline_style = true; } else if (!strcmp(attrs[a]->value, "double")) { style->underline_style = LS_DOUBLE; + presence->underline_style = true; } else if (!strcmp(attrs[a]->value, "none")) { style->underline_style = LS_NONE; + presence->underline_style = true; } else { cho_log(LOG_ERR, "Invalid value in attribute 'underline'."); return NULL; @@ -1165,14 +1307,18 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else { free(style->underline_color); style->underline_color = rgb_color; + presence->underline_color = true; } } else if (!strcmp(attrs[a]->name, "overline")) { if (!strcmp(attrs[a]->value, "single")) { style->overline_style = LS_SINGLE; + presence->overline_style = true; } else if (!strcmp(attrs[a]->value, "double")) { style->overline_style = LS_DOUBLE; + presence->overline_style = true; } else if (!strcmp(attrs[a]->value, "none")) { style->overline_style = LS_NONE; + presence->overline_style = true; } else { cho_log(LOG_ERR, "Invalid value in attribute 'overline'."); return NULL; @@ -1185,6 +1331,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else { free(style->overline_color); style->overline_color = rgb_color; + presence->overline_color = true; } } else if (!strcmp(attrs[a]->name, "rise")) { value_len = strlen(attrs[a]->value); @@ -1196,6 +1343,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe int percentage = atoi(&attrs[a]->value[1]); if (percentage != 0 && percentage <= 100) { style->rise = (style->font->size / 2) * percentage / 100.0; + presence->rise = true; } else { cho_log(LOG_ERR, "Invalid percentage in attribute 'rise'."); return NULL; @@ -1205,6 +1353,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe double rise = strtod(attrs[a]->value, NULL); if (rise != 0.0) { style->rise = rise; + presence->rise = true; } else { cho_log(LOG_ERR, "Invalid number in attribute 'rise'."); return NULL; @@ -1222,6 +1371,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe // TODO: Test if that's right double more = style->font->size / 2.0 * percentage / 100.0; style->rise += more; + presence->rise = true; } else { cho_log(LOG_ERR, "Invalid percentage in attribute 'rise'."); return NULL; @@ -1231,6 +1381,7 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe double rise = strtod(attrs[a]->value, NULL); if (rise != 0.0) { style->rise = rise; + presence->rise = true; } else { cho_log(LOG_ERR, "Invalid number in attribute 'rise'."); return NULL; @@ -1243,8 +1394,10 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else if (!strcmp(attrs[a]->name, "strikethrough")) { if (!strcmp(attrs[a]->value, "true")) { style->strikethrough = true; + presence->strikethrough = true; } else if (!strcmp(attrs[a]->value, "false")) { style->strikethrough = false; + presence->strikethrough = true; } else { cho_log(LOG_ERR, "Invalid value '%s' in attribute 'strikethrough'.", attrs[a]->value); return NULL; @@ -1257,9 +1410,11 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } else { free(style->strikethrough_color); style->strikethrough_color = rgb_color; + presence->strikethrough_color = true; } } else if (!strcmp(attrs[a]->name, "href")) { style->href = strdup(attrs[a]->value); + presence->href = true; } else { cho_log(LOG_ERR, "Invalid attribute '%s'.", attrs[a]->name); return NULL; @@ -1267,24 +1422,35 @@ cho_style_parse(const char *tag_name, struct Attr **attrs, struct ChoStyle *inhe } } else if (!strcmp(tag_name, "b")) { style->font->weight = FW_BOLD; + presence->font.weight = true; } else if (!strcmp(tag_name, "big")) { style->font->size *= 0.8; + presence->font.size = true; } else if (!strcmp(tag_name, "i")) { style->font->style = FS_ITALIC; + presence->font.style = true; } else if (!strcmp(tag_name, "s")) { style->strikethrough = true; + presence->strikethrough = true; } else if (!strcmp(tag_name, "sub")) { style->font->size *= 0.8; style->rise = -style->font->size * 0.3; + presence->font.size = true; + presence->rise = true; } else if (!strcmp(tag_name, "sup")) { style->font->size *= 0.8; style->rise = style->font->size * 0.3; + presence->font.size = true; + presence->rise = true; } else if (!strcmp(tag_name, "small")) { style->font->size *= 0.8; + presence->font.size = true; } else if (!strcmp(tag_name, "tt")) { style->font->family = FF_MONOSPACE; + presence->font.family = true; } else if (!strcmp(tag_name, "u")) { style->underline_style = LS_SINGLE; + presence->underline_style = true; } else { cho_log(LOG_ERR, "Invalid tag name '%s'.", tag_name); cho_style_free(style); @@ -1417,6 +1583,7 @@ cho_tag_new(void) tag->style = NULL; tag->attrs = NULL; tag->is_closed = false; + memset(&tag->style_presence, 0, sizeof(tag->style_presence)); return tag; } @@ -1473,7 +1640,7 @@ cho_metadata_new(void) struct ChoMetadata *meta = emalloc(sizeof(struct ChoMetadata)); meta->name = NULL; meta->value = NULL; - meta->style = NULL; + meta->style = cho_style_new_default(); return meta; } @@ -3654,6 +3821,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) g_config = config; g_chordpro_filepath = chordpro_filepath; bool is_chord_already_initialized = false; + bool directive_has_tag = false; char buf = 0; char prev_buf = '\n'; char directive_name[128]; @@ -4108,6 +4276,10 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se]->label->text = strdup(stripped_directive_value); } songs[so]->sections[se]->label->style = cho_style_new_from_config(TT_LABEL); + if (directive_has_tag) { + cho_style_complement(songs[so]->sections[se]->label->style, tags[ta]->style, &tags[ta]->style_presence); + directive_has_tag = false; + } li = 0; lines = &songs[so]->sections[se]->lines; *lines = emalloc(sizeof(struct ChoLine *)); @@ -4163,9 +4335,13 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) free(songs[so]->sections[se]->label->text); songs[so]->sections[se]->label->text = label; } else { - songs[so]->sections[se]->label = emalloc(sizeof(struct ChoText)); g_current_ttype = TT_LABEL; - songs[so]->sections[se]->label->style = cho_style_new_default(); + songs[so]->sections[se]->label = cho_text_new(); + songs[so]->sections[se]->label->text = label; + } + if (directive_has_tag) { + cho_style_complement(songs[so]->sections[se]->label->style, tags[ta]->style, &tags[ta]->style_presence); + directive_has_tag = false; } se++; songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); @@ -4193,6 +4369,10 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) g_current_ttype = TT_LABEL; (*lines)[li]->items[ly]->u.text->style = cho_style_new_default(); (*lines)[li]->items[ly]->u.text->text = label; + if (directive_has_tag) { + cho_style_complement((*lines)[li]->items[ly]->u.text->style, tags[ta]->style, &tags[ta]->style_presence); + directive_has_tag = false; + } te += strlen(label); } } else { @@ -4216,6 +4396,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) g_current_ttype = TT_LABEL; (*lines)[li]->items[ly]->u.text->style = cho_style_new_default(); (*lines)[li]->items[ly]->u.text->text = label; + if (directive_has_tag) { + cho_style_complement((*lines)[li]->items[ly]->u.text->style, tags[ta]->style, &tags[ta]->style_presence); + } te += strlen(label); } break; @@ -4239,7 +4422,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->metadata[m]->value = metadata_value; cho_style_free(songs[so]->metadata[m]->style); songs[so]->metadata[m]->style = cho_style_copy(directive->style); - m++; songs[so]->present_text_types[TT_TITLE] = true; break; case SUBTITLE: @@ -4249,7 +4431,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->metadata[m]->value = metadata_value; cho_style_free(songs[so]->metadata[m]->style); songs[so]->metadata[m]->style = cho_style_copy(directive->style); - m++; songs[so]->present_text_types[TT_SUBTITLE] = true; break; default: @@ -4261,16 +4442,19 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) } songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); songs[so]->metadata[m] = metadata; - m++; free(metadata_value); } else { songs[so]->metadata = erealloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); songs[so]->metadata[m] = cho_metadata_new(); songs[so]->metadata[m]->name = strdup(directive_name); songs[so]->metadata[m]->value = metadata_value; - m++; } } + if (directive_has_tag) { + cho_style_complement(songs[so]->metadata[m]->style, tags[ta]->style, &tags[ta]->style_presence); + directive_has_tag = false; + } + m++; break; case DT_FORMATTING: if ((*lines)[li]->items[ly]->is_text) { @@ -4289,6 +4473,10 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_style_free((*lines)[li]->items[ly]->u.text->style); (*lines)[li]->items[ly]->u.text->style = cho_style_copy(directive->style); (*lines)[li]->items[ly]->u.text->text = strdup(stripped_directive_value); + if (directive_has_tag) { + cho_style_complement((*lines)[li]->items[ly]->u.text->style, tags[ta]->style, &tags[ta]->style_presence); + directive_has_tag = false; + } te += strlen(stripped_directive_value); songs[so]->present_text_types[directive->ttype] = true; break; @@ -4411,6 +4599,11 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) state = STATE_LYRICS; break; } + if (buf == '<') { + prev_state = state; + state = STATE_MARKUP_TAG; + break; + } if (buf == '{') { cho_log(LOG_ERR, "Can't start a new directive if the previous one is not yet closed."); return NULL; @@ -4420,7 +4613,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) return NULL; } if (dv > 4094) { - cho_log(LOG_ERR, "Directive value can't be longer than 4095 bytes."); + cho_log(LOG_ERR, "Directive value can't be greater than 4095 bytes."); return NULL; } directive_value[dv] = buf; @@ -4529,7 +4722,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) return NULL; } tags[ta]->name = strdup(tag_start); - tag_style = cho_style_parse(tag_start, NULL, cho_tag_style_inherit(tags, ta-1)); + tag_style = cho_style_parse(tag_start, NULL, cho_tag_style_inherit(tags, ta-1), &tags[ta]->style_presence); if (!tag_style) { LOG_DEBUG("cho_style_parse failed."); return NULL; @@ -4559,6 +4752,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_style_free((*lines)[li]->text_above[c]->u.annot->style); (*lines)[li]->text_above[c]->u.annot->style = cho_style_copy(tag_style); break; + case STATE_DIRECTIVE_VALUE: + directive_has_tag = true; + break; default: cho_log(LOG_ERR, "Invalid prev_state '%s'.", state_enums[prev_state]); return NULL; @@ -4669,7 +4865,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); (*lines)[li]->items[ly] = cho_line_item_new(); } else { - tag_style = cho_style_parse(tag_start, tags[ta]->attrs, cho_tag_style_inherit(tags, ta-1)); + tag_style = cho_style_parse(tag_start, tags[ta]->attrs, cho_tag_style_inherit(tags, ta-1), &tags[ta]->style_presence); if (!tag_style) { LOG_DEBUG("cho_style_parse failed."); return NULL; @@ -4688,6 +4884,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_style_free((*lines)[li]->text_above[c]->u.annot->style); (*lines)[li]->text_above[c]->u.annot->style = cho_style_copy(tag_style); break; + case STATE_DIRECTIVE_VALUE: + directive_has_tag = true; + break; default: cho_log(LOG_ERR, "Invalid prev_state '%s'.", state_enums[prev_state]); return NULL; @@ -4759,7 +4958,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); (*lines)[li]->items[ly] = cho_line_item_new(); } else { - tag_style = cho_style_parse(tag_start, tags[ta]->attrs, cho_tag_style_inherit(tags, ta-1)); + tag_style = cho_style_parse(tag_start, tags[ta]->attrs, cho_tag_style_inherit(tags, ta-1), &tags[ta]->style_presence); if (!tag_style) { LOG_DEBUG("cho_style_parse failed."); return NULL; @@ -4778,6 +4977,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_style_free((*lines)[li]->text_above[c]->u.annot->style); (*lines)[li]->text_above[c]->u.annot->style = cho_style_copy(tag_style); break; + case STATE_DIRECTIVE_VALUE: + directive_has_tag = true; + break; default: cho_log(LOG_ERR, "Invalid prev_state '%s'.", state_enums[prev_state]); return NULL; diff --git a/chordpro.h b/chordpro.h @@ -28,6 +28,30 @@ enum Alignment : int8_t { A_RIGHT }; +struct FontPresence { + bool name; + bool family; + bool style; + bool weight; + bool size; +}; + +struct ChoStylePresence { + struct FontPresence font; + bool foreground_color; + bool background_color; + bool underline_style; + bool underline_color; + bool overline_style; + bool overline_color; + bool strikethrough; + bool strikethrough_color; + bool boxed; + bool boxed_color; + bool rise; + bool href; +}; + #include "config.h" @@ -314,6 +338,7 @@ struct StyleProperty { struct Tag { char *name; struct ChoStyle *style; + struct ChoStylePresence style_presence; struct Attr **attrs; bool is_closed; }; diff --git a/config.h b/config.h @@ -36,30 +36,6 @@ enum Instrument : int8_t { INS_UKULELE }; -struct FontPresence { - bool name; - bool family; - bool style; - bool weight; - bool size; -}; - -struct ChoStylePresence { - struct FontPresence font; - bool foreground_color; - bool background_color; - bool underline_style; - bool underline_color; - bool overline_style; - bool overline_color; - bool strikethrough; - bool strikethrough_color; - bool boxed; - bool boxed_color; - bool rise; - bool href; -}; - struct Note { char *note; char *sharp; diff --git a/out_pdf.c b/out_pdf.c @@ -179,6 +179,7 @@ fonts_get_all(struct ChoSong **songs, struct Config *config) struct Font **fonts = NULL; struct Font *font; struct ChoSong **so; + struct ChoMetadata **m; struct ChoSection **se; struct ChoLine **li; struct ChoLineItemAbove **above; @@ -196,6 +197,13 @@ fonts_get_all(struct ChoSong **songs, struct Config *config) } } } + for (m = (*so)->metadata; *m; m++) { + font = cho_font_copy((*m)->style->font); + added = fonts_add_if_not_in(&fonts, font); + if (!added) { + cho_font_free(font); + } + } for (se = (*so)->sections; *se; se++) { for (li = (*se)->lines; *li; li++) { for (above = (*li)->text_above; *above; above++) {