lorid

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

commit e321cff419173fb31c3dece2b5900079b170d7da
parent dcb6d62024e4b11f1339cf0a4e1ecf5635036e44
Author: nibo <nibo@relim.de>
Date:   Sun, 28 Jul 2024 21:59:05 +0200

Add a bunch of stuff

Diffstat:
MMakefile | 4++--
Mchordpro.c | 65+++++++++++++++++++++++++++++++----------------------------------
Mchordpro.h | 8++++++--
Mlorid.c | 36++++++++++++++++++------------------
Mout_pdf.c | 362+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mout_pdf.h | 15++++++++++++++-
Astr.c | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astr.h | 3+++
Mtodo | 3++-
9 files changed, 437 insertions(+), 157 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ all: - $(CC) -pedantic -Wall -Wextra -O2 fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig + $(CC) -pedantic -Wall -Wextra -O2 str.c fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig debug: - $(CC) -g -pedantic -Wall -Wextra fontconfig.c config.c chordpro.c out_pdf.c lorid.c -o lorid -lpdfio -ltoml -lfontconfig + $(CC) -g -pedantic -Wall -Wextra str.c 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 @@ -6,6 +6,7 @@ #include <ctype.h> #include "chordpro.h" #include "config.h" +#include "str.h" static const char *environment_directives[] = { "start_of_chorus", "soc", "end_of_chorus", "eoc", "chorus", @@ -289,15 +290,6 @@ void the_default_style_properties(void) } } -static char *string_remove_leading_whitespace(const char *str) -{ - int i = 0; - while (str[i] == ' ' || str[i] == '\t') { - i++; - } - return strdup(&str[i]); -} - static inline bool is_whitespace(char c) { if ( @@ -511,25 +503,6 @@ void cho_fonts_free(struct Font **fonts) free(fonts); } -char *cho_font_name_sanitize(const char *name) -{ - char *sanitized = NULL; - int i = 0; - int s = 0; - char c; - for (; name[i] != 0; i++) { - if (name[i] == ' ') - continue; - c = (char)tolower(name[i]); - sanitized = realloc(sanitized, (s+1) * sizeof(char)); - sanitized[s] = c; - s++; - } - sanitized = realloc(sanitized, (s+1) * sizeof(char)); - sanitized[s] = 0; - return sanitized; -} - enum FontFamily cho_font_family_parse(const char *str) { if (strcasecmp(str, "sans") == 0) { @@ -1293,10 +1266,21 @@ static struct ChoMetadata *cho_metadata_new(void) return meta; } +const char *cho_metadata_get(struct ChoMetadata **metadata, const char *name) +{ + int m; + for (m = 0; metadata[m]; m++) { + if (strcmp(metadata[m]->name, name) == 0) { + return metadata[m]->value; + } + } + return NULL; +} + static struct ChoMetadata *cho_metadata_split(const char *directive_value) { struct ChoMetadata *meta = cho_metadata_new(); - char *value = string_remove_leading_whitespace(directive_value); + char *value = str_remove_leading_whitespace(directive_value); int i = 0; int n = 0; int v = 0; @@ -1410,6 +1394,14 @@ static struct ChoSong *cho_song_new(void) 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) { int i = 0; @@ -1718,7 +1710,7 @@ END: return directive; } -struct ChoSong **cho_parse(FILE *fp, struct Config *config) +struct ChoSong **cho_songs_parse(FILE *fp, struct Config *config) { g_config = config; char buf = 0; @@ -1895,6 +1887,8 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config) break; case DT_PREAMBLE: // 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); 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 *)); @@ -1907,11 +1901,14 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config) songs[so] = cho_song_new(); se = 0; li = 0; + ly = 0; m = 0; songs[so]->sections = malloc((se+1) * sizeof(struct ChoSection *)); 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(); break; case DT_FONT: // TODO: Reset directive value to default @@ -1959,7 +1956,7 @@ struct ChoSong **cho_parse(FILE *fp, 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]->name = string_remove_leading_whitespace(directive_value); + songs[so]->sections[se]->name = str_remove_leading_whitespace(directive_value); li = 0; songs[so]->sections[se]->lines = malloc(sizeof(struct ChoLine *)); songs[so]->sections[se]->lines[li] = cho_line_new(); @@ -1999,7 +1996,7 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config) songs[so]->metadata = realloc(songs[so]->metadata, (m+1) * sizeof(struct ChoMetadata *)); songs[so]->metadata[m] = cho_metadata_new(); songs[so]->metadata[m]->name = strdup(directive_name); - songs[so]->metadata[m]->value = string_remove_leading_whitespace(directive_value); + songs[so]->metadata[m]->value = str_remove_leading_whitespace(directive_value); m++; } break; @@ -2015,7 +2012,7 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config) 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_duplicate(directive->style); - char *trimmed_directive_value = string_remove_leading_whitespace(directive_value); + char *trimmed_directive_value = str_remove_leading_whitespace(directive_value); songs[so]->sections[se]->lines[li]->lyrics[ly]->text = trimmed_directive_value; te += strlen(trimmed_directive_value); break; @@ -2024,7 +2021,7 @@ struct ChoSong **cho_parse(FILE *fp, struct Config *config) break; case DT_FONT: sprop.ftype = directive->ftype; - char *dir_value = string_remove_leading_whitespace(directive_value); + char *dir_value = str_remove_leading_whitespace(directive_value); switch (directive->sprop) { case SPT_FONT: sprop.u.font_name = malloc((strlen(dir_value)+1) * sizeof(char)); diff --git a/chordpro.h b/chordpro.h @@ -203,10 +203,14 @@ struct ChoSong { struct ChoSection **sections; }; -struct ChoSong **cho_parse(FILE *fp, struct Config *config); +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); + +const char *cho_metadata_get(struct ChoMetadata **metadata, const char *name); + struct Style *cho_style_new(void); void cho_style_free(struct Style *style); struct Style *cho_style_duplicate(struct Style *style); @@ -220,7 +224,7 @@ void cho_font_free(struct Font *font); struct Font *cho_font_duplicate(struct Font *font); void cho_fonts_free(struct Font **fonts); -char *cho_font_name_sanitize(const char *name); +char *cho_font_name_normalize(const char *name); enum FontFamily cho_font_family_parse(const char *str); const char *cho_font_family_to_string(enum FontFamily font_family); diff --git a/lorid.c b/lorid.c @@ -7,26 +7,33 @@ #include "config.h" #include "chordpro.h" #include "out_pdf.h" +#include "str.h" int main(int argc, char *argv[]) { static struct option long_options[] = { { "print-default-config", no_argument, 0, 'p' }, { "config", required_argument, 0, 'c' }, + { "output", required_argument, 0, 'o' }, { 0, 0, 0, 0 } }; int o, option_index; char *config_filepath = NULL; + char *output = NULL; FILE *fp; - while ((o = getopt_long(argc, argv, "pc:", long_options, &option_index)) != -1) { + while ((o = getopt_long(argc, argv, "pc:o:", long_options, &option_index)) != -1) { switch(o) { - case 'p': - config_print_default(); - return 0; - case 'c': - config_filepath = malloc((strlen(optarg)+1) * sizeof(char)); - strcpy(config_filepath, optarg); - break; + case 'p': + config_print_default(); + return 0; + case 'c': + config_filepath = malloc((strlen(optarg)+1) * sizeof(char)); + strcpy(config_filepath, optarg); + break; + case 'o': + output = realloc(output, (strlen(optarg)+1) * sizeof(char)); + strcpy(output, optarg); + break; } } struct Config *config = config_load(config_filepath); @@ -34,14 +41,6 @@ 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; @@ -55,15 +54,16 @@ int main(int argc, char *argv[]) fprintf(stderr, "Provide only one file.\n"); return 1; } - struct ChoSong **songs = cho_parse(fp, config); + struct ChoSong **songs = cho_songs_parse(fp, config); if (songs == NULL) { fprintf(stderr, "cho_parse failed.\n"); return 1; } - if (!out_pdf_new(argv[1], songs, config)) { + if (!out_pdf_new(argc == optind+1 ? argv[argc-1] : NULL, output ? output : NULL, songs, config)) { fprintf(stderr, "out_pdf_new failed.\n"); return 1; } + free(output); cho_songs_free(songs); config_free(config); fclose(fp); diff --git a/out_pdf.c b/out_pdf.c @@ -1,12 +1,14 @@ #include <stdbool.h> #include <stdint.h> #include <string.h> +#include <sys/stat.h> #include <pdfio.h> #include <pdfio-content.h> #include "chordpro.h" #include "config.h" #include "out_pdf.h" #include "fontconfig.h" +#include "str.h" static struct Fnt **g_fonts = NULL; @@ -25,47 +27,6 @@ static pdfio_obj_t *out_pdf_fnt_obj_get_by_name(const char *name) return NULL; } -static char *string_trim(const char *text) -{ - char *trimmed_text = NULL; - int begin = 0; - int end = 0; - int len = (int)strlen(text); - for (int i=0; i<len; i++) { - if ( - text[i] == ' ' || - text[i] == '\n' || - text[i] == '\t' || - text[i] == '\r' - ) - begin++; - else - break; - } - for (int i=len-1; i>=0; i--) { - if ( - text[i] == ' '|| - text[i] == '\n' || - text[i] == '\t' || - text[i] == '\r' - ) - end++; - else - break; - } - int k = 0; - for (int i=0; i<len; i++) { - if (i >= begin && i < len - end) { - trimmed_text = realloc(trimmed_text, (k+1) * sizeof(char)); - trimmed_text[k] = text[i]; - k++; - } - } - trimmed_text = realloc(trimmed_text, (k+1) * sizeof(char)); - trimmed_text[k] = 0; - return trimmed_text; -} - /* static void out_pdf_font_add(struct Font *font, struct Font ***array) { int a = 0; @@ -119,7 +80,7 @@ static void out_pdf_add_fonts(struct Font *font, struct Font ***fonts) while (font->name[i] != 0) { if (font->name[i] == ',') { part[p] = 0; - trimmed = string_trim(part); + trimmed = str_trim(part); new_font = malloc(sizeof(struct Font)); new_font->name = trimmed; new_font->family = font->family; @@ -140,7 +101,7 @@ static void out_pdf_add_fonts(struct Font *font, struct Font ***fonts) i++; } part[p] = 0; - trimmed = string_trim(part); + trimmed = str_trim(part); new_font = malloc(sizeof(struct Font)); new_font->name = trimmed; new_font->family = font->family; @@ -192,7 +153,7 @@ static struct Font **out_pdf_font_get_all(struct ChoSong **songs, struct Config return fonts; } -size_t out_pdf_text_find_fitting_length(const char *str, size_t len) +static size_t out_pdf_text_find_fitting_length(const char *str, size_t len) { char tmp[512]; strncpy((char *)&tmp, str, len); @@ -211,28 +172,68 @@ size_t out_pdf_text_find_fitting_length(const char *str, size_t len) return len; } -static bool out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y) +static enum FileType file_type(const char *path) { - if (!pdfioContentTextBegin(stream)) { - fprintf(stderr, "pdfioContentTextBegin failed.\n"); - return false; + struct stat s; + if (stat(path, &s) != 0) { + return F_ERROR; } - if (!pdfioContentTextMoveTo(stream, item->x, y)) { - fprintf(stderr, "pdfioContentTextMoveTo failed.\n"); - return false; + switch (s.st_mode & S_IFMT) { + case S_IFDIR: + return F_FOLDER; + case S_IFREG: + return F_REG_FILE; } - if (!pdfioContentTextShow(stream, true, item->text)) { - fprintf(stderr, "pdfioContentTextShow failed.\n"); - return false; + return F_OTHER; +} + +static char *filepath_add_ending_slash_if_missing(const char *path) +{ + size_t len = strlen(path); + if (path[len-1] == '/') { + return strdup(path); + } else { + char *path_with_slash = malloc((len+2) * sizeof(char)); + strcpy(path_with_slash, path); + path_with_slash[len] = '/'; + path_with_slash[len+1] = 0; + return path_with_slash; } - if (!pdfioContentTextEnd(stream)) { - fprintf(stderr, "pdfioContentTextEnd failed.\n"); - return false; +} + +static char *filepath_basename(const char *path) +{ + int begin = 0; + int i; + for (i = 0; path[i] != 0; i++) { + if (path[i] == '/') { + begin = i+1; + } } - return true; + return strdup(&path[begin]); +} + +static char *filepath_dirname(const char *path) +{ + char *dirname; + int end = 0; + int i; + for (i = 0; path[i] != 0; i++) { + if (path[i] == '/') { + end = i; + } + } + dirname = malloc((end+1)* sizeof(char)); + i = 0; + while (i<end) { + dirname[i] = path[i]; + i++; + } + dirname[i] = 0; + return dirname; } -static char *out_pdf_filename_create(const char *old) +static char *file_extension_replace_or_add(const char *old) { char *new = NULL; int mark = -1; @@ -270,6 +271,87 @@ static char *out_pdf_filename_create(const char *old) return new; } +static char *out_pdf_filename_generate_from_songs(struct ChoSong **songs) +{ + char *filename; + char *normalized_title; + const char *title; + int len = cho_song_count(songs); + if (len == 0) + return NULL; + if (len == 1) { + title = cho_metadata_get(songs[0]->metadata, "title"); + if (!title) { + fprintf(stderr, "INFO: Song has no title.\n"); + return NULL; + } + normalized_title = str_normalize(title); + filename = file_extension_replace_or_add(normalized_title); + free(normalized_title); + return filename; + } + return strdup("collection-of-songs.pdf"); + /* if (len > 1) { + } */ +} + +static char *out_pdf_filename_create(struct ChoSong **songs, const char *cho_filepath, const char *out) +{ + char *pdf_filepath = NULL; + char *pdf_filename; + char *tmp; + if (cho_filepath) { + pdf_filepath = file_extension_replace_or_add(cho_filepath); + pdf_filename = filepath_basename(pdf_filepath); + } else { + pdf_filename = out_pdf_filename_generate_from_songs(songs); + if (!pdf_filename) { + fprintf(stderr, "out_pdf_filename_generate_from_songs failed.\n"); + return NULL; + } + } + if (out) { + switch (file_type(out)) { + case F_ERROR: + tmp = filepath_dirname(out); + if (file_type(tmp) == F_FOLDER) { + free(pdf_filepath); + free(pdf_filename); + free(tmp); + return strdup(out); + } else { + free(tmp); + fprintf(stderr, "INFO: Invalid argument --output/-o value.\n"); + return NULL; + } + break; + case F_REG_FILE: + free(pdf_filepath); + free(pdf_filename); + return strdup(out); + case F_FOLDER: + tmp = filepath_add_ending_slash_if_missing(out); + tmp = realloc(tmp, (strlen(tmp)+strlen(pdf_filename)+1) * sizeof(char)); + strcat(tmp, pdf_filename); + free(pdf_filename); + free(pdf_filepath); + return tmp; + case F_OTHER: + fprintf(stderr, "INFO: Invalid argument --output/-o value. It doesn't refer to a folder or regular file.\n"); + return NULL; + default: + fprintf(stderr, "INFO: Invalid enum FileType value.\n"); + return NULL; + } + } else { + if (pdf_filepath) { + free(pdf_filename); + return pdf_filepath; + } + return pdf_filename; + } +} + static struct Fnt *out_pdf_fnt_new(void) { struct Fnt *fnt = malloc(sizeof(struct Fnt)); @@ -280,7 +362,7 @@ static struct Fnt *out_pdf_fnt_new(void) static char *out_pdf_fnt_name_create(struct Font *font) { - char *name = cho_font_name_sanitize(font->name); + char *name = str_normalize(font->name); const char *family = cho_font_family_to_string(font->family); const char *style = cho_font_style_to_string(font->style); const char *weight = cho_font_weight_to_string(font->weight); @@ -376,42 +458,107 @@ static bool out_pdf_font_set(pdfio_stream_t *stream, struct Font *font) return true; } -static bool out_pdf_style_apply(pdfio_stream_t *stream, struct Style *style) +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); + if (font_obj == NULL) { + fprintf(stderr, "out_pdf_fnt_obj_get_by_name failed.\n"); + return -1.0; + } + free(name); + return pdfioContentTextMeasure(font_obj, item->text, item->style->font->size); +} + +static bool out_pdf_draw_line(pdfio_stream_t *stream, struct TextLineItem *item, double y, double width, enum LineLocation line_location) { double red, green, blue; - if (!out_pdf_font_set(stream, style->font)) { + switch (line_location) { + case LL_UNDER: + red = item->style->underline_color->red / 255.0; + green = item->style->underline_color->green / 255.0; + blue = item->style->underline_color->blue / 255.0; + y -= 2.0; + break; + case LL_STRIKETHROUGH: + red = item->style->strikethrough_color->red / 255.0; + green = item->style->strikethrough_color->green / 255.0; + blue = item->style->strikethrough_color->blue / 255.0; + printf("add to y: %.2f\n", item->style->font->size * 0.21); + y += item->style->font->size * 0.3; + break; + case LL_OVER: + red = item->style->overline_color->red / 255.0; + green = item->style->overline_color->green / 255.0; + blue = item->style->overline_color->blue / 255.0; + break; + } + if (!pdfioContentPathMoveTo(stream, item->x, y)) { + fprintf(stderr, "pdfioContentPathMoveTo failed.\n"); + return false; + } + if (!pdfioContentPathLineTo(stream, item->x + width, y)) { + fprintf(stderr, "pdfioContentPathLineTo failed.\n"); + return false; + } + if (!pdfioContentSetStrokeColorRGB(stream, red, green, blue)) { + fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n"); + return false; + } + if (!pdfioContentStroke(stream)) { + fprintf(stderr, "pdfioContentStroke failed.\n"); + return false; + } + return true; +} + +static bool out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y) +{ + double red, green, blue, width; + // TODO: Maybe store the width in TextLineItem + width = text_width(item); + if (width == EMPTY) { + fprintf(stderr, "text_width failed.\n"); + return false; + } + if (!out_pdf_font_set(stream, item->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; + red = item->style->foreground_color->red / 255.0; + green = item->style->foreground_color->green / 255.0; + blue = item->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 - ); */ + if (!pdfioContentTextBegin(stream)) { + fprintf(stderr, "pdfioContentTextBegin failed.\n"); + return false; + } + if (!pdfioContentTextMoveTo(stream, item->x, y)) { + fprintf(stderr, "pdfioContentTextMoveTo failed.\n"); + return false; + } + if (!pdfioContentTextShow(stream, true, item->text)) { + fprintf(stderr, "pdfioContentTextShow failed.\n"); + return false; + } + if (!pdfioContentTextEnd(stream)) { + fprintf(stderr, "pdfioContentTextEnd failed.\n"); + return false; + } + if (item->style->underline_style == LS_SINGLE) { + out_pdf_draw_line(stream, item, y, width, LL_UNDER); + } else if (item->style->underline_style == LS_DOUBLE) { + } + if (item->style->strikethrough) { + out_pdf_draw_line(stream, item, y, width, LL_STRIKETHROUGH); + } 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 i = -1; @@ -538,6 +685,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config) 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]); + if (width == EMPTY) { + fprintf(stderr, "text_width failed.\n"); + return NULL; + } 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); @@ -558,6 +709,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config) 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]); + if (width == EMPTY) { + fprintf(stderr, "text_width failed.\n"); + return NULL; + } 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); @@ -660,6 +815,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config) text[t]->lines[tl]->items[tli]->x = PADDING; } else { last_text_line_item_width = text_width(text[t]->lines[tl]->items[tli-1]); + if (last_text_line_item_width == EMPTY) { + fprintf(stderr, "text_width failed.\n"); + return NULL; + } text[t]->lines[tl]->items[tli]->x = text[t]->lines[tl]->items[tli-1]->x + last_text_line_item_width; } int tlii = 0; @@ -677,6 +836,10 @@ static struct Text **text_create(struct ChoSong **songs, struct Config *config) 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]); + if (last_text_line_item_width == EMPTY) { + fprintf(stderr, "text_width failed.\n"); + return NULL; + } 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)); @@ -719,10 +882,7 @@ static void text_free(struct Text **text) 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); - } + cho_style_free(text[t]->lines[tl]->items[tli]->style); free(text[t]->lines[tl]->items[tli]); } free(text[t]->lines[tl]->items); @@ -739,24 +899,28 @@ bool out_pdf_set_title(pdfio_file_t *pdf, struct ChoSong **songs) { // Set pdf title only if single song exist if (songs[0] && !songs[1]) { - int m; - for (m = 0; songs[0]->metadata[m]; m++) { - if (strcmp(songs[0]->metadata[m]->name, "title") == 0) { - pdfioFileSetTitle(pdf, songs[0]->metadata[m]->value); - return true; - } + const char *title; + title = cho_metadata_get(songs[0]->metadata, "title"); + if (title) { + pdfioFileSetTitle(pdf, title); + return true; } return false; } return true; } -bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config *config) +bool out_pdf_new(const char *cho_filepath, const char *output_folder_or_file, 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); + char *pdf_filename = out_pdf_filename_create(songs, cho_filepath, output_folder_or_file); + if (!pdf_filename) { + fprintf(stderr, "out_pdf_filename_create failed.\n"); + return false; + } 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 }; + fprintf(stderr, "INFO: Writing pdf to file: '%s'\n", pdf_filename); pdfio_file_t *pdf = pdfioFileCreate(pdf_filename, "2.0", &media_box_a4, &crop_box, NULL, NULL); free(pdf_filename); if (!out_pdf_set_title(pdf, songs)) { @@ -816,16 +980,16 @@ bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config fprintf(stderr, "pdfioContentSetFillColorSpace failed.\n"); return 1; } + if (!pdfioContentSetStrokeColorSpace(page1_stream, "rgbcolorspace")) { + fprintf(stderr, "pdfioContentSetStrokeColorSpace failed.\n"); + return 1; + } 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); } } diff --git a/out_pdf.h b/out_pdf.h @@ -38,4 +38,17 @@ struct Text { struct TextLine **lines; }; -bool out_pdf_new(const char *cho_filename, struct ChoSong **songs, struct Config *config); +enum FileType { + F_ERROR, + F_FOLDER, + F_REG_FILE, + F_OTHER +}; + +enum LineLocation { + LL_OVER, + LL_STRIKETHROUGH, + LL_UNDER +}; + +bool out_pdf_new(const char *cho_filename, const char *output_folder_or_file, struct ChoSong **songs, struct Config *config); diff --git a/str.c b/str.c @@ -0,0 +1,98 @@ +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include "str.h" + +/* char *str_normalize(const char *str) +{ + char *normalized = NULL; + int i = 0; + int s = 0; + char c; + for (; str[i] != 0; i++) { + if (str[i] == ' ') + continue; + c = (char)tolower(str[i]); + normalized = realloc(normalized, (s+1) * sizeof(char)); + normalized[s] = c; + s++; + } + normalized = realloc(normalized, (s+1) * sizeof(char)); + normalized[s] = 0; + return normalized; +} */ + +char *str_normalize(const char *str) +{ + char *normalized = NULL; + int i = 0; + int n = 0; + char c; + for (; str[i] != 0; i++) { + if (str[i] == ' ') { + normalized = realloc(normalized, (n+1) * sizeof(char)); + normalized[n] = '-'; + n++; + continue; + } + if (str[i] == '\'' || str[i] == ',') + continue; + c = (char)tolower(str[i]); + normalized = realloc(normalized, (n+1) * sizeof(char)); + normalized[n] = c; + n++; + } + normalized = realloc(normalized, (n+1) * sizeof(char)); + normalized[n] = 0; + return normalized; +} + +char *str_trim(const char *str) +{ + char *trimmed = NULL; + int begin = 0; + int end = 0; + int len = (int)strlen(str); + for (int i=0; i<len; i++) { + if ( + str[i] == ' ' || + str[i] == '\n' || + str[i] == '\t' || + str[i] == '\r' + ) + begin++; + else + break; + } + for (int i=len-1; i>=0; i--) { + if ( + str[i] == ' '|| + str[i] == '\n' || + str[i] == '\t' || + str[i] == '\r' + ) + end++; + else + break; + } + int k = 0; + for (int i=0; i<len; i++) { + if (i >= begin && i < len - end) { + trimmed = realloc(trimmed, (k+1) * sizeof(char)); + trimmed[k] = str[i]; + k++; + } + } + trimmed = realloc(trimmed, (k+1) * sizeof(char)); + trimmed[k] = 0; + return trimmed; +} + +char *str_remove_leading_whitespace(const char *str) +{ + int i = 0; + while (str[i] == ' ' || str[i] == '\t') { + i++; + } + return strdup(&str[i]); +} diff --git a/str.h b/str.h @@ -0,0 +1,3 @@ +char *str_normalize(const char *str); +char *str_trim(const char *str); +char *str_remove_leading_whitespace(const char *str); diff --git a/todo b/todo @@ -1,4 +1,4 @@ -apply config in cho_parse() instead of out_pdf_new() +apply all config in cho_parse() instead of out_pdf_new() 'chorus' directive decide how to implement metadata directives @@ -16,3 +16,4 @@ introduce parse errors and continue execution and when it should fail metadata items need a style font, size, colour directives +new_song directive