commit 10908ceb7380ef4261ac978f76f3436ac67c678d
parent 95d0641f560ac7c9d85870d25b1618ba7ff83dbc
Author: nibo <nibo@relim.de>
Date: Sun, 4 Aug 2024 11:19:02 +0200
Implement chord style; Implement annotations
Diffstat:
| M | Makefile | | | 4 | +++- |
| M | chordpro.c | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
| M | chordpro.h | | | 22 | ++++++++++++++++++---- |
| M | config.c | | | 7 | +++++-- |
| M | out_pdf.c | | | 131 | ++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------- |
| M | out_pdf.h | | | 6 | ++++++ |
6 files changed, 193 insertions(+), 69 deletions(-)
diff --git a/Makefile b/Makefile
@@ -11,4 +11,6 @@ debug:
$(CC) ${CFLAGS} -g ${SRC} -o lorid ${LDFLAGS}
fontconfig:
$(CC) -g chordpro.c fontconfig.c -o fontconfig -lfontconfig
-.PHONY: all debug fontconfig
+parser:
+ $(CC) ${CFLAGS} -g util.c config.c chordpro.c lorid.c -o parser -ltoml
+.PHONY: all debug fontconfig parser
diff --git a/chordpro.c b/chordpro.c
@@ -66,6 +66,7 @@ struct StyleProperty default_style_properties[18] = {
};
static enum SongFragmentType g_current_ftype = SF_TEXT;
+static enum SongFragmentType g_prev_ftype = SF_TEXT;
static struct Config *g_config = NULL;
static const char *the_state(enum State state)
@@ -228,6 +229,8 @@ const char *the_song_fragment_type(enum SongFragmentType ftype)
return "SF_EMPTY";
case SF_CHORD:
return "SF_CHORD";
+ case SF_ANNOT:
+ return "SF_ANNOT";
case SF_CHORUS:
return "SF_CHORUS";
case SF_FOOTER:
@@ -756,9 +759,12 @@ struct Style *cho_style_new_from_config(void)
{
struct PrintableItem *printable_item;
switch (g_current_ftype) {
- /* case SF_CHORD:
+ case SF_CHORD:
printable_item = config_printable_item_get(g_config->printable_items, "chord");
- return cho_style_duplicate(printable_item->style); */
+ return cho_style_duplicate(printable_item->style);
+ case SF_ANNOT:
+ printable_item = config_printable_item_get(g_config->printable_items, "annotation");
+ 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);
@@ -1362,15 +1368,23 @@ static struct ChoMetadata *cho_metadata_split(const char *directive_value)
static struct ChoChord *cho_chord_new(void)
{
struct ChoChord *chord = malloc(sizeof(struct ChoChord));
- chord->position = -1;
+ chord->style = cho_style_new_default();
chord->chord = NULL;
return chord;
}
-int cho_chord_count(struct ChoChord **chords)
+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;
+}
+
+int cho_text_above_count(struct ChoLineItemAbove **text_above)
{
int i = 0;
- while (chords[i] != NULL)
+ while (text_above[i] != NULL)
i++;
return i;
}
@@ -1378,7 +1392,7 @@ int cho_chord_count(struct ChoChord **chords)
static struct ChoLine *cho_line_new(void)
{
struct ChoLine *line = malloc(sizeof(struct ChoLine));
- line->chords = NULL;
+ line->text_above = NULL;
line->lyrics = NULL;
return line;
}
@@ -1467,13 +1481,21 @@ static void cho_song_free(struct ChoSong *song)
free(song->sections[i]->lines[k]->lyrics[ly]);
ly++;
}
- while (song->sections[i]->lines[k]->chords[c] != NULL) {
- free(song->sections[i]->lines[k]->chords[c]->chord);
- free(song->sections[i]->lines[k]->chords[c]);
+ while (song->sections[i]->lines[k]->text_above[c] != NULL) {
+ if (song->sections[i]->lines[k]->text_above[c]->is_chord) {
+ cho_style_free(song->sections[i]->lines[k]->text_above[c]->u.chord->style);
+ free(song->sections[i]->lines[k]->text_above[c]->u.chord->chord);
+ free(song->sections[i]->lines[k]->text_above[c]->u.chord);
+ } else {
+ cho_style_free(song->sections[i]->lines[k]->text_above[c]->u.annot->style);
+ free(song->sections[i]->lines[k]->text_above[c]->u.annot->text);
+ free(song->sections[i]->lines[k]->text_above[c]->u.annot);
+ }
+ free(song->sections[i]->lines[k]->text_above[c]);
c++;
}
free(song->sections[i]->lines[k]->lyrics);
- free(song->sections[i]->lines[k]->chords);
+ free(song->sections[i]->lines[k]->text_above);
free(song->sections[i]->lines[k]);
ly = 0;
c = 0;
@@ -1781,6 +1803,7 @@ struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config)
int at = 0;
int atn = 0;
int atv = 0;
+ int ann = 0;
int chord_pos;
size_t read;
enum AttrValueSyntax avs = AVS_NO;
@@ -1837,7 +1860,7 @@ struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config)
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]->chords) {
+ if (!songs[so]->sections[se]->lines[li]->text_above) {
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 *));
@@ -1854,8 +1877,8 @@ struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config)
songs[so]->sections[se]->lines[li]->lyrics[ly] = NULL;
ly = 0;
te = 0;
- songs[so]->sections[se]->lines[li]->chords = realloc(songs[so]->sections[se]->lines[li]->chords, (c+1) * sizeof(struct ChoChord *));
- songs[so]->sections[se]->lines[li]->chords[c] = 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;
c = 0;
li++;
songs[so]->sections[se]->lines = realloc(songs[so]->sections[se]->lines, (li+1) * sizeof(struct ChoLine *));
@@ -2142,13 +2165,18 @@ struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config)
if (buf == ']') {
chord[ch] = 0;
ch = 0;
- songs[so]->sections[se]->lines[li]->chords = realloc(songs[so]->sections[se]->lines[li]->chords, (c+1) * sizeof(struct ChoChord *));
- songs[so]->sections[se]->lines[li]->chords[c] = cho_chord_new();
+ g_prev_ftype = g_current_ftype;
+ g_current_ftype = SF_CHORD;
+ 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] = malloc(sizeof(struct ChoLineItemAbove));
+ songs[so]->sections[se]->lines[li]->text_above[c]->is_chord = true;
chord_pos = cho_line_compute_chord_position(songs[so]->sections[se]->lines[li], ly, te);
- songs[so]->sections[se]->lines[li]->chords[c]->position = chord_pos;
- songs[so]->sections[se]->lines[li]->chords[c]->chord = strdup(chord);
+ songs[so]->sections[se]->lines[li]->text_above[c]->position = chord_pos;
+ songs[so]->sections[se]->lines[li]->text_above[c]->u.chord = cho_chord_new();
+ songs[so]->sections[se]->lines[li]->text_above[c]->u.chord->chord = strdup(chord);
memset(chord, 0, strlen(chord));
c++;
+ g_current_ftype = g_prev_ftype;
state = STATE_LYRICS;
break;
}
@@ -2161,7 +2189,25 @@ struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config)
break;
case STATE_ANNOTATION:
if (buf == ']') {
+ annotation[ann] = 0;
+ ann = 0;
+ g_prev_ftype = g_current_ftype;
+ g_current_ftype = SF_ANNOT;
+ 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] = malloc(sizeof(struct ChoLineItemAbove));
+ 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->text = strdup(annotation);
+ memset(annotation, 0, strlen(annotation));
+ c++;
+ g_current_ftype = g_prev_ftype;
+ state = STATE_LYRICS;
+ break;
}
+ annotation[ann] = buf;
+ ann++;
break;
case STATE_MARKUP_TAG_BEGIN:
MARKUP_TAG_BEGIN:
@@ -2371,8 +2417,18 @@ struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config)
free(tags);
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]->chords);
+ free(songs[so]->sections[se]->lines[li]->text_above);
free(songs[so]->sections[se]->lines[li]);
+
+ unsigned int i;
+ for (i = 0; i<LENGTH(default_style_properties); i++) {
+ if (default_style_properties[i].type == SPT_FONT) {
+ free(default_style_properties[i].u.font_name);
+ } else if (default_style_properties[i].type == SPT_COLOR) {
+ free(default_style_properties[i].u.foreground_color);
+ }
+ }
+
songs[so]->sections[se]->lines[li] = NULL;
songs[so]->metadata = realloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *));
songs[so]->metadata[m] = NULL;
diff --git a/chordpro.h b/chordpro.h
@@ -109,6 +109,7 @@ enum State {
enum SongFragmentType {
SF_EMPTY = -1,
SF_CHORD,
+ SF_ANNOT,
SF_CHORUS,
SF_FOOTER,
SF_GRID,
@@ -178,18 +179,31 @@ struct ChoMetadata {
};
struct ChoChord {
- // struct Style *style;
- int position;
+ struct Style *style;
char *chord;
};
+struct ChoAnnotation {
+ struct Style *style;
+ char *text;
+};
+
struct ChoLineItem {
struct Style *style;
char *text;
};
+struct ChoLineItemAbove {
+ int position;
+ bool is_chord;
+ union {
+ struct ChoChord *chord;
+ struct ChoAnnotation *annot;
+ } u;
+};
+
struct ChoLine {
- struct ChoChord **chords;
+ struct ChoLineItemAbove **text_above;
struct ChoLineItem **lyrics;
};
@@ -208,7 +222,7 @@ struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config);
int cho_song_count(struct ChoSong **songs);
void cho_songs_free(struct ChoSong **song);
int cho_line_item_count(struct ChoLineItem **items);
-int cho_chord_count(struct ChoChord **chords);
+int cho_text_above_count(struct ChoLineItemAbove **text_above);
const char *cho_metadata_get(struct ChoMetadata **metadata, const char *name);
diff --git a/config.c b/config.c
@@ -57,7 +57,7 @@ struct PrintableItem *config_printable_item_get(struct PrintableItem **items, co
static struct Config *config_load_default(void)
{
struct Config *config = malloc(sizeof(struct Config));
- config->printable_items = malloc(11 * sizeof(struct PrintableItem *));
+ config->printable_items = malloc(12 * 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;
@@ -85,7 +85,10 @@ static struct Config *config_load_default(void)
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;
+ config->printable_items[10] = config_printable_item_new("annotation");
+ config->printable_items[10]->style->font->name = strdup("Inter");
+ config->printable_items[10]->style->font->style = FS_ITALIC;
+ config->printable_items[11] = NULL;
return config;
}
diff --git a/out_pdf.c b/out_pdf.c
@@ -129,10 +129,23 @@ static struct Font **out_pdf_font_get_all(struct ChoSong **songs, struct Config
int se = 0;
int li = 0;
int ly = 0;
+ int ch = 0;
struct Style *style;
while (songs[so] != NULL) {
while (songs[so]->sections[se] != NULL) {
while (songs[so]->sections[se]->lines[li]) {
+ while (songs[so]->sections[se]->lines[li]->text_above[ch] != NULL) {
+ if (songs[so]->sections[se]->lines[li]->text_above[ch]->is_chord) {
+ style = songs[so]->sections[se]->lines[li]->text_above[ch]->u.chord->style;
+ } else {
+ style = songs[so]->sections[se]->lines[li]->text_above[ch]->u.annot->style;
+ }
+ if (style->font->name) {
+ out_pdf_add_fonts(style->font, &fonts);
+ }
+ 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) {
@@ -486,7 +499,7 @@ static bool out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item,
return true;
}
-static double line_width_until_chord(struct ChoLine *line, struct ChoChord *chord, struct SpaceNeeded *space)
+static double line_width_until_chord(struct ChoLine *line, struct ChoLineItemAbove *text_above, struct SpaceNeeded *space)
{
int i = -1;
int ly = -1;
@@ -497,7 +510,7 @@ static double line_width_until_chord(struct ChoLine *line, struct ChoChord *chor
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) {
+ if (pos == text_above->position) {
last_ly = ly;
last_i = i;
goto FOUND;
@@ -516,6 +529,10 @@ static double line_width_until_chord(struct ChoLine *line, struct ChoChord *chor
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) {
+ fprintf(stderr, "out_pdf_fnt_obj_get_by_name failed.\n");
+ return EMPTY;
+ }
if (ly == last_ly) {
char tmp[strlen(line->lyrics[ly]->text)+1];
strcpy((char *)&tmp, line->lyrics[ly]->text);
@@ -529,18 +546,44 @@ static double line_width_until_chord(struct ChoLine *line, struct ChoChord *chor
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)
+static enum Bool out_pdf_text_above_is_enough_space(struct ChoLine *line, struct ChoLineItemAbove *prev_text_above, struct ChoLineItemAbove *text_above, 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 + MIN_CHORD_GAP_WIDTH;
- return false;
+ double prev_text_above_width;
+ double cur = line_width_until_chord(line, text_above, NULL);
+ if (cur == EMPTY) {
+ return B_ERROR;
}
- return true;
+ double prev = line_width_until_chord(line, prev_text_above, space);
+ if (prev == EMPTY) {
+ return B_ERROR;
+ }
+ double width_between = cur - prev;
+ char *name;
+ pdfio_obj_t *font_obj;
+ if (prev_text_above->is_chord) {
+ name = out_pdf_fnt_name_create(prev_text_above->u.chord->style->font);
+ font_obj = out_pdf_fnt_obj_get_by_name(name);
+ if (!font_obj) {
+ fprintf(stderr, "out_pdf_fnt_obj_get_by_name failed.\n");
+ return B_ERROR;
+ }
+ free(name);
+ prev_text_above_width = pdfioContentTextMeasure(font_obj, prev_text_above->u.chord->chord, prev_text_above->u.chord->style->font->size);
+ } else {
+ name = out_pdf_fnt_name_create(prev_text_above->u.annot->style->font);
+ font_obj = out_pdf_fnt_obj_get_by_name(name);
+ if (!font_obj) {
+ fprintf(stderr, "out_pdf_fnt_obj_get_by_name failed.\n");
+ return B_ERROR;
+ }
+ free(name);
+ prev_text_above_width = pdfioContentTextMeasure(font_obj, prev_text_above->u.annot->text, prev_text_above->u.annot->style->font->size);
+ }
+ if (prev_text_above_width >= width_between) {
+ space->amount = prev_text_above_width - width_between + MIN_CHORD_GAP_WIDTH;
+ return B_FALSE;
+ }
+ return B_TRUE;
}
static struct SpaceNeeded *needs_space(struct SpaceNeeded **spaces, int ly, int i) {
@@ -558,15 +601,8 @@ static void text_line_set_lineheight(struct TextLine *line, enum SongFragmentTyp
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;
- }
+ if (line->items[tli]->style->font->size > biggest_font_size) {
+ biggest_font_size = line->items[tli]->style->font->size;
}
}
switch (ftype) {
@@ -585,7 +621,7 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config)
double width;
bool add_space_to_next_chord = false;
struct ChoLine **lines;
- struct ChoChord **chords;
+ struct ChoLineItemAbove **text_above;
struct SpaceNeeded space;
struct SpaceNeeded **spaces = NULL;
int sn = 0;
@@ -671,44 +707,48 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config)
}
lines = songs[so]->sections[se]->lines;
for (li = 0; lines[li]; li++, tl++) {
- chords = songs[so]->sections[se]->lines[li]->chords;
- int chords_len = cho_chord_count(chords);
- if (chords_len > 0) {
+ text_above = songs[so]->sections[se]->lines[li]->text_above;
+ int text_above_len = cho_text_above_count(text_above);
+ if (text_above_len > 0) {
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;
- 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++) {
+ enum Bool enough_space;
+ for (ch = 0; text_above[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));
+ double width_until = line_width_until_chord(lines[li], text_above[ch], NULL);
+ if (width_until == EMPTY) {
+ fprintf(stderr, "line_width_until_chord failed.\n");
+ return NULL;
+ }
if (tli == 0) {
- text[t]->lines[tl]->items[tli]->x = PADDING + line_width_until_chord(lines[li], chords[ch], NULL);
+ text[t]->lines[tl]->items[tli]->x = PADDING + width_until;
} else {
- text[t]->lines[tl]->items[tli]->x = PADDING + line_width_until_chord(lines[li], chords[ch], NULL) + added_space;
+ text[t]->lines[tl]->items[tli]->x = PADDING + width_until + 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) {
+ if (text_above[ch]->is_chord) {
+ text[t]->lines[tl]->items[tli]->style = cho_style_duplicate(text_above[ch]->u.chord->style);
+ text[t]->lines[tl]->items[tli]->text = strdup(text_above[ch]->u.chord->chord);
+ } else {
+ text[t]->lines[tl]->items[tli]->style = cho_style_duplicate(text_above[ch]->u.annot->style);
+ text[t]->lines[tl]->items[tli]->text = strdup(text_above[ch]->u.annot->text);
+ }
+ if (text_above_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
- )) {
+ enough_space = out_pdf_text_above_is_enough_space(songs[so]->sections[se]->lines[li], text_above[ch], text_above[ch+1], &space);
+ if (enough_space == B_ERROR) {
+ fprintf(stderr, "out_pdf_text_above_is_enough_space failed.\n");
+ return NULL;
+ }
+ if (!enough_space) {
spaces = realloc(spaces, (sn+1) * sizeof(struct SpaceNeeded *));
spaces[sn] = malloc(sizeof(struct SpaceNeeded));
spaces[sn]->line_item_index = space.line_item_index;
@@ -956,6 +996,9 @@ bool out_pdf_new(const char *cho_filepath, const char *output_folder_or_file, st
f++;
}
cho_fonts_free(needed_fonts);
+ for (f = 0; g_fonts[f]; f++) {
+ printf("%s\n", g_fonts[f]->name);
+ }
struct Text **text = text_create(songs, config);
if (!text) {
fprintf(stderr, "text_create failed.\n");
diff --git a/out_pdf.h b/out_pdf.h
@@ -6,6 +6,12 @@
#define LINE_LEN MEDIABOX_WIDTH - PADDING * 2
#define MIN_CHORD_GAP_WIDTH 5.0
+enum Bool {
+ B_ERROR = -1,
+ B_FALSE,
+ B_TRUE
+};
+
enum Alignment {
LEFT,
CENTER,