lorid

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

commit 983d4d0ada2f4230ec7c4d70f4e5766c628e1136
parent 7bb679d15c57fb3a03966df1cee2034567ef2eda
Author: nibo <nibo@relim.de>
Date:   Sun, 20 Oct 2024 17:37:35 +0200

Merge branch 'imagestuff'

Diffstat:
Mchordpro.c | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mchordpro.h | 32++++++++++++++++++++++++++------
Mutil.c | 43+++++++++++++++++++++++++++++++++++++++++++
Mutil.h | 18++++++++++++++++++
4 files changed, 368 insertions(+), 44 deletions(-)

diff --git a/chordpro.c b/chordpro.c @@ -12,14 +12,6 @@ #include "config.h" #include "util.h" -static enum SongFragmentType g_current_ftype = SF_TEXT; -static enum SongFragmentType g_prev_ftype = SF_TEXT; -static struct Config *g_config = NULL; -static const char *g_chordpro_filepath = NULL; -static int *g_transpose_history = NULL; -static int *g_transpose = NULL; -static size_t g_line_number = 1; - static const char *environment_directives[] = { "start_of_chorus", "soc", "end_of_chorus", "eoc", "chorus", "start_of_verse", "sov", "end_of_verse", "eov", @@ -183,9 +175,33 @@ static const char *chord_extensions_minor[] = { NULL }; +static enum SongFragmentType g_current_ftype = SF_TEXT; +static enum SongFragmentType g_prev_ftype = SF_TEXT; +static struct Config *g_config = NULL; +static const char *g_chordpro_filepath = NULL; +static int *g_transpose_history = NULL; +static int *g_transpose = NULL; +static size_t g_line_number = 1; +struct ChoImage **g_image_assets = NULL; +static int g_ia = 0; + #ifdef DEBUG -static char *chord_qualifier_enums[] = { +static const char *alignment_enums[] = { + "A_LEFT", + "A_CENTER", + "A_RIGHT" +}; + +static const char *anchor_enums[] = { + "AN_PAPER", + "AN_PAGE", + "AN_COLUMN", + "AN_LINE", + "AN_FLOAT" +}; + +static const char *chord_qualifier_enums[] = { "CQ_EMPTY", "CQ_MIN", "CQ_MAJ", @@ -193,7 +209,7 @@ static char *chord_qualifier_enums[] = { "CQ_DIM" }; -const char *directive_type_enums[] = { +static const char *directive_type_enums[] = { "DT_EMPTY", "DT_ENVIRONMENT", "DT_METADATA", @@ -206,7 +222,7 @@ const char *directive_type_enums[] = { "DT_CUSTOM" }; -const char *section_type_enums[] = { +static const char *section_type_enums[] = { "ST_EMPTY", "ST_NEWSONG", "ST_CHORUS", @@ -217,13 +233,13 @@ const char *section_type_enums[] = { "ST_CUSTOM" }; -const char *position_enums[] = { +static const char *position_enums[] = { "POS_EMPTY", "POS_START", "POS_END" }; -const char *song_fragment_type_enums[] = { +static const char *song_fragment_type_enums[] = { "SF_EMPTY", "SF_CHORD", "SF_ANNOT", @@ -238,7 +254,7 @@ const char *song_fragment_type_enums[] = { "SF_LABEL" }; -const char *style_property_type_enums[] = { +static const char *style_property_type_enums[] = { "SPT_EMPTY", "SPT_FONT", "SPT_SIZE", @@ -2178,17 +2194,18 @@ static struct ChoImage * cho_image_new(void) { struct ChoImage *image = malloc(sizeof(struct ChoImage)); + image->is_asset = false; image->id = NULL; image->src = NULL; - image->width = EMPTY; - image->height = EMPTY; - image->scale = EMPTY; + image->width = NULL; + image->height = NULL; + image->scale = NULL; image->align = A_CENTER; image->border = EMPTY; - image->spread_space = EMPTY; + image->spread_space = NULL; image->href = NULL; - image->x = EMPTY; - image->y = EMPTY; + image->x = NULL; + image->y = NULL; image->anchor = AN_FLOAT; image->dx = EMPTY; image->dy = EMPTY; @@ -2202,23 +2219,206 @@ cho_image_free(struct ChoImage *image) { free(image->id); free(image->src); + if (image->width) { + free(image->width); + } + if (image->height) { + free(image->height); + } + if (image->scale) { + free(image->scale); + } + if (image->spread_space) { + free(image->spread_space); + } + if (image->x) { + free(image->x); + } + if (image->y) { + free(image->y); + } free(image->href); free(image); } -enum OptionState { - OS_NAME, - OS_VALUE -}; +static struct ChoImage * +cho_image_find_asset(const char *id) +{ + int i; + for (i = 0; i<g_ia; i++) { + if (!strcmp(g_image_assets[i]->id, id)) { + return g_image_assets[i]; + } + } + return NULL; +} + +#ifdef DEBUG +static void +cho_debug_image_print(struct ChoImage *image) +{ + printf("---- BEGIN IMAGE ----\n"); + printf("is_asset: %d\n", image->is_asset); + if (image->id) { + printf("id: %s\n", image->id); + } else { + printf("id: NULL\n"); + } + if (image->src) { + printf("src: %s\n", image->src); + } else { + printf("src: NULL\n"); + } + if (image->width) { + printf("width: %s\n", double_or_percent_to_string(image->width)); + } else { + printf("width: NULL\n"); + } + if (image->height) { + printf("height: %s\n", double_or_percent_to_string(image->height)); + } else { + printf("height: NULL\n"); + } + if (image->scale) { + printf("scale: %s\n", double_or_percent_to_string(image->scale)); + } else { + printf("scale: NULL\n"); + } + printf("align: %s\n", alignment_enums[image->align]); + printf("border: %.1f\n", image->border); + if (image->spread_space) { + printf("spread_space: %s\n", double_or_percent_to_string(image->spread_space)); + } else { + printf("spread_space: NULL\n"); + } + if (image->href) { + printf("href: %s\n", image->href); + } else { + printf("href: NULL\n"); + } + if (image->x) { + printf("x: %s\n", double_or_percent_to_string(image->x)); + } else { + printf("x: NULL\n"); + } + if (image->y) { + printf("y: %s\n", double_or_percent_to_string(image->y)); + } else { + printf("y: NULL\n"); + } + printf("anchor: %s\n", anchor_enums[image->anchor]); + printf("dx: %.1f\n", image->dx); + printf("dy: %.1f\n", image->dy); + printf("w: %.1f\n", image->w); + printf("h: %.1f\n", image->h); + printf("---- END IMAGE ------\n"); +} +#endif /* DEBUG */ + +static bool +cho_image_option_parse(struct ChoImage *image, const char *name, const char *value) +{ + char *endptr; + if (!strcmp(name, "id")) { + image->id = strdup(value); + } else + if (!strcmp(name, "src")) { + image->src = strdup(value); + } else + if (!strcmp(name, "width")) { + image->width = double_or_percent_create(value); + if (!image->width) { + cho_log(LOG_ERR, "Invalid value in option 'width' in image directive."); + return false; + } + } else + if (!strcmp(name, "height")) { + image->height = double_or_percent_create(value); + if (!image->height) { + cho_log(LOG_ERR, "Invalid value in option 'height' in image directive."); + return false; + } + } else + if (!strcmp(name, "scale")) { + image->scale = double_or_percent_create(value); + if (!image->scale) { + cho_log(LOG_ERR, "Invalid value in option 'scale' in image directive."); + return false; + } + } else + if (!strcmp(name, "align")) { + if (!strcmp(value, "left")) { + image->align = A_LEFT; + } else + if (!strcmp(value, "right")) { + image->align = A_RIGHT; + } else + if (!strcmp(value, "center")) { + image->align = A_CENTER; + } else { + cho_log(LOG_ERR, "Invalid value in option 'align' in image directive."); + return false; + } + } else + if (!strcmp(name, "border")) { + image->border = strtod(value, &endptr); + if (value == endptr || errno == ERANGE) { + LOG_DEBUG("strtod failed."); + return false; + } + } else + if (!strcmp(name, "spread")) { + image->spread_space = double_or_percent_create(value); + if (!image->spread_space) { + cho_log(LOG_ERR, "Invalid value in option 'spread' in image directive."); + return false; + } + } else + if (!strcmp(name, "href")) { + image->href = strdup(value); + } else + if (!strcmp(name, "x")) { + image->x = double_or_percent_create(value); + if (!image->x) { + cho_log(LOG_ERR, "Invalid value in option 'x' in image directive."); + return false; + } + } else + if (!strcmp(name, "y")) { + image->y = double_or_percent_create(value); + if (!image->y) { + cho_log(LOG_ERR, "Invalid value in option 'y' in image directive."); + return false; + } + } else + if (!strcmp(name, "anchor")) { + if (!strcmp(value, "paper")) { + image->anchor = AN_PAPER; + } else + if (!strcmp(value, "page")) { + image->anchor = AN_PAGE; + } else + if (!strcmp(value, "column")) { + image->anchor = AN_COLUMN; + } else + if (!strcmp(value, "line")) { + image->anchor = AN_LINE; + } else + if (!strcmp(value, "float")) { + image->anchor = AN_FLOAT; + } else { + cho_log(LOG_ERR, "Invalid value in option 'anchor' in image directive."); + return false; + } + } + return true; +} static struct ChoImage * cho_image_directive_parse(const char *str) { struct ChoImage *image = cho_image_new(); - int i = 0; - while (is_whitespace(str[i])) { - i++; - } + struct ChoImage *asset; char c; enum OptionState state = OS_NAME; enum AttrValueSyntax avs = AVS_NO; @@ -2228,7 +2428,9 @@ cho_image_directive_parse(const char *str) int v = 0; memset(name, 0, sizeof(name)); memset(value, 0, sizeof(value)); - for (; str[i] != 0; i++) { + int option_count = 0; + int i; + for (i = 0; str[i] != 0; i++) { c = str[i]; switch (state) { case OS_NAME: @@ -2243,8 +2445,6 @@ cho_image_directive_parse(const char *str) } if (c == '=') { name[n] = 0; - memset(name, 0, n); - n = 0; state = OS_VALUE; break; } @@ -2282,8 +2482,14 @@ cho_image_directive_parse(const char *str) (avs == AVS_UNQUOTED && (c == ' ' || c == '\t')) ) { value[v] = 0; - printf("value: '%s'\n", value); + if (!cho_image_option_parse(image, name, value)) { + LOG_DEBUG("cho_image_option_check failed."); + return NULL; + } + option_count++; + memset(name, 0, n); memset(value, 0, v); + n = 0; v = 0; avs = AVS_NO; state = OS_NAME; @@ -2296,7 +2502,27 @@ cho_image_directive_parse(const char *str) } if (avs == AVS_UNQUOTED) { value[v] = 0; - printf("value: '%s'\n", value); + if (!cho_image_option_parse(image, name, value)) { + LOG_DEBUG("cho_image_option_check failed."); + return NULL; + } + option_count++; + } + if (image->id) { + if (image->src) { + if (option_count > 2) { + cho_log(LOG_ERR, "Defining an image asset disallows any options other than 'id' and 'src'."); + return NULL; + } + image->is_asset = true; + } else { + asset = cho_image_find_asset(image->id); + if (!asset) { + cho_log(LOG_ERR, "There is no image asset with the id '%s'.", image->id); + return NULL; + } + image->src = strdup(asset->src); + } } return image; } @@ -3280,12 +3506,25 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) te += strlen(trimmed_directive_value); break; case DT_IMAGE: - image = cho_image_directive_parse(directive_value); - if (!image) { - LOG_DEBUG("cho_image_directive_parse failed."); - return NULL; + if (strstr(directive_value, "=")) { + image = cho_image_directive_parse(directive_value); + if (!image) { + LOG_DEBUG("cho_image_directive_parse failed."); + return NULL; + } + } else { + image = cho_image_new(); + image->src = str_remove_leading_whitespace(directive_value); + } + if (image->is_asset) { + g_image_assets = realloc(g_image_assets, (g_ia+1) * sizeof(struct ChoImage *)); + g_image_assets[g_ia] = image; + g_ia++; + } else { + // Add image to ChoSong somehow + cho_debug_image_print(image); + cho_image_free(image); } - cho_image_free(image); break; case DT_PREAMBLE: cho_log(LOG_ERR, "Preamble directive '%s' can't have a value.", directive_name); @@ -3768,6 +4007,10 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs = realloc(songs, (so+1) * sizeof(struct ChoSong *)); songs[so] = NULL; free(g_transpose_history); + for (e = 0; e<g_ia; e++) { + cho_image_free(g_image_assets[e]); + } + free(g_image_assets); bool exist_title = false; for (so = 0; songs[so]; so++) { for (m = 0; songs[so]->metadata[m]; m++) { diff --git a/chordpro.h b/chordpro.h @@ -137,18 +137,24 @@ enum Anchor { AN_FLOAT }; +enum OptionState { + OS_NAME, + OS_VALUE +}; + struct ChoImage { + bool is_asset; char *id; char *src; - double width; - double height; - double scale; + struct DoubleOrPercent *width; + struct DoubleOrPercent *height; + struct DoubleOrPercent *scale; enum Alignment align; double border; - double spread_space; + struct DoubleOrPercent *spread_space; char *href; - double x; - double y; + struct DoubleOrPercent *x; + struct DoubleOrPercent *y; enum Anchor anchor; double dx; double dy; @@ -312,6 +318,20 @@ struct ChoLine { enum BreakType btype; }; +/* struct ChoSongLine { + struct ChoLineItemAbove **text_above; + struct ChoLineItem **lyrics; + enum BreakType btype; +}; + +struct ChoLine { + bool is_song_line; + union { + struct ChoSongLine *s; + struct ChoImage *i; + } u; +}; */ + struct ChoLabel { char *name; struct Style *style; diff --git a/util.c b/util.c @@ -275,3 +275,46 @@ filepath_dirname(const char *path) dirname[i] = 0; return dirname; } + +const char * +double_or_percent_to_string(struct DoubleOrPercent *dop) +{ + static char str[8+1]; + if (dop->is_percent) { + sprintf((char *)&str, "%d%%", dop->u.p); + } else { + if (dop->u.d > 999999) { + sprintf((char *)&str, ">999.999"); + } else { + sprintf((char *)&str, "%.1f", dop->u.d); + } + } + return str; +} + +struct DoubleOrPercent * +double_or_percent_create(const char *str) +{ + struct DoubleOrPercent *dop = malloc(sizeof(struct DoubleOrPercent)); + size_t len = strlen(str); + if (str[len-1] == '%') { + short p = str_parse_as_percent(str); + if (p == -1) { + LOG_DEBUG("str_parse_as_percent failed."); + return NULL; + } + dop->u.p = p; + dop->is_percent = true; + } else { + char *endptr; + double d; + d = strtod(str, &endptr); + if (str == endptr || errno == ERANGE) { + LOG_DEBUG("strtod failed."); + return NULL; + } + dop->u.d = d; + dop->is_percent = false; + } + return dop; +} diff --git a/util.h b/util.h @@ -17,6 +17,22 @@ enum FileType { F_OTHER }; +/* + INFO: Typically I would directly calculate the percent + and store it as a double but I can't do that here because + the percent refers to something in the pdf that will be + generated. At parsing stage there is no information about + the pdf. +*/ + +struct DoubleOrPercent { + bool is_percent; + union { + double d; + short p; + } u; +}; + void util_log(enum LogLevel level, const char *msg, ...); bool str_starts_with(const char *str, const char *part); @@ -32,3 +48,5 @@ char *filepath_add_ending_slash_if_missing(const char *path); char *filepath_basename(const char *path); char *filepath_dirname(const char *path); +const char *double_or_percent_to_string(struct DoubleOrPercent *dop); +struct DoubleOrPercent *double_or_percent_create(const char *str);