commit a76f6b36b9a99ac1135384cd14b1cf4a38ee4c18
parent ed7dcbe7bad163ef2dc76deb9cbd8f6e404a0bf4
Author: nibo <nibo@relim.de>
Date: Fri, 15 Nov 2024 18:49:01 +0100
Rewrite pdf generation engine
Diffstat:
| M | chordpro.c | | | 207 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
| M | chordpro.h | | | 15 | +++++++-------- |
| M | out_pdf.c | | | 2117 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
| M | out_pdf.h | | | 38 | +++++++++++++++++++++++++++++++++----- |
| M | util.c | | | 27 | ++++++++++++++++++++++++++- |
| M | util.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 {