commit 983d4d0ada2f4230ec7c4d70f4e5766c628e1136
parent 7bb679d15c57fb3a03966df1cee2034567ef2eda
Author: nibo <nibo@relim.de>
Date: Sun, 20 Oct 2024 17:37:35 +0200
Merge branch 'imagestuff'
Diffstat:
| M | chordpro.c | | | 319 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
| M | chordpro.h | | | 32 | ++++++++++++++++++++++++++------ |
| M | util.c | | | 43 | +++++++++++++++++++++++++++++++++++++++++++ |
| M | util.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);