commit f3ae24795f381aee6019836576f797b34befc3f7
parent f7126d012b878b189d3e5512e8bb9817e21209d4
Author: nibo <nibo@relim.de>
Date: Fri, 29 Nov 2024 13:59:45 +0100
WIP: chord diagrams
Diffstat:
10 files changed, 687 insertions(+), 15 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,7 +3,7 @@ PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
CFLAGS = -pedantic -Wall -Wextra -DVERSION=\"${VERSION}\"
LDFLAGS = -lpdfio -ltoml -lfontconfig
-SRC = util.c fontconfig.c config.c chordpro.c out_pdf.c lorid.c
+SRC = util.c fontconfig.c config.c chordpro.c chord_diagram.c out_pdf.c lorid.c
COLOR = 0
@@ -17,8 +17,6 @@ clean:
rm liblorid.so*
fontconfig:
$(CC) -g chordpro.c fontconfig.c -o fontconfig -lfontconfig
-parser:
- $(CC) ${CFLAGS} -g util.c config.c chordpro.c lorid.c -o parser -ltoml
lib:
$(CC) -fpic -c util.c fontconfig.c config.c chordpro.c out_pdf.c
$(CC) -shared *.o ${LDFLAGS} -o liblorid.so.${VERSION}
diff --git a/chord_diagram.c b/chord_diagram.c
@@ -0,0 +1,490 @@
+#include <string.h>
+#include <stdint.h>
+#include <pdfio.h>
+#include <pdfio-content.h>
+#include "util.h"
+#include "chord_diagram.h"
+
+static bool
+text_show(
+ pdfio_stream_t *stream,
+ const char *text,
+ double x,
+ double y
+)
+{
+ if (!pdfioContentSetTextRenderingMode(stream, PDFIO_TEXTRENDERING_FILL)) {
+ fprintf(stderr, "pdfioContentSetTextRenderingMode failed.\n");
+ return false;
+ }
+ if (!pdfioContentTextBegin(stream)) {
+ fprintf(stderr, "pdfioContentTextBegin failed.\n");
+ return false;
+ }
+ if (!pdfioContentTextMoveTo(stream, x, y)) {
+ fprintf(stderr, "pdfioContentTextMoveTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentTextShow(stream, true, text)) {
+ fprintf(stderr, "pdfioContentTextShow failed.\n");
+ return false;
+ }
+ if (!pdfioContentTextEnd(stream)) {
+ fprintf(stderr, "pdfioContentTextEnd failed.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool
+draw_rectangle(
+ pdfio_stream_t *stream,
+ double x,
+ double y,
+ double width,
+ double height,
+ enum TextRendering type
+)
+{
+ if (!pdfioContentSetFillColorRGB(stream, 0.0, 0.0, 0.0)) {
+ fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n");
+ return false;
+ }
+ if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
+ fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
+ return false;
+ }
+ if (!pdfioContentPathRect(stream, x, y, width, height)) {
+ fprintf(stderr, "pdfioContentPathRect failed.\n");
+ return false;
+ }
+ switch (type) {
+ case FILL_AND_STROKE:
+ if (!pdfioContentFillAndStroke(stream, true)) {
+ fprintf(stderr, "pdfioContentFillAndStroke failed.\n");
+ return false;
+ }
+ break;
+ case FILL:
+ if (!pdfioContentFill(stream, true)) {
+ fprintf(stderr, "pdfioContentFill failed.\n");
+ return false;
+ }
+ break;
+ case STROKE:
+ if (!pdfioContentStroke(stream)) {
+ fprintf(stderr, "pdfioContentStroke failed.\n");
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+
+static bool
+draw_line(
+ pdfio_stream_t *stream,
+ double x,
+ double y,
+ double width,
+ enum Direction direction
+)
+{
+ if (!pdfioContentPathMoveTo(stream, x, y)) {
+ fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
+ return false;
+ }
+ if (direction == HORIZONTAL) {
+ if (!pdfioContentPathLineTo(stream, x+width, y)) {
+ fprintf(stderr, "pdfioContentPathLineTo failed.\n");
+ return false;
+ }
+ } else {
+ if (!pdfioContentPathLineTo(stream, x, y+width)) {
+ fprintf(stderr, "pdfioContentPathLineTo failed.\n");
+ return false;
+ }
+ }
+ if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
+ fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
+ return false;
+ }
+ if (!pdfioContentStroke(stream)) {
+ fprintf(stderr, "pdfioContentFill failed.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool
+draw_bezier_oval_quarter(
+ pdfio_stream_t *stream,
+ double center_x,
+ double center_y,
+ double size_x,
+ double size_y
+)
+{
+ if (!pdfioContentPathMoveTo(stream, center_x - size_x, center_y)) {
+ fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentPathCurve(
+ stream,
+ center_x - size_x,
+ center_y - 0.552 * size_y,
+ center_x - 0.552 * size_x,
+ center_y - size_y,
+ center_x,
+ center_y - size_y
+ )) {
+ fprintf(stderr, "pdfioContentPathCurve failed.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool
+draw_bezier_oval(
+ pdfio_stream_t *stream,
+ double center_x,
+ double center_y,
+ double size_x,
+ double size_y
+)
+{
+ if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
+ fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
+ return false;
+ }
+ if (!draw_bezier_oval_quarter(stream, center_x, center_y, -size_x, size_y)) {
+ fprintf(stderr, "draw_bezier_oval_quarter failed.");
+ return false;
+ }
+ if (!draw_bezier_oval_quarter(stream, center_x, center_y, size_x, size_y)) {
+ fprintf(stderr, "draw_bezier_oval_quarter failed.");
+ return false;
+ }
+ if (!draw_bezier_oval_quarter(stream, center_x, center_y, size_x, -size_y)) {
+ fprintf(stderr, "draw_bezier_oval_quarter failed.");
+ return false;
+ }
+ if (!draw_bezier_oval_quarter(stream, center_x, center_y, -size_x, -size_y)) {
+ fprintf(stderr, "draw_bezier_oval_quarter failed.");
+ return false;
+ }
+ if (!pdfioContentStroke(stream)) {
+ fprintf(stderr, "pdfioContentStroke failed.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool
+draw_bezier_circle(
+ pdfio_stream_t *stream,
+ double center_x,
+ double center_y,
+ double size
+)
+{
+ if (!pdfioContentSetLineWidth(stream, size * 0.4)) {
+ fprintf(stderr, "pdfioContentSetLineWidth failed.\n");
+ return false;
+ }
+ if (!draw_bezier_oval(stream, center_x, center_y, size, size)) {
+ fprintf(stderr, "draw_bezier_oval failed.");
+ return false;
+ }
+ return true;
+}
+
+static bool
+draw_x(pdfio_stream_t *stream, double x, double y, double size)
+{
+ if (!pdfioContentSetLineWidth(stream, size * 0.4)) {
+ fprintf(stderr, "pdfioContentSetLineWidth failed.\n");
+ return false;
+ }
+ if (!pdfioContentSetStrokeColorRGB(stream, 0.0, 0.0, 0.0)) {
+ fprintf(stderr, "pdfioContentSetStrokeColorRGB failed.\n");
+ return false;
+ }
+ if (!pdfioContentPathMoveTo(stream, x - size, y - size)) {
+ fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentPathLineTo(stream, x + size, y + size)) {
+ fprintf(stderr, "pdfioContentPathLineTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentStroke(stream)) {
+ fprintf(stderr, "pdfioContentFill failed.\n");
+ return false;
+ }
+ if (!pdfioContentPathMoveTo(stream, x - size, y + size)) {
+ fprintf(stderr, "pdfioContentPathMoveTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentPathLineTo(stream, x + size, y - size)) {
+ fprintf(stderr, "pdfioContentPathLineTo failed.\n");
+ return false;
+ }
+ if (!pdfioContentStroke(stream)) {
+ fprintf(stderr, "pdfioContentFill failed.\n");
+ return false;
+ }
+ return true;
+}
+
+static bool
+is_valid_circle_char(char c)
+{
+ if (c >= '/' && c <= '9') {
+ return true;
+ }
+ if (c >= 'A' && c <= 'Z') {
+ return true;
+ }
+ return false;
+}
+
+static bool
+draw_circle_with_char_inside(
+ pdfio_stream_t *stream,
+ double x,
+ double y,
+ double field_width,
+ char c
+)
+{
+ if (!is_valid_circle_char(c)) {
+ fprintf(stderr, "is_valid_circle_char failed.\n");
+ return false;
+ }
+ char str[2];
+ str[0] = c;
+ str[1] = 0;
+ if (!pdfioContentSetTextFont(stream, "chord-diagram-symbols", field_width)) {
+ fprintf(stderr, "pdfioContentSetTextFont failed.\n");
+ return false;
+ }
+ /* INFO: Needs to be the color of the page background. This cannot be set at the moment. It will always be white. */
+ if (!pdfioContentSetFillColorRGB(stream, 1.0, 1.0, 1.0)) {
+ fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n");
+ return false;
+ }
+ if (!text_show(stream, "/", x-field_width/2, y-field_width/2.8)) {
+ fprintf(stderr, "text_show failed.");
+ return false;
+ }
+ if (!pdfioContentSetFillColorRGB(stream, 0.0, 0.0, 0.0)) {
+ fprintf(stderr, "pdfioContentSetFillColorRGB failed.\n");
+ return false;
+ }
+ if (!text_show(stream, (char *)&str, x-field_width/2, y-field_width/2.8)) {
+ fprintf(stderr, "text_show failed.");
+ return false;
+ }
+ return true;
+}
+
+static struct StringDiagram *
+string_diagram_new(void)
+{
+ struct StringDiagram *d = emalloc(sizeof(struct StringDiagram));
+ d->name = NULL;
+ d->base_fret = -2;
+ memset(d->frets, -2, sizeof(d->frets));
+ memset(d->fingers, -2, sizeof(d->fingers));
+ return d;
+}
+
+static int
+string_diagram_string_count(struct StringDiagram *d)
+{
+ int i = 0;
+ while (d->frets[i] != -2 && i < 12) {
+ i++;
+ }
+ return i;
+}
+
+static void
+string_diagram_free(struct StringDiagram *d)
+{
+ free(d->name);
+ free(d);
+}
+
+static bool
+string_diagram_is_valid(struct StringDiagram *d)
+{
+ int i;
+ size_t fret_count, finger_count;
+ if (d->base_fret < 1 || d->base_fret > 99) {
+ return false;
+ }
+ i = 0;
+ while (d->frets[i] != -2 && i < 12) {
+ i++;
+ }
+ fret_count = i;
+ i = 0;
+ while (d->fingers[i] != -2 && i < 12) {
+ i++;
+ }
+ finger_count = i;
+ if (finger_count > 0 && fret_count != finger_count) {
+ return false;
+ }
+ return true;
+}
+
+static char
+finger_to_char(int8_t finger)
+{
+ switch (finger) {
+ case 1:
+ return '1';
+ case 2:
+ return '2';
+ case 3:
+ return '3';
+ case 4:
+ return '4';
+ default:
+ return '/';
+ }
+}
+
+static bool
+draw_string_chord_diagram(
+ pdfio_stream_t *stream,
+ struct StringDiagram *diagram,
+ double x,
+ double y,
+ double width
+)
+{
+ int i;
+ int instrument_string_count = string_diagram_string_count(diagram);
+ double field_width = width / (instrument_string_count - 1);
+ double height = field_width * 4;
+ double vertical_lines_height = height + field_width / 2;
+ if (!pdfioContentSetLineWidth(stream, field_width * 0.09)) {
+ fprintf(stderr, "pdfioContentSetLineWidth failed.\n");
+ return false;
+ }
+ if (!draw_rectangle(stream, x, y, width, height, STROKE)) {
+ fprintf(stderr, "draw_rectangle failed.\n");
+ return false;
+ }
+ // from bottom to top draw horizontal lines
+ for (i = 0; i<4; i++) {
+ if (!draw_line(stream, x, y+field_width * (i+1), width, HORIZONTAL)) {
+ fprintf(stderr, "draw_line failed.\n");
+ return false;
+ }
+ }
+ // from left to right draw vertical lines
+ for (i = 0; i<instrument_string_count; i++) {
+ if (!draw_line(stream, x+field_width*i, y, vertical_lines_height, VERTICAL)) {
+ fprintf(stderr, "draw_line failed.\n");
+ return false;
+ }
+ }
+ if (diagram->base_fret == 1) {
+ if (!draw_rectangle(stream, x, y+height, width, field_width/2, FILL_AND_STROKE)) {
+ fprintf(stderr, "draw_rectangle failed.\n");
+ return false;
+ }
+ } else {
+ double base_pos_x;
+ char base_position[5];
+ base_position[4] = 0;
+ sprintf((char *)&base_position, "%d", diagram->base_fret);
+ if (diagram->base_fret > 9) {
+ base_pos_x = x - field_width - field_width / 2;
+ } else {
+ base_pos_x = x - field_width;
+ }
+ if (!pdfioContentSetTextFont(stream, "chord-diagram-regular-font", field_width*1.15)) {
+ fprintf(stderr, "pdfioContentSetTextFont failed.\n");
+ return false;
+ }
+ if (!text_show(stream, base_position, base_pos_x, y+field_width*3+field_width*0.1)) {
+ fprintf(stderr, "text_show failed.\n");
+ return false;
+ }
+ }
+ double y_above_diagram;
+ int8_t fret, finger;
+ for (i = 0; i<instrument_string_count; i++) {
+ y_above_diagram = y + height + field_width / 2 + field_width / 2.5;
+ fret = diagram->frets[i];
+ finger = diagram->fingers[i];
+ if (fret == -1) {
+ if (!draw_x(stream, x+(i*field_width), y_above_diagram, field_width / 4)) {
+ fprintf(stderr, "draw_x failed.\n");
+ return false;
+ }
+ } else
+ if (fret == 0) {
+ if (!draw_bezier_circle(stream, x+(i*field_width), y_above_diagram, field_width / 4)) {
+ fprintf(stderr, "draw_circle_with_char_inside failed.\n");
+ return false;
+ }
+ } else
+ if (fret < 5) {
+ if (!draw_circle_with_char_inside(stream,
+ x + i * field_width,
+ y + field_width/2 + field_width * (4 - fret),
+ field_width,
+ finger_to_char(finger)
+ )) {
+ fprintf(stderr, "draw_circle_with_char_inside failed.\n");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static void
+keyboard_diagram_free(struct KeyboardDiagram *d)
+{
+ free(d->name);
+ free(d);
+}
+
+void
+chord_diagram_free(struct ChordDiagram *d)
+{
+ if (d->is_string_instrument) {
+ string_diagram_free(d->u.sd);
+ } else {
+ keyboard_diagram_free(d->u.kd);
+ }
+ free(d);
+}
+
+bool
+chord_diagram_draw(
+ pdfio_stream_t *stream,
+ struct ChordDiagram *diagram,
+ double x,
+ double y,
+ double width
+)
+{
+ if (diagram->is_string_instrument) {
+ if (!draw_string_chord_diagram(stream, diagram->u.sd, x, y, width)) {
+ LOG_DEBUG("draw_string_chord_diagram failed.");
+ return false;
+ }
+ } else {
+ util_log(LOG_TODO, "draw_keyboard_chord_diagram()");
+ }
+ return true;
+}
diff --git a/chord_diagram.h b/chord_diagram.h
@@ -0,0 +1,35 @@
+#include <pdfio.h>
+
+enum TextRendering {
+ FILL,
+ STROKE,
+ FILL_AND_STROKE
+};
+
+enum Direction {
+ HORIZONTAL,
+ VERTICAL
+};
+
+struct StringDiagram {
+ char *name;
+ int8_t base_fret;
+ int8_t frets[12];
+ int8_t fingers[12];
+};
+
+struct KeyboardDiagram {
+ char *name;
+ int8_t keys[24];
+};
+
+struct ChordDiagram {
+ bool is_string_instrument;
+ union {
+ struct StringDiagram *sd;
+ struct KeyboardDiagram *kd;
+ } u;
+};
+
+void chord_diagram_free(struct ChordDiagram *d);
+bool chord_diagram_draw(pdfio_stream_t *stream, struct ChordDiagram *diagram, double x, double y, double width);
diff --git a/config.c b/config.c
@@ -5,6 +5,7 @@
#include "chordpro.h"
#include "config.h"
#include "util.h"
+#include "chord_diagram.h"
static char *naming_systems[] = {
"common",
@@ -159,6 +160,18 @@ config_naming_system_to_config_string(enum NamingSystem system)
return naming_systems[system];
}
+static enum Instrument
+config_instrument_parse(const char *str)
+{
+ if (!strcmp(str, "string")) {
+ return INS_STRING;
+ } else
+ if (!strcmp(str, "keyboard")) {
+ return INS_KEYBOARD;
+ }
+ return INS_UNKNOWN;
+}
+
static struct Note *
config_note_new(void)
{
@@ -314,6 +327,9 @@ config_load_default(void)
config->output->chorus = emalloc(sizeof(struct ConfigChorus));
config->output->chorus->label = strdup("Chorus");
config->output->chorus->quote = false;
+ config->output->diagram = emalloc(sizeof(struct ChordDiagram));
+ config->output->diagram->show = true;
+ config->output->diagram->instrument = INS_STRING;
config->output->system = NS_COMMON;
config->output->styles = emalloc(12 * sizeof(struct OutputStyle *));
config->output->styles[0] = config_output_style_new("title");
@@ -601,9 +617,10 @@ config_load(const char *filepath)
}
toml_table_t *output = toml_table_table(table, "output");
if (output) {
- toml_table_t *notes, *chorus;
+ toml_table_t *notes, *chorus, *diagram;
toml_value_t value;
enum NamingSystem system;
+ enum Instrument instrument;
struct Note **custom_notes;
chorus = toml_table_table(output, "chorus");
if (chorus) {
@@ -617,6 +634,23 @@ config_load(const char *filepath)
config->output->chorus->quote = value.u.b;
}
}
+ diagram = toml_table_table(output, "chord_diagram");
+ if (diagram) {
+ value = toml_table_bool(diagram, "show");
+ if (value.ok) {
+ config->output->diagram->show = value.u.b;
+ }
+ value = toml_table_string(diagram, "instrument");
+ if (value.ok) {
+ instrument = config_instrument_parse(value.u.s);
+ if (instrument == INS_UNKNOWN) {
+ util_log(LOG_ERR, "Unknown instrument '%s' in [output.chord_diagram].", value.u.s);
+ return NULL;
+ }
+ config->output->diagram->instrument = instrument;
+ free(value.u.s);
+ }
+ }
value = toml_table_string(output, "system");
if (value.ok) {
system = config_naming_system_parse(value.u.s);
@@ -729,6 +763,7 @@ config_free(struct Config *config)
config->output->notes++;
}
free(start_notes);
+ free(config->output->diagram);
free(config->output);
free(config->parser->chords);
start_notes = config->parser->notes;
diff --git a/config.h b/config.h
@@ -33,6 +33,12 @@ enum NoteType {
NT_FLAT
};
+enum Instrument {
+ INS_STRING,
+ INS_KEYBOARD,
+ INS_UNKNOWN
+};
+
struct ConfigChords {
enum NamingSystem system;
enum ParseMode mode;
@@ -48,8 +54,14 @@ struct ConfigParser {
struct Note **notes;
};
+struct ConfigChordDiagram {
+ bool show;
+ enum Instrument instrument;
+};
+
struct ConfigOutput {
struct ConfigChorus *chorus;
+ struct ConfigChordDiagram *diagram;
enum NamingSystem system;
struct OutputStyle **styles;
struct Note **notes;
diff --git a/fontconfig.c b/fontconfig.c
@@ -23,6 +23,31 @@ file_extension_is_ttc(const char *filepath)
}
char *
+fontconfig_fontpath_find_regular_font(void)
+{
+ char *filepath = NULL;
+ FcChar8 *file;
+ FcResult result;
+ FcPattern *match;
+ FcPattern *pattern = FcPatternCreate();
+ FcConfigSubstitute(NULL, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+ FcValue weight;
+ weight.type = FcTypeInteger;
+ weight.u.i = FC_WEIGHT_REGULAR;
+ FcPatternAdd(pattern, FC_WEIGHT, weight, FcFalse);
+ match = FcFontMatch(NULL, pattern, &result);
+ if (match && result == FcResultMatch) {
+ if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch) {
+ filepath = strdup((const char *)file);
+ }
+ }
+ FcPatternDestroy(pattern);
+ FcPatternDestroy(match);
+ return filepath;
+}
+
+char *
fontconfig_fontpath_find(struct Font *font, enum FontType font_type)
{
char *filepath = NULL;
diff --git a/fontconfig.h b/fontconfig.h
@@ -5,4 +5,5 @@ enum FontType {
FT_OTF
};
+char *fontconfig_fontpath_find_regular_font(void);
char *fontconfig_fontpath_find(struct Font *font, enum FontType font_type);
diff --git a/lorid.c b/lorid.c
@@ -70,11 +70,6 @@ main(int argc, char *argv[])
LOG_DEBUG("cho_songs_parse failed.");
return 1;
}
- struct ChoMetadata **m = songs[0]->metadata;
- while (*m) {
- printf("'%s' = '%s'\n", (*m)->name, (*m)->value);
- m++;
- }
char *pdf_filename = out_pdf_create(argc == optind+1 ? argv[argc-1] : NULL, output ? output : NULL, songs, config);
if (!pdf_filename) {
LOG_DEBUG("out_pdf_new failed.");
diff --git a/misc/ChordProSymbols.ttf b/misc/ChordProSymbols.ttf
Binary files differ.
diff --git a/out_pdf.c b/out_pdf.c
@@ -791,6 +791,59 @@ out_pdf_load_images(struct Obj ***images, pdfio_file_t *file, struct ChoSong **s
return true;
}
+static bool
+out_pdf_load_chord_diagram_fonts(void)
+{
+ struct Obj *fnt;
+ char *regular_font_filepath = fontconfig_fontpath_find_regular_font();
+ if (!regular_font_filepath) {
+ LOG_DEBUG("fontconfig_fontpath_find_regular_font failed.");
+ return false;
+ }
+ fnt = obj_new();
+ fnt->name = strdup("chord-diagram-regular-font");
+ fnt->value = pdfioFileCreateFontObjFromFile(g_pdf_file, regular_font_filepath, true);
+ free(regular_font_filepath);
+ objs_add_obj(&g_fonts, fnt);
+ fnt = obj_new();
+ fnt->name = strdup("chord-diagram-symbols");
+ fnt->value = pdfioFileCreateFontObjFromFile(g_pdf_file, "./misc/ChordProSymbols.ttf", true);
+ objs_add_obj(&g_fonts, fnt);
+ return true;
+}
+
+static bool
+out_pdf_get_chords(struct ChoSong **songs, char ***chords)
+{
+ struct ChoSong **s = songs;
+ struct ChoSection **se;
+ struct ChoLine **li;
+ struct ChoLineItemAbove **above;
+ char *name;
+ while (*s) {
+ se = (*s)->sections;
+ while (*se) {
+ li = (*se)->lines;
+ while (*li) {
+ above = (*li)->text_above;
+ while (*above) {
+ if ((*above)->is_chord) {
+ name = cho_chord_name_generate((*above)->u.chord);
+ printf("chord '%s'\n", name);
+ // TODO: Add name to chords
+ free(name);
+ }
+ above++;
+ }
+ li++;
+ }
+ se++;
+ }
+ s++;
+ }
+ return true;
+}
+
static double
line_width_until_text_above(
struct ChoLineItem **items,
@@ -1394,7 +1447,12 @@ pdf_content_create(
struct PDFText ***texts;
struct PDFImage ***imgs;
struct PDFContext ctx;
- double width, height;
+ double width, height, margin_bottom;
+ if (config->output->diagram->show) {
+ margin_bottom = 150.0;
+ } else {
+ margin_bottom = MARGIN_BOTTOM;
+ }
ctx.x = MARGIN_HORIZONTAL;
ctx.y = MEDIABOX_HEIGHT - MARGIN_TOP;
ctx.text = 0;
@@ -1542,7 +1600,7 @@ pdf_content_create(
ctx.y -= height;
}
}
- if (ctx.y < MARGIN_BOTTOM) {
+ if (ctx.y < margin_bottom) {
/* INFO: chords/annotations and their corresponding lyrics won't be splitted */
struct PDFText **tmp = NULL;
int tm = 0;
@@ -1636,7 +1694,7 @@ pdf_content_create(
left_items++;
}
ctx.y -= 8.0 + ctx.biggest_font_size;
- if (ctx.y < MARGIN_BOTTOM) {
+ if (ctx.y < margin_bottom) {
*texts = erealloc(*texts, (ctx.text+1) * sizeof(struct PDFText *));
(*texts)[ctx.text] = NULL;
*imgs = erealloc(*imgs, (ctx.image+1) * sizeof(struct PDFImage *));
@@ -1705,8 +1763,9 @@ pdf_content_free(struct PDFContent *content)
}
static bool
-pdf_content_render(struct PDFContent *content, pdfio_file_t *file)
+pdf_content_render(struct PDFContent *content, pdfio_file_t *file, struct Config *config)
{
+ int page = 1;
struct PDFPage **pages;
struct PDFText **texts;
struct PDFImage **imgs;
@@ -1743,17 +1802,30 @@ pdf_content_render(struct PDFContent *content, pdfio_file_t *file)
}
imgs++;
}
+ // TODO: Add chord diagrams to first page
+ /* if (page == 1) {
+ if (!chord_diagram_draw(stream, diagram, x, y, width)) {
+ LOG_DEBUG("chord_diagram_draw failed.");
+ return false;
+ }
+ } */
if (!pdfioStreamClose(stream)) {
LOG_DEBUG("pdfioStreamClose failed.");
return false;
}
pages++;
+ page++;
}
return true;
}
char *
-out_pdf_create(const char *cho_filepath, const char *output_folder_or_file, struct ChoSong **songs, struct Config *config)
+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);
@@ -1802,16 +1874,25 @@ out_pdf_create(const char *cho_filepath, const char *output_folder_or_file, stru
f++;
}
cho_fonts_free(needed_fonts);
+ if (!out_pdf_load_chord_diagram_fonts()) {
+ LOG_DEBUG("out_pdf_load_chord_diagram_fonts failed.");
+ return NULL;
+ }
if (!out_pdf_load_images(&img_objs, g_pdf_file, songs)) {
LOG_DEBUG("out_pdf_load_images failed.");
return NULL;
}
+ char **chords = NULL;
+ if (!out_pdf_get_chords(songs, &chords)) {
+ LOG_DEBUG("out_pdf_get_chords failed.");
+ return NULL;
+ }
struct PDFContent *pdf_content;
if (!pdf_content_create(&pdf_content, songs, config, img_objs)) {
LOG_DEBUG("pdf_content_create failed.");
return NULL;
}
- if (!pdf_content_render(pdf_content, g_pdf_file)) {
+ if (!pdf_content_render(pdf_content, g_pdf_file, config)) {
LOG_DEBUG("pdf_content_render failed.");
return NULL;
}