lorid

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

commit 84566db79a88cda5832ace7703ea6f4982e9cff5
parent be6c03f356990e4a791128d878f76bf7c3d1eafd
Author: nibo <nibo@relim.de>
Date:   Thu, 25 Jul 2024 18:02:25 +0200

First results with new out_pdf 'engine'

Diffstat:
MMakefile | 4++--
Mchordpro.c | 546+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mchordpro.h | 54+++++++++++++++++++++++++++++++++++++++++++++++++++---
Mconfig.c | 775+++++++++++++++++++++++++++++++------------------------------------------------
Mconfig.h | 22++++++++++++----------
Mfontconfig.c | 34+++++++++++++++++-----------------
Mlorid.c | 10+++++++++-
Mout_pdf.c | 638+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mout_pdf.h | 21+++++++++++++++++++++
Mtodo | 4++++
10 files changed, 1401 insertions(+), 707 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ all: - $(CC) -Wall -Wextra -O2 fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig + $(CC) -pedantic -Wall -Wextra -O2 fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig debug: - $(CC) -g -Wall -Wextra fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -I/usr/include/freetype2 -lpdfio -ltoml -lfontconfig + $(CC) -g -pedantic -Wall -Wextra fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig fontconfig: $(CC) -g chordpro.c fontconfig.c -o fontconfig -lfontconfig .PHONY: all debug fontconfig diff --git a/chordpro.c b/chordpro.c @@ -5,6 +5,7 @@ #include <string.h> #include <ctype.h> #include "chordpro.h" +#include "config.h" static const char *environment_directives[] = { "start_of_chorus", "soc", "end_of_chorus", "eoc", "chorus", @@ -42,6 +43,30 @@ static const char *font_directives[] = { "tocfont", "tocsize", "toccolour", */ }; +struct StyleProperty default_style_properties[18] = { + { SF_CHORD, SPT_FONT, { .font_name = NULL } }, + { SF_CHORD, SPT_SIZE, { .font_size = EMPTY } }, + { SF_CHORD, SPT_COLOR, { .foreground_color = NULL } }, + { SF_CHORUS, SPT_FONT, { .font_name = NULL } }, + { SF_CHORUS, SPT_SIZE, { .font_size = EMPTY } }, + { SF_CHORUS, SPT_COLOR, { .foreground_color = NULL } }, + { SF_GRID, SPT_FONT, { .font_name = NULL } }, + { SF_GRID, SPT_SIZE, { .font_size = EMPTY } }, + { SF_GRID, SPT_COLOR, { .foreground_color = NULL } }, + { SF_TAB, SPT_FONT, { .font_name = NULL } }, + { SF_TAB, SPT_SIZE, { .font_size = EMPTY } }, + { SF_TAB, SPT_COLOR, { .foreground_color = NULL } }, + { SF_TEXT, SPT_FONT, { .font_name = NULL } }, + { SF_TEXT, SPT_SIZE, { .font_size = EMPTY } }, + { SF_TEXT, SPT_COLOR, { .foreground_color = NULL } }, + { SF_TITLE, SPT_FONT, { .font_name = NULL } }, + { SF_TITLE, SPT_SIZE, { .font_size = EMPTY } }, + { SF_TITLE, SPT_COLOR, { .foreground_color = NULL } }, +}; + +static enum SongFragmentType g_current_ftype = SF_TEXT; +static struct Config *g_config = NULL; + static const char *the_state(enum State state) { switch (state) { @@ -72,6 +97,8 @@ static const char *the_state(enum State state) const char *the_dtype(enum DirectiveType dtype) { switch (dtype) { + case DT_EMPTY: + return "DT_EMPTY"; case DT_ENVIRONMENT: return "DT_ENVIRONMENT"; case DT_METADATA: @@ -112,6 +139,8 @@ const char *the_stype(enum SectionType stype) const char *the_pos(enum Position pos) { switch (pos) { + case POS_EMPTY: + return "POS_EMPTY"; case POS_START: return "POS_START"; case POS_END: @@ -167,14 +196,18 @@ const char *the_font_weight(enum FontWeight weight) const char *the_rgb_color(struct RGBColor *color) { - static char str[17]; - sprintf((char *)&str, "rgb(%d,%d,%d)", color->red, color->green, color->blue); + static char str[100]; + sprintf((char *)&str, "rgb(%d,%d,%d) ", color->red, color->green, color->blue); + size_t len = strlen((char *)&str); + sprintf((char *)&str[len], "\x1b[38;2;%d;%d;%dm🬎\x1b[0m", color->red, color->green, color->blue); return str; } const char *the_line_style(enum LineStyle style) { switch (style) { + case LS_EMPTY: + return "LS_EMPTY"; case LS_SINGLE: return "LS_SINGLE"; case LS_DOUBLE: @@ -185,6 +218,76 @@ const char *the_line_style(enum LineStyle style) return ""; } +const char *the_song_fragment_type(enum SongFragmentType ftype) +{ + switch (ftype) { + case SF_EMPTY: + return "SF_EMPTY"; + case SF_CHORD: + return "SF_CHORD"; + case SF_CHORUS: + return "SF_CHORUS"; + case SF_FOOTER: + return "SF_FOOTER"; + case SF_GRID: + return "SF_GRID"; + case SF_TAB: + return "SF_TAB"; + case SF_TOC: + return "SF_TOC"; + case SF_TEXT: + return "SF_TEXT"; + case SF_TITLE: + return "SF_TITLE"; + } + return ""; +} + +const char *the_style_property_type(enum StylePropertyType type) +{ + switch (type) { + case SPT_EMPTY: + return "SPT_EMPTY"; + case SPT_FONT: + return "SPT_FONT"; + case SPT_SIZE: + return "SPT_SIZE"; + case SPT_COLOR: + return "SPT_COLOR"; + } + return ""; +} + +void the_default_style_properties(void) +{ + unsigned int i; + for (i = 0; i<LENGTH(default_style_properties); i++) { + printf( + "%s %s ", + the_song_fragment_type(default_style_properties[i].ftype), + the_style_property_type(default_style_properties[i].type) + ); + switch (default_style_properties[i].type) { + case SPT_FONT: + if (default_style_properties[i].u.font_name) + printf("%s\n", default_style_properties[i].u.font_name); + else + printf("NULL\n"); + break; + case SPT_SIZE: + printf("%.1f\n", default_style_properties[i].u.font_size); + break; + case SPT_COLOR: + if (default_style_properties[i].u.foreground_color) + printf("%s\n", the_rgb_color(default_style_properties[i].u.foreground_color)); + else + printf("NULL\n"); + break; + default: + } + } +} + static char *string_remove_leading_whitespace(const char *str) { int i = 0; @@ -225,6 +328,14 @@ struct RGBColor *cho_rgbcolor_duplicate(struct RGBColor *color) return copy; } +const char *cho_rgbcolor_to_string(struct RGBColor *color) +{ + static char str[8]; + str[7] = 0; + sprintf((char *)&str, "#%02X%02X%02X", color->red, color->green, color->blue); + return (const char *)&str; +} + struct RGBColor *cho_rgbcolor_parse(const char *str) { struct RGBColor *color = malloc(sizeof(struct RGBColor)); @@ -305,9 +416,9 @@ struct RGBColor *cho_color_parse(const char *str) color->green = 48; color->blue = 48; } else if (strcmp(str, "green") == 0) { - color->red = 193; - color->green = 223; - color->blue = 193; + color->red = 0; + color->green = 126; + color->blue = 0; } else if (strcmp(str, "blue") == 0) { color->red = 0; color->green = 0; @@ -489,7 +600,7 @@ const char *cho_font_weight_to_string(enum FontWeight weight) void cho_font_print_as_toml(struct Font *font, const char *section) { - printf("[fonts.%s]\n", section); + printf("[styles.%s.font]\n", section); printf("\n"); printf("name = \"%s\"\n", font->name); printf("family = \"%s\"\n", cho_font_family_to_string(font->family)); @@ -499,6 +610,92 @@ void cho_font_print_as_toml(struct Font *font, const char *section) printf("\n"); } +enum LineStyle cho_linestyle_parse(const char *str) +{ + if (strcmp(str, "single") == 0) { + return LS_SINGLE; + } else if (strcmp(str, "double") == 0) { + return LS_DOUBLE; + } else if (strcmp(str, "none") == 0) { + return LS_NONE; + } else { + return LS_EMPTY; + } +} + +const char *cho_linestyle_to_string(enum LineStyle linestyle) +{ + switch (linestyle) { + case LS_SINGLE: + return "single"; + case LS_DOUBLE: + return "double"; + default: + return "none"; + } +} + +static bool cho_style_property_apply_default(enum SongFragmentType current_ftype, enum StylePropertyType ptype, struct Style *style) +{ + unsigned int i; + for (i = 0; i<LENGTH(default_style_properties); i++) { + if ( + default_style_properties[i].ftype == current_ftype && + default_style_properties[i].type == ptype + ) { + switch (ptype) { + case SPT_FONT: + if (default_style_properties[i].u.font_name) { + free(style->font->name); + style->font->name = strdup(default_style_properties[i].u.font_name); + return true; + } else { + return false; + } + break; + case SPT_SIZE: + if (default_style_properties[i].u.font_size != EMPTY) { + style->font->size = default_style_properties[i].u.font_size; + return true; + } else { + return false; + } + break; + case SPT_COLOR: + if (default_style_properties[i].u.foreground_color) { + free(style->foreground_color); + style->foreground_color = cho_rgbcolor_duplicate(default_style_properties[i].u.foreground_color); + return true; + } else { + return false; + } + break; + default: + } + } + } + return false; +} + +static void cho_style_apply_default(enum SongFragmentType current_ftype, struct Style *style) +{ + if (current_ftype == SF_CHORUS) { + if (!cho_style_property_apply_default(SF_CHORUS, SPT_FONT, style)) { + cho_style_property_apply_default(SF_TEXT, SPT_FONT, style); + } + if (!cho_style_property_apply_default(SF_CHORUS, SPT_SIZE, style)) { + cho_style_property_apply_default(SF_TEXT, SPT_SIZE, style); + } + if (!cho_style_property_apply_default(SF_CHORUS, SPT_COLOR, style)) { + cho_style_property_apply_default(SF_TEXT, SPT_COLOR, style); + } + } else { + cho_style_property_apply_default(current_ftype, SPT_FONT, style); + cho_style_property_apply_default(current_ftype, SPT_SIZE, style); + cho_style_property_apply_default(current_ftype, SPT_COLOR, style); + } +} + struct Style *cho_style_new(void) { struct Style *style = malloc(sizeof(struct Style)); @@ -518,19 +715,6 @@ struct Style *cho_style_new(void) return style; } -void cho_style_free(struct Style *style) -{ - cho_font_free(style->font); - free(style->foreground_color); - free(style->background_color); - free(style->underline_color); - free(style->overline_color); - free(style->strikethrough_color); - free(style->boxed_color); - free(style->href); - free(style); -} - struct Style *cho_style_duplicate(struct Style *style) { struct Style *copy = malloc(sizeof(struct Style)); @@ -550,10 +734,49 @@ struct Style *cho_style_duplicate(struct Style *style) return copy; } +struct Style *cho_style_new_from_config(void) +{ + struct PrintableItem *printable_item; + switch (g_current_ftype) { + /* case SF_CHORD: + printable_item = config_printable_item_get(g_config->printable_items, "chord"); + return cho_style_duplicate(printable_item->style); */ + case SF_GRID: + printable_item = config_printable_item_get(g_config->printable_items, "grid"); + return cho_style_duplicate(printable_item->style); + case SF_TAB: + printable_item = config_printable_item_get(g_config->printable_items, "tab"); + return cho_style_duplicate(printable_item->style); + default: + printable_item = config_printable_item_get(g_config->printable_items, "text"); + return cho_style_duplicate(printable_item->style); + } +} + +struct Style *cho_style_new_default(void) +{ + struct Style *style = cho_style_new_from_config(); + cho_style_apply_default(g_current_ftype, style); + return style; +} + +void cho_style_free(struct Style *style) +{ + cho_font_free(style->font); + free(style->foreground_color); + free(style->background_color); + free(style->underline_color); + free(style->overline_color); + free(style->strikethrough_color); + free(style->boxed_color); + free(style->href); + free(style); +} + struct Font *cho_style_font_desc_parse(const char *str) { struct Font *font = cho_font_new(); - float size = -1.0; + double size = -1.0; char **words = malloc(sizeof(char *)); int w = 0; words[w] = NULL; @@ -621,7 +844,7 @@ struct Font *cho_style_font_desc_parse(const char *str) if (stop_at == -1) stop_at = w; } else { - size = strtof(words[w], NULL); + size = strtod(words[w], NULL); if (size == 0.0) goto SKIP; font->size = size; @@ -665,7 +888,7 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St if (inherited_style) style = cho_style_duplicate(inherited_style); else - style = cho_style_new(); + style = cho_style_new_default(); if (strcmp(tag_name, "span") == 0) { int a = 0; while (attrs[a] != NULL) { @@ -890,12 +1113,10 @@ struct Style *cho_style_get(const char *tag_name, struct Attr **attrs, struct St style->strikethrough = true; } else if (strcmp(tag_name, "sub") == 0) { style->font->size *= 0.8; - style->rise = (style->font->size / 2) * 0.3; + style->rise = -style->font->size * 0.3; } else if (strcmp(tag_name, "sup") == 0) { 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; + style->rise = style->font->size * 0.3; } else if (strcmp(tag_name, "small") == 0) { style->font->size *= 0.8; } else if (strcmp(tag_name, "tt") == 0) { @@ -929,10 +1150,56 @@ void cho_style_print(struct Style *style) printf("href: %s\n", style->href); else printf("href: NULL\n"); - printf("---- END STYLE ------\n"); + printf("---- END STYLE ------\n\n"); +} + +void cho_style_print_as_toml(struct Style *style, const char *section) +{ + printf("foreground_color = \"%s\"\n", cho_rgbcolor_to_string(style->foreground_color)); + printf("background_color = \"%s\"\n", cho_rgbcolor_to_string(style->background_color)); + printf("underline_style = \"%s\"\n", cho_linestyle_to_string(style->underline_style)); + printf("underline_color = \"%s\"\n", cho_rgbcolor_to_string(style->underline_color)); + printf("overline_style = \"%s\"\n", cho_linestyle_to_string(style->overline_style)); + printf("overline_color = \"%s\"\n", cho_rgbcolor_to_string(style->overline_color)); + printf("strikethrough = %s\n", style->strikethrough ? "true" : "false"); + printf("strikethrough_color = \"%s\"\n", cho_rgbcolor_to_string(style->strikethrough_color)); + printf("boxed = %s\n", style->boxed ? "true" : "false"); + printf("boxed_color = \"%s\"\n", cho_rgbcolor_to_string(style->boxed_color)); + printf("rise = %.1f\n", style->rise); + printf("href = \"%s\"\n", style->href ? style->href : ""); + printf("\n"); + cho_font_print_as_toml(style->font, section); } -struct Attr *cho_tag_attr_new(void) +static void cho_style_change_default(struct StyleProperty sprop) +{ + if (sprop.type == SPT_EMPTY) + return; + unsigned int i; + for (i = 0; i<LENGTH(default_style_properties); i++) { + if ( + default_style_properties[i].ftype == sprop.ftype && + default_style_properties[i].type == sprop.type + ) { + switch (sprop.type) { + case SPT_FONT: + free(default_style_properties[i].u.font_name); + default_style_properties[i].u.font_name = strdup(sprop.u.font_name); + break; + case SPT_SIZE: + default_style_properties[i].u.font_size = sprop.u.font_size; + break; + case SPT_COLOR: + free(default_style_properties[i].u.foreground_color); + default_style_properties[i].u.foreground_color = cho_rgbcolor_duplicate(sprop.u.foreground_color); + break; + default: + } + } + } +} + +static struct Attr *cho_tag_attr_new(void) { struct Attr *attr = malloc(sizeof(struct Attr)); attr->name = NULL; @@ -940,14 +1207,14 @@ struct Attr *cho_tag_attr_new(void) return attr; } -void cho_tag_attr_free(struct Attr *attr) +static void cho_tag_attr_free(struct Attr *attr) { free(attr->name); free(attr->value); free(attr); } -void cho_tag_attrs_free(struct Attr **attrs) +static void cho_tag_attrs_free(struct Attr **attrs) { int a = 0; while (attrs[a] != NULL) { @@ -957,7 +1224,7 @@ void cho_tag_attrs_free(struct Attr **attrs) free(attrs); } -struct Tag *cho_tag_new(void) +static struct Tag *cho_tag_new(void) { struct Tag *tag = malloc(sizeof(struct Tag)); tag->name = NULL; @@ -967,7 +1234,7 @@ struct Tag *cho_tag_new(void) return tag; } -void cho_tag_free(struct Tag *tag) +static void cho_tag_free(struct Tag *tag) { free(tag->name); cho_style_free(tag->style); @@ -976,7 +1243,7 @@ void cho_tag_free(struct Tag *tag) free(tag); } -void cho_tag_close_last_unclosed(const char *tag_name, struct Tag **tags, int last_index) +static void cho_tag_close_last_unclosed(const char *tag_name, struct Tag **tags, int last_index) { int i = last_index; while (i >= 0) { @@ -989,7 +1256,7 @@ void cho_tag_close_last_unclosed(const char *tag_name, struct Tag **tags, int la fprintf(stderr, "INFO: Didn't find a start tag for the end tag '%s'.\n", tag_name); } -struct Style *cho_tag_style_inherit(struct Tag **tags, int prev_index) +static struct Style *cho_tag_style_inherit(struct Tag **tags, int prev_index) { int i = prev_index; while (i >= 0) { @@ -1006,13 +1273,13 @@ struct Style *cho_tag_style_inherit(struct Tag **tags, int prev_index) return NULL; } -void cho_directive_free(struct ChoDirective *directive) +static void cho_directive_free(struct ChoDirective *directive) { cho_style_free(directive->style); free(directive); } -struct ChoMetadata *cho_metadata_new(void) +static struct ChoMetadata *cho_metadata_new(void) { struct ChoMetadata *meta = malloc(sizeof(struct ChoMetadata)); meta->name = NULL; @@ -1020,7 +1287,7 @@ struct ChoMetadata *cho_metadata_new(void) return meta; } -struct ChoMetadata *cho_metadata_split(const char *directive_value) +static struct ChoMetadata *cho_metadata_split(const char *directive_value) { struct ChoMetadata *meta = cho_metadata_new(); char *value = string_remove_leading_whitespace(directive_value); @@ -1060,7 +1327,7 @@ struct ChoMetadata *cho_metadata_split(const char *directive_value) } } -struct ChoChord *cho_chord_new(void) +static struct ChoChord *cho_chord_new(void) { struct ChoChord *chord = malloc(sizeof(struct ChoChord)); chord->position = -1; @@ -1076,7 +1343,7 @@ int cho_chord_count(struct ChoChord **chords) return i; } -struct ChoLine *cho_line_new(void) +static struct ChoLine *cho_line_new(void) { struct ChoLine *line = malloc(sizeof(struct ChoLine)); line->chords = NULL; @@ -1084,7 +1351,7 @@ struct ChoLine *cho_line_new(void) return line; } -int cho_line_compute_chord_position(struct ChoLine *line, int ly, int te) +static int cho_line_compute_chord_position(struct ChoLine *line, int ly, int te) { if (ly == 0) return te; @@ -1097,15 +1364,15 @@ int cho_line_compute_chord_position(struct ChoLine *line, int ly, int te) return lyrics_len + te; } -struct ChoLineItem *cho_line_item_new(void) +static struct ChoLineItem *cho_line_item_new(void) { struct ChoLineItem *item = malloc(sizeof(struct ChoLineItem)); - item->style = cho_style_new(); + item->style = cho_style_new_default(); item->text = NULL; return item; } -void cho_line_item_free(struct ChoLineItem *item) +static void cho_line_item_free(struct ChoLineItem *item) { cho_style_free(item->style); free(item->text); @@ -1120,7 +1387,7 @@ int cho_line_item_count(struct ChoLineItem **items) return i; } -struct ChoSection *cho_section_new(void) +static struct ChoSection *cho_section_new(void) { struct ChoSection *section = malloc(sizeof(struct ChoSection)); section->type = ST_EMPTY; @@ -1129,7 +1396,7 @@ struct ChoSection *cho_section_new(void) return section; } -struct ChoSong *cho_song_new(void) +static struct ChoSong *cho_song_new(void) { struct ChoSong *song = malloc(sizeof(struct ChoSong)); song->metadata = NULL; @@ -1137,7 +1404,7 @@ struct ChoSong *cho_song_new(void) return song; } -void cho_song_free(struct ChoSong *song) +static void cho_song_free(struct ChoSong *song) { int i = 0; int k = 0; @@ -1191,10 +1458,21 @@ void cho_songs_free(struct ChoSong **songs) free(songs); } -struct ChoDirective *directive_parse(const char *name) +static struct ChoDirective *cho_directive_new(void) { struct ChoDirective *directive = malloc(sizeof(struct ChoDirective)); - directive->style = cho_style_new(); + directive->dtype = DT_EMPTY; + directive->stype = ST_EMPTY; + directive->position = POS_EMPTY; + directive->sprop = SPT_EMPTY; + directive->ftype = SF_EMPTY; + directive->style = cho_style_new_default(); + return directive; +} + +static struct ChoDirective *cho_directive_parse(const char *name) +{ + struct ChoDirective *directive = cho_directive_new(); int i = 0; if ( strcmp(name, environment_directives[0]) == 0 || @@ -1203,6 +1481,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_START; directive->stype = ST_CHORUS; + directive->ftype = SF_CHORUS; goto END; } else if ( strcmp(name, environment_directives[2]) == 0 || @@ -1211,6 +1490,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_END; directive->stype = ST_CHORUS; + directive->ftype = SF_TEXT; goto END; } else if ( strcmp(name, environment_directives[5]) == 0 || @@ -1219,6 +1499,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_START; directive->stype = ST_VERSE; + directive->ftype = SF_TEXT; goto END; } else if ( strcmp(name, environment_directives[7]) == 0 || @@ -1227,6 +1508,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_END; directive->stype = ST_VERSE; + directive->ftype = SF_TEXT; goto END; } else if ( strcmp(name, environment_directives[9]) == 0 || @@ -1235,6 +1517,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_START; directive->stype = ST_BRIDGE; + directive->ftype = SF_TEXT; goto END; } else if ( strcmp(name, environment_directives[11]) == 0 || @@ -1243,6 +1526,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_END; directive->stype = ST_BRIDGE; + directive->ftype = SF_TEXT; goto END; } else if ( strcmp(name, environment_directives[13]) == 0 || @@ -1251,6 +1535,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_START; directive->stype = ST_TAB; + directive->ftype = SF_TAB; goto END; } else if ( strcmp(name, environment_directives[15]) == 0 || @@ -1259,6 +1544,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_END; directive->stype = ST_TAB; + directive->ftype = SF_TEXT; goto END; } else if ( strcmp(name, environment_directives[17]) == 0 || @@ -1267,6 +1553,7 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_START; directive->stype = ST_GRID; + directive->ftype = SF_GRID; goto END; } else if ( strcmp(name, environment_directives[19]) == 0 || @@ -1275,13 +1562,12 @@ struct ChoDirective *directive_parse(const char *name) directive->dtype = DT_ENVIRONMENT; directive->position = POS_END; directive->stype = ST_GRID; + directive->ftype = SF_TEXT; goto END; } while (metadata_directives[i] != NULL) { if (strcmp(metadata_directives[i], name) == 0) { directive->dtype = DT_METADATA; - directive->stype = ST_EMPTY; - directive->position = -1; goto END; } i++; @@ -1294,8 +1580,6 @@ struct ChoDirective *directive_parse(const char *name) ) { directive->style->background_color = cho_rgbcolor_new(228, 228, 228); directive->dtype = DT_FORMATTING; - directive->stype = ST_EMPTY; - directive->position = -1; goto END; } else if ( strcmp(formatting_directives[3], name) == 0 || @@ -1303,8 +1587,6 @@ struct ChoDirective *directive_parse(const char *name) ) { directive->style->font->style = FS_ITALIC; directive->dtype = DT_FORMATTING; - directive->stype = ST_EMPTY; - directive->position = -1; goto END; } else if ( strcmp(formatting_directives[5], name) == 0 || @@ -1312,15 +1594,12 @@ struct ChoDirective *directive_parse(const char *name) ) { directive->style->boxed = true; directive->dtype = DT_FORMATTING; - directive->stype = ST_EMPTY; - directive->position = -1; goto END; } while (preamble_directives[i] != NULL) { if (strcmp(preamble_directives[i], name) == 0) { directive->dtype = DT_PREAMBLE; directive->stype = ST_NEWSONG; - directive->position = -1; goto END; } i++; @@ -1330,16 +1609,112 @@ struct ChoDirective *directive_parse(const char *name) strcmp(font_directives[1], name) == 0 ) { directive->dtype = DT_FONT; + directive->sprop = SPT_FONT; + directive->ftype = SF_CHORD; + goto END; + } else if ( + strcmp(font_directives[2], name) == 0 || + strcmp(font_directives[3], name) == 0 + ) { + directive->dtype = DT_FONT; + directive->sprop = SPT_SIZE; + directive->ftype = SF_CHORD; + goto END; + } else if (strcmp(font_directives[4], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_COLOR; + directive->ftype = SF_CHORD; + goto END; + } else if (strcmp(font_directives[5], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_FONT; + directive->ftype = SF_CHORUS; + goto END; + } else if (strcmp(font_directives[6], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_SIZE; + directive->ftype = SF_CHORUS; + goto END; + } else if (strcmp(font_directives[7], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_COLOR; + directive->ftype = SF_CHORUS; + goto END; + } else if (strcmp(font_directives[8], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_FONT; + directive->ftype = SF_GRID; + goto END; + } else if (strcmp(font_directives[9], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_SIZE; + directive->ftype = SF_GRID; + goto END; + } else if (strcmp(font_directives[10], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_COLOR; + directive->ftype = SF_GRID; + goto END; + } else if (strcmp(font_directives[11], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_FONT; + directive->ftype = SF_TAB; + goto END; + } else if (strcmp(font_directives[12], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_SIZE; + directive->ftype = SF_TAB; + goto END; + } else if (strcmp(font_directives[13], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_COLOR; + directive->ftype = SF_TAB; + goto END; + } else if ( + strcmp(font_directives[14], name) == 0 || + strcmp(font_directives[15], name) == 0 + ) { + directive->dtype = DT_FONT; + directive->sprop = SPT_FONT; + directive->ftype = SF_TEXT; + goto END; + } else if ( + strcmp(font_directives[16], name) == 0 || + strcmp(font_directives[17], name) == 0 + ) { + directive->dtype = DT_FONT; + directive->sprop = SPT_SIZE; + directive->ftype = SF_TEXT; + goto END; + } else if (strcmp(font_directives[18], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_COLOR; + directive->ftype = SF_TEXT; + goto END; + } else if (strcmp(font_directives[19], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_FONT; + directive->ftype = SF_TITLE; + goto END; + } else if (strcmp(font_directives[20], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_SIZE; + directive->ftype = SF_TITLE; + goto END; + } else if (strcmp(font_directives[21], name) == 0) { + directive->dtype = DT_FONT; + directive->sprop = SPT_COLOR; + directive->ftype = SF_TITLE; + goto END; } directive->dtype = DT_CUSTOM; - directive->stype = ST_EMPTY; - directive->position = -1; END: return directive; } -struct ChoSong **cho_parse(FILE *fp) +struct ChoSong **cho_parse(FILE *fp, struct Config *config) { + g_config = config; char buf = 0; char prev_buf = '\n'; char directive_name[16]; @@ -1454,13 +1829,14 @@ struct ChoSong **cho_parse(FILE *fp) if (buf == '}') { directive_name[dn] = 0; dn = 0; - directive = directive_parse(directive_name); + directive = cho_directive_parse(directive_name); /* printf( "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", directive_name, dtype(directive->dtype), the_stype(directive->stype), pos(directive->position) ); */ switch (directive->dtype) { case DT_ENVIRONMENT: + g_current_ftype = directive->ftype; switch (directive->position) { case POS_START: cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); @@ -1495,6 +1871,7 @@ struct ChoSong **cho_parse(FILE *fp) songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); } break; + default: } break; case DT_METADATA: @@ -1529,10 +1906,12 @@ struct ChoSong **cho_parse(FILE *fp) songs[so]->sections[se]->lines[li] = cho_line_new(); break; case DT_FONT: - // Reset diretive value to default + // Reset directive value to default break; case DT_CUSTOM: + fprintf(stderr, "INFO: Ignoring custom directive '%s'.\n", directive_name); break; + default: } cho_directive_free(directive); directive = NULL; @@ -1552,13 +1931,14 @@ struct ChoSong **cho_parse(FILE *fp) if (buf == '}') { directive_value[dv] = 0; dv = 0; - directive = directive_parse(directive_name); + directive = cho_directive_parse(directive_name); /* printf( "directive: '%s'\ndtype: %s, stype: %s, position: %s\n", directive_name, dtype(directive->dtype), the_stype(directive->stype), pos(directive->position) ); */ switch (directive->dtype) { case DT_ENVIRONMENT: + g_current_ftype = directive->ftype; switch (directive->position) { case POS_START: cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); @@ -1594,6 +1974,7 @@ struct ChoSong **cho_parse(FILE *fp) songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); } break; + default: } break; case DT_METADATA: @@ -1632,10 +2013,47 @@ struct ChoSong **cho_parse(FILE *fp) fprintf(stderr, "INFO: Preamble directive '%s' can't have a value.\n", directive_name); break; case DT_FONT: - // Set directive value globally + struct StyleProperty sprop; + sprop.ftype = directive->ftype; + char *dir_value = string_remove_leading_whitespace(directive_value); + switch (directive->sprop) { + case SPT_FONT: + sprop.u.font_name = malloc((strlen(dir_value)+1) * sizeof(char)); + strcpy(sprop.u.font_name, dir_value); + sprop.type = SPT_FONT; + break; + case SPT_SIZE: + sprop.u.font_size = strtod(dir_value, NULL); + if (sprop.u.font_size == 0.0) { + fprintf(stderr, "INFO: Font directive '%s' has an invalid value. Ignoring directive.\n", directive_name); + sprop.type = SPT_EMPTY; + break; + } + sprop.type = SPT_SIZE; + break; + case SPT_COLOR: + sprop.u.foreground_color = cho_color_parse(dir_value); + if (sprop.u.foreground_color == NULL) { + fprintf(stderr, "INFO: Font directive '%s' has an invalid value. Ignoring directive.\n", directive_name); + sprop.type = SPT_EMPTY; + break; + } + sprop.type = SPT_COLOR; + break; + default: + } + cho_style_change_default(sprop); + if (sprop.type == SPT_FONT) { + free(sprop.u.font_name); + } else if (sprop.type == SPT_COLOR) { + free(sprop.u.foreground_color); + } + free(dir_value); break; case DT_CUSTOM: + fprintf(stderr, "INFO: Ignoring custom directive '%s'.\n", directive_name); break; + default: } memset(directive_value, 0, strlen(directive_value)); cho_directive_free(directive); diff --git a/chordpro.h b/chordpro.h @@ -1,12 +1,18 @@ +#include "config.h" /* ChordPro markup language <https://chordpro.org/chordpro/chordpro_markup> */ #ifndef _CHORDPRO_H_ #define _CHORDPRO_H_ -#define RISE_MAX +#define LENGTH(x) (sizeof x / sizeof x[0]) + +// #define RISE_MAX +#define EMPTY -1.0 #define DEFAULT_FONT_SIZE 14.0 +#define DEFAULT_TITLE_FONT_SIZE 18.0 // Based on https://stackoverflow.com/a/417184 #define URL_MAX_LEN 2000 +#define FONT_NAME_MAX 100 enum FontFamily { FF_EMPTY = -1, @@ -36,9 +42,9 @@ struct Font { enum FontWeight weight; double size; }; -// "Inter-monospace-regular-regular-12.0" enum LineStyle { + LS_EMPTY = -1, LS_SINGLE, LS_DOUBLE, LS_NONE @@ -98,7 +104,40 @@ enum State { STATE_COMMENT }; +/* Similar to SectionType but different enough for a separate type */ +enum SongFragmentType { + SF_EMPTY = -1, + SF_CHORD, + SF_CHORUS, + SF_FOOTER, + SF_GRID, + SF_TAB, + SF_TOC, + SF_TEXT, + SF_TITLE +}; + +enum StylePropertyType { + SPT_EMPTY = -1, + SPT_FONT, + SPT_SIZE, + SPT_COLOR +}; + +union StylePropertyValue { + char *font_name; + double font_size; + struct RGBColor *foreground_color; +}; + +struct StyleProperty { + enum SongFragmentType ftype; + enum StylePropertyType type; + union StylePropertyValue u; +}; + enum DirectiveType { + DT_EMPTY = -1, DT_ENVIRONMENT, DT_METADATA, DT_FORMATTING, @@ -118,6 +157,7 @@ enum SectionType { }; enum Position { + POS_EMPTY = -1, POS_START, POS_END }; @@ -126,6 +166,8 @@ struct ChoDirective { enum DirectiveType dtype; enum SectionType stype; enum Position position; + enum StylePropertyType sprop; + enum SongFragmentType ftype; struct Style *style; }; @@ -161,12 +203,18 @@ struct ChoSong { struct ChoSection **sections; }; -struct ChoSong **cho_parse(FILE *fp); +struct ChoSong **cho_parse(FILE *fp, struct Config *config); void cho_songs_free(struct ChoSong **song); int cho_line_item_count(struct ChoLineItem **items); int cho_chord_count(struct ChoChord **chords); +struct Style *cho_style_new(void); +void cho_style_free(struct Style *style); +struct Style *cho_style_duplicate(struct Style *style); struct Font *cho_style_font_desc_parse(const char *str); +void cho_style_print_as_toml(struct Style *style, const char *section); +struct RGBColor *cho_color_parse(const char *str); +enum LineStyle cho_linestyle_parse(const char *str); struct Font *cho_font_new(void); void cho_font_free(struct Font *font); struct Font *cho_font_duplicate(struct Font *font); diff --git a/config.c b/config.c @@ -5,89 +5,295 @@ #include "chordpro.h" #include "config.h" -struct Config *config_load_default(void) +static const char *g_valid_styles[] = { + "title", + "subtitle", + "footer", + "text", + "chorus", + "chord", + "annotation", + "comment", + "comment_italic", + "comment_boxed", + "tab", + "label", + "toc", + "grid", + "grid_margin", + "empty", + "diagram", + "diagram_base", + "chordfingers" +}; + +static struct PrintableItem *config_printable_item_new(const char *name) +{ + struct PrintableItem *item = malloc(sizeof(struct PrintableItem)); + item->name = strdup(name); + item->style = cho_style_new(); + return item; +} + +static void config_printable_item_free(struct PrintableItem *item) +{ + free(item->name); + cho_style_free(item->style); + free(item); +} + +struct PrintableItem *config_printable_item_get(struct PrintableItem **items, const char *name) +{ + int i = 0; + while (items[i] != NULL) { + if (strcmp(items[i]->name, name) == 0) { + return items[i]; + } + i++; + } + return NULL; +} + +static struct Config *config_load_default(void) { struct Config *config = malloc(sizeof(struct Config)); - config->title_font = malloc(sizeof(struct Font)); - config->title_font->family = FF_NORMAL; - config->title_font->name = strdup("Inter"); - config->title_font->style = FS_ROMAN; - config->title_font->weight = FW_BOLD; - config->title_font->size = 18.0; - config->subtitle_font = malloc(sizeof(struct Font)); - config->subtitle_font->family = FF_NORMAL; - config->subtitle_font->name = strdup("Inter"); - config->subtitle_font->style = FS_ROMAN; - config->subtitle_font->weight = FW_REGULAR; - config->subtitle_font->size = 12.0; - config->text_font = malloc(sizeof(struct Font)); - config->text_font->name = strdup("Inter"); - config->text_font->family = FF_NORMAL; - config->text_font->style = FS_ROMAN; - config->text_font->weight = FW_REGULAR; - config->text_font->size = 14.0; - config->chord_font = malloc(sizeof(struct Font)); - config->chord_font->name = strdup("Inter"); - config->chord_font->family = FF_NORMAL; - config->chord_font->style = FS_ROMAN; - config->chord_font->weight = FW_BOLD; - config->chord_font->size = 14.0; - config->comment_font = malloc(sizeof(struct Font)); - config->comment_font->name = strdup("Inter"); - config->comment_font->family = FF_NORMAL; - config->comment_font->style = FS_ROMAN; - config->comment_font->weight = FW_REGULAR; - config->comment_font->size = 14.0; - config->comment_italic_font = malloc(sizeof(struct Font)); - config->comment_italic_font->name = strdup("Inter"); - config->comment_italic_font->family = FF_NORMAL; - config->comment_italic_font->style = FS_ITALIC; - config->comment_italic_font->weight = FW_BOLD; - config->comment_italic_font->size = 14.0; - config->comment_box_font = malloc(sizeof(struct Font)); - config->comment_box_font->name = strdup("Inter"); - config->comment_box_font->family = FF_NORMAL; - config->comment_box_font->style = FS_ROMAN; - config->comment_box_font->weight = FW_REGULAR; - config->comment_box_font->size = 14.0; - config->tab_font = malloc(sizeof(struct Font)); - config->tab_font->name = strdup("Inter"); - config->tab_font->family = FF_NORMAL; - config->tab_font->style = FS_ROMAN; - config->tab_font->weight = FW_REGULAR; - config->tab_font->size = 14.0; - config->grid_font = malloc(sizeof(struct Font)); - config->grid_font->name = strdup("Inter"); - config->grid_font->family = FF_NORMAL; - config->grid_font->style = FS_ROMAN; - config->grid_font->weight = FW_BOLD; - config->grid_font->size = 14.0; - config->label_font = malloc(sizeof(struct Font)); - config->label_font->name = strdup("Inter"); - config->label_font->family = FF_NORMAL; - config->label_font->style = FS_ITALIC; - config->label_font->weight = FW_REGULAR; - config->label_font->size = 14.0; + config->printable_items = malloc(11 * sizeof(struct PrintableItem *)); + config->printable_items[0] = config_printable_item_new("title"); + config->printable_items[0]->style->font->name = strdup("Inter"); + config->printable_items[0]->style->font->weight = FW_BOLD; + config->printable_items[0]->style->font->size = 18.0; + config->printable_items[1] = config_printable_item_new("subtitle"); + config->printable_items[1]->style->font->name = strdup("Inter"); + config->printable_items[1]->style->font->size = 12.0; + config->printable_items[2] = config_printable_item_new("text"); + config->printable_items[2]->style->font->name = strdup("Inter"); + config->printable_items[3] = config_printable_item_new("chord"); + config->printable_items[3]->style->font->name = strdup("Inter"); + config->printable_items[3]->style->font->weight = FW_BOLD; + config->printable_items[4] = config_printable_item_new("comment"); + config->printable_items[4]->style->font->name = strdup("Inter"); + config->printable_items[5] = config_printable_item_new("comment_italic"); + config->printable_items[5]->style->font->name = strdup("Inter"); + config->printable_items[5]->style->font->style = FS_ITALIC; + config->printable_items[6] = config_printable_item_new("comment_box"); + config->printable_items[6]->style->font->name = strdup("Inter"); + config->printable_items[7] = config_printable_item_new("tab"); + config->printable_items[7]->style->font->name = strdup("Inter"); + config->printable_items[8] = config_printable_item_new("grid"); + config->printable_items[8]->style->font->name = strdup("Inter"); + config->printable_items[8]->style->font->weight = FW_BOLD; + config->printable_items[9] = config_printable_item_new("label"); + config->printable_items[9]->style->font->name = strdup("Inter"); + config->printable_items[9]->style->font->style = FS_ITALIC; + config->printable_items[10] = NULL; return config; } +static void config_printable_item_print_as_toml(struct PrintableItem *item) +{ + printf("[styles.%s]\n\n", item->name); + cho_style_print_as_toml(item->style, item->name); +} + void config_print_default(void) { struct Config *config = config_load_default(); - printf("[fonts]\n\n"); - cho_font_print_as_toml(config->title_font, "title"); - cho_font_print_as_toml(config->subtitle_font, "subtitle"); - cho_font_print_as_toml(config->text_font, "text"); - cho_font_print_as_toml(config->chord_font, "chord"); - cho_font_print_as_toml(config->comment_font, "comment"); - cho_font_print_as_toml(config->comment_italic_font, "comment_italic"); - cho_font_print_as_toml(config->comment_box_font, "comment_box"); - cho_font_print_as_toml(config->tab_font, "tab"); - cho_font_print_as_toml(config->grid_font, "grid"); - cho_font_print_as_toml(config->label_font, "label"); + int i = 0; + printf("[styles]\n"); + while (config->printable_items[i] != NULL) { + config_printable_item_print_as_toml(config->printable_items[i]); + i++; + } config_free(config); } +static bool config_load_font(struct Font *font, toml_table_t *table, const char *key_name) +{ + enum FontFamily family; + enum FontStyle style; + enum FontWeight weight; + toml_value_t value; + value = toml_table_string(table, "name"); + if (value.ok) { + free(font->name); + font->name = value.u.s; + } + value = toml_table_string(table, "family"); + if (value.ok) { + family = cho_font_family_parse(value.u.s); + if (family != FF_EMPTY) { + font->family = family; + } else { + fprintf(stderr, "INFO: Config section [styles.%s.font] family value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_string(table, "style"); + if (value.ok) { + style = cho_font_style_parse(value.u.s); + if (style != FS_EMPTY) { + font->style = style; + } else { + fprintf(stderr, "INFO: Config section [styles.%s.font] style value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_string(table, "weight"); + if (value.ok) { + weight = cho_font_weight_parse(value.u.s); + if (weight != FW_EMPTY) { + font->weight = weight; + } else { + fprintf(stderr, "INFO: Config section [styles.%s.font] weight value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_int(table, "size"); + if (value.ok) { + font->size = value.u.i; + } + return true; +} + +static bool config_load_style(struct Style *style, toml_table_t *table, const char *key_name) +{ + toml_value_t value; + struct RGBColor *color; + enum LineStyle line_style; + toml_table_t *font_section = toml_table_table(table, "font"); + if (font_section) { + if (!config_load_font(style->font, font_section, key_name)) { + fprintf(stderr, "config_load_font failed.\n"); + return false; + } + } + value = toml_table_string(table, "foreground_color"); + if (value.ok) { + color = cho_color_parse(value.u.s); + if (color) { + free(style->foreground_color); + style->foreground_color = color; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] foreground color value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_string(table, "background_color"); + if (value.ok) { + color = cho_color_parse(value.u.s); + if (color) { + free(style->background_color); + style->background_color = color; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] background color value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_string(table, "underline_style"); + if (value.ok) { + line_style = cho_linestyle_parse(value.u.s); + if (line_style != LS_EMPTY) { + style->underline_style = line_style; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] underline style value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_string(table, "underline_color"); + if (value.ok) { + color = cho_color_parse(value.u.s); + if (color) { + free(style->underline_color); + style->underline_color = color; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] underline color value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_string(table, "overline_style"); + if (value.ok) { + line_style = cho_linestyle_parse(value.u.s); + if (line_style != LS_EMPTY) { + style->overline_style = line_style; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] overline style value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_string(table, "overline_color"); + if (value.ok) { + color = cho_color_parse(value.u.s); + if (color) { + free(style->overline_color); + style->overline_color = color; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] overline color value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_bool(table, "strikethrough"); + if (value.ok) { + style->strikethrough = value.u.b; + } + value = toml_table_string(table, "strikethrough_color"); + if (value.ok) { + color = cho_color_parse(value.u.s); + if (color) { + free(style->strikethrough_color); + style->strikethrough_color = color; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] strikethrough color value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_bool(table, "boxed"); + if (value.ok) { + style->boxed = value.u.b; + } + value = toml_table_string(table, "boxed_color"); + if (value.ok) { + color = cho_color_parse(value.u.s); + if (color) { + free(style->boxed_color); + style->boxed_color = color; + } else { + fprintf(stderr, "INFO: Config section [styles.%s] boxed color value is invalid.\n", key_name); + return false; + } + free(value.u.s); + } + value = toml_table_double(table, "rise"); + if (value.ok) { + style->rise = value.u.d; + } + value = toml_table_string(table, "href"); + if (value.ok) { + style->href = value.u.s; + } + return true; +} + +static bool config_is_style(const char *str) +{ + int i = 0; + while (g_valid_styles[i] != NULL) { + if (strcmp(g_valid_styles[i], str) == 0) + return true; + i++; + } + return false; +} + struct Config *config_load(const char *filepath) { struct Config *config = config_load_default(); @@ -104,399 +310,34 @@ struct Config *config_load(const char *filepath) } 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, family, style, weight, size; - enum FontFamily font_family; - enum FontStyle font_style; - enum FontWeight font_weight; + if (!table) { + fprintf(stderr, "toml_parse_file failed.\n"); + fprintf(stderr, "INFO: Config file is not a valid toml file.\n"); + return NULL; + } + toml_table_t *styles = toml_table_table(table, "styles"); + if (!styles) + goto END; 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"); - family = toml_table_string(section, "family"); - 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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config title section font family value.\n"); - return NULL; - } else { - config->title_font->family = font_family; - } - } - 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; + toml_table_t *key; + struct PrintableItem *item; + for (int i=0; i<toml_table_len(styles); i++) { + key_name = toml_table_key(styles, i, &unused); + if (config_is_style(key_name)) { + key = toml_table_table(styles, key_name); + if (key) { + item = config_printable_item_get(config->printable_items, key_name); + if (item) { + if (!config_load_style(item->style, key, key_name)) { + fprintf(stderr, "config_load_style failed.\n"); + return NULL; + } } - free(weight.u.s); } - if (size.ok) - config->title_font->size = size.u.i; - } else if (strcmp(key_name, "subtitle") == 0) { - if (name.ok) { - free(config->subtitle_font->name); - config->subtitle_font->name = name.u.s; - } - if (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config subtitle section font family value.\n"); - return NULL; - } else { - config->subtitle_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config subtitle section font style value.\n"); - return NULL; - } else { - config->subtitle_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 subtitle section font weight value.\n"); - return NULL; - } else { - config->subtitle_font->weight = font_weight; - } - free(weight.u.s); - } - if (size.ok) - config->subtitle_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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config text section font family value.\n"); - return NULL; - } else { - config->text_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config text 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 text 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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config chord section font family value.\n"); - return NULL; - } else { - config->chord_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config chord 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 chord 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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config comment section font family value.\n"); - return NULL; - } else { - config->comment_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config comment 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 comment 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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config comment_italic section font family value.\n"); - return NULL; - } else { - config->comment_italic_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config comment_italic 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 comment_italic 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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config comment_box section font family value.\n"); - return NULL; - } else { - config->comment_box_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config comment_box 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 comment_box 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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config tab section font family value.\n"); - return NULL; - } else { - config->tab_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config tab 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 tab 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 (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config grid section font family value.\n"); - return NULL; - } else { - config->grid_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config grid 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 grid 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 if (strcmp(key_name, "label") == 0) { - if (name.ok) { - free(config->label_font->name); - config->label_font->name = name.u.s; - } - if (family.ok) { - font_family = cho_font_family_parse(family.u.s); - if (font_family == FF_EMPTY) { - fprintf(stderr, "INFO: Invalid config label section font family value.\n"); - return NULL; - } else { - config->label_font->family = font_family; - } - } - if (style.ok) { - font_style = cho_font_style_parse(style.u.s); - if (font_style == FS_EMPTY) { - fprintf(stderr, "INFO: Invalid config label section font style value.\n"); - return NULL; - } else { - config->label_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 label section font weight value.\n"); - return NULL; - } else { - config->label_font->weight = font_weight; - } - free(weight.u.s); - } - if (size.ok) { - config->label_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); } } + END: toml_free(table); fclose(fp); return config; @@ -504,15 +345,11 @@ struct Config *config_load(const char *filepath) void config_free(struct Config *config) { - cho_font_free(config->title_font); - cho_font_free(config->subtitle_font); - cho_font_free(config->text_font); - cho_font_free(config->chord_font); - cho_font_free(config->comment_font); - cho_font_free(config->comment_italic_font); - cho_font_free(config->comment_box_font); - cho_font_free(config->tab_font); - cho_font_free(config->grid_font); - cho_font_free(config->label_font); + int i = 0; + while (config->printable_items[i] != NULL) { + config_printable_item_free(config->printable_items[i]); + i++; + } + free(config->printable_items); free(config); } diff --git a/config.h b/config.h @@ -1,16 +1,18 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +struct PrintableItem { + char *name; + struct Style *style; +}; + struct Config { - struct Font *title_font; - struct Font *subtitle_font; - struct Font *text_font; - struct Font *chord_font; - struct Font *comment_font; - struct Font *comment_italic_font; - struct Font *comment_box_font; - struct Font *tab_font; - struct Font *grid_font; - struct Font *label_font; + struct PrintableItem **printable_items; }; struct Config *config_load(const char *filepath); void config_free(struct Config *config); void config_print_default(void); +struct PrintableItem *config_printable_item_get(struct PrintableItem **items, const char *name); + +#endif /* _CONFIG_H_ */ diff --git a/fontconfig.c b/fontconfig.c @@ -2,8 +2,6 @@ #include <stdbool.h> #include <stdint.h> #include <string.h> -// #include <ft2build.h> -// #include <freetype/freetype.h> #include <fontconfig/fontconfig.h> #include "fontconfig.h" @@ -46,26 +44,28 @@ char *fontconfig_fontpath_find(struct Font *font, enum FontType font_type) FcValue style; style.type = FcTypeInteger; switch (font->style) { - case FS_ROMAN: - style.u.i = FC_SLANT_ROMAN; - break; - case FS_OBLIQUE: - style.u.i = FC_SLANT_OBLIQUE; - break; - case FS_ITALIC: - style.u.i = FC_SLANT_ITALIC; - break; + case FS_ROMAN: + style.u.i = FC_SLANT_ROMAN; + break; + case FS_OBLIQUE: + style.u.i = FC_SLANT_OBLIQUE; + break; + case FS_ITALIC: + style.u.i = FC_SLANT_ITALIC; + break; + default: } FcPatternAdd(pattern, FC_SLANT, style, FcFalse); FcValue weight; weight.type = FcTypeInteger; switch (font->weight) { - case FW_REGULAR: - weight.u.i = FC_WEIGHT_REGULAR; - break; - case FW_BOLD: - weight.u.i = FC_WEIGHT_BOLD; - break; + case FW_REGULAR: + weight.u.i = FC_WEIGHT_REGULAR; + break; + case FW_BOLD: + weight.u.i = FC_WEIGHT_BOLD; + break; + default: } FcPatternAdd(pattern, FC_WEIGHT, weight, FcFalse); FcFontSet *set = FcFontList(NULL, pattern, obj); diff --git a/lorid.c b/lorid.c @@ -34,6 +34,14 @@ int main(int argc, char *argv[]) fprintf(stderr, "config_load failed.\n"); return 1; } + /* int u = 0; + while (config->printable_items[u] != NULL) { + printf("---- BEGIN PRINTABLE ITEM ----\n"); + printf("name: %s\n", config->printable_items[u]->name); + cho_style_print(config->printable_items[u]->style); + printf("---- END PRINTABLE ITEM ------\n"); + u++; + } */ free(config_filepath); if (argc == optind) { fp = stdin; @@ -47,7 +55,7 @@ int main(int argc, char *argv[]) fprintf(stderr, "Provide only one file.\n"); return 1; } - struct ChoSong **songs = cho_parse(fp); + struct ChoSong **songs = cho_parse(fp, config); if (songs == NULL) { fprintf(stderr, "cho_parse failed.\n"); return 1; diff --git a/out_pdf.c b/out_pdf.c @@ -10,10 +10,10 @@ static struct Fnt **g_fonts = NULL; -static char current_font[200]; -static double current_font_size; -static double current_y = MEDIABOX_HEIGHT - 25.0; -static pdfio_obj_t *current_font_obj = NULL; +static char g_current_font_name[200]; +static double g_current_font_size; +static double g_current_y = MEDIABOX_HEIGHT - 25.0; +static pdfio_obj_t *g_current_font_obj = NULL; static pdfio_obj_t *out_pdf_fnt_obj_get_by_name(const char *name) { @@ -175,72 +175,19 @@ static void out_pdf_add_fonts(struct Font *font, struct Font ***fonts) cho_font_free(new_font); } -/* static void out_pdf_fonts_add_default(struct Font ***fonts) -{ - struct Font *font_regular = cho_font_new(); - font_regular->name = strdup("Inter"); - out_pdf_font_add(font_regular, fonts); - struct Font *font_bold = cho_font_new(); - font_bold->name = strdup("Inter"); - font_bold->weight = FW_BOLD; - out_pdf_font_add(font_bold, fonts); - struct Font *font_italic = cho_font_new(); - font_italic->name = strdup("Inter"); - font_italic->style = FS_ITALIC; - out_pdf_font_add(font_italic, fonts); - struct Font *font_italic_bold = cho_font_new(); - font_italic_bold->name = strdup("Inter"); - font_italic_bold->style = FS_ITALIC; - font_italic_bold->weight = FW_BOLD; - out_pdf_font_add(font_italic_bold, fonts); -} */ - static struct Font **out_pdf_font_get_all(struct ChoSong **songs, struct Config *config) { struct Font **fonts = NULL; struct Font *font; - // out_pdf_fonts_add_default(&fonts); bool added = false; - font = cho_font_duplicate(config->title_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->subtitle_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->text_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->chord_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->comment_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->comment_italic_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->comment_box_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->tab_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->grid_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); - font = cho_font_duplicate(config->label_font); - added = out_pdf_font_add_if_not_in(font, &fonts); - if (!added) - cho_font_free(font); + int i = 0; + while (config->printable_items[i] != NULL) { + font = cho_font_duplicate(config->printable_items[i]->style->font); + added = out_pdf_font_add_if_not_in(font, &fonts); + if (!added) + cho_font_free(font); + i++; + } int so = 0; int se = 0; int li = 0; @@ -268,18 +215,31 @@ static struct Font **out_pdf_font_get_all(struct ChoSong **songs, struct Config return fonts; } +static void out_pdf_adjust_line(struct ChoLine *line) +{ +} + 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)); + double width; + char line[strlen(lyrics_line)+1]; strcpy(line, lyrics_line); + // size_t line_len = strlen(line); line[chord->position] = 0; - double width = pdfioContentTextMeasure(current_font_obj, line, current_font_size); - free(line); + width = pdfioContentTextMeasure(g_current_font_obj, line, g_current_font_size); + /* if (chord->position < line_len) { + line[chord->position] = 0; + width = pdfioContentTextMeasure(g_current_font_obj, line, g_current_font_size); + } else { + double space_width = pdfioContentTextMeasure(g_current_font_obj, " ", g_current_font_size); + width = pdfioContentTextMeasure(g_current_font_obj, line, g_current_font_size); + width += (line_len - chord->position) * space_width; + } */ if (!pdfioContentTextBegin(stream)) { fprintf(stderr, "pdfioContentTextBegin failed.\n"); return false; } - if (!pdfioContentTextMoveTo(stream, PADDING + width, current_y)) { + if (!pdfioContentTextMoveTo(stream, PADDING + width, g_current_y)) { fprintf(stderr, "pdfioContentTextMoveTo failed.\n"); return false; } @@ -292,43 +252,41 @@ static bool out_pdf_chord_show(pdfio_stream_t *stream, const char *lyrics_line, return false; } if (linebreak) { - current_y -= current_font_size + 2.0; + g_current_y -= g_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) +size_t out_pdf_text_find_fitting_length(const char *str, size_t len) { - 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) { - case CENTER: - x_continue = PADDING; - x = PADDING + (LINE_LEN - width) / 2; - break; - case LEFT: - x_continue = PADDING; - x = PADDING; - break; - case CONTINUE: - x = x_continue; - x_continue += width; - break; + char tmp[512]; + strncpy((char *)&tmp, str, len); + tmp[len] = 0; + + double width; + width = pdfioContentTextMeasure(g_current_font_obj, tmp, g_current_font_size); + + while (width > LINE_LEN) { + if (tmp[len] == ' ' || tmp[len] == '\t') { + tmp[len] = 0; + width = pdfioContentTextMeasure(g_current_font_obj, tmp, g_current_font_size); + } + len--; } - // printf("x: %f, y: %f\n", x, current_y); + return len; +} + +static bool out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y) +{ if (!pdfioContentTextBegin(stream)) { fprintf(stderr, "pdfioContentTextBegin failed.\n"); return false; } - if (!pdfioContentTextMoveTo(stream, x, current_y)) { + if (!pdfioContentTextMoveTo(stream, item->x, y)) { fprintf(stderr, "pdfioContentTextMoveTo failed.\n"); return false; } - if (!pdfioContentTextShow(stream, true, str)) { + if (!pdfioContentTextShow(stream, true, item->text)) { fprintf(stderr, "pdfioContentTextShow failed.\n"); return false; } @@ -336,17 +294,9 @@ static bool out_pdf_text_show(pdfio_stream_t *stream, const char *str, enum Alig fprintf(stderr, "pdfioContentTextEnd failed.\n"); return false; } - if (linebreak) { - 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_create(const char *old) { char *new = NULL; @@ -471,7 +421,10 @@ static void out_pdf_fnt_add(struct Fnt *fnt, struct Fnt ***array) static bool out_pdf_font_set(pdfio_stream_t *stream, struct Font *font) { char *name = out_pdf_fnt_name_create(font); - printf("setting font '%s'\n", name); + if (strcmp(name, g_current_font_name) == 0 && font->size == g_current_font_size) { + free(name); + return true; + } if (!pdfioContentSetTextFont(stream, name, font->size)) { fprintf(stderr, "pdfioContentSetTextFont failed.\n"); return false; @@ -481,18 +434,369 @@ static bool out_pdf_font_set(pdfio_stream_t *stream, struct Font *font) fprintf(stderr, "out_pdf_fnt_obj_get_by_name failed.\n"); return false; } - strcpy(current_font, name); - current_font_obj = font_obj; - current_font_size = font->size; + strcpy(g_current_font_name, name); + g_current_font_obj = font_obj; + g_current_font_size = font->size; free(name); return true; } -bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config *config) +static bool out_pdf_style_apply(pdfio_stream_t *stream, struct Style *style) { - if (config->label_font) { - printf("label_font is not NULL.\n"); + double red, green, blue; + if (!out_pdf_font_set(stream, style->font)) { + fprintf(stderr, "out_pdf_font_set failed.\n"); + return false; } + red = style->foreground_color->red / 255.0; + green = style->foreground_color->green / 255.0; + blue = style->foreground_color->blue / 255.0; + // printf("foreground_color: %s\n", the_rgb_color(style->foreground_color)); + // printf("%f, %f, %f\n", red, green, blue); + if (!pdfioContentSetFillColorRGB(stream, red, green, blue)) { + fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n"); + return false; + } + // cho_style_print(style); + /* printf("name: %s\n", style->font->name); + printf("size: %.1f\n", style->font->size); + printf( + "\x1b[38;2;%d;%d;%dmRGBCOLOR\x1b[0m\n", + style->foreground_color->red, + style->foreground_color->green, + style->foreground_color->blue + ); */ + return true; +} + +static double text_width(struct TextLineItem *item) +{ + char *name = out_pdf_fnt_name_create(item->style->font); + pdfio_obj_t *font_obj = out_pdf_fnt_obj_get_by_name(name); + free(name); + return pdfioContentTextMeasure(font_obj, item->text, item->style->font->size); +} + +static double line_width_until_chord(struct ChoLine *line, struct ChoChord *chord, struct SpaceNeeded *space) +{ + int ly, i, last_ly, last_i; + int pos = 0; + char *name; + double width = 0.0; + pdfio_obj_t *font_obj; + for (ly = 0; line->lyrics[ly]; ly++) { + for (i = 0; line->lyrics[ly]->text[i]; i++) { + if (pos == chord->position) { + last_ly = ly; + last_i = i; + goto FOUND; + } + pos++; + } + } + /* If the chord is the last thing in the line */ + last_ly = ly; + last_i = i; + FOUND: + if (space) { + space->line_item_index = last_ly; + space->text_index = last_i; + } + for (ly = 0; line->lyrics[ly] && ly <= last_ly; ly++) { + name = out_pdf_fnt_name_create(line->lyrics[ly]->style->font); + font_obj = out_pdf_fnt_obj_get_by_name(name); + if (ly == last_ly) { + char tmp[strlen(line->lyrics[ly]->text)+1]; + strcpy((char *)&tmp, line->lyrics[ly]->text); + tmp[last_i] = 0; + width += pdfioContentTextMeasure(font_obj, (const char *)&tmp, line->lyrics[ly]->style->font->size); + } else { + width += pdfioContentTextMeasure(font_obj, line->lyrics[ly]->text, line->lyrics[ly]->style->font->size); + } + free(name); + } + return width; +} + +static bool out_pdf_chord_is_enough_space(struct ChoLine *line, struct ChoChord *prev_chord, struct ChoChord *chord, struct Font *chord_font, struct SpaceNeeded *space) +{ + double width_between_chords = line_width_until_chord(line, chord, NULL) - line_width_until_chord(line, prev_chord, space); + char *name = out_pdf_fnt_name_create(chord_font); + pdfio_obj_t *font_obj = out_pdf_fnt_obj_get_by_name(name); + free(name); + double prev_chord_width = pdfioContentTextMeasure(font_obj, prev_chord->chord, chord_font->size); + if (prev_chord_width > width_between_chords) { + space->amount = prev_chord_width - width_between_chords; + return false; + } + return true; +} + +static struct SpaceNeeded *needs_space(struct SpaceNeeded **spaces, int ly, int i) { + int sn; + for (sn = 0; spaces[sn]; sn++) { + if (spaces[sn]->line_item_index == ly && spaces[sn]->text_index == i) { + return spaces[sn]; + } + } + return NULL; +} + +static void text_line_set_lineheight(struct TextLine *line, enum SongFragmentType ftype) +{ + double biggest_font_size = 0.0; + int tli; + for (tli = 0; line->items[tli]; tli++) { + // TODO: Implement chord style + if (line->items[tli]->style) { + if (line->items[tli]->style->font->size > biggest_font_size) { + biggest_font_size = line->items[tli]->style->font->size; + } + } else { + if (14.0 > biggest_font_size) { + biggest_font_size = 14.0; + } + } + } + switch (ftype) { + case SF_CHORD: + line->height = 8.0 + biggest_font_size; + break; + default: + line->height = 2.0 + biggest_font_size; + } +} + +static struct Text **text_create(struct ChoSong **songs, struct Config *config) +{ + struct PrintableItem *printable_item; + int so, se, li, ly, ch; + double width; + bool add_space_to_next_chord = false; + struct ChoLine **lines; + struct ChoChord **chords; + struct SpaceNeeded space; + struct SpaceNeeded **spaces = NULL; + int sn = 0; + int t = 0; + int tl = 0; + int tli = 0; + int m; + struct Text **text = NULL; + for (so = 0; songs[so]; so++, t++) { + text = realloc(text, (t+1) * sizeof(struct Text *)); + text[t] = malloc(sizeof(struct Text)); + text[t]->lines = NULL; + for (m = 0; songs[so]->metadata[m]; m++) { + if (strcmp(songs[so]->metadata[m]->name, "title") == 0) { + printable_item = config_printable_item_get(config->printable_items, "title"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return NULL; + } + text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); + text[t]->lines[tl] = malloc(sizeof(struct TextLine)); + text[t]->lines[tl]->items = malloc(2 * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[0] = malloc(sizeof(struct TextLineItem)); + text[t]->lines[tl]->items[0]->text = strdup(songs[so]->metadata[m]->value); + text[t]->lines[tl]->items[0]->style = cho_style_duplicate(printable_item->style); + width = text_width(text[t]->lines[tl]->items[0]); + text[t]->lines[tl]->items[0]->x = PADDING + (LINE_LEN - width) / 2; + text[t]->lines[tl]->items[1] = NULL; + text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); + tl++; + } + } + for (m = 0; songs[so]->metadata[m]; m++) { + if (strcmp(songs[so]->metadata[m]->name, "subtitle") == 0) { + printable_item = config_printable_item_get(config->printable_items, "subtitle"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return NULL; + } + text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); + text[t]->lines[tl] = malloc(sizeof(struct TextLine)); + text[t]->lines[tl]->items = malloc(2 * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[0] = malloc(sizeof(struct TextLineItem)); + text[t]->lines[tl]->items[0]->text = strdup(songs[so]->metadata[m]->value); + text[t]->lines[tl]->items[0]->style = cho_style_duplicate(printable_item->style); + width = text_width(text[t]->lines[tl]->items[0]); + text[t]->lines[tl]->items[0]->x = PADDING + (LINE_LEN - width) / 2; + text[t]->lines[tl]->items[1] = NULL; + text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); + tl++; + } + } + text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); + text[t]->lines[tl] = malloc(sizeof(struct TextLine)); + text[t]->lines[tl]->items = NULL; + text[t]->lines[tl]->height = 30.0; + tl++; + for (se = 0; songs[so]->sections[se]; se++) { + if (songs[so]->sections[se]->name) { + text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); + text[t]->lines[tl] = malloc(sizeof(struct TextLine)); + text[t]->lines[tl]->items = malloc(2 * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[0] = malloc(sizeof(struct TextLineItem)); + text[t]->lines[tl]->items[0]->text = strdup(songs[so]->sections[se]->name); + printable_item = config_printable_item_get(config->printable_items, "label"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return NULL; + } + text[t]->lines[tl]->items[0]->style = cho_style_duplicate(printable_item->style); + text[t]->lines[tl]->items[0]->x = PADDING; + text[t]->lines[tl]->items[1] = NULL; + text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); + tl++; + } + lines = songs[so]->sections[se]->lines; + for (li = 0; lines[li]; li++, tl++) { + text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); + text[t]->lines[tl] = malloc(sizeof(struct TextLine)); + text[t]->lines[tl]->items = NULL; + chords = songs[so]->sections[se]->lines[li]->chords; + int chords_len = cho_chord_count(chords); + printable_item = config_printable_item_get(config->printable_items, "chord"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return NULL; + } + double added_space = 0.0; + for (ch = 0; chords[ch]; ch++, tli++) { + text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[tli] = malloc(sizeof(struct TextLineItem)); + if (tli == 0) { + text[t]->lines[tl]->items[tli]->x = PADDING + line_width_until_chord(lines[li], chords[ch], NULL); + } else { + text[t]->lines[tl]->items[tli]->x = PADDING + line_width_until_chord(lines[li], chords[ch], NULL) + added_space; + } + if (add_space_to_next_chord) { + added_space += space.amount; + text[t]->lines[tl]->items[tli]->x += space.amount; + add_space_to_next_chord = false; + } + // TODO: implement chord style + text[t]->lines[tl]->items[tli]->style = cho_style_duplicate(printable_item->style); + text[t]->lines[tl]->items[tli]->text = strdup(chords[ch]->chord); + if (chords_len == ch+1) { + break; + } + if (!out_pdf_chord_is_enough_space( + songs[so]->sections[se]->lines[li], + chords[ch], + chords[ch+1], + printable_item->style->font, + &space + )) { + spaces = realloc(spaces, (sn+1) * sizeof(struct SpaceNeeded *)); + spaces[sn] = malloc(sizeof(struct SpaceNeeded)); + spaces[sn]->line_item_index = space.line_item_index; + spaces[sn]->text_index = space.text_index; + spaces[sn]->amount = space.amount; + sn++; + add_space_to_next_chord = true; + } + } + spaces = realloc(spaces, (sn+1) * sizeof(struct SpaceNeeded *)); + spaces[sn] = NULL; + tli++; + text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[tli] = NULL; + text_line_set_lineheight(text[t]->lines[tl], SF_CHORD); + tli = 0; + tl++; + text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); + text[t]->lines[tl] = malloc(sizeof(struct TextLine)); + text[t]->lines[tl]->items = NULL; + for (ly = 0; lines[li]->lyrics[ly]; ly++) { + int ii; + double last_text_line_item_width; + struct SpaceNeeded *sp; + text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[tli] = malloc(sizeof(struct TextLineItem)); + text[t]->lines[tl]->items[tli]->text = NULL; + text[t]->lines[tl]->items[tli]->style = cho_style_duplicate(lines[li]->lyrics[ly]->style); + if (ly == 0) { + text[t]->lines[tl]->items[tli]->x = PADDING; + } else { + last_text_line_item_width = text_width(text[t]->lines[tl]->items[tli-1]); + text[t]->lines[tl]->items[tli]->x = text[t]->lines[tl]->items[tli-1]->x + last_text_line_item_width; + } + int tlii = 0; + for (ii = 0; lines[li]->lyrics[ly]->text[ii] != 0; ii++) { + if (sp = needs_space(spaces, ly, ii)) { + text[t]->lines[tl]->items[tli]->text = realloc(text[t]->lines[tl]->items[tli]->text, (tlii+1) * sizeof(char)); + text[t]->lines[tl]->items[tli]->text[tlii] = lines[li]->lyrics[ly]->text[ii]; + tlii++; + text[t]->lines[tl]->items[tli]->text = realloc(text[t]->lines[tl]->items[tli]->text, (tlii+1) * sizeof(char)); + text[t]->lines[tl]->items[tli]->text[tlii] = 0; + tlii = 0; + tli++; + text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[tli] = malloc(sizeof(struct TextLineItem)); + text[t]->lines[tl]->items[tli]->text = NULL; + text[t]->lines[tl]->items[tli]->style = cho_style_duplicate(lines[li]->lyrics[ly]->style); + last_text_line_item_width = text_width(text[t]->lines[tl]->items[tli-1]); + text[t]->lines[tl]->items[tli]->x = text[t]->lines[tl]->items[tli-1]->x + last_text_line_item_width + sp->amount; + } else { + text[t]->lines[tl]->items[tli]->text = realloc(text[t]->lines[tl]->items[tli]->text, (tlii+1) * sizeof(char)); + text[t]->lines[tl]->items[tli]->text[tlii] = lines[li]->lyrics[ly]->text[ii]; + tlii++; + } + } + text[t]->lines[tl]->items[tli]->text = realloc(text[t]->lines[tl]->items[tli]->text, (tlii+1) * sizeof(char)); + text[t]->lines[tl]->items[tli]->text[tlii] = 0; + } + tli++; + text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); + text[t]->lines[tl]->items[tli] = NULL; + text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); + tli = 0; + for (sn = 0; spaces[sn]; sn++) { + free(spaces[sn]); + } + free(spaces); + spaces = NULL; + sn = 0; + } + text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); + text[t]->lines[tl] = NULL; + tl = 0; + } + } + text = realloc(text, (t+1) * sizeof(struct Text *)); + text[t] = NULL; + return text; +} + +static void text_free(struct Text **text) +{ + int t, tl, tli; + for (t = 0; text[t]; t++) { + for (tl = 0; text[t]->lines[tl]; tl++) { + if (text[t]->lines[tl]->items) { + for (tli = 0; text[t]->lines[tl]->items[tli]; tli++) { + free(text[t]->lines[tl]->items[tli]->text); + // NULL check because chord style is not yet implemented + if (text[t]->lines[tl]->items[tli]->style) { + cho_style_free(text[t]->lines[tl]->items[tli]->style); + } + free(text[t]->lines[tl]->items[tli]); + } + free(text[t]->lines[tl]->items); + } + free(text[t]->lines[tl]); + } + free(text[t]->lines); + free(text[t]); + } + free(text); +} + +bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config *config) +{ + memset(&g_current_font_name, 0, sizeof(g_current_font_name)); char *pdf_filename = out_pdf_filename_create(cho_filename); pdfio_rect_t media_box_a4 = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; pdfio_rect_t crop_box = { 36.0, 36.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; @@ -506,7 +810,6 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config while (needed_fonts[f] != NULL) { fontpath = fontconfig_fontpath_find(needed_fonts[f], FT_TTF); if (fontpath) { - printf("ttf fontpath: %s\n", fontpath); fnt = out_pdf_fnt_new(); fnt->name = out_pdf_fnt_name_create(needed_fonts[f]); fnt->font = pdfioFileCreateFontObjFromFile(pdf, fontpath, true); @@ -519,7 +822,6 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config } else { fontpath = fontconfig_fontpath_find(needed_fonts[f], FT_OTF); if (fontpath) { - printf("otf fontpath: %s\n", fontpath); fnt = out_pdf_fnt_new(); fnt->name = out_pdf_fnt_name_create(needed_fonts[f]); fnt->font = pdfioFileCreateFontObjFromFile(pdf, fontpath, true); @@ -538,33 +840,53 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config f++; } cho_fonts_free(needed_fonts); - f = 0; + /* f = 0; while (g_fonts[f] != NULL) { printf("name: %s\n", g_fonts[f]->name); f++; + } */ + struct Text **text = text_create(songs, config); + if (!text) { + fprintf(stderr, "text_create failed.\n"); + return false; + } + pdfio_array_t *color_array = pdfioArrayCreateColorFromStandard(pdf, 3, PDFIO_CS_ADOBE); + if (!pdfioPageDictAddColorSpace(page1_dict, "rgbcolorspace", color_array)) { + fprintf(stderr, "pdfioPageDictAddColorSpace failed.\n"); + return 1; } pdfio_stream_t *page1_stream = pdfioFileCreatePage(pdf, page1_dict); - if (!out_pdf_font_set(page1_stream, config->text_font)) { - fprintf(stderr, "out_pdf_font_set failed.\n"); - return false; + if (!pdfioContentSetFillColorSpace(page1_stream, "rgbcolorspace")) { + fprintf(stderr, "pdfioContentSetFillColorSpace failed.\n"); + return 1; } - if (!pdfioContentSetFillColorRGB(page1_stream, 0.0, 0.0, 0.0)) { - fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n"); - return false; + double y = MEDIABOX_HEIGHT - 25.0; + int t, tl, tli; + for (t = 0; text[t]; t++) { + for (tl = 0; text[t]->lines[tl]; tl++) { + if (text[t]->lines[tl]->items) { + for (tli = 0; text[t]->lines[tl]->items[tli]; tli++) { + // NULL check because chord style is not yet implemented + if (text[t]->lines[tl]->items[tli]->style) { + out_pdf_style_apply(page1_stream, text[t]->lines[tl]->items[tli]->style); + } + out_pdf_text_show(page1_stream, text[t]->lines[tl]->items[tli], y); + } + } + y -= text[t]->lines[tl]->height; + } + } + text_free(text); + /* pdfio_array_t *color_array = pdfioArrayCreateColorFromStandard(pdf, 3, PDFIO_CS_ADOBE); + if (!pdfioPageDictAddColorSpace(page1_dict, "rgbcolorspace", color_array)) { + fprintf(stderr, "pdfioPageDictAddColorSpace failed.\n"); + return 1; + } + pdfio_stream_t *page1_stream = pdfioFileCreatePage(pdf, page1_dict); + if (!pdfioContentSetFillColorSpace(page1_stream, "rgbcolorspace")) { + fprintf(stderr, "pdfioContentSetFillColorSpace failed.\n"); + return 1; } - // 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)) { - fprintf(stderr, "pdfioContentSetLineWidth failed.\n"); - return false; - } */ int so = 0; int se = 0; int m = 0; @@ -572,12 +894,22 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config int ly = 0; int ch = 0; struct ChoChord **chords; + struct PrintableItem *printable_item; + if (!pdfioContentTextBegin(page1_stream)) { + fprintf(stderr, "pdfioContentTextBegin failed.\n"); + return false; + } 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 (!out_pdf_font_set(page1_stream, config->title_font)) { + printable_item = config_printable_item_get(config->printable_items, "title"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return false; + } + if (!out_pdf_font_set(page1_stream, printable_item->style->font)) { fprintf(stderr, "out_pdf_font_set failed.\n"); return false; } @@ -592,7 +924,12 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config while (songs[so]->metadata[m] != NULL) { if (strcmp(songs[so]->metadata[m]->name, "subtitle") == 0) { const char *subtitle = songs[so]->metadata[m]->value; - if (!out_pdf_font_set(page1_stream, config->subtitle_font)) { + printable_item = config_printable_item_get(config->printable_items, "subtitle"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return false; + } + if (!out_pdf_font_set(page1_stream, printable_item->style->font)) { fprintf(stderr, "out_pdf_font_set failed.\n"); return false; } @@ -611,7 +948,12 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config while (songs[so]->sections[se] != NULL) { if (songs[so]->sections[se]->name) { const char *section_name = songs[so]->sections[se]->name; - if (!out_pdf_font_set(page1_stream, config->label_font)) { + printable_item = config_printable_item_get(config->printable_items, "label"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return false; + } + if (!out_pdf_font_set(page1_stream, printable_item->style->font)) { fprintf(stderr, "out_pdf_font_set failed.\n"); return false; } @@ -621,21 +963,29 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config } } while (songs[so]->sections[se]->lines[li] != NULL) { + out_pdf_adjust_line(songs[so]->sections[se]->lines[li]); chords = songs[so]->sections[se]->lines[li]->chords; int count = cho_chord_count(chords); if (count > 0) { - if (!out_pdf_font_set(page1_stream, config->chord_font)) { + printable_item = config_printable_item_get(config->printable_items, "chord"); + if (!printable_item) { + fprintf(stderr, "config_printable_item_get failed.\n"); + return false; + } + if (!out_pdf_font_set(page1_stream, printable_item->style->font)) { 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) { + my_helper(lyrics_line, ch == 0 ? NULL : chords[ch-1], chords[ch]); if (!out_pdf_chord_show(page1_stream, lyrics_line, chords[ch], false)) { fprintf(stderr, "out_pdf_chord_show failed.\n"); return false; } ch++; } + my_helper(lyrics_line, ch == 0 ? NULL : chords[ch-1], chords[ch]); if (!out_pdf_chord_show(page1_stream, lyrics_line, chords[ch], true)) { fprintf(stderr, "out_pdf_chord_show failed.\n"); return false; @@ -644,14 +994,15 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config free(lyrics_line); } char *text; + struct Style *style; int items_count = cho_line_item_count(songs[so]->sections[se]->lines[li]->lyrics); - if (!out_pdf_font_set(page1_stream, config->text_font)) { - 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 (!out_pdf_style_apply(page1_stream, songs[so]->sections[se]->lines[li]->lyrics[ly]->style)) { + fprintf(stderr, "out_pdf_style_apply failed.\n"); + return false; + } + style = songs[so]->sections[se]->lines[li]->lyrics[ly]->style; if (!out_pdf_text_show(page1_stream, text, CONTINUE, false)) { fprintf(stderr, "out_pdf_text_show failed.\n"); return false; @@ -659,6 +1010,11 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config ly++; } text = songs[so]->sections[se]->lines[li]->lyrics[ly]->text; + if (!out_pdf_style_apply(page1_stream, songs[so]->sections[se]->lines[li]->lyrics[ly]->style)) { + fprintf(stderr, "out_pdf_style_apply failed.\n"); + return false; + } + style = songs[so]->sections[se]->lines[li]->lyrics[ly]->style; if (!out_pdf_text_show(page1_stream, text, CONTINUE, true)) { fprintf(stderr, "out_pdf_text_show failed.\n"); return false; @@ -666,13 +1022,13 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config ly = 0; li++; } - current_y -= 10.0; + g_current_y -= 10.0; li = 0; se++; } se = 0; so++; - } + } */ if (!pdfioStreamClose(page1_stream)) { fprintf(stderr, "pdfioStreamClose failed.\n"); return false; diff --git a/out_pdf.h b/out_pdf.h @@ -18,4 +18,25 @@ struct Fnt { pdfio_obj_t *font; }; +struct SpaceNeeded { + int line_item_index; + int text_index; + double amount; +}; + +struct TextLineItem { + char *text; + struct Style *style; + double x; +}; + +struct TextLine { + struct TextLineItem **items; + double height; +}; + +struct Text { + struct TextLine **lines; +}; + bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config *config); diff --git a/todo b/todo @@ -1,3 +1,4 @@ +apply config is cho_parse() instead of out_pdf_new() 'chorus' directive decide how to implement metadata directives @@ -11,3 +12,6 @@ the whole chords advanced stuff transpose define chords chord diagrams +introduce parse errors + still very unclear to me when the parser should warn + and continue execution and when it should fail