lorid

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

commit a76f6b36b9a99ac1135384cd14b1cf4a38ee4c18
parent ed7dcbe7bad163ef2dc76deb9cbd8f6e404a0bf4
Author: nibo <nibo@relim.de>
Date:   Fri, 15 Nov 2024 18:49:01 +0100

Rewrite pdf generation engine

Diffstat:
Mchordpro.c | 207++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mchordpro.h | 15+++++++--------
Mout_pdf.c | 2117++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mout_pdf.h | 38+++++++++++++++++++++++++++++++++-----
Mutil.c | 27++++++++++++++++++++++++++-
Mutil.h | 9++++++++-
6 files changed, 1514 insertions(+), 899 deletions(-)

diff --git a/chordpro.c b/chordpro.c @@ -309,6 +309,10 @@ cho_log(enum LogLevel level, const char *msg, ...) log_level = " ERR"; color = COLOR_BOLD_RED; break; + case LOG_TODO: + log_level = "TODO"; + color = COLOR_BOLD_BLUE; + break; } if (isatty(2)) { if (g_chordpro_filepath) { @@ -352,6 +356,9 @@ cho_log(enum LogLevel level, const char *msg, ...) case LOG_ERR: log_level = " ERR"; break; + case LOG_TODO: + log_level = "TODO"; + break; } if (g_chordpro_filepath) { fprintf(stderr, "%s:%ld: %s: ", g_chordpro_filepath, g_line_number, @@ -1965,7 +1972,8 @@ cho_image_new(void) image->src = NULL; image->width = NULL; image->height = NULL; - image->scale = NULL; + image->width_scale = NULL; + image->height_scale = NULL; image->align = A_CENTER; image->border = EMPTY; image->spread_space = NULL; @@ -1995,8 +2003,11 @@ cho_image_free(struct ChoImage *image) if (image->height) { free(image->height); } - if (image->scale) { - free(image->scale); + if (image->width_scale) { + free(image->width_scale); + } + if (image->height_scale) { + free(image->height_scale); } if (image->spread_space) { free(image->spread_space); @@ -2032,7 +2043,8 @@ cho_image_copy(struct ChoImage *image) copy->src = strdup(image->src); copy->width = size_copy(image->width); copy->height = size_copy(image->height); - copy->scale = size_copy(image->scale); + copy->width_scale = size_copy(image->width_scale); + copy->height_scale = size_copy(image->height_scale); copy->align = image->align; copy->border = image->border; copy->spread_space = size_copy(image->spread_space); @@ -2061,7 +2073,7 @@ cho_image_find_asset(const char *id) } #ifdef DEBUG -static void +void cho_debug_image_print(struct ChoImage *image) { printf("---- BEGIN IMAGE ----\n"); @@ -2086,10 +2098,15 @@ cho_debug_image_print(struct ChoImage *image) } else { printf("height: NULL\n"); } - if (image->scale) { - printf("scale: %s\n", size_to_string(image->scale)); + if (image->width_scale) { + printf("width_scale: %s\n", size_to_string(image->width_scale)); } else { - printf("scale: NULL\n"); + printf("width_scale: NULL\n"); + } + if (image->height_scale) { + printf("height_scale: %s\n", size_to_string(image->height_scale)); + } else { + printf("height_scale: NULL\n"); } printf("align: %s\n", alignment_enums[image->align]); printf("border: %.1f\n", image->border); @@ -2175,16 +2192,42 @@ cho_image_option_parse(struct ChoImage *image, const char *name, const char *val image->height = size; } else if (!strcmp(name, "scale")) { - size = size_create(value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in option 'scale' in image directive."); - return false; - } - if (size->type == ST_EM || size->type == ST_EX) { - cho_log(LOG_ERR, "Invalid type of value in option 'scale' in image directive. Allowed types are: point, percentage."); - return false; + char *comma; + if ((comma = strchr(value, ','))) { + *comma = 0; + size = size_create(value); + if (!size) { + cho_log(LOG_ERR, "Invalid value in option 'scale' in image directive."); + return false; + } + if (size->type == ST_EM || size->type == ST_EX) { + cho_log(LOG_ERR, "Invalid type of value in option 'scale' in image directive. Allowed types are: point, percentage."); + return false; + } + image->width_scale = size; + size = size_create(++comma); + if (!size) { + cho_log(LOG_ERR, "Invalid value in option 'scale' in image directive."); + return false; + } + if (size->type == ST_EM || size->type == ST_EX) { + cho_log(LOG_ERR, "Invalid type of value in option 'scale' in image directive. Allowed types are: point, percentage."); + return false; + } + image->height_scale = size; + } else { + size = size_create(value); + if (!size) { + cho_log(LOG_ERR, "Invalid value in option 'scale' in image directive."); + return false; + } + if (size->type == ST_EM || size->type == ST_EX) { + cho_log(LOG_ERR, "Invalid type of value in option 'scale' in image directive. Allowed types are: point, percentage."); + return false; + } + image->width_scale = size; + image->height_scale = size_copy(size); } - image->scale = size; } else if (!strcmp(name, "align")) { if (!strcmp(value, "left")) { @@ -2445,16 +2488,42 @@ cho_image_tag_parse(struct Attr **attrs) image->dy = size; } else if (!strcmp(attrs[a]->name, "scale")) { - size = size_create(attrs[a]->value); - if (!size) { - cho_log(LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); - return false; - } - if (size->type == ST_EM || size->type == ST_EX) { - cho_log(LOG_ERR, "Invalid type of value in attribute 'scale' in 'img' tag. Allowed types are: point, percent"); - return false; + char *comma; + if ((comma = strchr(attrs[a]->value, ','))) { + *comma = 0; + size = size_create(attrs[a]->value); + if (!size) { + cho_log(LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); + return false; + } + if (size->type == ST_EM || size->type == ST_EX) { + cho_log(LOG_ERR, "Invalid type of value in attribute 'scale' in 'img' tag. Allowed types are: point, percent"); + return false; + } + image->width_scale = size; + size = size_create(++comma); + if (!size) { + cho_log(LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); + return false; + } + if (size->type == ST_EM || size->type == ST_EX) { + cho_log(LOG_ERR, "Invalid type of value in attribute 'scale' in 'img' tag. Allowed types are: point, percent"); + return false; + } + image->height_scale = size; + } else { + size = size_create(attrs[a]->value); + if (!size) { + cho_log(LOG_ERR, "Invalid value in attribute 'scale' in 'img' tag."); + return false; + } + if (size->type == ST_EM || size->type == ST_EX) { + cho_log(LOG_ERR, "Invalid type of value in attribute 'scale' in 'img' tag. Allowed types are: point, percent"); + return false; + } + image->width_scale = size; + image->height_scale = size_copy(size); } - image->scale = size; } else if (!strcmp(attrs[a]->name, "align")) { if (!strcmp(attrs[a]->value, "left")) { @@ -3215,7 +3284,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) char buf = 0; char prev_buf = '\n'; char directive_name[128]; - char directive_value[128]; + char directive_value[4096]; char chord[15]; char tag_start[6]; char tag_end[6]; @@ -3271,6 +3340,7 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) g_line_number++; } // printf("state: %s, prev_state: %s, prev_buf: %c, buf: %c\n", state_enums[state], state_enums[prev_state], prev_buf, buf); + // printf("state: %s, buf: %c\n", state_enums[state], buf); if (buf == '\r') { continue; } @@ -3433,7 +3503,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se]->lines[li]->text_above[c] = NULL; c = 0; cho_line_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] = NULL; li = 0; se++; @@ -3824,7 +3893,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) g_image_assets[g_ia] = image; g_ia++; } else { - // Add image to ChoSong somehow 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; @@ -3917,6 +3985,10 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) cho_log(LOG_ERR, "Newline character inside a directive value is invalid."); return NULL; } + if (dv > 4094) { + cho_log(LOG_ERR, "Directive value can't be longer than 4095 bytes."); + return NULL; + } directive_value[dv] = buf; dv++; break; @@ -3936,7 +4008,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) } cho_chord_free(tmp_chord); is_chord_already_initialized = false; - // cho_debug_chord_print(songs[so]->sections[se]->lines[li]->text_above[c]->u.chord); } else { 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)); @@ -3947,7 +4018,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) if (!songs[so]->sections[se]->lines[li]->text_above[c]->u.chord->is_canonical) { cho_log(LOG_INFO, "Didn't recognize the chord '%s'.", songs[so]->sections[se]->lines[li]->text_above[c]->u.chord->name); } - // cho_debug_chord_print(songs[so]->sections[se]->lines[li]->text_above[c]->u.chord); } memset(chord, 0, strlen(chord)); c++; @@ -4001,17 +4071,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) break; } if (buf == '<') { - if (ann > 0) { - songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->text = realloc(songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->text, (ann+1) * sizeof(char)); - songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->text[ann] = 0; - ann = 0; - c++; - 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; - songs[so]->sections[se]->lines[li]->text_above[c]->position = chord_pos; - songs[so]->sections[se]->lines[li]->text_above[c]->u.annot = cho_text_new(); - } prev_state = STATE_ANNOTATION; state = STATE_MARKUP_TAG; break; @@ -4050,6 +4109,17 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) songs[so]->sections[se]->lines[li]->text_above[c]->u.chord->style = cho_style_copy(tag_style); break; case STATE_ANNOTATION: + if (ann > 0) { + songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->text = realloc(songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->text, (ann+1) * sizeof(char)); + songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->text[ann] = 0; + ann = 0; + c++; + 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; + songs[so]->sections[se]->lines[li]->text_above[c]->position = chord_pos; + songs[so]->sections[se]->lines[li]->text_above[c]->u.annot = cho_text_new(); + } cho_style_free(songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->style); songs[so]->sections[se]->lines[li]->text_above[c]->u.annot->style = cho_style_copy(tag_style); break; @@ -4158,6 +4228,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) return NULL; } songs[so]->sections[se]->lines[li]->items[ly]->u.image = image; + 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(); } else { tag_style = cho_style_parse(tag_start, tags[ta]->attrs, cho_tag_style_inherit(tags, ta-1)); if (!tag_style) { @@ -4245,6 +4318,9 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) return NULL; } songs[so]->sections[se]->lines[li]->items[ly]->u.image = image; + 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(); } else { tag_style = cho_style_parse(tag_start, tags[ta]->attrs, cho_tag_style_inherit(tags, ta-1)); if (!tag_style) { @@ -4327,7 +4403,6 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) 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); @@ -4373,3 +4448,49 @@ cho_songs_parse(FILE *fp, const char *chordpro_filepath, struct Config *config) } return songs; } + +#ifdef DEBUG +void +cho_debug_songs_print(struct ChoSong **songs) +{ + struct ChoSong **s = songs; + struct ChoSection **se; + struct ChoLine **li; + struct ChoLineItem **it; + struct ChoLineItemAbove **above; + char *name; + while (*s) { + se = (*s)->sections; + while (*se) { + printf("## Section\n"); + li = (*se)->lines; + while (*li) { + printf("## Line\n"); + above = (*li)->text_above; + it = (*li)->items; + while (*above) { + if ((*above)->is_chord) { + name = cho_chord_name_generate((*above)->u.chord); + printf("chord: %s\n", name); + free(name); + } else { + printf("annotation: %s\n", (*above)->u.annot->text); + } + above++; + } + while (*it) { + if ((*it)->is_text) { + printf("text: %s\n", (*it)->u.text->text); + } else { + printf("image: %s\n", (*it)->u.image->src); + } + it++; + } + li++; + } + se++; + } + s++; + } +} +#endif /* DEBUG */ diff --git a/chordpro.h b/chordpro.h @@ -12,11 +12,6 @@ #define URL_MAX_LEN 2000 #define FONT_NAME_MAX 100 -#define COLOR_BOLD_RED "\033[1;31m" -#define COLOR_BOLD_ORANGE "\033[1;33m" -#define COLOR_BOLD_WHITE "\033[1;37m" -#define COLOR_RESET "\033[0m" - enum EnvironmentDirective { START_OF_CHORUS, SOC, END_OF_CHORUS, EOC, CHORUS, START_OF_VERSE, SOV, END_OF_VERSE, EOV, @@ -148,7 +143,8 @@ struct ChoImage { char *src; struct Size *width; struct Size *height; - struct Size *scale; + struct Size *width_scale; + struct Size *height_scale; enum Alignment align; double border; struct Size *spread_space; @@ -257,10 +253,10 @@ enum ChordQualifier { }; enum BreakType { + BT_EMPTY, BT_LINE, BT_PAGE, - BT_COLUMN, - BT_EMPTY + BT_COLUMN }; /* @@ -352,6 +348,8 @@ void cho_style_print_as_toml(struct Style *style, const char *section); struct RGBColor *cho_color_parse(const char *str); enum LineStyle cho_linestyle_parse(const char *str); +// const char *cho_image_name_create(struct ChoImage *image, const char *dirname); + struct Font *cho_font_new(void); void cho_font_free(struct Font *font); void cho_font_print(struct Font *font); @@ -367,5 +365,6 @@ const char *cho_font_weight_to_config_string(enum FontWeight weight); void cho_font_print_as_toml(struct Font *font, const char *section); void cho_debug_style_print(struct Style *style); +// void cho_debug_songs_print(struct ChoSong **songs); #endif /* _CHORDPRO_H_ */ diff --git a/out_pdf.c b/out_pdf.c @@ -3,6 +3,7 @@ #include <string.h> #include <pdfio.h> #include <pdfio-content.h> +#include <sys/stat.h> #include "chordpro.h" #include "config.h" #include "out_pdf.h" @@ -13,6 +14,8 @@ static struct Fnt **g_fonts = NULL; static char g_current_font_name[200]; static double g_current_font_size; static pdfio_obj_t *g_current_font_obj = NULL; +static char g_cho_dirpath[PATH_MAX]; +static pdfio_file_t *pdf_file = NULL; static pdfio_obj_t * out_pdf_fnt_obj_get_by_name(const char *name) @@ -350,92 +353,40 @@ out_pdf_font_set(pdfio_stream_t *stream, struct Font *font) return true; } -static void -text_line_item_free(struct TextLineItem *item) -{ - free(item->text); - cho_style_free(item->style); - free(item); -} - -static double -text_line_set_lineheight(struct TextLine *line, enum SongFragmentType ftype) -{ - double biggest_font_size = 0.0; - int tli; - for (tli = 0; line->items[tli]; tli++) { - if (line->items[tli]->style->font->size > biggest_font_size) { - biggest_font_size = line->items[tli]->style->font->size; - } - } - switch (ftype) { - case SF_CHORD: - line->height = 2.0 + biggest_font_size; - break; - default: - line->height = 8.0 + biggest_font_size; - } - return line->height; -} - -static struct TextLine * -text_line_create(const char *str, struct Style *style) -{ - struct TextLine *line = malloc(sizeof(struct TextLine)); - line->items = malloc(2 * sizeof(struct TextLineItem *)); - line->items[0] = malloc(sizeof(struct TextLineItem)); - line->items[0]->text = strdup(str); - line->items[0]->style = cho_style_copy(style); - line->items[0]->x = MARGIN_HORIZONTAL; - line->items[1] = NULL; - text_line_set_lineheight(line, SF_TEXT); - line->btype = BT_LINE; - return line; -} - -static void -text_line_free(struct TextLine *line) -{ - if (line->items) { - struct TextLineItem **start = line->items; - while (*line->items) { - text_line_item_free(*line->items); - line->items++; - } - free(start); - } - free(line); -} - static double -text_width(struct TextLineItem *item) +text_width(const char *text, struct Style *style) { - char *name = out_pdf_fnt_name_create(item->style->font); + char *name = out_pdf_fnt_name_create(style->font); pdfio_obj_t *font_obj = out_pdf_fnt_obj_get_by_name(name); if (!font_obj) { LOG_DEBUG("out_pdf_fnt_obj_get_by_name failed."); return -1.0; } free(name); - return pdfioContentTextMeasure(font_obj, item->text, item->style->font->size); + return pdfioContentTextMeasure(font_obj, text, style->font->size); } -static enum Bool -text_fits(const char *str, struct Style *style) +static double +text_above_width(struct ChoLineItemAbove *above) { + char *name; double width; - char *name = out_pdf_fnt_name_create(style->font); - pdfio_obj_t *font_obj = out_pdf_fnt_obj_get_by_name(name); - if (!font_obj) { - LOG_DEBUG("out_pdf_fnt_obj_get_by_name failed."); - return B_ERROR; - } - free(name); - width = pdfioContentTextMeasure(font_obj, str, style->font->size); - if (width > LINE_WIDTH) { - return B_FALSE; + if (above->is_chord) { + name = cho_chord_name_generate(above->u.chord); + width = text_width(name, above->u.chord->style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return EMPTY; + } + free(name); + } else { + width = text_width(above->u.annot->text, above->u.annot->style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return EMPTY; + } } - return B_TRUE; + return width; } static int @@ -450,128 +401,61 @@ find_whitespace(const char *str, size_t start) return -1; } -static char * -text_find_fitting(const char *str, struct Style *style) +static int +text_find_fitting(const char *str, struct Style *style, double x) { size_t len = strlen(str); size_t start = len - 1; char tmp[len+1]; strcpy((char *)&tmp, str); - enum Bool fits; + double width; int i; do { i = find_whitespace((const char *)&tmp, start); if (i == -1) { - util_log(LOG_ERR, "Can' split text because no whitespace was found."); - return NULL; + util_log(LOG_ERR, "Can't split text because no whitespace was found."); + return EMPTY; } tmp[i] = 0; - fits = text_fits((const char *)&tmp, style); - if (fits == B_ERROR) { - LOG_DEBUG("text_fits failed."); - return NULL; + width = text_width((const char *)&tmp, style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return EMPTY; } start = i - 1; - } while (!fits); - return strdup((char *)&tmp); -} - -static struct TextLine ** -text_split_by_whitespace(const char *str, struct Style *style) -{ - struct TextLine **lines = NULL; - struct TextLine *line; - int tl = 0; - char tmp[strlen(str)+1]; - strcpy((char *)&tmp, str); - char *text = (char *)&tmp; - size_t fitting_len; - char *fitting; - enum Bool fits; - while (1) { - fits = text_fits(text, style); - if (fits == B_ERROR) { - LOG_DEBUG("text_fits failed."); - return NULL; - } - if (fits) { - if (text[0] == ' ' || text[0] == '\t') { - text++; - } - line = text_line_create(text, style); - lines = realloc(lines, (tl+2) * sizeof(struct TextLine *)); - lines[tl] = line; - tl++; - lines[tl] = NULL; - break; - } - fitting = text_find_fitting(text, style); - if (!fitting) { - LOG_DEBUG("text_find_fitting failed."); - return NULL; - } - line = text_line_create(fitting, style); - lines = realloc(lines, (tl+1) * sizeof(struct TextLine *)); - lines[tl] = line; - tl++; - fitting_len = strlen(fitting); - text += fitting_len; - free(fitting); - } - return lines; -} - -static enum Bool -text_line_fits(struct TextLine *line) -{ - double width; - int i = 0; - while (line->items[i] != NULL) { - i++; - } - if (i == 0) { - return B_TRUE; - } - width = text_width(line->items[i-1]); - if (width == EMPTY) { - LOG_DEBUG("text_width failed."); - return B_ERROR; - } - if (line->items[i-1]->x + width > LINE_WIDTH) { - return B_FALSE; - } - return B_TRUE; + } while (x + width > LINE_WIDTH); + return i; } static bool -out_pdf_draw_line(pdfio_stream_t *stream, struct TextLineItem *item, double y, double width, enum LineLocation line_location) +out_pdf_draw_line(pdfio_stream_t *stream, struct PDFText *text, double y, double width, enum LineLocation line_location) { double red, green, blue; 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; + red = text->style->underline_color->red / 255.0; + green = text->style->underline_color->green / 255.0; + blue = text->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; - y += item->style->font->size * 0.3; + red = text->style->strikethrough_color->red / 255.0; + green = text->style->strikethrough_color->green / 255.0; + blue = text->style->strikethrough_color->blue / 255.0; + y += text->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; - y += item->style->font->size * 0.8; + red = text->style->overline_color->red / 255.0; + green = text->style->overline_color->green / 255.0; + blue = text->style->overline_color->blue / 255.0; + y += text->style->font->size * 0.8; break; } - if (!pdfioContentPathMoveTo(stream, item->x, y)) { + if (!pdfioContentPathMoveTo(stream, text->x, y)) { LOG_DEBUG("pdfioContentPathMoveTo failed."); return false; } - if (!pdfioContentPathLineTo(stream, item->x + width, y)) { + if (!pdfioContentPathLineTo(stream, text->x + width, y)) { LOG_DEBUG("pdfioContentPathLineTo failed."); return false; } @@ -587,22 +471,22 @@ out_pdf_draw_line(pdfio_stream_t *stream, struct TextLineItem *item, double y, d } static bool -out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y) +out_pdf_text_show(pdfio_stream_t *stream, struct PDFText *text) { double red, green, blue, width; - // TODO: Maybe store the width in TextLineItem - width = text_width(item); + // TODO: Maybe store the width in PDFText!? + width = text_width(text->text, text->style); if (width == EMPTY) { LOG_DEBUG("text_width failed."); return false; } - if (!out_pdf_font_set(stream, item->style->font)) { + if (!out_pdf_font_set(stream, text->style->font)) { LOG_DEBUG("out_pdf_font_set failed."); return false; } - red = item->style->foreground_color->red / 255.0; - green = item->style->foreground_color->green / 255.0; - blue = item->style->foreground_color->blue / 255.0; + red = text->style->foreground_color->red / 255.0; + green = text->style->foreground_color->green / 255.0; + blue = text->style->foreground_color->blue / 255.0; if (!pdfioContentSetFillColorRGB(stream, red, green, blue)) { LOG_DEBUG("pdfioContentSetFillColorRGB failed."); return false; @@ -611,15 +495,15 @@ out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y) LOG_DEBUG("pdfioContentTextBegin failed."); return false; } - if (!pdfioContentTextMoveTo(stream, item->x, y)) { + if (!pdfioContentTextMoveTo(stream, text->x, text->y)) { LOG_DEBUG("pdfioContentTextMoveTo failed."); return false; } - if (!pdfioContentSetTextRise(stream, item->style->rise)) { + if (!pdfioContentSetTextRise(stream, text->style->rise)) { LOG_DEBUG("pdfioContentSetTextRise failed."); return false; } - if (!pdfioContentTextShow(stream, true, item->text)) { + if (!pdfioContentTextShow(stream, true, text->text)) { LOG_DEBUG("pdfioContentTextShow failed."); return false; } @@ -627,30 +511,30 @@ out_pdf_text_show(pdfio_stream_t *stream, struct TextLineItem *item, double y) LOG_DEBUG("pdfioContentTextEnd failed."); 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) { - out_pdf_draw_line(stream, item, y, width, LL_UNDER); - out_pdf_draw_line(stream, item, y-1.5, width, LL_UNDER); - } - if (item->style->strikethrough) { - out_pdf_draw_line(stream, item, y, width, LL_STRIKETHROUGH); - } - if (item->style->overline_style == LS_SINGLE) { - out_pdf_draw_line(stream, item, y, width, LL_OVER); - } else if (item->style->overline_style == LS_DOUBLE) { - out_pdf_draw_line(stream, item, y, width, LL_OVER); - out_pdf_draw_line(stream, item, y+1.5, width, LL_OVER); - } - if (item->style->boxed) { - red = item->style->boxed_color->red / 255.0; - green = item->style->boxed_color->green / 255.0; - blue = item->style->boxed_color->blue / 255.0; + if (text->style->underline_style == LS_SINGLE) { + out_pdf_draw_line(stream, text, text->y, width, LL_UNDER); + } else if (text->style->underline_style == LS_DOUBLE) { + out_pdf_draw_line(stream, text, text->y, width, LL_UNDER); + out_pdf_draw_line(stream, text, text->y-1.5, width, LL_UNDER); + } + if (text->style->strikethrough) { + out_pdf_draw_line(stream, text, text->y, width, LL_STRIKETHROUGH); + } + if (text->style->overline_style == LS_SINGLE) { + out_pdf_draw_line(stream, text, text->y, width, LL_OVER); + } else if (text->style->overline_style == LS_DOUBLE) { + out_pdf_draw_line(stream, text, text->y, width, LL_OVER); + out_pdf_draw_line(stream, text, text->y+1.5, width, LL_OVER); + } + if (text->style->boxed) { + red = text->style->boxed_color->red / 255.0; + green = text->style->boxed_color->green / 255.0; + blue = text->style->boxed_color->blue / 255.0; if (!pdfioContentSetStrokeColorRGB(stream, red, green, blue)) { LOG_DEBUG("pdfioContentSetFillColorRGB failed."); return false; } - if (!pdfioContentPathRect(stream, item->x - 2.0, y - 2.0, width + 4.0, item->style->font->size * 0.8 + 4.0)) { + if (!pdfioContentPathRect(stream, text->x - 2.0, text->y - 2.0, width + 4.0, text->style->font->size * 0.8 + 4.0)) { LOG_DEBUG("pdfioContentPathRect failed."); return false; } @@ -662,606 +546,39 @@ 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) -{ - int i = -1; - int ly = -1; - int last_ly, last_i; - int pos = 0; - char *name; - double width = 0.0; - pdfio_obj_t *font_obj; - 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; - goto FOUND; - } - pos++; - } - } - /* If the chord is the last thing in the line */ - last_ly = ly; - last_i = i; - FOUND: - if (space) { - space->line_item_index = last_ly; - space->text_index = last_i; - } - for (ly = 0; line->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 { - // TODO: calculate width of image and append to 'width' var. - } - } - return width; -} - -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 prev_text_above_width; - double cur = line_width_until_chord(line, text_above, NULL); - if (cur == EMPTY) { - return B_ERROR; - } - 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) { - LOG_DEBUG("out_pdf_fnt_obj_get_by_name failed."); - return B_ERROR; - } - free(name); - prev_text_above_width = pdfioContentTextMeasure(font_obj, prev_text_above->u.chord->name, 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) { - LOG_DEBUG("out_pdf_fnt_obj_get_by_name failed."); - 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) -{ - int sn; - for (sn = 0; spaces[sn]; sn++) { - if (spaces[sn]->line_item_index == ly && spaces[sn]->text_index == i) { - return spaces[sn]; - } - } - return NULL; -} - -static struct Text ** -text_create(struct ChoSong **songs, struct Config *config) -{ - struct OutputStyle *output_style; - int so, se, li, ly, ch; - double width; - double y = MEDIABOX_HEIGHT - MARGIN_TOP; - bool add_space_to_next_chord = false; - struct ChoLine **lines; - struct ChoLineItemAbove **text_above; - struct SpaceNeeded space; - struct SpaceNeeded **spaces = NULL; - struct TextLineMetadata **lines_metadata = NULL; - struct TextLine **text_lines = NULL; - int tlm = 0; - int sn = 0; - int t = 0; - int tl = 0; - int tli = 0; - int m; - struct Text **text = NULL; - enum Bool fits; - for (so = 0; songs[so]; so++, t++) { - text = realloc(text, (t+1) * sizeof(struct Text *)); - text[t] = malloc(sizeof(struct Text)); - text[t]->lines = NULL; - for (m = 0; songs[so]->metadata[m]; m++) { - if (!strcmp(songs[so]->metadata[m]->name, "title")) { - fits = text_fits(songs[so]->metadata[m]->value, songs[so]->metadata[m]->style); - if (fits == B_ERROR) { - LOG_DEBUG("text_fits failed."); - return NULL; - } - if (fits) { - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = malloc(sizeof(struct TextLine)); - text[t]->lines[tl]->items = malloc(2 * sizeof(struct TextLineItem *)); - text[t]->lines[tl]->items[0] = malloc(sizeof(struct TextLineItem)); - text[t]->lines[tl]->items[0]->text = strdup(songs[so]->metadata[m]->value); - text[t]->lines[tl]->items[0]->style = cho_style_copy(songs[so]->metadata[m]->style); - width = text_width(text[t]->lines[tl]->items[0]); - if (width == EMPTY) { - LOG_DEBUG("text_width failed."); - return NULL; - } - text[t]->lines[tl]->items[0]->x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; - text[t]->lines[tl]->items[1] = NULL; - text[t]->lines[tl]->btype = BT_LINE; - text[t]->lines[tl]->ftype = SF_TITLE; - y -= text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); - if (y < MARGIN_BOTTOM) { - y = MEDIABOX_HEIGHT - MARGIN_TOP; - text[t]->lines[tl]->btype = BT_PAGE; - } - tl++; - } else { - util_log(LOG_WARN, "Title doesn't fit on one line."); - text_lines = text_split_by_whitespace(songs[so]->metadata[m]->value, songs[so]->metadata[m]->style); - for (int i = 0; text_lines[i]; i++) { - width = text_width(text_lines[i]->items[0]); - if (width == EMPTY) { - LOG_DEBUG("text_width failed."); - return NULL; - } - text_lines[i]->items[0]->x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = text_lines[i]; - tl++; - } - free(text_lines); - } - } - } - for (m = 0; songs[so]->metadata[m]; m++) { - if (!strcmp(songs[so]->metadata[m]->name, "subtitle")) { - fits = text_fits(songs[so]->metadata[m]->value, songs[so]->metadata[m]->style); - if (fits == B_ERROR) { - LOG_DEBUG("text_fits failed."); - return NULL; - } - if (fits) { - output_style = config_output_style_get(config->output->styles, "subtitle"); - if (!output_style) { - LOG_DEBUG("config_output_style_get failed."); - return NULL; - } - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = malloc(sizeof(struct TextLine)); - text[t]->lines[tl]->items = malloc(2 * sizeof(struct TextLineItem *)); - text[t]->lines[tl]->items[0] = malloc(sizeof(struct TextLineItem)); - text[t]->lines[tl]->items[0]->text = strdup(songs[so]->metadata[m]->value); - text[t]->lines[tl]->items[0]->style = cho_style_copy(output_style->style); - width = text_width(text[t]->lines[tl]->items[0]); - if (width == EMPTY) { - LOG_DEBUG("text_width failed."); - return NULL; - } - text[t]->lines[tl]->items[0]->x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; - text[t]->lines[tl]->items[1] = NULL; - text[t]->lines[tl]->btype = BT_LINE; - text[t]->lines[tl]->ftype = SF_SUBTITLE; - y -= text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); - if (y < MARGIN_BOTTOM) { - y = MEDIABOX_HEIGHT - MARGIN_TOP; - text[t]->lines[tl]->btype = BT_PAGE; - } - tl++; - } else { - util_log(LOG_WARN, "Subtitle doesn't fit on one line."); - text_lines = text_split_by_whitespace(songs[so]->metadata[m]->value, songs[so]->metadata[m]->style); - for (int i = 0; text_lines[i]; i++) { - width = text_width(text_lines[i]->items[0]); - if (width == EMPTY) { - LOG_DEBUG("text_width failed."); - return NULL; - } - text_lines[i]->items[0]->x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = text_lines[i]; - tl++; - } - free(text_lines); - } - } - } - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = malloc(sizeof(struct TextLine)); - text[t]->lines[tl]->items = NULL; - text[t]->lines[tl]->btype = BT_LINE; - text[t]->lines[tl]->height = 30.0; - tl++; - for (se = 0; songs[so]->sections[se]; se++) { - if (songs[so]->sections[se]->label) { - 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; - } - if (fits) { - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = malloc(sizeof(struct TextLine)); - text[t]->lines[tl]->items = malloc(2 * sizeof(struct TextLineItem *)); - text[t]->lines[tl]->items[0] = malloc(sizeof(struct TextLineItem)); - text[t]->lines[tl]->items[0]->text = strdup(songs[so]->sections[se]->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; - text[t]->lines[tl]->btype = BT_LINE; - text[t]->lines[tl]->ftype = SF_LABEL; - y -= text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); - if (y < MARGIN_BOTTOM) { - y = MEDIABOX_HEIGHT - MARGIN_TOP; - text[t]->lines[tl]->btype = BT_PAGE; - } - 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->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) { - LOG_DEBUG("text_width failed."); - return NULL; - } - // text_lines[i]->items[0]->x = MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = text_lines[i]; - tl++; - } - free(text_lines); - } - } - lines = songs[so]->sections[se]->lines; - for (li = 0; lines[li]; li++, tl++) { - text_above = songs[so]->sections[se]->lines[li]->text_above; - int text_above_len = cho_text_above_count(text_above); - // TODO: The code inside the if statement seems to me too complicated, so simplify if possible - if (text_above_len > 0) { - lines_metadata = realloc(lines_metadata, (tlm+1) * sizeof(struct TextLineMetadata *)); - lines_metadata[tlm] = malloc(sizeof(struct TextLineMetadata)); - lines_metadata[tlm]->text_index = t; - lines_metadata[tlm]->text_line_index = tl; - lines_metadata[tlm]->is_lyrics = false; - tlm++; - 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; - double added_space = 0.0; - 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) { - LOG_DEBUG("line_width_until_chord failed."); - return NULL; - } - if (tli == 0) { - text[t]->lines[tl]->items[tli]->x = MARGIN_HORIZONTAL + width_until; - } else { - text[t]->lines[tl]->items[tli]->x = MARGIN_HORIZONTAL + width_until + added_space; - } - if ( - ch > 0 && - !text_above[ch-1]->is_chord && - !text_above[ch]->is_chord && - text_above[ch-1]->position == text_above[ch]->position - ) { - int b; - struct TextLineItem tmp; - for (b=ch-1; b >= 0 && text_above[b]->position == text_above[ch]->position; b--) { - tmp.style = text_above[b]->u.annot->style; - tmp.text = text_above[b]->u.annot->text; - width = text_width(&tmp); - if (width == EMPTY) { - LOG_DEBUG("text_width failed."); - return NULL; - } - text[t]->lines[tl]->items[tli]->x += width; - } - } - 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; - } - if (text_above[ch]->is_chord) { - text[t]->lines[tl]->items[tli]->style = cho_style_copy(text_above[ch]->u.chord->style); - text[t]->lines[tl]->items[tli]->text = cho_chord_name_generate(text_above[ch]->u.chord); - } else { - text[t]->lines[tl]->items[tli]->style = cho_style_copy(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; - } - 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) { - LOG_DEBUG("out_pdf_text_above_is_enough_space failed."); - 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; - spaces[sn]->text_index = space.text_index; - spaces[sn]->amount = space.amount; - sn++; - add_space_to_next_chord = true; - } - } - spaces = realloc(spaces, (sn+1) * sizeof(struct SpaceNeeded *)); - spaces[sn] = NULL; - tli++; - text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); - text[t]->lines[tl]->items[tli] = NULL; - fits = text_line_fits(text[t]->lines[tl]); - if (fits == B_ERROR) { - LOG_DEBUG("text_line_fits failed."); - return NULL; - } - if (!fits) { - util_log(LOG_WARN, "text line (chords/annotations) doesn't fit."); - } - text[t]->lines[tl]->btype = lines[li]->btype; - text[t]->lines[tl]->ftype = SF_CHORD; - y -= text_line_set_lineheight(text[t]->lines[tl], SF_CHORD); - if (y < MARGIN_BOTTOM) { - // INFO: we don't split the chords and corresponding lyrics - y = MEDIABOX_HEIGHT - MARGIN_TOP; - y -= text[t]->lines[tl]->height; - text[t]->lines[tl-1]->btype = BT_PAGE; - } - tli = 0; - tl++; - } - lines_metadata = realloc(lines_metadata, (tlm+1) * sizeof(struct TextLineMetadata *)); - lines_metadata[tlm] = malloc(sizeof(struct TextLineMetadata)); - lines_metadata[tlm]->text_index = t; - lines_metadata[tlm]->text_line_index = tl; - lines_metadata[tlm]->is_lyrics = true; - tlm++; - 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]->items[ly]; ly++) { - if (!lines[li]->items[ly]->is_text) { - util_log(LOG_INFO, "image with src '%s'.", 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; - struct SpaceNeeded *sp; - text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); - text[t]->lines[tl]->items[tli] = malloc(sizeof(struct TextLineItem)); - text[t]->lines[tl]->items[tli]->text = NULL; - text[t]->lines[tl]->items[tli]->style = cho_style_copy(lines[li]->items[ly]->u.text->style); - if (ly == 0) { - text[t]->lines[tl]->items[tli]->x = MARGIN_HORIZONTAL; - } else { - 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."); - 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; - 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]->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; - tlii = 0; - tli++; - text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); - text[t]->lines[tl]->items[tli] = malloc(sizeof(struct TextLineItem)); - text[t]->lines[tl]->items[tli]->text = NULL; - text[t]->lines[tl]->items[tli]->style = cho_style_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."); - 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)); - 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; - tli++; - } - text[t]->lines[tl]->items = realloc(text[t]->lines[tl]->items, (tli+1) * sizeof(struct TextLineItem *)); - text[t]->lines[tl]->items[tli] = NULL; - fits = text_line_fits(text[t]->lines[tl]); - if (fits == B_ERROR) { - LOG_DEBUG("text_line_fits failed."); - return NULL; - } - if (!fits) { - util_log(LOG_WARN, "text line (lyrics) doesn't fit."); - } - text[t]->lines[tl]->btype = lines[li]->btype; - text[t]->lines[tl]->ftype = SF_TEXT; - y -= text_line_set_lineheight(text[t]->lines[tl], SF_TEXT); - if (y < MARGIN_BOTTOM) { - if (text[t]->lines[tl-1]->ftype == SF_CHORD) { - text[t]->lines[tl-2]->btype = BT_PAGE; - y = MEDIABOX_HEIGHT - MARGIN_TOP; - y -= text[t]->lines[tl-1]->height; - y -= text[t]->lines[tl]->height; - } else { - text[t]->lines[tl-1]->btype = BT_PAGE; - y = MEDIABOX_HEIGHT - MARGIN_TOP; - y -= text[t]->lines[tl]->height; - } - } - tli = 0; - if (spaces) { - for (sn = 0; spaces[sn]; sn++) { - free(spaces[sn]); - } - free(spaces); - spaces = NULL; - sn = 0; - } - } - text[t]->lines[tl-1]->height += SECTION_GAP_WIDTH; - } - text[t]->lines = realloc(text[t]->lines, (tl+1) * sizeof(struct TextLine *)); - text[t]->lines[tl] = NULL; - tl = 0; - } - text = realloc(text, (t+1) * sizeof(struct Text *)); - text[t] = NULL; - for (m = 0; m<tlm; m++) { - free(lines_metadata[m]); - } - free(lines_metadata); - return text; -} - -static void -text_free(struct Text *text) -{ - struct TextLine **start = text->lines; - while (*text->lines) { - text_line_free(*text->lines); - text->lines++; - } - free(start); - free(text); -} - -static void -texts_free(struct Text **texts) -{ - struct Text **start = texts; - while (*texts) { - text_free(*texts); - texts++; - } - free(start); -} - -static pdfio_dict_t * -annot_create(pdfio_file_t *pdf, const char *uri, pdfio_rect_t *rect) +static bool +annot_add(struct PDFContext *ctx, struct Style *style, double width) { - pdfio_dict_t *annot = pdfioDictCreate(pdf); + pdfio_rect_t rect; + pdfio_dict_t *annot, *action; + rect.x1 = ctx->x; + rect.x2 = ctx->x + width; + rect.y1 = ctx->y - 2.0; + rect.y2 = ctx->y + style->font->size * 0.8; + annot = pdfioDictCreate(pdf_file); if (!pdfioDictSetName(annot, "Subtype", "Link")) { LOG_DEBUG("pdfioDictSetName failed."); - return NULL; + return false; } - if (!pdfioDictSetRect(annot, "Rect", rect)) { + if (!pdfioDictSetRect(annot, "Rect", &rect)) { LOG_DEBUG("pdfioDictSetRect failed."); - return NULL; + return false; } - pdfio_dict_t *action = pdfioDictCreate(pdf); + action = pdfioDictCreate(pdf_file); if (!pdfioDictSetName(action, "S", "URI")) { LOG_DEBUG("pdfioDictSetName failed."); - return NULL; + return false; } - if (!pdfioDictSetString(action, "URI", uri)) { + if (!pdfioDictSetString(action, "URI", style->href)) { LOG_DEBUG("pdfioDictSetString failed."); - return NULL; + return false; } if (!pdfioDictSetDict(annot, "A", action)) { LOG_DEBUG("pdfioDictSetDict failed."); - return NULL; - } - return annot; -} - -static bool -annots_create(pdfio_file_t *pdf, pdfio_array_t *annots, struct Text **text) -{ - struct TextLineItem *item; - pdfio_dict_t *annot; - pdfio_rect_t rect; - double y = MEDIABOX_HEIGHT - MARGIN_TOP; - double width; - int t, tl, tli; - pdfio_array_t *page_annots = pdfioArrayCreate(pdf); - if (!page_annots) { - LOG_DEBUG("pdfioArrayCreate failed."); return false; } - 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++) { - item = text[t]->lines[tl]->items[tli]; - if (item->style->href) { - width = text_width(item); - rect.x1 = item->x; - rect.x2 = item->x + width; - rect.y1 = y - 2.0; - rect.y2 = y + item->style->font->size * 0.8; - annot = annot_create(pdf, item->style->href, &rect); - if (!annot) { - LOG_DEBUG("annot_create failed."); - return false; - } - if (!pdfioArrayAppendDict(page_annots, annot)) { - LOG_DEBUG("pdfioArrayAppendDict failed."); - return false; - } - } - } - } - if (text[t]->lines[tl]->btype == BT_PAGE) { - if (!pdfioArrayAppendArray(annots, page_annots)) { - LOG_DEBUG("pdfioArrayAppendArray failed."); - return false; - } - page_annots = pdfioArrayCreate(pdf); - if (!page_annots) { - LOG_DEBUG("pdfioArrayCreate failed."); - return false; - } - y = MEDIABOX_HEIGHT - MARGIN_TOP; - } else { - y -= text[t]->lines[tl]->height; - } - } - } - if (!pdfioArrayAppendArray(annots, page_annots)) { - LOG_DEBUG("pdfioArrayAppendArray failed."); + if (!pdfioArrayAppendDict(ctx->content->pages[ctx->page]->annots, annot)) { + LOG_DEBUG("pdfioArrayAppendDict failed."); return false; } return true; @@ -1284,60 +601,1202 @@ out_pdf_set_title(pdfio_file_t *pdf, struct ChoSong **songs) } static pdfio_stream_t * -out_pdf_page_create(pdfio_file_t *pdf, pdfio_array_t *annots, int page_no) +out_pdf_page_create(pdfio_file_t *pdf, struct Image **imgs, pdfio_array_t *annots) { pdfio_dict_t *page_dict; - pdfio_array_t *page_annots; pdfio_stream_t *page_stream; - int f; pdfio_array_t *color_array = pdfioArrayCreateColorFromStandard(pdf, 3, PDFIO_CS_ADOBE); + int f; page_dict = pdfioDictCreate(pdf); if (!pdfioPageDictAddColorSpace(page_dict, "rgbcolorspace", color_array)) { LOG_DEBUG("pdfioPageDictAddColorSpace failed."); + return NULL; + } + if (!pdfioDictSetArray(page_dict, "Annots", annots)) { + LOG_DEBUG("pdfioDictSetArray failed."); return false; } + if (imgs) { + for (f = 0; imgs[f]; f++) { + if (!pdfioPageDictAddImage(page_dict, imgs[f]->name, imgs[f]->obj)) { + LOG_DEBUG("pdfioPageDictAddImage failed."); + return NULL; + } + } + } for (f = 0; g_fonts[f]; f++) { if (!pdfioPageDictAddFont(page_dict, g_fonts[f]->name, g_fonts[f]->font)) { LOG_DEBUG("pdfioPageDictAddFont failed."); - return false; + return NULL; } } - page_annots = pdfioArrayGetArray(annots, page_no); - if (!pdfioDictSetArray(page_dict, "Annots", page_annots)) { - LOG_DEBUG("pdfioDictSetArray failed."); - return false; - } page_stream = pdfioFileCreatePage(pdf, page_dict); if (!pdfioContentSetFillColorSpace(page_stream, "rgbcolorspace")) { LOG_DEBUG("pdfioContentSetFillColorSpace failed."); - return false; + return NULL; } if (!pdfioContentSetStrokeColorSpace(page_stream, "rgbcolorspace")) { LOG_DEBUG("pdfioContentSetStrokeColorSpace failed."); - return false; + return NULL; } return page_stream; } -/* static bool -pdf_content_create(struct PDFContent *out, struct ChoSong **songs, struct Config *config) +static struct Obj * +obj_new(void) { - return true; -} */ + struct Obj *obj = malloc(sizeof(struct Obj)); + obj->name = NULL; + obj->value = NULL; + return obj; +} -char * -out_pdf_create(const char *cho_filepath, const char *output_folder_or_file, struct ChoSong **songs, struct Config *config) +static void +obj_free(struct Obj *obj) { - memset(&g_current_font_name, 0, sizeof(g_current_font_name)); - char *pdf_filename = out_pdf_filename_create(songs, cho_filepath, output_folder_or_file); - if (!pdf_filename) { - LOG_DEBUG("out_pdf_filename_create failed."); + free(obj->name); + free(obj); +} + +static void +objs_free(struct Obj **objs) +{ + if (!objs) { + return; + } + struct Obj **start = objs; + while (*objs) { + obj_free(*objs); + objs++; + } + free(start); +} + +static bool +objs_has(struct Obj **objs, const char *name) +{ + if (!objs) { + return false; + } + struct Obj **o = objs; + while (*o) { + if (!strcmp((*o)->name, name)) { + return true; + } + o++; + } + return false; +} + +static pdfio_obj_t * +objs_get_obj(struct Obj **objs, const char *name) +{ + struct Obj **o = objs; + while (*o) { + if (!strcmp((*o)->name, name)) { + return (*o)->value; + } + o++; + } + return NULL; +} + +static double +image_width(struct ChoImage *image, pdfio_obj_t *obj) +{ + double width; + width = pdfioImageGetWidth(obj); + if (image->width) { + if (image->width->type == ST_POINT) { + width = image->width->d; + } else + if (image->width->type == ST_PERCENT) { + width = LINE_WIDTH * image->width->d; + } + } + if (image->width_scale) { + width *= image->width_scale->d; + } + width += image->border; + return width; +} + +static double +image_height(struct ChoImage *image, pdfio_obj_t *obj) +{ + double height; + height = pdfioImageGetHeight(obj); + if (image->height) { + if (image->height->type == ST_POINT) { + height = image->height->d; + } else + if (image->height->type == ST_PERCENT) { + height = LINE_WIDTH * image->height->d; + } + } + if (image->height_scale) { + height *= image->height_scale->d; + } + height += image->border; + return height; +} + +static char * +image_name(struct ChoImage *image) +{ + char tmp[PATH_MAX]; + char *image_path; + struct stat s; + if (strchr(image->src, '/')) { + image_path = image->src; + } else { + strcpy((char *)&tmp, g_cho_dirpath); + strcat((char *)&tmp, "/"); + strcat((char *)&tmp, image->src); + image_path = (char *)&tmp; + } + if (stat(image_path, &s)) { + LOG_DEBUG("stat failed."); return NULL; } + memset(tmp, 0, PATH_MAX); + sprintf((char *)&tmp, "%ld", s.st_ino); + return strdup(tmp); +} + +static bool +out_pdf_load_images(struct Obj ***images, pdfio_file_t *file, struct ChoSong **songs) +{ + struct ChoSong **s = songs; + struct ChoSection **se; + struct ChoLine **li; + struct ChoLineItem **items; + int i = 0; + char filepath[PATH_MAX]; + char *name; + char *image_filepath; + while (*s) { + se = (*s)->sections; + while (*se) { + li = (*se)->lines; + while (*li) { + items = (*li)->items; + while (*items) { + if (!(*items)->is_text) { + memset(filepath, 0, PATH_MAX); + strcpy((char *)&filepath, g_cho_dirpath); + strcat((char *)&filepath, "/"); + strcat((char *)&filepath, (*items)->u.image->src); + name = image_name((*items)->u.image); + if (!objs_has(*images, name)) { + *images = realloc(*images, (i+2) * sizeof(struct Obj *)); + (*images)[i] = obj_new(); + (*images)[i]->name = strdup(name); + if (strchr((*items)->u.image->src, '/')) { + image_filepath = (*items)->u.image->src; + } else { + image_filepath = (char *)&filepath; + } + (*images)[i]->value = pdfioFileCreateImageObjFromFile(file, image_filepath, true); + if (!(*images)[i]->value) { + LOG_DEBUG("pdfioFileCreateImageObjFromFile failed."); + return false; + } + (*images)[i+1] = NULL; + } + free(name); + i++; + } + items++; + } + li++; + } + se++; + } + s++; + } + return true; +} + +static double +line_width_until_text_above( + struct ChoLineItem **items, + struct ChoLineItemAbove *above, + struct Obj **img_objs, + struct SpaceNeeded *space +) +{ + int i = EMPTY; + int k = EMPTY; + int save_i = EMPTY; + int save_k = EMPTY; + int pos = 0; + double width = 0.0; + char *name; + pdfio_obj_t *font_obj; + pdfio_obj_t *obj; + struct ChoText *text; + for (i = 0; items[i]; i++) { + if (items[i]->is_text) { + for (k = 0; items[i]->u.text->text[k]; k++) { + if (above->position == pos) { + save_i = i; + save_k = k; + goto FOUND; + } + pos++; + } + } + } + /* INFO: If the chord/annotation is the last thing in the line */ + save_i = i; + save_k = k; + FOUND: + if (space) { + space->line_item_index = save_i; + space->text_index = save_k; + } + for (i = 0; items[i] && i <= save_i; i++) { + if (items[i]->is_text) { + text = items[i]->u.text; + name = out_pdf_fnt_name_create(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 (save_i == i) { + char tmp[strlen(text->text)+1]; + strcpy((char *)&tmp, text->text); + tmp[save_k] = 0; + width += pdfioContentTextMeasure(font_obj, (const char *)&tmp, text->style->font->size); + } else { + width += pdfioContentTextMeasure(font_obj, text->text, text->style->font->size); + } + free(name); + } else { + name = image_name(items[i]->u.image); + obj = objs_get_obj(img_objs, name); + if (!obj) { + LOG_DEBUG("objs_get_obj failed."); + return EMPTY; + } + width += image_width(items[i]->u.image, obj); + free(name); + } + } + return width; +} + +static struct Image * +image_new(void) +{ + struct Image *image = malloc(sizeof(struct Image)); + image->x = 0.0; + image->y = 0.0; + image->width = 0.0; + image->height = 0.0; + image->name = NULL; + image->obj = NULL; + return image; +} + +static void +image_free(struct Image *image) +{ + if (!image) { + return; + } + free(image->name); + free(image); +} + +static struct PDFText * +pdf_text_new(void) +{ + struct PDFText *t = malloc(sizeof(struct PDFText)); + t->text = NULL; + t->style = NULL; + t->x = 0.0; + t->y = 0.0; + return t; +} + +static void +pdf_text_free(struct PDFText *text) +{ + if (!text) { + return; + } + free(text->text); + cho_style_free(text->style); + free(text); +} + +static struct PDFPage * +pdf_page_new() +{ + struct PDFPage *page = malloc(sizeof(struct PDFPage)); + page->texts = NULL; + page->images = NULL; + page->annots = pdfioArrayCreate(pdf_file); + return page; +} + +static void +pdf_page_free(struct PDFPage *page) +{ + if (!page) { + return; + } + struct PDFText **t; + struct Image **i; + t = page->texts; + if (t) { + while (*t) { + pdf_text_free(*t); + t++; + } + free(page->texts); + } + i = page->images; + if (i) { + while (*i) { + image_free(*i); + i++; + } + free(page->images); + } + free(page); +} + +static void +spaces_free(struct SpaceNeeded **spaces) +{ + if (!spaces) { + return; + } + struct SpaceNeeded **s = spaces; + while (*s) { + free(*s); + s++; + } + free(spaces); +} + +static bool +calc_space_between_text_above( + struct ChoLineItem **items, + struct ChoLineItemAbove **text_above, + struct Obj **img_objs, + struct SpaceNeeded ***spaces +) +{ + if (!*text_above) { + return true; + } + int sp = 0; + int i; + for (i = 1; text_above[i]; i++) { + struct SpaceNeeded space = { + .line_item_index = 0, + .text_index = 0, + .text_above_index = 0, + .amount = 0.0 + }; + double width_between; + double width_until_cur; + double width_until_prev; + double prev_width; + width_until_cur = line_width_until_text_above(items, text_above[i], img_objs, NULL); + if (width_until_cur == EMPTY) { + LOG_DEBUG("line_width_until_text_above failed."); + return false; + } + i--; + width_until_prev = line_width_until_text_above(items, text_above[i], img_objs, &space); + if (width_until_prev == EMPTY) { + LOG_DEBUG("line_width_until_text_above failed."); + return false; + } + prev_width = text_above_width(text_above[i]); + if (prev_width == EMPTY) { + LOG_DEBUG("text_above_width failed."); + return false; + } + i++; + width_between = width_until_cur - width_until_prev; + if (prev_width >= width_between) { + space.text_above_index = i; + space.amount = prev_width - width_between + MIN_CHORD_GAP_WIDTH; + *spaces = realloc(*spaces, (sp+1) * sizeof(struct SpaceNeeded *)); + (*spaces)[sp] = malloc(sizeof(struct SpaceNeeded)); + *((*spaces)[sp]) = space; + sp++; + } else { + } + } + *spaces = realloc(*spaces, (sp+1) * sizeof(struct SpaceNeeded *)); + (*spaces)[sp] = NULL; + return true; +} + +static double +item_width(struct ChoLineItem *item, int i, struct SpaceNeeded **spaces, struct Obj **img_objs) +{ + double width; + struct SpaceNeeded **s = spaces; + if (item->is_text) { + width = text_width(item->u.text->text, item->u.text->style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return EMPTY; + } + if (s) { + while (*s) { + if ((*s)->line_item_index == i) { + width += (*s)->amount; + } + s++; + } + } + } else { + char *name; + pdfio_obj_t *obj; + name = image_name(item->u.image); + if (!name) { + LOG_DEBUG("image_name failed."); + return EMPTY; + } + obj = objs_get_obj(img_objs, name); + if (!obj) { + LOG_DEBUG("objs_get_obj failed."); + return EMPTY; + } + free(name); + width = image_width(item->u.image, obj); + } + return width; +} + +static struct CharPosition * +items_find_position_to_break_line( + struct ChoLineItem **items, + struct SpaceNeeded **spaces, + struct Obj **img_objs +) +{ + struct CharPosition *pos = malloc(sizeof(struct CharPosition)); + pos->line_item_index = -1; + pos->text_index = -1; + double width = 0.0; + double d; + struct ChoLineItem **it = items; + int i; + for (i = 0; it[i]; i++) { + d = item_width(it[i], i, spaces, img_objs); + if (d == EMPTY) { + LOG_DEBUG("item_width failed."); + return NULL; + } + if (width + d > LINE_WIDTH) { + if (it[i]->is_text) { + pos->line_item_index = i; + pos->text_index = text_find_fitting( + it[i]->u.text->text, + it[i]->u.text->style, + width + ); + if (pos->text_index == EMPTY) { + LOG_DEBUG("text_find_fitting failed."); + return NULL; + } + } else { + pos->line_item_index = i; + pos->text_index = -1; + } + return pos; + } else { + width += d; + } + } + return pos; +} + +static double +images_find_biggest_height(struct ChoLineItem **left_items, int line_item_index, struct Obj **img_objs) +{ + struct ChoLineItem **items = left_items; + char *name; + pdfio_obj_t *obj; + int i = 0; + double end = line_item_index; + double biggest = 0.0; + double height; + if (end == -1) { + end = 10000; + } + while (*items && i <= end) { + if (!(*items)->is_text) { + name = image_name((*items)->u.image); + obj = objs_get_obj(img_objs, name); + if (!obj) { + LOG_DEBUG("objs_get_obj failed."); + return EMPTY; + } + height = image_height((*items)->u.image, obj); + if (height > biggest) { + biggest = height; + } + free(name); + } + items++; + i++; + } + return biggest; +} + +static int +text_above_find_index_to_break_line(struct ChoLineItem **items, struct ChoLineItemAbove **text_above, struct CharPosition *pos) +{ + int position = 0; + int i, k; + if (pos->text_index == -1) { + for (i = 0; items[i]; i++) { + if (pos->line_item_index == i) { + goto FOUND; + } + if (items[i]->is_text) { + for (k = 0; items[i]->u.text->text[k]; k++) { + position++; + } + } + } + } else { + for (i = 0; items[i]; i++) { + if (items[i]->is_text) { + for (k = 0; items[i]->u.text->text[k]; k++) { + if (pos->line_item_index == i && pos->text_index == k) { + goto FOUND; + } + position++; + } + } + } + } + return -2; + FOUND: + for (i = 0; text_above[i]; i++) { + if (text_above[i]->position >= position) { + return i; + } + } + return -1; +} + +static void +text_above_update_positions(struct ChoLineItemAbove **aboves, size_t consumed_lyrics) +{ + struct ChoLineItemAbove **a = aboves; + while (*a) { + (*a)->position -= consumed_lyrics + 1; // why plus one? + a++; + } +} + +static bool +pdf_texts_add_lyrics( + struct ChoLineItem *item, + struct PDFContext *ctx, + int i +) +{ + struct PDFText ***texts; + struct SpaceNeeded **sp; + double width; + int t = 0; + int c; + texts = &ctx->content->pages[ctx->page]->texts; + *texts = realloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); + (*texts)[ctx->text] = pdf_text_new(); + if (!ctx->spaces) { + (*texts)[ctx->text]->text = strdup(item->u.text->text); + (*texts)[ctx->text]->style = cho_style_copy(item->u.text->style); + (*texts)[ctx->text]->x = ctx->x; + (*texts)[ctx->text]->y = ctx->y; + width = text_width((*texts)[ctx->text]->text, item->u.text->style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + if (item->u.text->style->href) { + if (!annot_add(ctx, item->u.text->style, width)) { + LOG_DEBUG("annot_add failed."); + return false; + } + } + ctx->x += width; + if ((*texts)[ctx->text]->style->font->size > ctx->biggest_font_size) { + ctx->biggest_font_size = (*texts)[ctx->text]->style->font->size; + } + ctx->consumed_lyrics += strlen((*texts)[ctx->text]->text); + ctx->text++; + return true; + } + for (c = 0; item->u.text->text[c]; c++) { + (*texts)[ctx->text]->text = realloc((*texts)[ctx->text]->text, (t+1) * sizeof(char)); + (*texts)[ctx->text]->text[t] = item->u.text->text[c]; + t++; + sp = ctx->spaces; + while (*sp) { + if ((*sp)->line_item_index == i && (*sp)->text_index == c) { + (*texts)[ctx->text]->text = realloc((*texts)[ctx->text]->text, (t+1) * sizeof(char)); + (*texts)[ctx->text]->text[t] = 0; + (*texts)[ctx->text]->style = cho_style_copy(item->u.text->style); + (*texts)[ctx->text]->x = ctx->x; + (*texts)[ctx->text]->y = ctx->y; + width = text_width((*texts)[ctx->text]->text, item->u.text->style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + if (item->u.text->style->href) { + if (!annot_add(ctx, item->u.text->style, width)) { + LOG_DEBUG("annot_add failed."); + return false; + } + } + ctx->x += width; + ctx->x += (*sp)->amount; + t = 0; + ctx->consumed_lyrics += strlen((*texts)[ctx->text]->text); + ctx->text++; + *texts = realloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); + (*texts)[ctx->text] = pdf_text_new(); + } + sp++; + } + } + (*texts)[ctx->text]->text = realloc((*texts)[ctx->text]->text, (t+1) * sizeof(char)); + (*texts)[ctx->text]->text[t] = 0; + (*texts)[ctx->text]->style = cho_style_copy(item->u.text->style); + (*texts)[ctx->text]->x = ctx->x; + (*texts)[ctx->text]->y = ctx->y; + width = text_width((*texts)[ctx->text]->text, item->u.text->style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + if (item->u.text->style->href) { + if (!annot_add(ctx, item->u.text->style, width)) { + LOG_DEBUG("annot_add failed."); + return false; + } + } + ctx->x += width; + if ((*texts)[ctx->text]->style->font->size > ctx->biggest_font_size) { + ctx->biggest_font_size = (*texts)[ctx->text]->style->font->size; + } + ctx->consumed_lyrics += strlen((*texts)[ctx->text]->text); + ctx->text++; + return true; +} + +static double +calc_x(double width, enum Alignment align) +{ + if (align == A_CENTER) { + return MARGIN_HORIZONTAL + (LINE_WIDTH - width) / 2; + } + return MARGIN_HORIZONTAL; +} + +static bool +pdf_texts_add_text( + struct PDFContext *ctx, + const char *text, + struct Style *style, + enum Alignment align +) +{ + struct PDFText ***texts; + char str[strlen(text)+1]; + double width; + int index; + strcpy((char *)&str, text); + texts = &ctx->content->pages[ctx->page]->texts; + width = text_width(text, style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + if (width > LINE_WIDTH) { + char *t = (char *)&str; + while (width > LINE_WIDTH) { + index = text_find_fitting(t, style, 0.0); + if (index == EMPTY) { + LOG_DEBUG("text_find_fitting failed."); + return false; + } + t[index] = 0; + width = text_width(t, style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + ctx->x = calc_x(width, align); + *texts = realloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); + (*texts)[ctx->text] = pdf_text_new(); + (*texts)[ctx->text]->text = strdup(t); + (*texts)[ctx->text]->style = cho_style_copy(style); + (*texts)[ctx->text]->x = ctx->x; + (*texts)[ctx->text]->y = ctx->y; + if (style->href) { + if (!annot_add(ctx, style, width)) { + LOG_DEBUG("annot_add failed."); + return false; + } + } + ctx->text++; + ctx->y -= 8.0 + style->font->size; + t += index+1; + width = text_width(t, style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + } + width = text_width(t, style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + ctx->x = calc_x(width, align); + *texts = realloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); + (*texts)[ctx->text] = pdf_text_new(); + (*texts)[ctx->text]->text = strdup(t); + (*texts)[ctx->text]->style = cho_style_copy(style); + (*texts)[ctx->text]->x = ctx->x; + (*texts)[ctx->text]->y = ctx->y; + if (style->href) { + if (!annot_add(ctx, style, width)) { + LOG_DEBUG("annot_add failed."); + return false; + } + } + ctx->text++; + ctx->y -= 8.0 + style->font->size; + } else { + ctx->x = calc_x(width, align); + *texts = realloc(*texts, (ctx->text+1) * sizeof(struct PDFText *)); + (*texts)[ctx->text] = pdf_text_new(); + (*texts)[ctx->text]->text = strdup(text); + (*texts)[ctx->text]->style = cho_style_copy(style); + (*texts)[ctx->text]->x = ctx->x; + (*texts)[ctx->text]->y = ctx->y; + if (style->href) { + if (!annot_add(ctx, style, width)) { + LOG_DEBUG("annot_add failed."); + return false; + } + } + ctx->text++; + ctx->y -= 8.0 + style->font->size; + } + return true; +} + +static bool +pdf_content_create( + struct PDFContent **out, + struct ChoSong **songs, + struct Config *config, + struct Obj **img_objs +) +{ + struct ChoSong **s; + struct ChoMetadata **m; + struct ChoSection **se; + struct ChoLine **li; + struct PDFText ***texts; + struct Image ***imgs; + struct PDFContext ctx; + double width, height; + ctx.x = MARGIN_HORIZONTAL; + ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; + ctx.text = 0; + ctx.image = 0; + ctx.page = 0; + ctx.spaces = NULL; + ctx.content = malloc(sizeof(struct PDFContent)); + ctx.content->pages = malloc(sizeof(struct PDFPage *)); + ctx.content->pages[ctx.page] = pdf_page_new(); + s = songs; + texts = &ctx.content->pages[ctx.page]->texts; + imgs = &ctx.content->pages[ctx.page]->images; + m = (*s)->metadata; + while (*m) { + if (!strcmp((*m)->name, "title")) { + if (!pdf_texts_add_text(&ctx, (*m)->value, (*m)->style, A_CENTER)) { + LOG_DEBUG("pdf_texts_add_text failed."); + return false; + } + } + m++; + } + m = (*s)->metadata; + while (*m) { + if (!strcmp((*m)->name, "subtitle")) { + /* + INFO: (*m)->style will be ignored and the config style will be + used because the subtitle style can only be manipulated from the + config file + */ + struct OutputStyle *output_style; + output_style = config_output_style_get(config->output->styles, "subtitle"); + if (!output_style) { + LOG_DEBUG("config_output_style_get failed."); + return NULL; + } + if (!pdf_texts_add_text(&ctx, (*m)->value, output_style->style, A_CENTER)) { + LOG_DEBUG("pdf_texts_add_text failed."); + return false; + } + } + m++; + } + ctx.y -= 30.0; + while (*s) { + se = (*s)->sections; + while (*se) { + if ((*se)->label) { + if (!pdf_texts_add_text(&ctx, (*se)->label->text, (*se)->label->style, A_LEFT)) { + LOG_DEBUG("pdf_texts_add_text failed."); + return false; + } + } + li = (*se)->lines; + while (*li) { + int item_index; + int text_above_index; + struct CharPosition *pos; + struct ChoLineItemAbove **left_aboves = (*li)->text_above; + struct ChoLineItem **left_items = (*li)->items; + while (*left_aboves || *left_items) { + ctx.consumed_lyrics = 0; + ctx.biggest_font_size = 0.0; + ctx.prev_added_space = 0.0; + char *string; + struct Style *style; + if (!calc_space_between_text_above(left_items, left_aboves, img_objs, &ctx.spaces)) { + LOG_DEBUG("calc_space_between_text_above failed."); + return false; + } + pos = items_find_position_to_break_line(left_items, ctx.spaces, img_objs); + if (pos->line_item_index == -1) { + item_index = 10000; + text_above_index = 10000; + } else { + item_index = pos->line_item_index; + text_above_index = text_above_find_index_to_break_line(left_items, left_aboves, pos); + if (text_above_index == -2) { + LOG_DEBUG("text_above_find_index_to_break_line failed."); + return false; + } + if (text_above_index == -1) { + text_above_index = 20000; + } + } + int i; + for (i = 0; left_aboves[i] && i<text_above_index; i++) { + width = line_width_until_text_above(left_items, left_aboves[i], img_objs, NULL); + if (width == EMPTY) { + LOG_DEBUG("line_width_until_text_aboave failed."); + return false; + } + ctx.x = MARGIN_HORIZONTAL + width + ctx.prev_added_space; + struct SpaceNeeded **sp = ctx.spaces; + while (*sp) { + if ((*sp)->text_above_index == i) { + ctx.x += (*sp)->amount; + ctx.prev_added_space += (*sp)->amount; + } + sp++; + } + *texts = realloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = pdf_text_new(); + (*texts)[ctx.text]->x = ctx.x; + (*texts)[ctx.text]->y = ctx.y; + if (left_aboves[i]->is_chord) { + string = cho_chord_name_generate(left_aboves[i]->u.chord); + style = cho_style_copy(left_aboves[i]->u.chord->style); + } else { + string = strdup(left_aboves[i]->u.annot->text); + style = cho_style_copy(left_aboves[i]->u.annot->style); + } + (*texts)[ctx.text]->text = string; + (*texts)[ctx.text]->style = style; + if (style->href) { + width = text_width(string, style); + if (width == EMPTY) { + LOG_DEBUG("text_width failed."); + return false; + } + if (!annot_add(&ctx, style, width)) { + LOG_DEBUG("annot_add failed."); + return false; + } + } + if (style->font->size > ctx.biggest_font_size) { + ctx.biggest_font_size = style->font->size; + } + ctx.text++; + } + height = images_find_biggest_height(left_items, pos->line_item_index, img_objs); + if (height == EMPTY) { + LOG_DEBUG("images_find_biggest_height failed."); + return false; + } + if (i > 0) { + left_aboves += i; + if (height > 2.0 + ctx.biggest_font_size) { + ctx.y -= height; + } else { + ctx.y -= 2.0 + ctx.biggest_font_size; + } + } else { + if (height > 0.0) { + ctx.y -= height; + } + } + if (ctx.y < MARGIN_BOTTOM) { + /* INFO: chords/annotations and their corresponding lyrics won't be splitted */ + struct PDFText **tmp = NULL; + int tm = 0; + double prev_y = (*texts)[ctx.text-1]->y; + ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; + for (int p = ctx.text-1; prev_y == (*texts)[p]->y; p--) { + (*texts)[p]->y = ctx.y; + tmp = realloc(tmp, (tm+1) * sizeof(struct PDFText *)); + tmp[tm] = (*texts)[p]; + tm++; + *texts = realloc(*texts, (--ctx.text) * sizeof(struct PDFText *)); + } + *texts = realloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = NULL; + *imgs = realloc(*imgs, (ctx.image+1) * sizeof(struct Image *)); + (*imgs)[ctx.image] = NULL; + ctx.text = 0; + ctx.image = 0; + ctx.page++; + ctx.content->pages = realloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); + ctx.content->pages[ctx.page] = pdf_page_new(); + texts = &ctx.content->pages[ctx.page]->texts; + imgs = &ctx.content->pages[ctx.page]->images; + for (int i=0; i<tm; i++) { + *texts = realloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = tmp[i]; + ctx.text++; + } + free(tmp); + if (i > 0) { + if (height > 2.0 + ctx.biggest_font_size) { + ctx.y -= height; + } else { + ctx.y -= 2.0 + ctx.biggest_font_size; + } + } else { + ctx.y -= height; + } + } + ctx.biggest_font_size = 0.0; + ctx.x = MARGIN_HORIZONTAL; + i = 0; + while (*left_items && i < item_index) { + if ((*left_items)->is_text) { + pdf_texts_add_lyrics(*left_items, &ctx, i); + } else { + *imgs = realloc(*imgs, (ctx.image+1) * sizeof(struct Image *)); + (*imgs)[ctx.image] = image_new(); + (*imgs)[ctx.image]->name = image_name((*left_items)->u.image); + (*imgs)[ctx.image]->obj = objs_get_obj(img_objs, (*imgs)[ctx.image]->name); + if (!(*imgs)[ctx.image]->obj) { + LOG_DEBUG("objs_get_obj failed."); + return false; + } + (*imgs)[ctx.image]->width = image_width((*left_items)->u.image, (*imgs)[ctx.image]->obj); + (*imgs)[ctx.image]->height = image_height((*left_items)->u.image, (*imgs)[ctx.image]->obj); + (*imgs)[ctx.image]->x = ctx.x; + (*imgs)[ctx.image]->y = ctx.y; + ctx.x += (*imgs)[ctx.image]->width; + ctx.image++; + } + i++; + left_items++; + } + if (pos->line_item_index != -1 && pos->text_index != -1) { + if ((*left_items)->is_text) { + char *tmp; + (*left_items)->u.text->text[pos->text_index] = 0; + tmp = strdup(&(*left_items)->u.text->text[pos->text_index+1]); + pdf_texts_add_lyrics(*left_items, &ctx, i); + free((*left_items)->u.text->text); + (*left_items)->u.text->text = tmp; + } + } else + if (pos->line_item_index != -1 && pos->text_index == -1) { + if (!(*left_items)->is_text) { + *imgs = realloc(*imgs, (ctx.image+1) * sizeof(struct Image *)); + (*imgs)[ctx.image] = image_new(); + (*imgs)[ctx.image]->name = image_name((*left_items)->u.image); + (*imgs)[ctx.image]->obj = objs_get_obj(img_objs, (*imgs)[ctx.image]->name); + if (!(*imgs)[ctx.image]->obj) { + LOG_DEBUG("objs_get_obj failed."); + return false; + } + (*imgs)[ctx.image]->width = image_width((*left_items)->u.image, (*imgs)[ctx.image]->obj); + (*imgs)[ctx.image]->height = image_height((*left_items)->u.image, (*imgs)[ctx.image]->obj); + (*imgs)[ctx.image]->x = ctx.x; + (*imgs)[ctx.image]->y = ctx.y; + ctx.image++; + } + left_items++; + } + ctx.y -= 8.0 + ctx.biggest_font_size; + if (ctx.y < MARGIN_BOTTOM) { + *texts = realloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = NULL; + *imgs = realloc(*imgs, (ctx.image+1) * sizeof(struct Image *)); + (*imgs)[ctx.image] = NULL; + ctx.text = 0; + ctx.image = 0; + ctx.page++; + ctx.content->pages = realloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); + ctx.content->pages[ctx.page] = pdf_page_new(); + texts = &ctx.content->pages[ctx.page]->texts; + imgs = &ctx.content->pages[ctx.page]->images; + ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; + } + spaces_free(ctx.spaces); + ctx.spaces = NULL; + free(pos); + pos = NULL; + text_above_update_positions(left_aboves, ctx.consumed_lyrics); + } + if ((*li)->btype == BT_PAGE) { + *texts = realloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = NULL; + *imgs = realloc(*imgs, (ctx.image+1) * sizeof(struct Image *)); + (*imgs)[ctx.image] = NULL; + ctx.text = 0; + ctx.image = 0; + ctx.page++; + ctx.content->pages = realloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); + ctx.content->pages[ctx.page] = pdf_page_new(); + texts = &ctx.content->pages[ctx.page]->texts; + imgs = &ctx.content->pages[ctx.page]->images; + ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP; + } + li++; + } + ctx.y -= SECTION_GAP_WIDTH; + se++; + } + s++; + } + *texts = realloc(*texts, (ctx.text+1) * sizeof(struct PDFText *)); + (*texts)[ctx.text] = NULL; + *imgs = realloc(*imgs, (ctx.image+1) * sizeof(struct Image *)); + (*imgs)[ctx.image] = NULL; + ctx.page++; + ctx.content->pages = realloc(ctx.content->pages, (ctx.page+1) * sizeof(struct PDFPage *)); + ctx.content->pages[ctx.page] = NULL; + *out = ctx.content; + return true; +} + +static void +pdf_content_free(struct PDFContent *content) +{ + if (!content) { + return; + } + struct PDFPage **p; + p = content->pages; + while (*p) { + pdf_page_free(*p); + p++; + } + free(content->pages); + free(content); +} + +static bool +pdf_content_render(struct PDFContent *content, pdfio_file_t *file) +{ + struct PDFPage **pages; + struct PDFText **texts; + struct Image **imgs; + pdfio_stream_t *stream; + pages = content->pages; + while (*pages) { + texts = (*pages)->texts; + imgs = (*pages)->images; + stream = out_pdf_page_create(file, imgs, (*pages)->annots); + if (!stream) { + LOG_DEBUG("out_pdf_page_create failed."); + return false; + } + while (*texts) { + if (!out_pdf_text_show(stream, *texts)) { + LOG_DEBUG("out_pdf_text_show failed."); + return false; + } + texts++; + } + while (*imgs) { + if ( + !pdfioContentDrawImage( + stream, + (*imgs)->name, + (*imgs)->x, + (*imgs)->y, + (*imgs)->width, + (*imgs)->height + ) + ) { + LOG_DEBUG("pdfioContentDrawImage failed."); + return false; + } + imgs++; + } + if (!pdfioStreamClose(stream)) { + LOG_DEBUG("pdfioStreamClose failed."); + return false; + } + pages++; + } + return true; +} + +char * +out_pdf_create(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)); + memset(&g_cho_dirpath, 0, PATH_MAX); + char *dirpath = filepath_dirname(cho_filepath); + strcpy((char *)&g_cho_dirpath, dirpath); + free(dirpath); + char *pdf_filename = out_pdf_filename_create(songs, cho_filepath, output_folder_or_file); + if (!pdf_filename) { + LOG_DEBUG("out_pdf_filename_create failed."); + return NULL; + } + struct Obj **img_objs = NULL; 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 }; - pdfio_file_t *pdf = pdfioFileCreate(pdf_filename, "2.0", &media_box_a4, &crop_box, NULL, NULL); - if (!out_pdf_set_title(pdf, songs)) { + pdfio_rect_t crop_box = { 0.0, 0.0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT }; + pdf_file = pdfioFileCreate(pdf_filename, "2.0", &media_box_a4, &crop_box, NULL, NULL); + if (!out_pdf_set_title(pdf_file, songs)) { LOG_DEBUG("out_pdf_set_title failed."); return NULL; } @@ -1350,7 +1809,7 @@ out_pdf_create(const char *cho_filepath, const char *output_folder_or_file, stru if (fontpath) { fnt = out_pdf_fnt_new(); fnt->name = out_pdf_fnt_name_create(needed_fonts[f]); - fnt->font = pdfioFileCreateFontObjFromFile(pdf, fontpath, true); + fnt->font = pdfioFileCreateFontObjFromFile(pdf_file, fontpath, true); out_pdf_fnt_add(fnt, &g_fonts); free(fontpath); } else { @@ -1358,7 +1817,7 @@ out_pdf_create(const char *cho_filepath, const char *output_folder_or_file, stru if (fontpath) { fnt = out_pdf_fnt_new(); fnt->name = out_pdf_fnt_name_create(needed_fonts[f]); - fnt->font = pdfioFileCreateFontObjFromFile(pdf, fontpath, true); + fnt->font = pdfioFileCreateFontObjFromFile(pdf_file, fontpath, true); out_pdf_fnt_add(fnt, &g_fonts); free(fontpath); } else { @@ -1370,50 +1829,26 @@ out_pdf_create(const char *cho_filepath, const char *output_folder_or_file, stru f++; } cho_fonts_free(needed_fonts); - struct Text **text = text_create(songs, config); - if (!text) { - LOG_DEBUG("text_create failed."); + if (!out_pdf_load_images(&img_objs, pdf_file, songs)) { + LOG_DEBUG("out_pdf_load_images failed."); return NULL; } - pdfio_array_t *annots = pdfioArrayCreate(pdf); - if (!annots_create(pdf, annots, text)) { - LOG_DEBUG("annotations_create failed."); + struct PDFContent *pdf_content; + if (!pdf_content_create(&pdf_content, songs, config, img_objs)) { + LOG_DEBUG("pdf_content_create failed."); return NULL; } - pdfio_stream_t *page_stream; - int p = 0; - page_stream = out_pdf_page_create(pdf, annots, p); - double y = MEDIABOX_HEIGHT - MARGIN_TOP; - 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++) { - out_pdf_text_show(page_stream, text[t]->lines[tl]->items[tli], y); - } - } - if (text[t]->lines[tl]->btype == BT_PAGE) { - if (!pdfioStreamClose(page_stream)) { - LOG_DEBUG("pdfioStreamClose failed."); - return NULL; - } - p++; - page_stream = out_pdf_page_create(pdf, annots, p); - y = MEDIABOX_HEIGHT - MARGIN_TOP; - } else { - y -= text[t]->lines[tl]->height; - } - } - } - if (!pdfioStreamClose(page_stream)) { - LOG_DEBUG("pdfioStreamClose failed."); + if (!pdf_content_render(pdf_content, pdf_file)) { + LOG_DEBUG("pdf_content_render failed."); return NULL; } - if (!pdfioFileClose(pdf)) { + objs_free(img_objs); + pdf_content_free(pdf_content); + if (!pdfioFileClose(pdf_file)) { LOG_DEBUG("pdfioFileClose failed."); return NULL; } - texts_free(text); + pdf_file = NULL; out_pdf_fnts_free(g_fonts); g_fonts = NULL; return pdf_filename; diff --git a/out_pdf.h b/out_pdf.h @@ -4,12 +4,15 @@ #define MEDIABOX_HEIGHT 878.0 #define MEDIABOX_WIDTH 631.0 #define MARGIN_TOP 40.0 -#define MARGIN_BOTTOM 180.0 +#define MARGIN_BOTTOM 40.0 +// #define MARGIN_BOTTOM 180.0 #define MARGIN_HORIZONTAL 80.0 #define LINE_WIDTH MEDIABOX_WIDTH - MARGIN_HORIZONTAL * 2 #define MIN_CHORD_GAP_WIDTH 5.0 #define SECTION_GAP_WIDTH 10.0 +#define PATH_MAX 4096 + // TODO: Find a good way to get rid of this type enum Bool { B_ERROR = -1, @@ -22,13 +25,24 @@ struct Fnt { pdfio_obj_t *font; }; +struct Obj { + char *name; + pdfio_obj_t *value; +}; + struct SpaceNeeded { int line_item_index; int text_index; + int text_above_index; double amount; }; -/* struct TextLineItem { +struct CharPosition { + int line_item_index; + int text_index; +}; + +struct TextLineItem { char *text; struct Style *style; double x; @@ -49,7 +63,7 @@ struct TextLine { struct Text { struct TextLine **lines; -}; */ +}; struct Image { double x; @@ -60,7 +74,7 @@ struct Image { pdfio_obj_t *obj; }; -struct Text { +struct PDFText { char *text; struct Style *style; double x; @@ -68,8 +82,9 @@ struct Text { }; struct PDFPage { - struct Text **texts; + struct PDFText **texts; struct Image **images; + pdfio_array_t *annots; }; struct PDFContent { @@ -82,4 +97,17 @@ enum LineLocation { LL_UNDER }; +struct PDFContext { + struct PDFContent *content; + double x; + double y; + int text; + int image; + int page; + struct SpaceNeeded **spaces; + double biggest_font_size; + size_t consumed_lyrics; + double prev_added_space; +}; + char *out_pdf_create(const char *cho_filename, const char *output_folder_or_file, struct ChoSong **songs, struct Config *config); diff --git a/util.c b/util.c @@ -27,6 +27,10 @@ util_log(enum LogLevel level, const char *msg, ...) log_level = " ERR"; color = "31"; break; + case LOG_TODO: + log_level = "TODO"; + color = "34"; + break; } fprintf(stderr, "\033[1;%sm%s\033[0m: ", color, log_level); va_list va; @@ -45,6 +49,9 @@ util_log(enum LogLevel level, const char *msg, ...) case LOG_ERR: log_level = " ERR"; break; + case LOG_TODO: + log_level = "TODO"; + break; } fprintf(stderr, "%s: ", log_level); va_list va; @@ -76,7 +83,7 @@ str_normalize(const char *str) int n = 0; char c; for (; str[i] != 0; i++) { - if (str[i] == ' ') { + if (str[i] == ' ' || str[i] == '/' || str[i] == '.') { normalized = realloc(normalized, (n+1) * sizeof(char)); normalized[n] = '-'; n++; @@ -310,6 +317,7 @@ size_create(const char *str) util_log(LOG_ERR, "invalid percentage."); return NULL; } + size->d = d / 100.0; size->type = ST_PERCENT; } else if (len > 2 && str[len-2] == 'e' && str[len-1] == 'm') { @@ -341,3 +349,20 @@ size_to_string(struct Size *size) } return str; } + +/* double +size_as_points(struct Size *size) +{ + // TODO: which font size should be used here? An image has no font. + double font_size = 14.0; + switch (size->type) { + case ST_POINT: + return size->d; + case ST_PERCENT: + return size->d / 100.0; + case ST_EM: + return size->d * font_size; + case ST_EX: + return size->d * font_size * 0.5; + } +} */ diff --git a/util.h b/util.h @@ -4,10 +4,17 @@ #define LOG_DEBUG(msg) #endif +#define COLOR_BOLD_RED "\033[1;31m" +#define COLOR_BOLD_ORANGE "\033[1;33m" +#define COLOR_BOLD_WHITE "\033[1;37m" +#define COLOR_BOLD_BLUE "\033[1;34m" +#define COLOR_RESET "\033[0m" + enum LogLevel { LOG_INFO, LOG_WARN, - LOG_ERR + LOG_ERR, + LOG_TODO }; enum FileType {