lorid

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

commit fdafe328e5c6b9b4c4cc7d300cde2f922753e84b
parent 983d4d0ada2f4230ec7c4d70f4e5766c628e1136
Author: nibo <nibo@relim.de>
Date:   Tue, 22 Oct 2024 10:46:16 +0200

Change struct 'ChoLineItem' to contain either text or an image

The struct 'ChoLineItem' is the fundamental type for this
project. Changing its definition meant changing a lot of
code in the whole project.

Also create a basis type for styled text 'ChoText'. This type
can be then used in different places. Every text in this
chordpro implementation should have a corresponding style.

Diffstat:
Mchordpro.c | 859++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Mchordpro.h | 34+++++++++-------------------------
Mout_pdf.c | 69++++++++++++++++++++++++++++++++++++++++-----------------------------
Mutil.c | 14++++++++++++++
Mutil.h | 1+
5 files changed, 521 insertions(+), 456 deletions(-)

diff --git a/chordpro.c b/chordpro.c @@ -1930,266 +1930,6 @@ cho_chord_name_generate(struct ChoChord *chord) return strdup(chord->name); } -static struct ChoAnnotation * -cho_annotation_new(void) -{ - struct ChoAnnotation *annot = malloc(sizeof(struct ChoAnnotation)); - annot->style = cho_style_new_default(); - annot->text = NULL; - return annot; -} - -static void -cho_annotation_free(struct ChoAnnotation *annot) -{ - cho_style_free(annot->style); - free(annot->text); - free(annot); -} - -static struct ChoAnnotation * -cho_annotation_copy(struct ChoAnnotation *annot) -{ - struct ChoAnnotation *copy = malloc(sizeof(struct ChoAnnotation)); - copy->style = cho_style_copy(annot->style); - copy->text = strdup(annot->text); - return copy; -} - -int -cho_text_above_count(struct ChoLineItemAbove **text_above) -{ - int i = 0; - while (text_above[i] != NULL) - i++; - return i; -} - -static void -cho_text_above_free(struct ChoLineItemAbove *text_above) -{ - if (text_above->is_chord) { - cho_chord_free(text_above->u.chord); - } else { - cho_annotation_free(text_above->u.annot); - } - free(text_above); -} - -static struct ChoLineItemAbove * -cho_text_above_copy(struct ChoLineItemAbove *text_above) -{ - struct ChoLineItemAbove *copy = malloc(sizeof(struct ChoLineItemAbove)); - copy->position = text_above->position; - copy->is_chord = text_above->is_chord; - if (copy->is_chord) { - copy->u.chord = cho_chord_copy(text_above->u.chord); - } else { - copy->u.annot = cho_annotation_copy(text_above->u.annot); - } - return copy; -} - -static struct ChoLineItem * -cho_line_item_new(void) -{ - struct ChoLineItem *item = malloc(sizeof(struct ChoLineItem)); - item->style = cho_style_new_default(); - item->text = NULL; - return item; -} - -static void -cho_line_item_free(struct ChoLineItem *item) -{ - cho_style_free(item->style); - free(item->text); - free(item); -} - -static struct ChoLineItem * -cho_line_item_copy(struct ChoLineItem *item) -{ - struct ChoLineItem *copy = malloc(sizeof(struct ChoLineItem)); - copy->style = cho_style_copy(item->style); - copy->text = strdup(item->text); - return copy; -} - -int -cho_line_item_count(struct ChoLineItem **items) -{ - int i = 0; - while (items[i] != NULL) - i++; - return i; -} - -static struct ChoLine * -cho_line_new(void) -{ - struct ChoLine *line = malloc(sizeof(struct ChoLine)); - line->text_above = NULL; - line->lyrics = NULL; - line->btype = BT_LINE; - return line; -} - -static void -cho_line_free(struct ChoLine *line) -{ - struct ChoLineItem **start_lyrics = line->lyrics; - while (*line->lyrics) { - cho_line_item_free(*line->lyrics); - line->lyrics++; - } - struct ChoLineItemAbove **start_text_above = line->text_above; - while (*line->text_above) { - cho_text_above_free(*line->text_above); - line->text_above++; - } - free(start_lyrics); - free(start_text_above); - free(line); -} - -static int -cho_line_compute_chord_position(struct ChoLine *line, int ly, int te) -{ - if (ly == 0) - return te; - ly--; - size_t lyrics_len = 0; - while (ly >= 0) { - lyrics_len += strlen(line->lyrics[ly]->text); - ly--; - } - return lyrics_len + te; -} - -static struct ChoLabel * -cho_section_label_copy(struct ChoLabel *label) -{ - struct ChoLabel *copy = malloc(sizeof(struct ChoLabel)); - copy->name = strdup(label->name); - copy->style = cho_style_copy(label->style); - return copy; -} - -static void -cho_section_label_free(struct ChoLabel *label) -{ - free(label->name); - cho_style_free(label->style); - free(label); -} - -static struct ChoSection * -cho_section_new(void) -{ - struct ChoSection *section = malloc(sizeof(struct ChoSection)); - section->type = ST_EMPTY; - section->label = NULL; - section->lines = NULL; - return section; -} - -static void -cho_section_free(struct ChoSection *section) -{ - if (section->label) { - cho_section_label_free(section->label); - } - struct ChoLine **start_lines = section->lines; - while (*section->lines) { - cho_line_free(*section->lines); - section->lines++; - } - free(start_lines); - free(section); -} - -static struct ChoSection * -cho_section_copy(struct ChoSection *section) -{ - struct ChoSection *copy = malloc(sizeof(struct ChoSection)); - copy->type = section->type; - if (section->label) { - copy->label = cho_section_label_copy(section->label); - } else { - copy->label = NULL; - } - copy->lines = NULL; - int li, c, ly; - for (li = 0; section->lines[li]; li++) { - copy->lines = realloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); - copy->lines[li] = cho_line_new(); - copy->lines[li]->btype = section->lines[li]->btype; - for (c = 0; section->lines[li]->text_above[c]; c++) { - copy->lines[li]->text_above = realloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - copy->lines[li]->text_above[c] = cho_text_above_copy(section->lines[li]->text_above[c]); - } - copy->lines[li]->text_above = realloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - copy->lines[li]->text_above[c] = NULL; - for (ly = 0; section->lines[li]->lyrics[ly]; ly++) { - copy->lines[li]->lyrics = realloc(copy->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - copy->lines[li]->lyrics[ly] = cho_line_item_copy(section->lines[li]->lyrics[ly]); - } - copy->lines[li]->lyrics = realloc(copy->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - copy->lines[li]->lyrics[ly] = NULL; - } - copy->lines = realloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); - copy->lines[li] = NULL; - return copy; -} - -static struct ChoSong * -cho_song_new(void) -{ - struct ChoSong *song = malloc(sizeof(struct ChoSong)); - song->metadata = NULL; - song->sections = NULL; - return song; -} - -int -cho_song_count(struct ChoSong **songs) -{ - int i = 0; - while (songs[i] != NULL) - i++; - return i; -} - -static void -cho_song_free(struct ChoSong *song) -{ - struct ChoMetadata **start_meta = song->metadata; - struct ChoSection **start_section = song->sections; - while (*song->metadata) { - cho_metadata_free(*song->metadata); - song->metadata++; - } - while (*song->sections) { - cho_section_free(*song->sections); - song->sections++; - } - free(start_meta); - free(start_section); - free(song); -} - -void -cho_songs_free(struct ChoSong **songs) -{ - struct ChoSong **start_song = songs; - while (*songs) { - cho_song_free(*songs); - songs++; - } - free(start_song); -} - static struct ChoImage * cho_image_new(void) { @@ -2242,6 +1982,30 @@ cho_image_free(struct ChoImage *image) } static struct ChoImage * +cho_image_copy(struct ChoImage *image) +{ + struct ChoImage *copy = malloc(sizeof(struct ChoImage)); + copy->is_asset = image->is_asset; + copy->id = strdup(image->id); + copy->src = strdup(image->src); + copy->width = double_or_percent_copy(image->width); + copy->height = double_or_percent_copy(image->height); + copy->scale = double_or_percent_copy(image->scale); + copy->align = image->align; + copy->border = image->border; + copy->spread_space = double_or_percent_copy(image->spread_space); + copy->href = strdup(image->href); + copy->x = double_or_percent_copy(image->x); + copy->y = double_or_percent_copy(image->y); + copy->anchor = image->anchor; + copy->dx = image->dx; + copy->dy = image->dy; + copy->w = image->w; + copy->h = image->h; + return copy; +} + +static struct ChoImage * cho_image_find_asset(const char *id) { int i; @@ -2531,6 +2295,252 @@ cho_image_directive_parse(const char *str) { } */ +static struct ChoText * +cho_text_new(void) +{ + struct ChoText *text = malloc(sizeof(struct ChoText)); + text->style = cho_style_new_default(); + text->text = NULL; + return text; +} + +static void +cho_text_free(struct ChoText *text) +{ + cho_style_free(text->style); + free(text->text); + free(text); +} + +static struct ChoText * +cho_text_copy(struct ChoText *text) +{ + struct ChoText *copy = malloc(sizeof(struct ChoText)); + copy->style = cho_style_copy(text->style); + copy->text = strdup(text->text); + return copy; +} + +int +cho_text_above_count(struct ChoLineItemAbove **text_above) +{ + int i = 0; + while (text_above[i] != NULL) + i++; + return i; +} + +static void +cho_text_above_free(struct ChoLineItemAbove *text_above) +{ + if (text_above->is_chord) { + cho_chord_free(text_above->u.chord); + } else { + cho_text_free(text_above->u.annot); + } + free(text_above); +} + +static struct ChoLineItemAbove * +cho_text_above_copy(struct ChoLineItemAbove *text_above) +{ + struct ChoLineItemAbove *copy = malloc(sizeof(struct ChoLineItemAbove)); + copy->position = text_above->position; + copy->is_chord = text_above->is_chord; + if (copy->is_chord) { + copy->u.chord = cho_chord_copy(text_above->u.chord); + } else { + copy->u.annot = cho_text_copy(text_above->u.annot); + } + return copy; +} + +static struct ChoLineItem * +cho_line_item_new(void) +{ + struct ChoLineItem *item = malloc(sizeof(struct ChoLineItem)); + item->is_text = true; + item->u.text = cho_text_new(); + return item; +} + +static void +cho_line_item_free(struct ChoLineItem *item) +{ + if (item->is_text) { + cho_text_free(item->u.text); + } else { + cho_image_free(item->u.image); + } + free(item); +} + +static struct ChoLineItem * +cho_line_item_copy(struct ChoLineItem *item) +{ + struct ChoLineItem *copy = malloc(sizeof(struct ChoLineItem)); + if (item->is_text) { + copy->is_text = true; + copy->u.text = cho_text_copy(item->u.text); + } else { + copy->is_text = false; + copy->u.image = cho_image_copy(item->u.image); + } + return copy; +} + +static struct ChoLine * +cho_line_new(void) +{ + struct ChoLine *line = malloc(sizeof(struct ChoLine)); + line->text_above = NULL; + line->items = NULL; + line->btype = BT_LINE; + return line; +} + +static void +cho_line_free(struct ChoLine *line) +{ + struct ChoLineItem **start_items = line->items; + while (*line->items) { + cho_line_item_free(*line->items); + line->items++; + } + struct ChoLineItemAbove **start_text_above = line->text_above; + while (*line->text_above) { + cho_text_above_free(*line->text_above); + line->text_above++; + } + free(start_items); + free(start_text_above); + free(line); +} + +/* TODO: Does this still work? */ +static int +cho_line_compute_chord_position(struct ChoLine *line, int ly, int te) +{ + if (ly == 0) { + return te; + } + ly--; + size_t lyrics_len = 0; + while (ly >= 0) { + if (line->items[ly]->is_text) { + lyrics_len += strlen(line->items[ly]->u.text->text); + } + ly--; + } + return lyrics_len + te; +} + +static struct ChoSection * +cho_section_new(void) +{ + struct ChoSection *section = malloc(sizeof(struct ChoSection)); + section->type = ST_EMPTY; + section->label = NULL; + section->lines = NULL; + return section; +} + +static void +cho_section_free(struct ChoSection *section) +{ + if (section->label) { + cho_text_free(section->label); + } + struct ChoLine **start_lines = section->lines; + while (*section->lines) { + cho_line_free(*section->lines); + section->lines++; + } + free(start_lines); + free(section); +} + +static struct ChoSection * +cho_section_copy(struct ChoSection *section) +{ + struct ChoSection *copy = malloc(sizeof(struct ChoSection)); + copy->type = section->type; + if (section->label) { + copy->label = cho_text_copy(section->label); + } else { + copy->label = NULL; + } + copy->lines = NULL; + int li, c, ly; + for (li = 0; section->lines[li]; li++) { + copy->lines = realloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); + copy->lines[li] = cho_line_new(); + copy->lines[li]->btype = section->lines[li]->btype; + for (c = 0; section->lines[li]->text_above[c]; c++) { + copy->lines[li]->text_above = realloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); + copy->lines[li]->text_above[c] = cho_text_above_copy(section->lines[li]->text_above[c]); + } + copy->lines[li]->text_above = realloc(copy->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); + copy->lines[li]->text_above[c] = NULL; + for (ly = 0; section->lines[li]->items[ly]; ly++) { + copy->lines[li]->items = realloc(copy->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + copy->lines[li]->items[ly] = cho_line_item_copy(section->lines[li]->items[ly]); + } + copy->lines[li]->items = realloc(copy->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + copy->lines[li]->items[ly] = NULL; + } + copy->lines = realloc(copy->lines, (li+1) * sizeof(struct ChoLine *)); + copy->lines[li] = NULL; + return copy; +} + +static struct ChoSong * +cho_song_new(void) +{ + struct ChoSong *song = malloc(sizeof(struct ChoSong)); + song->metadata = NULL; + song->sections = NULL; + return song; +} + +int +cho_song_count(struct ChoSong **songs) +{ + int i = 0; + while (songs[i] != NULL) + i++; + return i; +} + +static void +cho_song_free(struct ChoSong *song) +{ + struct ChoMetadata **start_meta = song->metadata; + struct ChoSection **start_section = song->sections; + while (*song->metadata) { + cho_metadata_free(*song->metadata); + song->metadata++; + } + while (*song->sections) { + cho_section_free(*song->sections); + song->sections++; + } + free(start_meta); + free(start_section); + free(song); +} + +void +cho_songs_free(struct ChoSong **songs) +{ + struct ChoSong **start_song = songs; + while (*songs) { + cho_song_free(*songs); + songs++; + } + free(start_song); +} + static struct ChoSection * cho_find_previous_chorus(struct ChoSection **sections, int se) { @@ -2985,8 +2995,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se] = cho_section_new(); songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); struct Tag **tags = NULL; struct Style *tag_style; struct StyleProperty sprop; @@ -3018,16 +3028,20 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) break; } if (buf == '<') { - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - te = 0; - if (strlen(songs[so]->sections[se]->lines[li]->lyrics[ly]->text) == 0) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + te = 0; + if (strlen(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + } else { + ly++; + } } else { ly++; } - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); prev_state = STATE_LYRICS; state = STATE_MARKUP_TAG; break; @@ -3042,29 +3056,33 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_log(LOG_ERR, "Tag has to be closed on same line."); return NULL; } - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - if (strlen(songs[so]->sections[se]->lines[li]->lyrics[ly]->text) == 0) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); - if (ly == 0) { - if ( - !songs[so]->sections[se]->lines[li]->text_above && - songs[so]->sections[se]->lines[li]->btype == BT_LINE - ) { - free(songs[so]->sections[se]->lines[li]->lyrics); - free(songs[so]->sections[se]->lines[li]); - songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); - songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); - break; + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + if (strlen(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + if (ly == 0) { + if ( + !songs[so]->sections[se]->lines[li]->text_above && + songs[so]->sections[se]->lines[li]->btype == BT_LINE + ) { + free(songs[so]->sections[se]->lines[li]->items); + free(songs[so]->sections[se]->lines[li]); + songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); + songs[so]->sections[se]->lines[li] = cho_line_new(); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); + break; + } } + } else { + ly++; } } else { ly++; } - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = NULL; + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = NULL; ly = 0; te = 0; songs[so]->sections[se]->lines[li]->text_above = realloc(songs[so]->sections[se]->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); @@ -3073,12 +3091,12 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) li++; songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); break; } - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = buf; + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = buf; te++; break; case STATE_DIRECTIVE_NAME: @@ -3099,8 +3117,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) memset(custom_directive, 0, sizeof(custom_directive)); strcpy(custom_directive, &directive_name[9]); } - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); - free(songs[so]->sections[se]->lines[li]->lyrics); + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + free(songs[so]->sections[se]->lines[li]->items); ly = 0; free(songs[so]->sections[se]->lines[li]); songs[so]->sections[se]->lines[li] = NULL; @@ -3111,8 +3129,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) li = 0; songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); break; case POS_END: if (directive->stype == songs[so]->sections[se]->type) { @@ -3121,8 +3139,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) break; } } - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); - free(songs[so]->sections[se]->lines[li]->lyrics); + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + free(songs[so]->sections[se]->lines[li]->items); ly = 0; free(songs[so]->sections[se]->lines[li]); songs[so]->sections[se]->lines[li] = NULL; @@ -3132,8 +3150,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) li = 0; songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); } break; case POS_EMPTY: @@ -3141,11 +3159,13 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) chorus = cho_find_previous_chorus(songs[so]->sections, se); if (chorus) { if (config->output->chorus->quote) { - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + } ly++; - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = NULL; + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = NULL; ly = 0; te = 0; songs[so]->sections[se]->lines[li]->text_above = realloc(songs[so]->sections[se]->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); @@ -3163,27 +3183,31 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se] = cho_section_new(); songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); } else { if (chorus->label) { - label = strdup(chorus->label->name); + label = strdup(chorus->label->text); } else { label = strdup(config->output->chorus->label); } - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - if (strlen(songs[so]->sections[se]->lines[li]->lyrics[ly]->text) == 0) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + if (strlen(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + } else { + ly++; + } } else { ly++; } - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); g_current_ftype = SF_LABEL; - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_new_default(); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = label; + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_new_default(); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = label; te += strlen(label); } } else { @@ -3191,19 +3215,23 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_log(LOG_WARN, "Can't quote chorus because it's not defined previously."); } label = strdup(config->output->chorus->label); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - if (strlen(songs[so]->sections[se]->lines[li]->lyrics[ly]->text) == 0) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + if (strlen(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + } else { + ly++; + } } else { ly++; } - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); g_current_ftype = SF_LABEL; - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_new_default(); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = label; + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_new_default(); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = label; te += strlen(label); } break; @@ -3228,8 +3256,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) return NULL; case DT_PREAMBLE: // INFO: The only preamble directive is 'new_song' - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); - free(songs[so]->sections[se]->lines[li]->lyrics); + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + free(songs[so]->sections[se]->lines[li]->items); free(songs[so]->sections[se]->lines[li]); songs[so]->sections[se]->lines[li] = NULL; songs[so]->metadata = realloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); @@ -3252,8 +3280,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se] = cho_section_new(); songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); break; case DT_FONT: sprop.ftype = directive->ftype; @@ -3335,8 +3363,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) memset(custom_directive, 0, sizeof(custom_directive)); strcpy(custom_directive, &directive_name[9]); } - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); - free(songs[so]->sections[se]->lines[li]->lyrics); + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + free(songs[so]->sections[se]->lines[li]->items); ly = 0; free(songs[so]->sections[se]->lines[li]); songs[so]->sections[se]->lines[li] = NULL; @@ -3344,7 +3372,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections = realloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); songs[so]->sections[se] = cho_section_new(); songs[so]->sections[se]->type = directive->stype; - songs[so]->sections[se]->label = malloc(sizeof(struct ChoLabel)); + songs[so]->sections[se]->label = malloc(sizeof(struct ChoText)); if (strstr(directive_value, "=")) { label = cho_directive_label_parse(directive_name, directive_value); if (!label) { @@ -3352,21 +3380,21 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_log(LOG_ERR, "Failed to parse the section label. You have to ways of specifying a label:\n\t\t\t1. {start_of_*: label=\"Label name\"}\n\t\t\t2. {start_of*: Label name}"); return NULL; } - songs[so]->sections[se]->label->name = label; + songs[so]->sections[se]->label->text = label; } else { - songs[so]->sections[se]->label->name = str_remove_leading_whitespace(directive_value); + songs[so]->sections[se]->label->text = str_remove_leading_whitespace(directive_value); } songs[so]->sections[se]->label->style = cho_style_new_from_config(SF_LABEL); li = 0; songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); break; case POS_END: if (directive->stype == songs[so]->sections[se]->type) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); - free(songs[so]->sections[se]->lines[li]->lyrics); + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + free(songs[so]->sections[se]->lines[li]->items); ly = 0; free(songs[so]->sections[se]->lines[li]); songs[so]->sections[se]->lines[li] = NULL; @@ -3376,8 +3404,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) li = 0; songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); } break; case POS_EMPTY: @@ -3386,11 +3414,13 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) chorus = cho_find_previous_chorus(songs[so]->sections, se); if (chorus) { if (config->output->chorus->quote) { - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + } ly++; - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = NULL; + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = NULL; ly = 0; te = 0; songs[so]->sections[se]->lines[li]->text_above = realloc(songs[so]->sections[se]->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); @@ -3404,10 +3434,10 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections = realloc(songs[so]->sections, (se+1) * sizeof(struct ChoSection *)); songs[so]->sections[se] = cho_section_copy(chorus); if (songs[so]->sections[se]->label) { - free(songs[so]->sections[se]->label->name); - songs[so]->sections[se]->label->name = label; + free(songs[so]->sections[se]->label->text); + songs[so]->sections[se]->label->text = label; } else { - songs[so]->sections[se]->label = malloc(sizeof(struct ChoLabel)); + songs[so]->sections[se]->label = malloc(sizeof(struct ChoText)); g_current_ftype = SF_LABEL; songs[so]->sections[se]->label->style = cho_style_new_default(); } @@ -3416,41 +3446,49 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se] = cho_section_new(); songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); - songs[so]->sections[se]->lines[li]->lyrics = malloc(sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); + songs[so]->sections[se]->lines[li]->items = malloc(sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); } else { - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - if (strlen(songs[so]->sections[se]->lines[li]->lyrics[ly]->text) == 0) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + if (strlen(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + } else { + ly++; + } } else { ly++; } - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); g_current_ftype = SF_LABEL; - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_new_default(); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = label; + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_new_default(); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = label; te += strlen(label); } } else { if (config->output->chorus->quote) { cho_log(LOG_WARN, "Can't quote chorus because it's not defined previously."); } - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - if (strlen(songs[so]->sections[se]->lines[li]->lyrics[ly]->text) == 0) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + if (strlen(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + } else { + ly++; + } } else { ly++; } - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); g_current_ftype = SF_LABEL; - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_new_default(); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = label; + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_new_default(); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = label; te += strlen(label); } break; @@ -3490,19 +3528,23 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) } break; case DT_FORMATTING: - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - if (strlen(songs[so]->sections[se]->lines[li]->lyrics[ly]->text) == 0) { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + if (strlen(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) == 0) { + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + } else { + ly++; + } } else { ly++; } - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = cho_line_item_new(); - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_copy(directive->style); + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_copy(directive->style); char *trimmed_directive_value = str_remove_leading_whitespace(directive_value); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = trimmed_directive_value; + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = trimmed_directive_value; te += strlen(trimmed_directive_value); break; case DT_IMAGE: @@ -3522,8 +3564,18 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) g_ia++; } else { // Add image to ChoSong somehow - cho_debug_image_print(image); - cho_image_free(image); + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + te = 0; + } + ly++; + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = cho_line_item_new(); + cho_text_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text); + songs[so]->sections[se]->lines[li]->items[ly]->is_text = false; + songs[so]->sections[se]->lines[li]->items[ly]->u.image = image; + // cho_debug_image_print(image); } break; case DT_PREAMBLE: @@ -3651,7 +3703,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se]->lines[li]->text_above[c]->is_chord = false; chord_pos = cho_line_compute_chord_position(songs[so]->sections[se]->lines[li], ly, te); songs[so]->sections[se]->lines[li]->text_above[c]->position = chord_pos; - songs[so]->sections[se]->lines[li]->text_above[c]->u.annot = cho_annotation_new(); + songs[so]->sections[se]->lines[li]->text_above[c]->u.annot = cho_text_new(); state = STATE_ANNOTATION; break; } @@ -3698,7 +3750,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se]->lines[li]->text_above[c] = malloc(sizeof(struct ChoLineItemAbove)); songs[so]->sections[se]->lines[li]->text_above[c]->is_chord = false; songs[so]->sections[se]->lines[li]->text_above[c]->position = chord_pos; - songs[so]->sections[se]->lines[li]->text_above[c]->u.annot = cho_annotation_new(); + songs[so]->sections[se]->lines[li]->text_above[c]->u.annot = cho_text_new(); } prev_state = STATE_ANNOTATION; state = STATE_MARKUP_TAG; @@ -3726,8 +3778,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) tags[ta]->style = tag_style; switch (prev_state) { case STATE_LYRICS: - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_copy(tag_style); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_copy(tag_style); break; case STATE_CHORD: cho_style_free(songs[so]->sections[se]->lines[li]->text_above[c]->u.chord->style); @@ -3841,8 +3893,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) tags[ta]->style = tag_style; switch (prev_state) { case STATE_LYRICS: - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_copy(tag_style); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_copy(tag_style); break; case STATE_CHORD: cho_style_free(songs[so]->sections[se]->lines[li]->text_above[c]->u.chord->style); @@ -3917,8 +3969,8 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) tags[ta]->style = tag_style; switch (prev_state) { case STATE_LYRICS: - cho_style_free(songs[so]->sections[se]->lines[li]->lyrics[ly]->style); - songs[so]->sections[se]->lines[li]->lyrics[ly]->style = cho_style_copy(tag_style); + cho_style_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text->style); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->style = cho_style_copy(tag_style); break; case STATE_CHORD: cho_style_free(songs[so]->sections[se]->lines[li]->text_above[c]->u.chord->style); @@ -3976,23 +4028,26 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) e++; } free(tags); - if (songs[so]->sections[se]->lines[li]->lyrics[ly]->text) { - songs[so]->sections[se]->lines[li]->lyrics[ly]->text = realloc(songs[so]->sections[se]->lines[li]->lyrics[ly]->text, (te+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->lyrics[ly]->text[te] = 0; - ly++; - songs[so]->sections[se]->lines[li]->lyrics = realloc(songs[so]->sections[se]->lines[li]->lyrics, (ly+1) * sizeof(struct ChoLineItem *)); - songs[so]->sections[se]->lines[li]->lyrics[ly] = NULL; - songs[so]->sections[se]->lines[li]->text_above = realloc(songs[so]->sections[se]->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); - songs[so]->sections[se]->lines[li]->text_above[c] = NULL; - li++; - songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); - songs[so]->sections[se]->lines[li] = NULL; - } else { - cho_line_item_free(songs[so]->sections[se]->lines[li]->lyrics[ly]); - free(songs[so]->sections[se]->lines[li]->lyrics); - free(songs[so]->sections[se]->lines[li]->text_above); - free(songs[so]->sections[se]->lines[li]); - songs[so]->sections[se]->lines[li] = NULL; + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + if (songs[so]->sections[se]->lines[li]->items[ly]->u.text->text) { + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text = realloc(songs[so]->sections[se]->lines[li]->items[ly]->u.text->text, (te+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->items[ly]->u.text->text[te] = 0; + ly++; + songs[so]->sections[se]->lines[li]->items = realloc(songs[so]->sections[se]->lines[li]->items, (ly+1) * sizeof(struct ChoLineItem *)); + songs[so]->sections[se]->lines[li]->items[ly] = NULL; + songs[so]->sections[se]->lines[li]->text_above = realloc(songs[so]->sections[se]->lines[li]->text_above, (c+1) * sizeof(struct ChoLineItemAbove *)); + songs[so]->sections[se]->lines[li]->text_above[c] = NULL; + li++; + songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *)); + songs[so]->sections[se]->lines[li] = NULL; + } else { + // cho_text_free(songs[so]->sections[se]->lines[li]->items[ly]->u.text); + cho_line_item_free(songs[so]->sections[se]->lines[li]->items[ly]); + free(songs[so]->sections[se]->lines[li]->items); + free(songs[so]->sections[se]->lines[li]->text_above); + free(songs[so]->sections[se]->lines[li]); + songs[so]->sections[se]->lines[li] = NULL; + } } if (!cho_style_reset_default()) { LOG_DEBUG("cho_style_reset_default failed."); diff --git a/chordpro.h b/chordpro.h @@ -293,14 +293,17 @@ struct ChoChord { char *bass; }; -struct ChoAnnotation { +struct ChoText { struct Style *style; char *text; }; struct ChoLineItem { - struct Style *style; - char *text; + bool is_text; + union { + struct ChoImage *image; + struct ChoText *text; + } u; }; struct ChoLineItemAbove { @@ -308,38 +311,19 @@ struct ChoLineItemAbove { bool is_chord; union { struct ChoChord *chord; - struct ChoAnnotation *annot; + struct ChoText *annot; } u; }; struct ChoLine { struct ChoLineItemAbove **text_above; - struct ChoLineItem **lyrics; - enum BreakType btype; -}; - -/* struct ChoSongLine { - struct ChoLineItemAbove **text_above; - struct ChoLineItem **lyrics; + struct ChoLineItem **items; enum BreakType btype; }; -struct ChoLine { - bool is_song_line; - union { - struct ChoSongLine *s; - struct ChoImage *i; - } u; -}; */ - -struct ChoLabel { - char *name; - struct Style *style; -}; - struct ChoSection { enum SectionType type; - struct ChoLabel *label; + struct ChoText *label; struct ChoLine **lines; }; diff --git a/out_pdf.c b/out_pdf.c @@ -133,10 +133,12 @@ out_pdf_font_get_all(struct ChoSong **songs, struct Config *config) ch++; } ch = 0; - while (songs[so]->sections[se]->lines[li]->lyrics[ly] != NULL) { - style = songs[so]->sections[se]->lines[li]->lyrics[ly]->style; - if (style->font->name) { - out_pdf_add_fonts(style->font, &fonts); + while (songs[so]->sections[se]->lines[li]->items[ly] != NULL) { + if (songs[so]->sections[se]->lines[li]->items[ly]->is_text) { + style = songs[so]->sections[se]->lines[li]->items[ly]->u.text->style; + if (style->font->name) { + out_pdf_add_fonts(style->font, &fonts); + } } ly++; } @@ -660,6 +662,7 @@ out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y) return true; } +/* TODO: Does this still work? */ static double line_width_until_chord(struct ChoLine *line, struct ChoLineItemAbove *text_above, struct SpaceNeeded *space) { @@ -670,8 +673,8 @@ line_width_until_chord(struct ChoLine *line, struct ChoLineItemAbove *text_above 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++) { + for (ly = 0; line->items[ly]; ly++) { + for (i = 0; line->items[ly]->u.text->text[i]; i++) { if (pos == text_above->position) { last_ly = ly; last_i = i; @@ -688,22 +691,26 @@ line_width_until_chord(struct ChoLine *line, struct ChoLineItemAbove *text_above 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 (!font_obj) { - LOG_DEBUG("out_pdf_fnt_obj_get_by_name failed."); - return EMPTY; - } - 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); + for (ly = 0; line->items[ly] && ly <= last_ly; ly++) { + if (line->items[ly]->is_text) { + name = out_pdf_fnt_name_create(line->items[ly]->u.text->style->font); + font_obj = out_pdf_fnt_obj_get_by_name(name); + if (!font_obj) { + LOG_DEBUG("out_pdf_fnt_obj_get_by_name failed."); + return EMPTY; + } + if (ly == last_ly) { + char tmp[strlen(line->items[ly]->u.text->text)+1]; + strcpy((char *)&tmp, line->items[ly]->u.text->text); + tmp[last_i] = 0; + width += pdfioContentTextMeasure(font_obj, (const char *)&tmp, line->items[ly]->u.text->style->font->size); + } else { + width += pdfioContentTextMeasure(font_obj, line->items[ly]->u.text->text, line->items[ly]->u.text->style->font->size); + } + free(name); } else { - width += pdfioContentTextMeasure(font_obj, line->lyrics[ly]->text, line->lyrics[ly]->style->font->size); + // TODO: calculate width of image and append to 'width' var. } - free(name); } return width; } @@ -894,7 +901,7 @@ text_create(struct ChoSong **songs, struct Config *config) tl++; for (se = 0; songs[so]->sections[se]; se++) { if (songs[so]->sections[se]->label) { - fits = text_fits(songs[so]->sections[se]->label->name, songs[so]->sections[se]->label->style); + fits = text_fits(songs[so]->sections[se]->label->text, songs[so]->sections[se]->label->style); if (fits == B_ERROR) { LOG_DEBUG("text_fits failed."); return NULL; @@ -904,7 +911,7 @@ text_create(struct ChoSong **songs, struct Config *config) 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]->label->name); + text[t]->lines[tl]->items[0]->text = strdup(songs[so]->sections[se]->label->text); text[t]->lines[tl]->items[0]->style = cho_style_copy(songs[so]->sections[se]->label->style); text[t]->lines[tl]->items[0]->x = MARGIN_HORIZONTAL; text[t]->lines[tl]->items[1] = NULL; @@ -918,7 +925,7 @@ text_create(struct ChoSong **songs, struct Config *config) tl++; } else { util_log(LOG_WARN, "Section name doesn't fit on one line."); - text_lines = text_split_by_whitespace(songs[so]->sections[se]->label->name, songs[so]->sections[se]->label->style); + text_lines = text_split_by_whitespace(songs[so]->sections[se]->label->text, songs[so]->sections[se]->label->style); for (int i = 0; text_lines[i]; i++) { width = text_width(text_lines[i]->items[0]); if (width == EMPTY) { @@ -1046,7 +1053,11 @@ text_create(struct ChoSong **songs, struct Config *config) 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++) { + for (ly = 0; lines[li]->items[ly]; ly++) { + if (!lines[li]->items[ly]->is_text) { + printf("src: %s\n", lines[li]->items[ly]->u.image->src); + continue; + } // out_pdf_text_find_fitting_length(lines[li]->lyrics[ly]); int ii; double last_text_line_item_width; @@ -1054,7 +1065,7 @@ text_create(struct ChoSong **songs, struct Config *config) 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_copy(lines[li]->lyrics[ly]->style); + text[t]->lines[tl]->items[tli]->style = cho_style_copy(lines[li]->items[ly]->u.text->style); if (ly == 0) { text[t]->lines[tl]->items[tli]->x = MARGIN_HORIZONTAL; } else { @@ -1066,10 +1077,10 @@ text_create(struct ChoSong **songs, struct Config *config) 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++) { + for (ii = 0; lines[li]->items[ly]->u.text->text[ii] != 0; ii++) { if (spaces && (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]; + text[t]->lines[tl]->items[tli]->text[tlii] = lines[li]->items[ly]->u.text->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; @@ -1078,7 +1089,7 @@ text_create(struct ChoSong **songs, struct Config *config) 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_copy(lines[li]->lyrics[ly]->style); + text[t]->lines[tl]->items[tli]->style = cho_style_copy(lines[li]->items[ly]->u.text->style); last_text_line_item_width = text_width(text[t]->lines[tl]->items[tli-1]); if (last_text_line_item_width == EMPTY) { LOG_DEBUG("text_width failed."); @@ -1087,7 +1098,7 @@ text_create(struct ChoSong **songs, struct Config *config) 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]; + text[t]->lines[tl]->items[tli]->text[tlii] = lines[li]->items[ly]->u.text->text[ii]; tlii++; } } diff --git a/util.c b/util.c @@ -293,6 +293,20 @@ double_or_percent_to_string(struct DoubleOrPercent *dop) } struct DoubleOrPercent * +double_or_percent_copy(struct DoubleOrPercent *dop) +{ + struct DoubleOrPercent *copy = malloc(sizeof(struct DoubleOrPercent)); + if (dop->is_percent) { + copy->is_percent = true; + copy->u.p = dop->u.p; + } else { + copy->is_percent = false; + copy->u.d = dop->u.d; + } + return copy; +} + +struct DoubleOrPercent * double_or_percent_create(const char *str) { struct DoubleOrPercent *dop = malloc(sizeof(struct DoubleOrPercent)); diff --git a/util.h b/util.h @@ -49,4 +49,5 @@ char *filepath_basename(const char *path); char *filepath_dirname(const char *path); const char *double_or_percent_to_string(struct DoubleOrPercent *dop); +struct DoubleOrPercent *double_or_percent_copy(struct DoubleOrPercent *dop); struct DoubleOrPercent *double_or_percent_create(const char *str);