lorid

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

commit cc59a9879c0371a85dd89b1a79e04482cf3db342
parent 36abc048f17df2f33f223ff4f21607c64c80e172
Author: nibo <nibo@relim.de>
Date:   Tue, 28 Jan 2025 14:34:13 +0100

Implement tab section

In a tab section markup tags and directives aren't
available.

Diffstat:
Mchordpro.c | 134+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mchordpro.h | 1+
Mconfig.c | 3++-
Mout_pdf.c | 11++++++++---
4 files changed, 143 insertions(+), 6 deletions(-)

diff --git a/chordpro.c b/chordpro.c @@ -25,6 +25,7 @@ static const char *state_enums[] = { "STATE_DIRECTIVE_VALUE", "STATE_CHORD", "STATE_ANNOTATION", + "STATE_TAB", "STATE_MARKUP_TAG_START", "STATE_MARKUP_TAG_END", "STATE_MARKUP_TAG", @@ -3790,6 +3791,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) g_config = config; g_chordpro_filepath = chordpro_filepath; bool is_chord_already_initialized = false; + bool is_maybe_end_of_tab_directive = false; bool directive_has_tag = false; char buf = 0; char prev_buf = '\n'; @@ -3862,6 +3864,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) switch (state) { case STATE_LYRICS: if (prev_buf == '\n' && buf == '#') { + prev_state = STATE_LYRICS; state = STATE_COMMENT; break; } @@ -4179,9 +4182,13 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_log(LOG_ERR, "Invalid directive '%s'.", directive_name); return NULL; } + if (directive->stype == ST_TAB) { + state = STATE_TAB; + } else { + state = STATE_LYRICS; + } cho_directive_free(directive); directive = NULL; - state = STATE_LYRICS; break; } if (buf == '{') { @@ -4681,6 +4688,129 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) (*lines)[li]->text_above[c]->u.annot->text[ann] = buf; ann++; break; + case STATE_TAB: + // INFO: similar to STATE_LYRICS but without markup and directives + if (prev_buf == '\n' && buf == '#') { + prev_state = STATE_TAB; + state = STATE_COMMENT; + break; + } + if (is_maybe_end_of_tab_directive) { + if (buf == '}') { + directive_name[dn] = 0; + dn = 0; + is_maybe_end_of_tab_directive = false; + if (!strcmp(directive_name, "end_of_tab") || !strcmp(directive_name, "eot")) { + cho_line_item_free((*lines)[li]->items[ly]); + free((*lines)[li]->items); + ly = 0; + free((*lines)[li]); + (*lines)[li] = NULL; + se++; + songs[so]->sections = erealloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); + songs[so]->sections[se] = cho_section_new(); + li = 0; + lines = &songs[so]->sections[se]->lines; + *lines = emalloc(sizeof(struct ChoLine *)); + (*lines)[li] = cho_line_new(); + (*lines)[li]->items = emalloc(sizeof(struct ChoLineItem *)); + (*lines)[li]->items[ly] = cho_line_item_new(); + g_current_ttype = TT_TEXT; + state = STATE_LYRICS; + break; + } else { + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = '{'; + te++; + char *k; + for (k = (char *)&directive_name; *k; k++, te++) { + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = *k; + } + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = '}'; + te++; + } + break; + } + if (buf == ' ' || buf == ':') { + directive_name[dn] = 0; + dn = 0; + is_maybe_end_of_tab_directive = false; + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = '{'; + te++; + char *k; + for (k = (char *)&directive_name; *k; k++, te++) { + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = *k; + } + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = buf; + te++; + break; + } + directive_name[dn] = buf; + dn++; + break; + } + if (prev_buf == '\n' && buf == '{') { + is_maybe_end_of_tab_directive = true; + break; + } + if (buf == '\n') { + if (prev_buf == '\\') { + // INFO: This will later overwrite the backslash + te--; + break; + } + if (ta > -1 && !tags[ta]->is_closed && strcmp(tags[ta]->name, "img")) { + cho_log(LOG_ERR, "Tag has to be closed on same line."); + return NULL; + } + if ((*lines)[li]->items[ly]->is_text) { + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = 0; + if (strlen((*lines)[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free((*lines)[li]->items[ly]); + if (ly == 0) { + if ( + !(*lines)[li]->text_above && + (*lines)[li]->btype == BT_LINE + ) { + free((*lines)[li]->items); + free((*lines)[li]); + *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); + (*lines)[li] = cho_line_new(); + (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + (*lines)[li]->items[ly] = cho_line_item_new(); + break; + } + } + } else { + ly++; + } + } else { + ly++; + } + (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + (*lines)[li]->items[ly] = NULL; + ly = 0; + te = 0; + (*lines)[li]->text_above = erealloc((*lines)[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); + (*lines)[li]->text_above[c] = NULL; + c = 0; + li++; + *lines = erealloc(*lines, (li+1) * sizeof(struct ChoLine *)); + (*lines)[li] = cho_line_new(); + (*lines)[li]->items = erealloc((*lines)[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + (*lines)[li]->items[ly] = cho_line_item_new(); + break; + } + (*lines)[li]->items[ly]->u.text->text = erealloc((*lines)[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + (*lines)[li]->items[ly]->u.text->text[te] = buf; + te++; + break; case STATE_MARKUP_TAG_START: MARKUP_TAG_START: if (buf == '>') { @@ -4981,7 +5111,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) break; case STATE_COMMENT: if (buf == '\n') { - state = STATE_LYRICS; + state = prev_state; break; } break; diff --git a/chordpro.h b/chordpro.h @@ -178,6 +178,7 @@ enum State { STATE_DIRECTIVE_VALUE, STATE_CHORD, STATE_ANNOTATION, + STATE_TAB, STATE_MARKUP_TAG_START, STATE_MARKUP_TAG_END, STATE_MARKUP_TAG, diff --git a/config.c b/config.c @@ -446,7 +446,8 @@ config_load_default(void) config->output->styles[TT_GRID]->font->name = strdup(DEFAULT_FONT_FAMILY); config->output->styles[TT_GRID]->font->weight = FW_BOLD; config->output->styles[TT_TAB] = cho_style_new(); - config->output->styles[TT_TAB]->font->name = strdup(DEFAULT_FONT_FAMILY); + config->output->styles[TT_TAB]->font->name = strdup("Courier"); + // config->output->styles[TT_TAB]->font->family = FF_MONOSPACE; config->output->styles[TT_TOC] = cho_style_new(); config->output->styles[TT_TOC]->font->name = strdup(DEFAULT_FONT_FAMILY); config->output->styles[TT_TOC]->font->size = 12.0; diff --git a/out_pdf.c b/out_pdf.c @@ -256,15 +256,19 @@ fontpath_count_fonts(FcChar8 *path) static char * fontpath_find(struct Font *font, enum FontType font_type) { + FcObjectSet *obj; + FcPattern *pattern; char *filepath = NULL; - FcObjectSet *obj = FcObjectSetBuild(FC_FAMILY, FC_SLANT, FC_WEIGHT, FC_FONTFORMAT, FC_FONT_WRAPPER, FC_FILE, NULL); - FcPattern *pattern = FcPatternCreate(); + obj = FcObjectSetBuild(FC_FAMILY, FC_SLANT, FC_WIDTH, FC_WEIGHT, FC_FONTFORMAT, FC_FONT_WRAPPER, FC_FILE, NULL); + pattern = FcPatternCreate(); FcValue family = { .type = FcTypeString, .u.s = (FcChar8 *)font->name }; FcPatternAdd(pattern, FC_FAMILY, family, FcFalse); FcValue font_wrapper = { .type = FcTypeString, .u.s = (FcChar8 *)"SFNT" }; FcPatternAdd(pattern, FC_FONT_WRAPPER, font_wrapper, FcFalse); FcValue variable = { .type = FcTypeBool, .u.b = FcFalse }; FcPatternAdd(pattern, FC_VARIABLE, variable, FcFalse); + FcValue width = { .type = FcTypeInteger, .u.i = FC_WIDTH_NORMAL }; + FcPatternAdd(pattern, FC_WIDTH, width, FcFalse); /* TODO: Also handle FF_NORMAL, FF_SANS and FF_SERIF, but how? */ if (font->family == FF_MONOSPACE) { FcValue spacing = { .type = FcTypeInteger, .u.i = FC_MONO }; @@ -310,7 +314,8 @@ fontpath_find(struct Font *font, enum FontType font_type) FcPatternAdd(pattern, FC_WEIGHT, weight, FcFalse); FcFontSet *set = FcFontList(NULL, pattern, obj); FcChar8 *file; - for (int i=0; i<set->nfont; i++) { + int i; + for (i = 0; i<set->nfont; i++) { if (FcPatternGetString(set->fonts[i], FC_FILE, 0, &file) == FcResultMatch) { if ( !file_extension_equals((const char *)file, "ttc") &&