lorid

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

commit 6b6cb82f00af07b909daf787245dd4cea3e76d34
parent 6904c2a57c8635c9e773fd3cd11ad8d6a84a7da9
Author: nibo <nibo@relim.de>
Date:   Fri, 28 Jun 2024 18:08:44 +0200

Edit a lot

Diffstat:
MMakefile | 4++--
Mchordpro.c | 338+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mchordpro.h | 27+++++++++++++++++++++------
Aconfig.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.h | 20++++++++++++++++++++
Mlorid.c | 105+++++++++++++++++++++++++++----------------------------------------------------
Mout_pdf.c | 288+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mout_pdf.h | 3++-
Mtodo | 1+
9 files changed, 957 insertions(+), 164 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,4 +1,4 @@ all: - $(CC) -Wall -Wextra -O2 chordpro.c out_pdf.c lorid.c -o lorid -lpdfio + $(CC) -Wall -Wextra -O2 config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml debug: - $(CC) -g -Wall -Wextra chordpro.c out_pdf.c lorid.c -o lorid -lpdfio + $(CC) -g -Wall -Wextra config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml diff --git a/chordpro.c b/chordpro.c @@ -31,6 +31,17 @@ static const char *preamble_directives[] = { "new_song", "ns", NULL }; +static const char *font_directives[] = { + "chordfont", "cf", "chordsize", "cs", "chordcolour", + "chorusfont", "chorussize", "choruscolour", + "gridfont", "gridsize", "gridcolour", + "tabfont", "tabsize", "tabcolour", + "textfont", "tf", "textsize", "ts", "textcolour", + "titlefont", "titlesize", "titlecolour" + /* "footerfont", "footersize", "footercolour", + "tocfont", "tocsize", "toccolour", */ +}; + static const char *the_state(enum State state) { switch (state) { @@ -69,6 +80,8 @@ const char *the_dtype(enum DirectiveType dtype) return "DT_FORMATTING"; case DT_PREAMBLE: return "DT_PREAMBLE"; + case DT_FONT: + return "DT_FONT"; case DT_CUSTOM: return "DT_CUSTOM"; } @@ -78,6 +91,8 @@ const char *the_dtype(enum DirectiveType dtype) const char *the_stype(enum SectionType stype) { switch (stype) { + case ST_EMPTY: + return "ST_EMPTY"; case ST_CHORUS: return "ST_CHORUS"; case ST_VERSE: @@ -108,6 +123,8 @@ const char *the_pos(enum Position pos) const char *the_font_family(enum FontFamily font_family) { switch (font_family) { + case FF_EMPTY: + return "FF_EMPTY"; case FF_NORMAL: return "FF_NORMAL"; case FF_SANS: @@ -123,6 +140,8 @@ const char *the_font_family(enum FontFamily font_family) const char *the_font_style(enum FontStyle style) { switch (style) { + case FS_EMPTY: + return "FS_EMPTY"; case FS_NORMAL: return "FS_NORMAL"; case FS_OBLIQUE: @@ -136,6 +155,8 @@ const char *the_font_style(enum FontStyle style) const char *the_font_weight(enum FontWeight weight) { switch (weight) { + case FW_EMPTY: + return "FW_EMPTY"; case FW_NORMAL: return "FW_NORMAL"; case FW_BOLD: @@ -323,14 +344,88 @@ struct RGBColor *cho_color_parse(const char *str) return color; } +static struct Font *cho_font_new(void) +{ + struct Font *font = malloc(sizeof(struct Font)); + font->name = NULL; + font->family = FF_NORMAL; + font->style = FS_NORMAL; + font->weight = FW_NORMAL; + font->size = DEFAULT_FONT_SIZE; + return font; +} + +static struct Font *cho_font_duplicate(struct Font *font) +{ + struct Font *copy = malloc(sizeof(struct Font)); + if (font->name) + copy->name = strdup(font->name); + else + copy->name = NULL; + copy->family = font->family; + copy->style = font->style; + copy->weight = font->weight; + copy->size = font->size; + return copy; +} + +void cho_font_print(struct Font *font) +{ + printf("---- BEGIN FONT ----\n"); + if (font->name) + printf("font name: %s\n", font->name); + else + printf("font name: NULL\n"); + printf("font family: %s\n", the_font_family(font->family)); + printf("font style: %s\n", the_font_style(font->style)); + printf("font weight: %s\n", the_font_weight(font->weight)); + printf("font size: %f\n", font->size); + printf("---- END FONT ------\n"); +} + +void cho_font_free(struct Font *font) +{ + free(font->name); + free(font); +} + +enum FontStyle cho_font_style_parse(const char *str) +{ + if (strcasecmp(str, "italic") == 0) { + return FS_ITALIC; + } else if (strcasecmp(str, "oblique") == 0) { + return FS_OBLIQUE; + } else if (strcasecmp(str, "normal") == 0) { + return FS_NORMAL; + } else if (strcasecmp(str, "regular") == 0) { + return FS_NORMAL; + } else { + return FS_EMPTY; + } +} + +enum FontWeight cho_font_weight_parse(const char *str) +{ + if (strcasecmp(str, "bold") == 0) { + return FW_BOLD; + } else if (strcasecmp(str, "regular") == 0) { + return FW_NORMAL; + } else if (strcasecmp(str, "normal") == 0) { + return FW_NORMAL; + } else { + return FW_EMPTY; + } +} + struct Style *cho_style_new(void) { struct Style *style = malloc(sizeof(struct Style)); - style->font = NULL; + style->font = cho_font_new(); + /* style->font = NULL; style->font_family = FF_NORMAL; style->font_size = DEFAULT_FONT_SIZE; style->font_style = FS_NORMAL; - style->font_weight = FW_NORMAL; + style->font_weight = FW_NORMAL; */ style->foreground_color = cho_rgbcolor_new(20, 20, 20); style->background_color = cho_rgbcolor_new(255, 255, 255); style->underline_style = LS_NONE; @@ -348,7 +443,8 @@ struct Style *cho_style_new(void) void cho_style_free(struct Style *style) { - free(style->font); + cho_font_free(style->font); + // free(style->font); free(style->foreground_color); free(style->background_color); free(style->underline_color); @@ -362,11 +458,12 @@ void cho_style_free(struct Style *style) struct Style *cho_style_duplicate(struct Style *style) { struct Style *copy = malloc(sizeof(struct Style)); - copy->font = style->font ? strdup(style->font) : NULL; + copy->font = cho_font_duplicate(style->font); + /* copy->font = style->font ? strdup(style->font) : NULL; copy->font_family = style->font_family; copy->font_size = style->font_size; copy->font_style = style->font_style; - copy->font_weight = style->font_weight; + copy->font_weight = style->font_weight; */ copy->foreground_color = cho_rgbcolor_duplicate(style->foreground_color); copy->background_color = cho_rgbcolor_duplicate(style->background_color); copy->underline_style = style->underline_style; @@ -382,11 +479,115 @@ struct Style *cho_style_duplicate(struct Style *style) return copy; } +struct Font *cho_style_font_desc_parse(const char *str) +{ + struct Font *font = cho_font_new(); + float size = -1.0; + char **words = malloc(sizeof(char *)); + int w = 0; + words[w] = NULL; + int i = 0; + int k = 0; + while (str[i] != 0) { + if (str[i] == ' ') { + words[w] = realloc(words[w], (k+1) * sizeof(char)); + words[w][k] = 0; + if (strlen(words[w]) == 0) + free(words[w]); + else + w++; + k = 0; + words = realloc(words, (w+1) * sizeof(char *)); + words[w] = NULL; + } else { + words[w] = realloc(words[w], (k+1) * sizeof(char)); + words[w][k] = str[i]; + k++; + } + i++; + } + words[w] = realloc(words[w], (k+1) * sizeof(char)); + words[w][k] = 0; + w++; + words = realloc(words, (w+1) * sizeof(char *)); + words[w] = NULL; + w = 0; + int stop_at = -1; + while (words[w] != NULL) { + if (strcasecmp(words[w], "italic") == 0) { + font->style = FS_ITALIC; + if (stop_at == -1) + stop_at = w; + } else if (strcasecmp(words[w], "bold") == 0) { + font->weight = FW_BOLD; + if (stop_at == -1) + stop_at = w; + } else if (strcasecmp(words[w], "oblique") == 0) { + font->style = FS_OBLIQUE; + if (stop_at == -1) + stop_at = w; + } else if (strcasecmp(words[w], "regular") == 0) { + font->weight = FW_NORMAL; + if (stop_at == -1) + stop_at = w; + } else if (strcasecmp(words[w], "normal") == 0) { + font->style = FS_NORMAL; + if (stop_at == -1) + stop_at = w; + } else if (strcasecmp(words[w], "sans") == 0) { + font->family = FF_SANS; + if (stop_at == -1) + stop_at = w; + } else if (strcasecmp(words[w], "serif") == 0) { + font->family = FF_SERIF; + if (stop_at == -1) + stop_at = w; + } else if (strcasecmp(words[w], "monospace") == 0) { + font->family = FF_MONOSPACE; + if (stop_at == -1) + stop_at = w; + } else { + size = strtof(words[w], NULL); + if (size == 0.0) + goto SKIP; + font->size = size; + if (stop_at == -1) + stop_at = w; + } + SKIP: + w++; + } + k = 0; + int n = 0; + for (int i=0; i<stop_at; i++) { + while (words[i][k] != 0) { + font->name = realloc(font->name, (n+1) * sizeof(char)); + font->name[n] = words[i][k]; + n++; + k++; + } + font->name = realloc(font->name, (n+1) * sizeof(char)); + font->name[n] = ' '; + n++; + k = 0; + } + n--; + font->name[n] = 0; + w = 0; + while (words[w] != NULL) { + free(words[w]); + w++; + } + free(words); + return font; +} + struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct Style *inherited_style) { size_t value_len, last_char; struct RGBColor *rgb_color; struct Style *style; + struct Font *font; if (inherited_style) style = cho_style_duplicate(inherited_style); else @@ -395,19 +596,27 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St int a = 0; while (attrs[a] != NULL) { if (strcmp(attrs[a]->name, "font_desc") == 0) { - style->font = strdup(attrs[a]->value); + font = cho_style_font_desc_parse(attrs[a]->value); + cho_font_free(style->font); + style->font = font; + /* style->font = strdup(font->name); + style->font_family = font->family; + style->font_style = font->style; + style->font_weight = font->weight; + style->font_size = font->size; + cho_font_free(font); */ } else if ( strcmp(attrs[a]->name, "font_family") == 0 || strcmp(attrs[a]->name, "face") == 0 ) { if (strcmp(attrs[a]->value, "normal") == 0) { - style->font_family = FF_NORMAL; + style->font->family = FF_NORMAL; } else if (strcmp(attrs[a]->value, "sans") == 0) { - style->font_family = FF_SANS; + style->font->family = FF_SANS; } else if (strcmp(attrs[a]->value, "serif") == 0) { - style->font_family = FF_SERIF; + style->font->family = FF_SERIF; } else if (strcmp(attrs[a]->value, "monospace") == 0) { - style->font_family = FF_MONOSPACE; + style->font->family = FF_MONOSPACE; } else { fprintf(stderr, "INFO: Invalid value in attribute 'font_family/face'.\n"); } @@ -419,7 +628,7 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St attrs[a]->value[last_char] = 0; int percentage = atoi(attrs[a]->value); if (percentage != 0 && percentage <= 100) { - style->font_size *= percentage / 100.0; + style->font->size *= percentage / 100.0; } else { fprintf(stderr, "INFO: Invalid percentage in attribute 'size'.\n"); } @@ -429,50 +638,50 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St } else if (isdigit(attrs[a]->value[0]) != 0) { float size = strtof(attrs[a]->value, NULL); if (size != 0.0) - style->font_size = size; + style->font->size = size; else fprintf(stderr, "INFO: Invalid number in attribute 'size'.\n"); } else if (strcmp(attrs[a]->value, "xx-small") == 0) { - style->font_size *= 0.8; - style->font_size *= 0.8; - style->font_size *= 0.8; + style->font->size *= 0.8; + style->font->size *= 0.8; + style->font->size *= 0.8; } else if (strcmp(attrs[a]->value, "x-small") == 0) { - style->font_size *= 0.8; - style->font_size *= 0.8; + style->font->size *= 0.8; + style->font->size *= 0.8; } else if (strcmp(attrs[a]->value, "small") == 0) { - style->font_size *= 0.8; + style->font->size *= 0.8; // } else if (strcmp(attrs[a]->value, "medium") == 0) { } else if (strcmp(attrs[a]->value, "large") == 0) { - style->font_size *= 1.8; + style->font->size *= 1.8; } else if (strcmp(attrs[a]->value, "x-large") == 0) { - style->font_size *= 1.8; - style->font_size *= 1.8; + style->font->size *= 1.8; + style->font->size *= 1.8; } else if (strcmp(attrs[a]->value, "xx-large") == 0) { - style->font_size *= 1.8; - style->font_size *= 1.8; - style->font_size *= 1.8; + style->font->size *= 1.8; + style->font->size *= 1.8; + style->font->size *= 1.8; } else if (strcmp(attrs[a]->value, "larger") == 0) { - style->font_size *= 1.8; + style->font->size *= 1.8; } else if (strcmp(attrs[a]->value, "smaller") == 0) { - style->font_size *= 0.8; + style->font->size *= 0.8; } else { fprintf(stderr, "INFO: Invalid value '%s' for the attribute 'size'.\n", attrs[a]->value); } } else if (strcmp(attrs[a]->name, "style") == 0) { if (strcmp(attrs[a]->value, "normal") == 0) { - style->font_style = FS_NORMAL; + style->font->style = FS_NORMAL; } else if (strcmp(attrs[a]->value, "oblique") == 0) { - style->font_style = FS_OBLIQUE; + style->font->style = FS_OBLIQUE; } else if (strcmp(attrs[a]->value, "italic") == 0) { - style->font_style = FS_ITALIC; + style->font->style = FS_ITALIC; } else { fprintf(stderr, "INFO: Invalid value in attribute 'style'.\n"); } } else if (strcmp(attrs[a]->name, "weight") == 0) { if (strcmp(attrs[a]->value, "normal") == 0) { - style->font_style = FS_NORMAL; - } else if (strcmp(attrs[a]->value, "oblique") == 0) { - style->font_style = FS_OBLIQUE; + style->font->weight = FW_NORMAL; + } else if (strcmp(attrs[a]->value, "bold") == 0) { + style->font->weight = FW_BOLD; } else { fprintf(stderr, "INFO: Invalid value in attribute 'weight'.\n"); } @@ -537,7 +746,7 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St attrs[a]->value[last_char] = 0; int percentage = atoi(&attrs[a]->value[1]); if (percentage != 0 && percentage <= 100) { - style->rise = (style->font_size / 2) * percentage / 100.0; + style->rise = (style->font->size / 2) * percentage / 100.0; } else { fprintf(stderr, "INFO: Invalid percentage in attribute 'rise'.\n"); } @@ -558,7 +767,7 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St int percentage = atoi(attrs[a]->value); if (percentage != 0 && percentage <= 100) { // TODO: Test if that's right - float more = style->font_size / 2.0 * percentage / 100.0; + float more = style->font->size / 2.0 * percentage / 100.0; style->rise += more; } else { fprintf(stderr, "INFO: Invalid percentage in attribute 'rise'.\n"); @@ -598,25 +807,25 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St a++; } } else if (strcmp(tag_name, "b") == 0) { - style->font_weight = FW_BOLD; + style->font->weight = FW_BOLD; } else if (strcmp(tag_name, "big") == 0) { - style->font_size *= 0.8; + style->font->size *= 0.8; } else if (strcmp(tag_name, "i") == 0) { - style->font_style = FS_ITALIC; + style->font->style = FS_ITALIC; } else if (strcmp(tag_name, "s") == 0) { style->strikethrough = true; } else if (strcmp(tag_name, "sub") == 0) { - style->font_size *= 0.8; - style->rise = (style->font_size / 2) * 0.3; + style->font->size *= 0.8; + style->rise = (style->font->size / 2) * 0.3; } else if (strcmp(tag_name, "sup") == 0) { - style->font_size *= 0.8; - float thirty_percent = (style->font_size / 2) * 0.3; + style->font->size *= 0.8; + float thirty_percent = (style->font->size / 2) * 0.3; // TODO: Is that right? style->rise = thirty_percent - thirty_percent * 2; } else if (strcmp(tag_name, "small") == 0) { - style->font_size *= 0.8; + style->font->size *= 0.8; } else if (strcmp(tag_name, "tt") == 0) { - style->font_family = FF_MONOSPACE; + style->font->family = FF_MONOSPACE; } else if (strcmp(tag_name, "u") == 0) { style->underline_style = LS_SINGLE; } else { @@ -630,14 +839,7 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St void cho_style_print(struct Style *style) { printf("---- BEGIN STYLE ----\n"); - if (style->font) - printf("font: %s\n", style->font); - else - printf("font: NULL\n"); - printf("font_family: %s\n", the_font_family(style->font_family)); - printf("font_size: %f\n", style->font_size); - printf("font_style: %s\n", the_font_style(style->font_style)); - printf("font_weight: %s\n", the_font_weight(style->font_weight)); + cho_font_print(style->font); printf("foreground_color: %s\n", the_rgb_color(style->foreground_color)); printf("background_color: %s\n", the_rgb_color(style->background_color)); printf("underline_style: %s\n", the_line_style(style->underline_style)); @@ -792,6 +994,14 @@ struct ChoChord *cho_chord_new(void) return chord; } +int cho_chord_count(struct ChoChord **chords) +{ + int i = 0; + while (chords[i] != NULL) + i++; + return i; +} + struct ChoLine *cho_line_new(void) { struct ChoLine *line = malloc(sizeof(struct ChoLine)); @@ -839,7 +1049,7 @@ int cho_line_item_count(struct ChoLineItem **items) struct ChoSection *cho_section_new(void) { struct ChoSection *section = malloc(sizeof(struct ChoSection)); - section->type = -1; + section->type = ST_EMPTY; section->name = NULL; section->lines = NULL; return section; @@ -996,7 +1206,7 @@ struct ChoDirective *directive_parse(const char *name) while (metadata_directives[i] != NULL) { if (strcmp(metadata_directives[i], name) == 0) { directive->dtype = DT_METADATA; - directive->stype = -1; + directive->stype = ST_EMPTY; directive->position = -1; goto END; } @@ -1010,16 +1220,16 @@ struct ChoDirective *directive_parse(const char *name) ) { directive->style->background_color = cho_rgbcolor_new(228, 228, 228); directive->dtype = DT_FORMATTING; - directive->stype = -1; + directive->stype = ST_EMPTY; directive->position = -1; goto END; } else if ( strcmp(formatting_directives[3], name) == 0 || strcmp(formatting_directives[4], name) == 0 ) { - directive->style->font_style = FS_ITALIC; + directive->style->font->style = FS_ITALIC; directive->dtype = DT_FORMATTING; - directive->stype = -1; + directive->stype = ST_EMPTY; directive->position = -1; goto END; } else if ( @@ -1028,7 +1238,7 @@ struct ChoDirective *directive_parse(const char *name) ) { directive->style->boxed = true; directive->dtype = DT_FORMATTING; - directive->stype = -1; + directive->stype = ST_EMPTY; directive->position = -1; goto END; } @@ -1041,8 +1251,14 @@ struct ChoDirective *directive_parse(const char *name) } i++; } + if ( + strcmp(font_directives[0], name) == 0 || + strcmp(font_directives[1], name) == 0 + ) { + directive->dtype = DT_FONT; + } directive->dtype = DT_CUSTOM; - directive->stype = -1; + directive->stype = ST_EMPTY; directive->position = -1; END: return directive; @@ -1238,6 +1454,9 @@ struct ChoSong **cho_parse(FILE *fp) songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); break; + case DT_FONT: + // Reset diretive value to default + break; case DT_CUSTOM: break; } @@ -1338,6 +1557,9 @@ struct ChoSong **cho_parse(FILE *fp) case DT_PREAMBLE: fprintf(stderr, "INFO: Preamble directive '%s' can't have a value.\n", directive_name); break; + case DT_FONT: + // Set directive value globally + break; case DT_CUSTOM: break; } diff --git a/chordpro.h b/chordpro.h @@ -6,6 +6,7 @@ #define URL_MAX_LEN 2000 enum FontFamily { + FF_EMPTY = -1, FF_NORMAL, FF_SANS, FF_SERIF, @@ -13,16 +14,26 @@ enum FontFamily { }; enum FontStyle { + FS_EMPTY = -1, FS_NORMAL, FS_OBLIQUE, FS_ITALIC }; enum FontWeight { + FW_EMPTY = -1, FW_NORMAL, FW_BOLD }; +struct Font { + char *name; + enum FontFamily family; + enum FontStyle style; + enum FontWeight weight; + double size; +}; + enum LineStyle { LS_SINGLE, LS_DOUBLE, @@ -36,11 +47,7 @@ struct RGBColor { }; struct Style { - char *font; // If NULL use default font. - enum FontFamily font_family; - float font_size; - enum FontStyle font_style; - enum FontWeight font_weight; + struct Font *font; struct RGBColor *foreground_color; struct RGBColor *background_color; enum LineStyle underline_style; @@ -51,7 +58,7 @@ struct Style { struct RGBColor *strikethrough_color; bool boxed; struct RGBColor *boxed_color; - float rise; + double rise; char *href; }; @@ -92,10 +99,12 @@ enum DirectiveType { DT_METADATA, DT_FORMATTING, DT_PREAMBLE, + DT_FONT, DT_CUSTOM }; enum SectionType { + ST_EMPTY = -1, ST_NEWSONG, ST_CHORUS, ST_VERSE, @@ -151,8 +160,14 @@ struct ChoSong { struct ChoSong **cho_parse(FILE *fp); void cho_songs_free(struct ChoSong **song); int cho_line_item_count(struct ChoLineItem **items); +int cho_chord_count(struct ChoChord **chords); +struct Font *cho_style_font_desc_parse(const char *str); +void cho_font_free(struct Font *font); +enum FontStyle cho_font_style_parse(const char *str); +enum FontWeight cho_font_weight_parse(const char *str); /* Debugging */ +void cho_font_print(struct Font *font); void cho_style_print(struct Style *style); const char *the_dtype(enum DirectiveType dtype); const char *the_stype(enum SectionType stype); diff --git a/config.c b/config.c @@ -0,0 +1,335 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <toml.h> +#include "chordpro.h" +#include "config.h" + +struct Config *config_load_default(void) +{ + struct Config *config = malloc(sizeof(struct Config)); + config->title = malloc(sizeof(struct ConfigSection)); + config->title->font_name = strdup("Inter"); + config->title->font_style = FS_NORMAL; + config->title->font_weight = FW_BOLD; + config->title->font_size = 18.0; + config->text = malloc(sizeof(struct ConfigSection)); + config->text->font_name = strdup("Inter"); + config->text->font_style = FS_NORMAL; + config->text->font_weight = FW_NORMAL; + config->text->font_size = 14.0; + config->chord = malloc(sizeof(struct ConfigSection)); + config->chord->font_name = strdup("Inter"); + config->chord->font_style = FS_NORMAL; + config->chord->font_weight = FW_BOLD; + config->chord->font_size = 14.0; + config->comment = malloc(sizeof(struct ConfigSection)); + config->comment->font_name = strdup("Inter"); + config->comment->font_style = FS_NORMAL; + config->comment->font_weight = FW_NORMAL; + config->comment->font_size = 14.0; + config->comment_italic = malloc(sizeof(struct ConfigSection)); + config->comment_italic->font_name = strdup("Inter"); + config->comment_italic->font_style = FS_ITALIC; + config->comment_italic->font_weight = FW_BOLD; + config->comment_italic->font_size = 14.0; + config->comment_box = malloc(sizeof(struct ConfigSection)); + config->comment_box->font_name = strdup("Inter"); + config->comment_box->font_style = FS_NORMAL; + config->comment_box->font_weight = FW_NORMAL; + config->comment_box->font_size = 14.0; + config->tab = malloc(sizeof(struct ConfigSection)); + config->tab->font_name = strdup("Inter"); + config->tab->font_style = FS_NORMAL; + config->tab->font_weight = FW_NORMAL; + config->tab->font_size = 14.0; + config->grid = malloc(sizeof(struct ConfigSection)); + config->grid->font_name = strdup("Inter"); + config->grid->font_style = FS_NORMAL; + config->grid->font_weight = FW_BOLD; + config->grid->font_size = 14.0; + return config; +} + +struct Config *config_load(const char *filepath) +{ + struct Config *config = config_load_default(); + if (filepath == NULL) { + filepath = "~/.config/lorid/config.toml"; + } + FILE *fp = fopen(filepath, "r"); + if (fp == NULL) { + fprintf(stderr, "INFO: Couldn't open config file '%s'. Using default configuration.\n", filepath); + return config; + } + char errbuf[200]; + toml_table_t *table = toml_parse_file(fp, (char *)&errbuf, sizeof(errbuf)); + toml_table_t *fonts = toml_table_table(table, "fonts"); + toml_table_t *section; + toml_value_t name, style, weight, size; + enum FontStyle font_style; + enum FontWeight font_weight; + int unused; + const char *key_name; + for (int i=0; i<toml_table_len(fonts); i++) { + key_name = toml_table_key(fonts, i, &unused); + section = toml_table_table(fonts, key_name); + name = toml_table_string(section, "name"); + style = toml_table_string(section, "style"); + weight = toml_table_string(section, "weight"); + size = toml_table_int(section, "size"); + if (strcmp(key_name, "title") == 0) { + if (name.ok) { + free(config->title->font_name); + config->title->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->title->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->title->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) + config->title->font_size = size.u.i; + } else if (strcmp(key_name, "text") == 0) { + if (name.ok) { + free(config->text->font_name); + config->text->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->text->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->text->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) + config->text->font_size = size.u.i; + } else if (strcmp(key_name, "chord") == 0) { + if (name.ok) { + free(config->chord->font_name); + config->chord->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->chord->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->chord->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) { + config->chord->font_size = size.u.i; + } + } else if (strcmp(key_name, "comment") == 0) { + if (name.ok) { + free(config->comment->font_name); + config->comment->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->comment->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->comment->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) { + config->comment->font_size = size.u.i; + } + } else if (strcmp(key_name, "comment_italic") == 0) { + if (name.ok) { + free(config->comment_italic->font_name); + config->comment_italic->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->comment_italic->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->comment_italic->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) { + config->comment_italic->font_size = size.u.i; + } + } else if (strcmp(key_name, "comment_box") == 0) { + if (name.ok) { + free(config->comment_box->font_name); + config->comment_box->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->comment_box->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->comment_box->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) { + config->comment_box->font_size = size.u.i; + } + } else if (strcmp(key_name, "tab") == 0) { + if (name.ok) { + free(config->tab->font_name); + config->tab->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->tab->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->tab->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) { + config->tab->font_size = size.u.i; + } + } else if (strcmp(key_name, "grid") == 0) { + if (name.ok) { + free(config->grid->font_name); + config->grid->font_name = name.u.s; + } + if (style.ok) { + font_style = cho_font_style_parse(style.u.s); + if (font_style == FS_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font style value.\n"); + return NULL; + } else { + config->grid->font_style = font_style; + } + free(style.u.s); + } + if (weight.ok) { + font_weight = cho_font_weight_parse(weight.u.s); + if (font_weight == FW_EMPTY) { + fprintf(stderr, "INFO: Invalid config title section font weight value.\n"); + return NULL; + } else { + config->grid->font_weight = font_weight; + } + free(weight.u.s); + } + if (size.ok) { + config->grid->font_size = size.u.i; + } + } else { + fprintf(stderr, "INFO: Ignoring [fonts.%s] section in config file.\n", key_name); + if (name.ok) + free(name.u.s); + if (style.ok) + free(style.u.s); + if (weight.ok) + free(weight.u.s); + } + } + toml_free(table); + fclose(fp); + return config; +} + +static void config_section_free(struct ConfigSection *section) +{ + free(section->font_name); + free(section); +} + +void config_free(struct Config *config) +{ + config_section_free(config->title); + config_section_free(config->text); + config_section_free(config->chord); + config_section_free(config->comment); + config_section_free(config->comment_italic); + config_section_free(config->comment_box); + config_section_free(config->tab); + config_section_free(config->grid); + free(config); +} diff --git a/config.h b/config.h @@ -0,0 +1,20 @@ +struct ConfigSection { + char *font_name; + enum FontStyle font_style; + enum FontWeight font_weight; + float font_size; +}; + +struct Config { + struct ConfigSection *title; + struct ConfigSection *text; + struct ConfigSection *chord; + struct ConfigSection *comment; + struct ConfigSection *comment_italic; + struct ConfigSection *comment_box; + struct ConfigSection *tab; + struct ConfigSection *grid; +}; + +struct Config *config_load(const char *filepath); +void config_free(struct Config *config); diff --git a/lorid.c b/lorid.c @@ -6,26 +6,47 @@ #include <getopt.h> #include "chordpro.h" #include "out_pdf.h" - -void print_hex(const char *str) -{ - int i = 0; - while (str[i] != 0) { - printf("%02X ", str[i]); - i++; - } - putchar('\n'); -} +#include "config.h" +#include <fontconfig/fontconfig.h> int main(int argc, char *argv[]) { - if (argc != 2) { - fprintf(stderr, "Provide a file.\n"); + static struct option long_options[] = { + { "print-default-config", no_argument, 0, 'p' }, + { "config", required_argument, 0, 'c' }, + { 0, 0, 0, 0 } + }; + int o, option_index; + char *config_filepath = NULL; + FILE *fp; + while ((o = getopt_long(argc, argv, "pc:", long_options, &option_index)) != -1) { + switch(o) { + case 'p': + printf("--print-default-config\n"); + return 0; + case 'c': + config_filepath = malloc((strlen(optarg)+1) * sizeof(char)); + strcpy(config_filepath, optarg); + break; + } + } + struct Config *config = config_load(config_filepath); + if (config == NULL) { + fprintf(stderr, "config_load failed.\n"); return 1; } - FILE *fp = fopen(argv[1], "r"); - if (fp == NULL) { - fprintf(stderr, "fopen failed.\n"); + free(config_filepath); + config_free(config); + if (argc == optind) { + fp = stdin; + } else if (argc == optind+1) { + fp = fopen(argv[argc-1], "r"); + if (fp == NULL) { + fprintf(stderr, "fopen failed.\n"); + return 1; + } + } else { + fprintf(stderr, "Provide only one file.\n"); return 1; } struct ChoSong **songs = cho_parse(fp); @@ -37,60 +58,6 @@ int main(int argc, char *argv[]) fprintf(stderr, "out_pdf_new failed.\n"); return 1; } - /*int m = 0;*/ - /*printf("---- BEGIN METADATA ----\n");*/ - /*while (song->metadata[m] != NULL) {*/ - /* if (song->metadata[m]->name) {*/ - /* printf("name: %s", song->metadata[m]->name);*/ - /* }*/ - /* if (song->metadata[m]->value) {*/ - /* printf(", value: %s\n", song->metadata[m]->value);*/ - /* } else {*/ - /* printf("\n");*/ - /* }*/ - /* m++;*/ - /*}*/ - /*printf("---- END METADATA ------\n");*/ - /* struct ChoChord **chords; - enum SectionType stype; - struct ChoLineItem *line_item; - int s = 0; - int li = 0; - int c = 0; - int ly = 0; - struct ChoSong *song = songs[0]; - while (song->sections[s] != NULL) { - printf("---- BEGIN SECTION ----\n"); - stype = song->sections[s]->type; - printf("stype: %s\n", the_stype(stype)); - if (song->sections[s]->name) { - printf("name: %s\n", song->sections[s]->name); - } - while (song->sections[s]->lines[li] != NULL) { - printf("---- BEGIN LINE ----\n"); - chords = song->sections[s]->lines[li]->chords; - while (chords[c] != NULL) { - printf("index: %d, chord: %s | ", chords[c]->position, chords[c]->chord); - c++; - } - printf("\n"); - c = 0; - while (song->sections[s]->lines[li]->lyrics[ly] != NULL) { - line_item = song->sections[s]->lines[li]->lyrics[ly]; - if (strlen(line_item->text) > 0) { - printf("text: %s\n", line_item->text); - // cho_style_print(line_item->style); - } - ly++; - } - ly = 0; - li++; - printf("---- END LINE ------\n"); - } - li = 0; - s++; - printf("---- END SECTION ------\n"); - } */ cho_songs_free(songs); fclose(fp); return 0; diff --git a/out_pdf.c b/out_pdf.c @@ -8,7 +8,7 @@ static char current_font[17]; static double current_font_size; -static double current_y = MEDIABOX_HEIGHT - PADDING / 2; +static double current_y = MEDIABOX_HEIGHT - 25.0; static pdfio_obj_t *current_font_obj = NULL; static pdfio_obj_t *inter_regular; static pdfio_obj_t *inter_bold; @@ -35,7 +35,29 @@ static pdfio_obj_t *get_font_obj_by_name(const char *name) return NULL; } -static bool SetTextFont(pdfio_stream_t *stream, const char *name, double size) +char *out_pdf_concat_line_items(struct ChoLineItem **items) +{ + char *line = NULL; + int size = 1; + int i = 0; + int k = 0; + int li = 0; + while (items[i] != NULL) { + size += strlen(items[i]->text); + line = realloc(line, size * sizeof(char)); + while (items[i]->text[k] != 0) { + line[li] = items[i]->text[k]; + li++; + k++; + } + k = 0; + i++; + } + line[li] = 0; + return line; +} + +static bool out_pdf_font_set(pdfio_stream_t *stream, const char *name, double size) { if (!pdfioContentSetTextFont(stream, name, size)) { fprintf(stderr, "pdfioContentSetTextFont failed.\n"); @@ -52,11 +74,190 @@ static bool SetTextFont(pdfio_stream_t *stream, const char *name, double size) return true; } -static bool ShowText(pdfio_stream_t *stream, const char *str, enum Alignment align, bool linebreak) +char *string_trim(const char *text) +{ + char *trimmed_text = NULL; + int begin = 0; + int end = 0; + int len = (int)strlen(text); + for (int i=0; i<len; i++) { + if ( + text[i] == ' ' || + text[i] == '\n' || + text[i] == '\t' || + text[i] == '\r' + ) + begin++; + else + break; + } + for (int i=len-1; i>=0; i--) { + if ( + text[i] == ' '|| + text[i] == '\n' || + text[i] == '\t' || + text[i] == '\r' + ) + end++; + else + break; + } + int k = 0; + for (int i=0; i<len; i++) { + if (i >= begin && i < len - end) { + trimmed_text = realloc(trimmed_text, (k+1) * sizeof(char)); + trimmed_text[k] = text[i]; + k++; + } + } + trimmed_text = realloc(trimmed_text, (k+1) * sizeof(char)); + trimmed_text[k] = 0; + return trimmed_text; +} + +static bool out_pdf_add_if_not_in(struct Font *font, struct Font ***array) +{ + int a = 0; + if (!*array) { + *array = realloc(*array, 2 * sizeof(struct Font *)); + (*array)[0] = font; + (*array)[1] = NULL; + return true; + } else { + while ((*array)[a] != NULL) { + if ( + strcmp((*array)[a]->name, font->name) == 0 && + (*array)[a]->family == font->family && + (*array)[a]->style == font->style && + (*array)[a]->weight == font->weight + ) + return false; + a++; + } + *array = realloc(*array, (a+2) * sizeof(struct Font *)); + (*array)[a] = font; + (*array)[a+1] = NULL; + return true; + } +} + +static void out_pdf_add_fonts(struct Font *font, struct Font ***fonts) +{ + bool added = false; + char part[100]; + char *trimmed = NULL; + int i = 0; + int p = 0; + struct Font *new_font = NULL; + while (font->name[i] != 0) { + if (font->name[i] == ',') { + part[p] = 0; + trimmed = string_trim(part); + new_font = malloc(sizeof(struct Font)); + new_font->name = trimmed; + new_font->family = font->family; + new_font->style = font->style; + new_font->weight = font->weight; + new_font->size = font->size; + added = out_pdf_add_if_not_in(new_font, fonts); + if (!added) { + cho_font_free(new_font); + trimmed = NULL; + } + memset(part, 0, strlen(part)); + p = 0; + } else { + part[p] = font->name[i]; + p++; + } + i++; + } + part[p] = 0; + trimmed = string_trim(part); + new_font = malloc(sizeof(struct Font)); + new_font->name = trimmed; + new_font->family = font->family; + new_font->style = font->style; + new_font->weight = font->weight; + new_font->size = font->size; + added = out_pdf_add_if_not_in(new_font, fonts); + if (!added) + cho_font_free(new_font); +} + +static void out_pdf_font_get_all(struct ChoSong **songs) +{ + struct Font **fonts = NULL; + int so = 0; + int se = 0; + int li = 0; + int ly = 0; + struct Style *style; + while (songs[so] != NULL) { + while (songs[so]->sections[se] != NULL) { + while (songs[so]->sections[se]->lines[li]) { + while (songs[so]->sections[se]->lines[li]->lyrics[ly] != NULL) { + style = songs[so]->sections[se]->lines[li]->lyrics[ly]->style; + if (style->font->name) { + out_pdf_add_fonts(style->font, &fonts); + } + ly++; + } + ly = 0; + li++; + } + li = 0; + se++; + } + se = 0; + so++; + } + int f = 0; + if (fonts) { + while (fonts[f] != NULL) { + // cho_font_print(fonts[f]); + cho_font_free(fonts[f]); + f++; + } + free(fonts); + } +} + +static bool out_pdf_chord_show(pdfio_stream_t *stream, const char *lyrics_line, struct ChoChord *chord, bool linebreak) +{ + char *line = malloc((strlen(lyrics_line)+1) * sizeof(char)); + strcpy(line, lyrics_line); + line[chord->position] = 0; + double width = pdfioContentTextMeasure(inter_regular, line, 16); + free(line); + if (!pdfioContentTextBegin(stream)) { + fprintf(stderr, "pdfioContentTextBegin failed.\n"); + return false; + } + if (!pdfioContentTextMoveTo(stream, PADDING + width, current_y)) { + fprintf(stderr, "pdfioContentTextMoveTo failed.\n"); + return false; + } + if (!pdfioContentTextShow(stream, true, chord->chord)) { + fprintf(stderr, "pdfioContentTextShow failed.\n"); + return false; + } + if (!pdfioContentTextEnd(stream)) { + fprintf(stderr, "pdfioContentTextEnd failed.\n"); + return false; + } + if (linebreak) { + current_y -= current_font_size + 2.0; + } + return true; +} + +static bool out_pdf_text_show(pdfio_stream_t *stream, const char *str, enum Alignment align, bool linebreak) { static double x_continue = PADDING; double x; double width = pdfioContentTextMeasure(current_font_obj, str, current_font_size); + // printf("text '%s' has width: '%f'\n", str, width); if (LINE_LEN < width) return false; switch (align) { @@ -91,12 +292,16 @@ static bool ShowText(pdfio_stream_t *stream, const char *str, enum Alignment ali return false; } if (linebreak) { - current_y -= current_font_size + 5.0; + current_y -= current_font_size + 8.0; x_continue = PADDING; } return true; } +/* void out_pdf_style_set(pdfio_stream_t *stream, struct Style *style) +{ +} */ + static char *out_pdf_filename(const char *old) { char *new = NULL; @@ -142,6 +347,7 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs) pdfio_rect_t crop_box = { 36.0, 36.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; pdfio_file_t *pdf = pdfioFileCreate(pdf_filename, "2.0", &media_box_a4, &crop_box, NULL, NULL); free(pdf_filename); + out_pdf_font_get_all(songs); inter_regular = pdfioFileCreateFontObjFromFile(pdf, "./fonts/Inter-Regular.ttf", true); inter_bold = pdfioFileCreateFontObjFromFile(pdf, "./fonts/Inter-Bold.ttf", true); inter_italic = pdfioFileCreateFontObjFromFile(pdf, "./fonts/Inter-Italic.ttf", true); @@ -174,46 +380,49 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs) return false; } pdfio_stream_t *page1_stream = pdfioFileCreatePage(pdf, page1_dict); - if (!SetTextFont(page1_stream, "Inter-Regular", 14.0)) { - fprintf(stderr, "SetTextFont failed.\n"); + if (!out_pdf_font_set(page1_stream, "Inter-Regular", 14.0)) { + fprintf(stderr, "out_pdf_font_set failed.\n"); return false; } if (!pdfioContentSetFillColorRGB(page1_stream, 0.0, 0.0, 0.0)) { fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n"); return false; } - if (!pdfioContentSetTextCharacterSpacing(page1_stream, -0.2)) { + // Setting character spacing destroys pdfioContentTextMeasure() results + /* if (!pdfioContentSetTextCharacterSpacing(page1_stream, 1.0)) { fprintf(stderr, "pdfioContentSetTextCharacterSpacing failed.\n"); return false; - } + } */ /* if (!pdfioContentSetTextLeading(page1_stream, 1.5)) { fprintf(stderr, "pdfioContentSetTextLeading failed.\n"); return false; } */ - if (!pdfioContentSetLineWidth(page1_stream, LINE_LEN)) { + /* if (!pdfioContentSetLineWidth(page1_stream, LINE_LEN)) { fprintf(stderr, "pdfioContentSetLineWidth failed.\n"); return false; - } + } */ int so = 0; int se = 0; int m = 0; int li = 0; int ly = 0; + int ch = 0; + struct ChoChord **chords; while (songs[so] != NULL) { while (songs[so]->metadata[m] != NULL) { if (strcmp(songs[so]->metadata[m]->name, "title") == 0) { const char *title = songs[so]->metadata[m]->value; pdfioFileSetTitle(pdf, title); - if (!SetTextFont(page1_stream, "Inter-Bold", 18.0)) { - fprintf(stderr, "SetTextFont failed.\n"); + if (!out_pdf_font_set(page1_stream, "Inter-Bold", 18.0)) { + fprintf(stderr, "out_pdf_font_set failed.\n"); return false; } - if (!ShowText(page1_stream, title, CENTER, true)) { - fprintf(stderr, "ShowText failed.\n"); + if (!out_pdf_text_show(page1_stream, title, CENTER, true)) { + fprintf(stderr, "out_pdf_text_show failed.\n"); return false; } - if (!ShowText(page1_stream, "", CENTER, true)) { - fprintf(stderr, "ShowText failed.\n"); + if (!out_pdf_text_show(page1_stream, "", CENTER, true)) { + fprintf(stderr, "out_pdf_text_show failed.\n"); return false; } } @@ -223,33 +432,56 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs) while (songs[so]->sections[se] != NULL) { if (songs[so]->sections[se]->name) { const char *section_name = songs[so]->sections[se]->name; - if (!SetTextFont(page1_stream, "Inter-Italic", 14.0)) { - fprintf(stderr, "SetTextFont failed.\n"); + if (!out_pdf_font_set(page1_stream, "Inter-Italic", 14.0)) { + fprintf(stderr, "out_pdf_font_set failed.\n"); return false; } - if (!ShowText(page1_stream, section_name, LEFT, true)) { - fprintf(stderr, "ShowText failed.\n"); + if (!out_pdf_text_show(page1_stream, section_name, LEFT, true)) { + fprintf(stderr, "out_pdf_text_show failed.\n"); return false; } } - if (!SetTextFont(page1_stream, "Inter-Regular", 16.0)) { - fprintf(stderr, "SetTextFont failed.\n"); - return false; - } while (songs[so]->sections[se]->lines[li] != NULL) { + chords = songs[so]->sections[se]->lines[li]->chords; + int count = cho_chord_count(chords); + if (count > 0) { + if (!out_pdf_font_set(page1_stream, "Inter-Bold", 15.0)) { + fprintf(stderr, "out_pdf_font_set failed.\n"); + return false; + } + char *lyrics_line = out_pdf_concat_line_items(songs[so]->sections[se]->lines[li]->lyrics); + while (ch < count - 1) { + if (!out_pdf_chord_show(page1_stream, lyrics_line, chords[ch], false)) { + fprintf(stderr, "out_pdf_chord_show failed.\n"); + return false; + } + ch++; + } + if (!out_pdf_chord_show(page1_stream, lyrics_line, chords[ch], true)) { + fprintf(stderr, "out_pdf_chord_show failed.\n"); + return false; + } + ch = 0; + free(lyrics_line); + } char *text; int items_count = cho_line_item_count(songs[so]->sections[se]->lines[li]->lyrics); + if (!out_pdf_font_set(page1_stream, "Inter-Regular", 16.0)) { + fprintf(stderr, "out_pdf_font_set failed.\n"); + return false; + } while (ly < items_count - 1) { + // out_pdf_style_set(page1_stream, songs[so]->sections[se]->lines[li]->lyrics[ly]->style); text = songs[so]->sections[se]->lines[li]->lyrics[ly]->text; - if (!ShowText(page1_stream, text, CONTINUE, false)) { - fprintf(stderr, "ShowText failed.\n"); + if (!out_pdf_text_show(page1_stream, text, CONTINUE, false)) { + fprintf(stderr, "out_pdf_text_show failed.\n"); return false; } ly++; } text = songs[so]->sections[se]->lines[li]->lyrics[ly]->text; - if (!ShowText(page1_stream, text, CONTINUE, true)) { - fprintf(stderr, "ShowText failed.\n"); + if (!out_pdf_text_show(page1_stream, text, CONTINUE, true)) { + fprintf(stderr, "out_pdf_text_show failed.\n"); return false; } ly = 0; diff --git a/out_pdf.h b/out_pdf.h @@ -2,7 +2,8 @@ #define MEDIABOX_WIDTH 595.0 #define PAGE_HEIGHT MEDIABOX_HEIGHT - 36.0 #define PAGE_WIDTH MEDIABOX_WIDTH - 36.0 -#define PADDING 50.0 +// #define PADDING 50.0 +#define PADDING 80.0 #define LINE_LEN MEDIABOX_WIDTH - PADDING * 2 enum Alignment { diff --git a/todo b/todo @@ -2,6 +2,7 @@ decide how to implement metadata directives %{blabla} +font, size, colour directives 'image' directive; decide if implement https://chordpro.org/chordpro/directives-image/ chordpro markup