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:
| M | chordpro.c | | | 252 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
| M | chordpro.h | | | 25 | +++++++++++++++++++++++++ |
| M | config.h | | | 24 | ------------------------ |
| M | out_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++) {