lorid

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

commit a44300eb8e15cea892c5542df86fdf7a4109a7f8
parent 65edd010d82cf93469ea6be773528e7ee4744017
Author: nibo <nibo@relim.de>
Date:   Mon,  2 Dec 2024 19:39:07 +0100

parser: add chord diagrams

Diffstat:
Mchord_diagram.c | 56+++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mchord_diagram.h | 4++--
Mchordpro.c | 220++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mchordpro.h | 15+++++++++++++++
Mutil.c | 16++++++++++++++++
Mutil.h | 1+
6 files changed, 307 insertions(+), 5 deletions(-)

diff --git a/chord_diagram.c b/chord_diagram.c @@ -9,7 +9,7 @@ #include "chord_diagram.h" #include "diagrams.h" -bool +static bool text_show( pdfio_stream_t *stream, const char *text, @@ -503,6 +503,15 @@ string_diagram_draw( return true; } +static struct KeyboardDiagram * +keyboard_diagram_new(void) +{ + struct KeyboardDiagram *d = emalloc(sizeof(struct KeyboardDiagram)); + d->name = NULL; + memset(d->keys, -2, sizeof(d->keys)); + return d; +} + static void keyboard_diagram_free(struct KeyboardDiagram *d) { @@ -510,6 +519,20 @@ keyboard_diagram_free(struct KeyboardDiagram *d) free(d); } +struct ChordDiagram * +chord_diagram_new(bool is_string_instrument) +{ + struct ChordDiagram *d = emalloc(sizeof(struct ChordDiagram)); + if (is_string_instrument) { + d->is_string_instrument = true; + d->u.sd = string_diagram_new(); + } else { + d->is_string_instrument = false; + d->u.kd = keyboard_diagram_new(); + } + return d; +} + void chord_diagram_free(struct ChordDiagram *d) { @@ -591,3 +614,34 @@ chord_diagram_draw( } return true; } + +#ifdef DEBUG +void +debug_chord_diagram_print(struct ChordDiagram *diagram) +{ + int i; + printf("---- CHORD DIAGRAM BEGIN ----\n"); + if (diagram->is_string_instrument) { + printf("name: %s\n", diagram->u.sd->name); + printf("base-fret: %d\n", diagram->u.sd->base_fret); + printf("frets: "); + for (i = 0; i<12; i++) { + printf("%d ", diagram->u.sd->frets[i]); + } + printf("\n"); + printf("fingers: "); + for (i = 0; i<12; i++) { + printf("%d ", diagram->u.sd->fingers[i]); + } + printf("\n"); + } else { + printf("name: %s\n", diagram->u.kd->name); + printf("keys: "); + for (i = 0; i<24; i++) { + printf("%d ", diagram->u.kd->keys[i]); + } + printf("\n"); + } + printf("---- CHORD DIAGRAM END ------\n"); +} +#endif /* DEBUG */ diff --git a/chord_diagram.h b/chord_diagram.h @@ -31,9 +31,9 @@ struct ChordDiagram { } u; }; +struct ChordDiagram *chord_diagram_new(bool is_string_instrument); void chord_diagram_free(struct ChordDiagram *d); void chord_diagrams_free(struct ChordDiagram **diagrams); bool chord_diagram_draw(pdfio_stream_t *stream, struct ChordDiagram *diagram, double x, double y, double width); struct ChordDiagram **chord_diagrams_create(struct Config *config, struct ChoChord ***chords); - -bool text_show( pdfio_stream_t *stream, const char *text, double x, double y); +void debug_chord_diagram_print(struct ChordDiagram *diagram); diff --git a/chordpro.c b/chordpro.c @@ -11,6 +11,7 @@ #include "chordpro.h" #include "config.h" #include "util.h" +#include "chord_diagram.h" static const char *font_families[] = { "normal", "sans", "serif", "monospace", "empty" }; static const char *font_styles[] = { "normal", "oblique", "italic", "empty" }; @@ -2629,6 +2630,218 @@ cho_image_tag_parse(struct Attr **attrs) return image; } +static +int8_t +char_to_positive_int(char c) +{ + if (c > '0' && c <= '9') { + return c - 48; + } + return -1; +} + +static struct ChordDiagram * +cho_chord_diagram_parse(const char *str) +{ + struct ChordDiagram *diagram = NULL; + enum ChordDiagramState state = CDS_NAME; + enum ChordDiagramContent current_content = CDC_EMPTY; + enum ChordDiagramContent future_content = CDC_EMPTY; + char c; + char name[20]; + char option[10]; + char key[3]; + int n = 0; + int o = 0; + int k = 0; + int f = 0; + int i; + int fret_count = 0; + int finger_count = 0; + int8_t number = -2; + long l; + for (i = 0; str[i] != 0; i++) { + c = str[i]; + // printf("c '%c' state '%d'\n", c, state); + switch (state) { + case CDS_NAME: + if (is_whitespace(c)) { + if (n == 0) { + break; + } + name[n] = 0; + state = CDS_OPTION_NAME; + break; + } + if (n > 18) { + cho_log(LOG_ERR, "Chord name in chord diagram is too long."); + return NULL; + } + name[n] = c; + n++; + break; + case CDS_OPTION_NAME: + CDS_OPTION_NAME_LABEL: + if (is_whitespace(c)) { + if (o == 0) { + break; + } + option[o] = 0; + if (!strcmp(option, "base-fret")) { + state = CDS_BASE_FRET; + future_content = CDC_STRING; + } else + if (!strcmp(option, "frets")) { + state = CDS_FRETS; + future_content = CDC_STRING; + } else + if (!strcmp(option, "fingers")) { + state = CDS_FINGERS; + future_content = CDC_STRING; + } else + if (!strcmp(option, "keys")) { + state = CDS_KEYS; + future_content = CDC_KEYBOARD; + } else { + cho_log(LOG_ERR, "Invalid option '%s' in define directive.", option); + return NULL; + } + memset(option, 0, o); + o = 0; + if (current_content == CDC_EMPTY) { + current_content = future_content; + switch (future_content) { + case CDC_STRING: + diagram = chord_diagram_new(true); + diagram->u.sd->name = strdup(name); + break; + case CDC_KEYBOARD: + diagram = chord_diagram_new(false); + diagram->u.kd->name = strdup(name); + break; + } + } + break; + } + if (o > 8) { + cho_log(LOG_ERR, "Option in chord diagram is too long."); + return NULL; + } + option[o] = c; + o++; + break; + case CDS_BASE_FRET: + if (is_whitespace(c)) { + if (diagram->u.sd->base_fret == -2) { + break; + } + state = CDS_OPTION_NAME; + break; + } + number = char_to_positive_int(c); + if (number == -1) { + cho_log(LOG_ERR, "Invalid base-fret value '%c' in chord diagram.", c); + return NULL; + } + diagram->u.sd->base_fret = number; + state = CDS_OPTION_NAME; + break; + case CDS_FRETS: + if (is_whitespace(c)) { + break; + } + if (isalpha(c)) { + f = 0; + state = CDS_OPTION_NAME; + goto CDS_OPTION_NAME_LABEL; + } + number = char_to_positive_int(c); + if (number == -1) { + cho_log(LOG_ERR, "Invalid frets value '%c' in chord diagram.", c); + return NULL; + } + if (f > 11) { + cho_log(LOG_ERR, "Too many fret values in chord diagram."); + return NULL; + } + diagram->u.sd->frets[f] = number; + f++; + fret_count++; + break; + case CDS_FINGERS: + if (is_whitespace(c)) { + break; + } + if (isalpha(c)) { + f = 0; + state = CDS_OPTION_NAME; + goto CDS_OPTION_NAME_LABEL; + } + number = char_to_positive_int(c); + if (number == -1) { + cho_log(LOG_ERR, "Invalid fingers value '%c' in chord diagram.", c); + return NULL; + } + if (f > 11) { + cho_log(LOG_ERR, "Too many finger values in chord diagram."); + return NULL; + } + diagram->u.sd->fingers[f] = number; + f++; + finger_count++; + break; + case CDS_KEYS: + if (is_whitespace(c)) { + if (k == 0) { + break; + } + key[k] = 0; + k = 0; + l = str_to_number(key); + if (l == -1) { + cho_log(LOG_ERR, "Invalid number in keys in chord diagram."); + return NULL; + } + if (f > 23) { + cho_log(LOG_ERR, "Too many key values in chord diagram."); + return NULL; + } + diagram->u.kd->keys[f] = (int8_t)l; + f++; + break; + } + if (k > 1) { + cho_log(LOG_ERR, "Too high key value in chord diagram. '%d'", k); + printf("key '%s'\n", key); + return NULL; + } + key[k] = c; + k++; + break; + } + } + if (current_content == CDC_KEYBOARD) { + key[k] = 0; + if (strlen(key) > 0) { + l = str_to_number(key); + if (l == -1) { + cho_log(LOG_ERR, "Invalid number in keys in chord diagram."); + return NULL; + } + if (f > 23) { + cho_log(LOG_ERR, "Too many key values in chord diagram."); + return NULL; + } + diagram->u.kd->keys[f] = (int8_t)l; + } + } + if (current_content == CDC_STRING && fret_count != finger_count) { + cho_log(LOG_ERR, "The number of frets (%d) and fingers (%d) in the chord diagram must be equal.", fret_count, finger_count); + return NULL; + } + return diagram; +} + static struct ChoText * cho_text_new(void) { @@ -3412,6 +3625,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) struct ChoChord *tmp_chord; struct ChoSection *chorus; struct ChoImage *image; + struct ChordDiagram *diagram; while (feof(fp) == 0) { read = fread(&buf, 1, 1, fp); if (read == 1) { @@ -3716,7 +3930,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) th--; break; case DEFINE: - cho_log(LOG_INFO, "Guys, we have a define here."); + cho_log(LOG_WARN, "Ignoring chord directive '%s' because it has no value.", directive_name); break; } break; @@ -4059,7 +4273,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) th++; break; case DEFINE: - cho_log(LOG_INFO, "Guys, we have a define with a value here."); + diagram = cho_chord_diagram_parse(directive_value); + debug_chord_diagram_print(diagram); + chord_diagram_free(diagram); break; } break; diff --git a/chordpro.h b/chordpro.h @@ -12,6 +12,21 @@ #define URL_MAX_LEN 2000 #define FONT_NAME_MAX 100 +enum ChordDiagramState { + CDS_NAME, + CDS_OPTION_NAME, + CDS_BASE_FRET, + CDS_FRETS, + CDS_FINGERS, + CDS_KEYS +}; + +enum ChordDiagramContent { + CDC_EMPTY, + CDC_STRING, + CDC_KEYBOARD +}; + enum MetadataDirective { TITLE, SUBTITLE diff --git a/util.c b/util.c @@ -7,6 +7,7 @@ #include <sys/stat.h> #include <errno.h> #include <assert.h> +#include <limits.h> #include "util.h" void * @@ -240,6 +241,21 @@ strs_free(char **strs) free(strs); } +long +str_to_number(const char *str) +{ + long n; + char *endptr; + n = strtol(str, &endptr, 10); + if (str == endptr) { + return -1; + } + if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) { + return -1; + } + return n; +} + enum FileType file_type(const char *path) { diff --git a/util.h b/util.h @@ -50,6 +50,7 @@ void strs_free(char **strs); bool strs_has(char **strs, const char *str); void strs_add(char ***strs, const char *str); int str_compare(const char *a, const char *b); +long str_to_number(const char *str); enum FileType file_type(const char *path); char *file_extension_replace_or_add(const char *old, const char *extension);